Skip to content

symfonic.agent.cutover.prompt_block_contract

prompt_block_contract

C1-F and C1-O: what a prompt block becomes on the kernel line (TA8.38).

Fourteen rows -- prompt_blocks and the thirteen members of :class:~symfonic.core.prompt.blocks.spec.PromptBlockSpec that TA8.25 counted separately -- and this module is the written argument for why TA8.38 did not apply their recorded NORMALISED_PER_ROW disposition as "route the retained specs through the kernel prompting contribution".

TA8.51 supersedes half of this argument, and the half it supersedes is named here rather than left for a reader to notice. TA8.38's override rested on two facts: the port could not carry these rows, and admitting the field without a port would accept specs and resolve none of them. S01 (TA8.51) built the port -- AsyncContributionSource.aread beside the unchanged ContributionSource.read, awaited by :func:~symfonic.capabilities.prompting.compiler.compile_prompt_async, driven per row on the kernel's run, stream and stream_typed doors -- so the first fact is no longer true and this module no longer claims it.

The second fact stands, and it is now the whole reason these rows are still MISSING CONTRACT: a port is not an admission. Nothing yet routes config.prompt_blocks into a PromptContribution, no per-row parity has been measured between a PromptBlockSpec and the contribution it maps onto, and ALLOWED_FRAMEWORK_FIELDS is unchanged. Admitting the field on the strength of "the seam exists" would assert that the migrated path honours the value, which is the session_id mistake with a newer excuse. The follow-up that owes that work is named in :func:async_seam_argument.

Historical note, kept because the mapping below was written against it. The original mismatch was real: ContributionSource.read(SourceRequest) was an ordinary synchronous call while :class:~symfonic.core.prompt.blocks.protocol.BlockSource declared async def load(scope, block_id) and PromptBlockResolver awaited it. TA8.38 refused to route the rows across that gap rather than pretending it was not there.

The rule neither lane will break is the one TA8.19 found broken for session_id: a value threaded to nothing is not migrated. Putting prompt_blocks on :data:~symfonic.agent.cutover.policy.ALLOWED_FRAMEWORK_FIELDS today would assert that the migrated path honours it at any value, and no route reads it at any value -- :meth:SymfonicAgent._build_block_injector is reached from the legacy prompt paths only, and the kernel route compiles from the bundle's single HmsSystemSource. The specs would be accepted, validated, and never resolved.

The capability was written down before it was wired, which is what let TA8.51 start from a mapping rather than from thirteen field descriptions. :data:PROMPT_BLOCK_CONTRACT names, per row, the member of the kernel contract that carries the same meaning. That is the "write the new contract before the code depends on it" half of a REPLACE, published ahead of a migration TA8.38 could not finish. Four rows had no counterpart when it was published and said so in those words rather than being quietly dropped -- the honest output of a mapping exercise is the holes it found. TA8.51 built the four members those holes named, so every row now maps onto a member that exists, and :data:SEAM_BUILT_PROMPT_BLOCK_ROWS keeps the record of which four had to be built rather than found. That set is the cost of the move, recorded rather than absorbed.

What TA8.38 did move. Before it, all fourteen rows recorded unknown on run and stream and had no stream_typed measurement. They are driven on all three and record refused-to-legacy by name -- a decision rather than an absence. That refusal is the envelope's default-deny, not a refusal contract in TA8.26's sense; calling it one would be the "silence, not a refusal" mistake.

async_seam_argument

async_seam_argument() -> str

The override argument, in one place, for every row that cites it.

A function rather than fourteen copies of a paragraph: the argument is one argument, and fourteen spellings of it would be fourteen chances for the reason to drift from the evidence.

Rewritten by TA8.51, because its first half stopped being true. TA8.38 wrote this when the port could not carry these rows at all; S01 built the port, so the sentence "the contract does not have an asynchronous source seam" would now be a false statement standing as fourteen rows' recorded reason. What survives unchanged is the half that actually keeps the rows refused: a port is not an admission, and nothing yet routes the configuration value through it.

Source code in src/symfonic/agent/cutover/prompt_block_contract.py
def async_seam_argument() -> str:
    """The override argument, in one place, for every row that cites it.

    A function rather than fourteen copies of a paragraph: the argument is one
    argument, and fourteen spellings of it would be fourteen chances for the
    reason to drift from the evidence.

    **Rewritten by TA8.51, because its first half stopped being true.** TA8.38
    wrote this when the port could not carry these rows at all; S01 built the
    port, so the sentence "the contract does not have an asynchronous source
    seam" would now be a false statement standing as fourteen rows' recorded
    reason. What survives unchanged is the half that actually keeps the rows
    refused: a port is not an admission, and nothing yet routes the
    configuration value through it.
    """
    return (
        "the kernel prompting contribution port grew its asynchronous half in "
        "TA8.51 (S01): AsyncContributionSource.aread sits beside the unchanged "
        "ContributionSource.read, compile_prompt_async awaits it, and all "
        "fourteen rows are driven per row on the kernel's run, stream and "
        "stream_typed doors with the body newly appearing in the compiled "
        "prompt. That closes the seam TA8.38 recorded as missing and closes it "
        "only. These rows stay MISSING CONTRACT because a port is not an "
        "admission: nothing routes config.prompt_blocks into a "
        "PromptContribution, no per-row parity has been measured between a "
        "PromptBlockSpec member and the contribution member it maps onto, and "
        "ALLOWED_FRAMEWORK_FIELDS is unchanged. Admitting the field on the "
        "strength of the seam alone would assert that the migrated path honours "
        "the value at any value, which is the silent drop TA8.19 found for "
        "session_id wearing a newer excuse. The rows therefore stay refused to "
        "legacy, where they are still served; the contract they move to is "
        "published above; and the work that would move them is "
        f"{ADMISSION_FOLLOW_UP}"
    )