Skip to content

symfonic.capabilities.prompting.procedural

procedural

What a scope has learned to do, put in front of the model.

Phase 12 promotes a repeated action into a draft skill and a human approves it. Approval is what makes a skill active; this is what makes an active skill reach the turn. Without it the promotion is storage: a row an operator can list and a model never sees, which is the distinction between a memory system and a table.

It renders untrusted, and it does not have to ask. untrusted is left at the default, so _tier_for places this on a learned tier inside the untrusted-data wrapper. That is the correct answer rather than a cautious one: a skill's text is derived from what a user said across earlier turns, so rendering it at an authored tier would let a turn's phrasing become an instruction the deployment appears to have written -- across conversations, and after the fact. The one prompt-injection path with a delay built in.

Approved only. Drafts are what the offline path writes for review, and a draft in the prompt would be a skill acting before anyone approved it. The default read asks for active skills; a deployment that wants to preview drafts passes include_drafts and is choosing that.

Awaited, because the store is. Declared through aread rather than read: a procedural layer is a store, and a source that pretended otherwise would have to block a running loop to answer.

ProceduralSkillsSource dataclass

ProceduralSkillsSource(layer: Any, scope: Any, cue: str = '', limit: int = DEFAULT_LIMIT, include_drafts: bool = False, untrusted: bool = True, offline_safe: bool = False, scope_aware: bool = True)

The active skills for a turn's scope, rendered for the prompt.

aread async

aread(request: SourceRequest) -> SourceRead

The skills this scope may act on, or nothing at all.

A store fault renders an empty block rather than raising. The turn has an answer to give and a missing procedure block costs the model a hint; a raised exception costs the user the turn.

Source code in src/symfonic/capabilities/prompting/procedural.py
async def aread(self, request: SourceRequest) -> SourceRead:
    """The skills this scope may act on, or nothing at all.

    A store fault renders an empty block rather than raising. The turn has
    an answer to give and a missing procedure block costs the model a hint;
    a raised exception costs the user the turn.
    """
    from symfonic.memory.layers.procedural.skill_render import (
        render_skill_for_prompt,
    )

    try:
        skills = await self.layer.query_skills(
            self.scope,
            self.cue,
            top_k=self.limit,
            include_drafts=self.include_drafts,
        )
    except Exception:  # noqa: BLE001 - a store fault is not a turn fault
        skills = []

    rendered = [render_skill_for_prompt(skill) for skill in skills or ()]
    body = "\n".join(line for line in rendered if line and line.strip())
    text = f"{HEADING}\n{body}" if body else ""
    return SourceRead(
        text=text,
        revision=_digest(text, request.contribution_id),
        untrusted=self.untrusted,
    )