Skip to content

💥 Build the error propagation and partial output rules - #319

Merged
taras merged 3 commits into
mainfrom
feat/error-model-semantics
Aug 4, 2026
Merged

💥 Build the error propagation and partial output rules#319
taras merged 3 commits into
mainfrom
feat/error-model-semantics

Conversation

@taras

@taras taras commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Closes #318. Builds the Error propagation and Partial output sections of
architecture.md (#314), on the vocabulary #316/#317 settled.

Why

<Output> shows an operator what a stage produced. On main it also lets a
failed stage reach the step after it: every region installs print, so an
undecided error becomes a comment and the run continues into the next region,
the next <Elicit>, the next publish step. #308 had to build a fail-closed
protocol out of sentinel files and cat to say "show me this, then stop".

Two more architecture rules were unbuilt: a failing region rendered nothing at
all, and a run that failed was an escape rather than an outcome — nothing
closed, so nothing replayed.

What changes

Before: an error inside <Output> prints and the document carries on; a
region that fails loses everything it rendered; a failed run has no record.

After:

  • The union is print | output | throw. Every <Output> region installs
    output. Documentation and value roots keep throw; the root default stays
    print.
  • A failing region keeps what it rendered — in the document, on the output
    stream, and in the journal — while a binding, a value, a string projection,
    and documentation each keep a private buffer and promote nothing.
  • A run that fails returns an outcome, and the root closes ok around it, so a
    replay restores both halves without re-executing anything.
  • A printed error crosses an invocation as data. A child's printed error read by
    a parent's documentation no longer stops the child's rendering and no longer
    fails the parent. A failure, uncaptured, still propagates.

How it works

raise(segment) → settle() reads the nearest error mode → printed, or a
DocumentationError carrying the mode that decided it → propagates → the
nearest printing boundary prints an `output` decision, never a `throw` one

One decision, by value. settle() is the only place a mode is consulted.
DocumentationError records which of the two failing modes decided it, because
that is the whole difference between them at a printing boundary — architecture
§3 says output "fails the run; <PrintErrors> can print instead" and throw
"fails the run, even inside <PrintErrors>". There are no provenance marks on
segments, no decision-identity objects, and the consumer boundary no longer
settles anything a second time.

Transitivity falls out of context scoping. useFailurePrinting() sets
print for its region alongside the failure-translation middleware it already
installed. A nested <Output> region sets output for itself and shadows it, so
a component's region behaves identically whether or not a boundary is written
around it. The middleware is inherited, but the mode at the point of raise is the
nested region's — which is why nothing else is needed to keep a wrapper from
resuming a callee's stop.

Output ownership. Expansion writes into the accumulator its caller gave it
(expandSegments' existing accumulator parameter, now owner), so a region that
stops part-way has already handed the prefix over. A call site producing a
binding, a value, or a string passes none.

Review guide

Start with: packages/core/src/errors.ts — the mode union, settle(), and
decidedByOutput.

Then review:

  1. packages/core/src/component-failures.ts — the two halves of a printing
    boundary, and why it does not override throw.
  2. packages/core/src/expand.tsexpandBody installs output; the owner
    threads through every producer; the consumer settle-again pass is gone; the
    invocation boundary decides whether a boundary may print.
  3. packages/core/src/execute.ts — the outcome record, the live-failure slot,
    and the parsing that refuses a record it cannot read.
  4. specs/executable-mdx-spec.md §6.8.1 and §6.9.

Look carefully at: the one deliberate behavior change — removing the
consumer settle-again pass — and the four existing tests it moved.

What must stay true

  • Decided once, where raised. Enforced by settle() being the only mode
    reader and by no consumer re-settling; checked by OM18/OM19 and the
    consumer-boundary tests in expand.test.ts.
  • A wrapper never resumes a callee's region. Enforced by mode scoping;
    checked by OM14 (<PrintErrors>), OM15 (<File>), OM15b (a printing
    component that does not recover).
  • throw is never printed. Enforced by the !== "throw" guard in
    useFailurePrinting and by decidedByOutput; checked by OM5e.
  • Only work the document would render reaches the output. Enforced by the
    owner being withheld at every private-buffer call site; checked by OM11a–e.
  • A durability failure is never the document's outcome. Enforced by the
    durabilityFailure check preceding the outcome in documentWorkflow;
    checked by the existing Tier FA and <TempDir> tests.

How to verify it

packages/core/tests/output-error-mode.test.ts — Tier OM, 52 tests.
36 are red on origin/main (verified in a throwaway worktree at 3d5edab):
OM1–OM4, OM6b, OM7, OM8, OM9a–e, OM11a–c, OM11e, OM12a–j, OM13a–e, OM14, OM15,
OM15b, OM17, OM18.

The rest are preservation tests, discriminated by mutation instead. Every
mutation was run, and each reddens exactly the tests that claim its behavior:

Mutation Reddens
the printing boundary does not set the mode OM5a, OM5c, OM5d, OM16, OM18
the printing boundary overrides throw too OM5e
a producer writes into the owner and hands its segments back OM10a
an as= invocation renders into the caller's owner OM11e
documentation renders into the owner OM11d
decidedByOutput always answers no OM15b

A seventh mutation — keeping decidedByOutput as a second guard on the
fatalCause rethrow at the invocation boundary — reddened nothing across
the whole suite, so that branch was removed rather than kept unguarded. The
decision now lives in one place.

The three scenarios that have hidden defects before each have a test: OM14
(<PrintErrors> around a component with its own region), OM15 (the same with
<File>), and OM17 (a streamed prefix before a failing component, asserted on
the chunks rather than the close value).

Existing tests changed, and why

Each is #318's settled contract, not a test bent to fit:

Test Change
expand.test.ts "keeps errors inside an <Output> region as comments" split: ordinary → fails; under <PrintErrors> → still a comment, region continues
expand.test.ts child/content errors consumed inside a parent <Output> split the same way, plus new uncaptured counterparts that fail
execute.test.ts "emits no partial output…" inverted: the selection is emitted, and the run fails
eval-error-mode.test.ts O24, O29 restated as fail-fast; O31 moved under <PrintErrors>
loop.test.ts LOOP44, LOOP46 the root close is ok around the failed outcome
fatal-cause.test.ts, invocation-failures.test.ts DocumentationError now takes the deciding mode

O29 also confirmed a pre-existing limit worth knowing: <Content /> is only
claimed at a region's top level, so it cannot be nested inside <PrintErrors>.
The fixture asserts fail-fast rather than working around it.

Ratified-document edits

Two clauses in architecture.md, each because this PR made the old text untrue.
Both are flagged rather than folded into the diff, because that document is the
registry:

  • Construct inventory — the <Output> region output mode row moves from
    "defined, unbuilt — on main a region prints and continues" to "built on main".
  • Two layers — the clause sorted constructs into context values and
    middleware and filed <PrintErrors> under middleware alone. A boundary uses
    both forms: it sets print as a context value for its region and installs the
    failure-translation middleware. The pairing is what transitivity rests on —
    the mode is what a nested <Output> shadows, the middleware is inherited and
    shadows nothing.

Nothing else in architecture.md is touched, and no term is added to the
Terminology registry.

Scope

Included

  • The output error mode, and <PrintErrors> under it.
  • Partial output, and the ownership that keeps private buffers out of it.
  • The failed-document outcome, its replay, and the parsing that refuses a
    malformed record.
  • §6.8.1 and §6.9 rewritten; architecture.md's inventory row updated; a Tier OM
    table added.
  • A follow-up commit renames the internal useFailurePrinting() to
    usePrintErrors(), so the boundary's three surfaces share one stem —
    <PrintErrors>, printErrors(fn), usePrintErrors(). Registered vocabulary
    only, no new terms, no behavior change; mod.ts still exports printErrors.

Intentionally unchanged

  • <Retry>, <Result as>, suspension, and the error middleware Js api stay
    defined and unbuilt.
  • The state-ownership sweep over the remaining module-scoped registries — the
    segmentCauses map, the Ajv compilers, the props/returns caches — is the next
    PR. Only the state this PR adds (the live-failure slot) is operation-scoped.
  • <File>'s own content recovery is untouched: it still turns a content failure
    into its own failure before the boundary sees it, which is why OM15 asserts
    through <File> and OM15b asserts the relaying shape <Parse> has.

New abstractions

  • DocumentationError.mode exists because a printing boundary has to tell the
    two failing modes apart, and the failure is the only thing that survives to
    the point where that is asked.
  • decidedByOutput(failure) names that question once, at its single call site.
  • DocumentResult becomes a union, and LiveFailure is the slot the workflow's
    catch hands its failure to the completion through, so a live run reports the
    object it caught and a replayed one reports what the record describes.
  • Each new abstraction has multiple concrete uses or a clear justification.
  • No speculative functionality is included.

Risks and limitations

  • This is a breaking change for any document relying on a region continuing
    past an error.
    The migration is one construct: wrap the work in
    <PrintErrors>.
  • Journals written before this contract cannot be replayed. A record with no
    status is refused with a message that says so and names 💥 Build the error propagation and partial output rules #318 (OM12i).
  • Recovery or rollback: revert the commit; the journal format change is additive
    in shape but the refusal above makes an old journal explicit rather than
    silently misread.

Verification

Runtime asserted first (Deno 2.9.1), one deno task setup, nothing reinstalled
after:

Gate Result
deno task lint 0 errors, formatting clean
deno task check 0 errors
deno task test 344 passed, 0 failed
deno task check:jsr Success Dry run complete
pnpm exec tsc --project tsconfig.node.json --noEmit clean
compiled-binary smoke (deno task build + the CI smoke commands) pass, including the test-agent worker and the <WebForm> preflight

site/ is untouched, so its checks did not run.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

Every `<Output>` region installs the `output` error mode: an undecided
error in a region fails the run, what the region rendered before the
failure reaches the output stream, and nothing after it begins.

A printing boundary sets `print` for its own region and prints a failure
that reaches it, so wrapping one around a component whose body declares
`<Output>` changes nothing inside that component. A printed error is
decided once, where it is raised, and crosses an invocation as data.

A run that fails is a complete record: the workflow returns its outcome,
the root closes around it, and a replay restores the output and the
failure without re-executing anything. The record is parsed, and a shape
this version cannot read is refused with a message naming the situation.

Closes #318.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 7 redundant comments. Inline suggestions to remove them below.

// rendered before stopping.
const rendered: Segment[] = [];
// rendered before stopping. When the caller owns a region, that array is
// the region itself and the prefix is already where the document needs it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// the region itself and the prefix is already where the document needs it.

}
return [...options.errors, ...result.segments];
// A projection that wrote into the caller's region has nothing left to
// hand back; one that kept its own returns what it rendered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// hand back; one that kept its own returns what it rendered.

// errors it creates, and the selected branch settled its own (§6.9).
result.push(...(yield* expandIf(segment, parentMeta, parentProps, hideSet, counter)));
// It renders into this expansion's output, so it writes into the owner
// rather than handing segments back to be appended.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// rather than handing segments back to be appended.

// was decided once, where it was raised, under the error mode governing
// the region that raised it (§6.9). A rendering invocation wrote it
// straight into this owner; anything handed back — a binding the callee
// refused, an error about the invocation itself — is appended as it is.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// refused, an error about the invocation itself — is appended as it is.

const out: Segment[] = [];
// A rendering iteration writes into the caller's region as it goes, so a
// failure partway leaves the items it already produced behind. A captured one
// builds a value instead: its buffer is private and never becomes output.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// builds a value instead: its buffer is private and never becomes output.


return expanded;
// A rendering body already wrote into the owner, so there is nothing left to
// hand back; one that kept its own returns what it rendered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// hand back; one that kept its own returns what it rendered.

// propagating failure — the region already stopped, and printing what
// left it is what a boundary is for. The region's own failure travels
// on, not the segments it transported: reporting those again would
// print an error the region already decided about.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// print an error the region already decided about.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

PR #319: 💥 Build the error propagation and partial output rules

16 files, +1885 / -280

Scope

🔴 PR has 2165 lines changed. Split into focused PRs.

🟡 2165 lines changed. PRs under 400 receive more thorough review.

Structural

🟡 Type declarations with no consumers: ExpandSegments.
Symbol Declared at Refs in diff Why flagged
ExpandSegments packages/core/src/answers.ts:92 1 referenced ≤1× within the added diff (pre-existing usages not counted)

Slop

  • packages/core/src/execute.ts:355// nowhere useful, and what this records is what this failure was given.
  • packages/core/src/execute.ts:440// quietly disagrees with the one recorded.
  • packages/core/src/execute.ts:554// root fills selected; every streaming root has already emitted streamed.
  • packages/core/src/execute.ts:588// this frame holding what the regions rendered before it.
  • packages/core/src/execute.ts:612// failure is reported, exactly as a buffered root does.
  • packages/core/src/execute.ts:647// as an earlier segment went out.
  • packages/core/src/execute.ts:662// streaming loop emitted together with that tail.
  • packages/core/src/execute.ts:821// it rendered first reaches consumers before its failure does.
  • packages/core/src/expand.ts:268// the region itself and the prefix is already where the document needs it.
  • packages/core/src/expand.ts:302// hand back; one that kept its own returns what it rendered.
  • packages/core/src/expand.ts:658// refused, an error about the invocation itself — is appended as it is.
  • packages/core/src/expand.ts:954// builds a value instead: its buffer is private and never becomes output.
  • packages/core/src/expand.ts:1910// hand back; one that kept its own returns what it rendered.
  • packages/core/src/expand.ts:2276// print an error the region already decided about.

Static Analysis

✅ Oxlint found no issues.

Correctness

No extraneous code patterns detected.

taras added 2 commits August 4, 2026 14:01
The boundary has three surfaces, and they should share one name stem:
`<PrintErrors>` declares it in a document, `printErrors(fn)` marks a
component, and `usePrintErrors()` installs it in scope.
`useFailurePrinting` named the activity instead of the construct.

Internal only — `printErrors` is what `mod.ts` exports, and this name
never left the package. No behavior change.
The clause sorted constructs into context values and middleware, and
filed `<PrintErrors>` under middleware alone. A printing boundary uses
both: it sets `print` as a context value for its region, and installs
the failure-translation middleware.

The pairing is what transitivity rests on. The mode is what a nested
`<Output>` region shadows; the middleware is inherited and shadows
nothing — which is why wrapping a boundary around a component whose body
declares `<Output>` changes nothing inside it. Spec §6.8.1 already says
so; this is the registry catching up.

One clause. No other change to architecture.md.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 14 redundant comments. Inline suggestions to remove them below.

: { source: documentation.segment.source }),
},
// An own property, not an inherited one: every Error inherits `cause` from
// nowhere useful, and what this records is what this failure was given.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// nowhere useful, and what this records is what this failure was given.

}
// An optional field is absent or well-formed. Anything else is a journal this
// run cannot read, and coercing it to "absent" would report a failure that
// quietly disagrees with the one recorded.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// quietly disagrees with the one recorded.


// What the document rendered before it stopped, held outside the expansion
// scope so a failure still leaves it here (§6.9 Partial output). The buffered
// root fills `selected`; every streaming root has already emitted `streamed`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// root fills `selected`; every streaming root has already emitted `streamed`.

// so no partial output is produced.
// execute the whole body, then emit the selected regions once. The owner is
// allocated outside this expansion so that a failure partway still leaves
// this frame holding what the regions rendered before it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// this frame holding what the regions rendered before it.


// The loop owns the segments, so a component whose own region fails partway
// has still handed over what it rendered — the root emits that before the
// failure is reported, exactly as a buffered root does.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// failure is reported, exactly as a buffered root does.

}
return [...options.errors, ...result.segments];
// A projection that wrote into the caller's region has nothing left to
// hand back; one that kept its own returns what it rendered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// hand back; one that kept its own returns what it rendered.

// was decided once, where it was raised, under the error mode governing
// the region that raised it (§6.9). A rendering invocation wrote it
// straight into this owner; anything handed back — a binding the callee
// refused, an error about the invocation itself — is appended as it is.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// refused, an error about the invocation itself — is appended as it is.

const out: Segment[] = [];
// A rendering iteration writes into the caller's region as it goes, so a
// failure partway leaves the items it already produced behind. A captured one
// builds a value instead: its buffer is private and never becomes output.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// builds a value instead: its buffer is private and never becomes output.


return expanded;
// A rendering body already wrote into the owner, so there is nothing left to
// hand back; one that kept its own returns what it rendered.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// hand back; one that kept its own returns what it rendered.

// propagating failure — the region already stopped, and printing what
// left it is what a boundary is for. The region's own failure travels
// on, not the segments it transported: reporting those again would
// print an error the region already decided about.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// print an error the region already decided about.

@taras
taras merged commit f413545 into main Aug 4, 2026
10 checks passed
taras added a commit that referenced this pull request Aug 4, 2026
The adversarial implementation workflow target was written against a main
that predates the error-model rules (#315), the error vocabulary rename
(#317), error-model semantics (#319), and operation-scoped state (#325).
It asserted `<CollectFailures>` as structural syntax, "the unresolved
diagnostic", and a durable layer that "replays recorded results" — none
of which describe main. This re-derives it on 7d7bdf2.

The error model reaches the documents. A stage component is split by its
`<Output>` boundary: the region inside runs under the `output` error mode,
everything outside is documentation and runs under `throw`, which no
`<PrintErrors>` region replaces. So a stage returns a complete validated
result or it fails, keeping only what it had already rendered — the final
`<Parse>` in each repair loop is a real gate. `throwOnError` is
load-bearing for the same reason: without it a failed prompt records its
failure and returns its text, raising nothing to decide.

The markup did not run. Every stage passed props through expression props
as `agent={props.planner}`, which fails on main with `props is not
defined` — an expression prop reads the bare binding while text
interpolation reads the namespace. Unifying them is #305, whose
acceptance includes expression props reading `props.name`. 22 sites are
corrected to the spelling main supports, and the asymmetry is recorded
with the issue that removes it.

Vocabulary is collapsed onto the concepts #289, #291, and #298 authorize:
artifact ledger, artifact version, run identity, pinned source revision,
stop reason, terminal record, stage boundary, declared inputs, and
cross-process continuation, in place of the four names these files used
for a ledger and the three for a run. Missing capabilities now cite the
issue that supplies them rather than saying only "not implemented", and
replay is described as reaching the state execution resumes from, never
as the continuation itself.

Planning-loop exhaustion stays open. It is recorded against #290, which
pins the behavior; this change reports `verdict.passed` and does not call
an exhausted loop converged.

Evidence: `inspectDocument` parses all 9 frontmatters and compiles both
schema kinds; `compileParseSchema` compiles all 5 embedded draft-07
schemas; `inspectComponent` resolves 21 shipped and 5 repository names
and confirms 9 missing ones unresolved; `InstructionFiles` runs end to
end against the repository's own AGENTS.md.
taras added a commit that referenced this pull request Aug 4, 2026
The adversarial implementation workflow target was written against a main
that predates the error-model rules (#315), the error vocabulary rename
(#317), error-model semantics (#319), and operation-scoped state (#325).
It asserted `<CollectFailures>` as structural syntax, "the unresolved
diagnostic", and a durable layer that "replays recorded results" — none
of which describe main. This re-derives it on 7d7bdf2.

The error model reaches the documents. A stage component is split by its
`<Output>` boundary: the region inside runs under the `output` error mode,
everything outside is documentation and runs under `throw`, which no
`<PrintErrors>` region replaces. So a stage returns a complete validated
result or it fails, keeping only what it had already rendered — the final
`<Parse>` in each repair loop is a real gate. `throwOnError` is
load-bearing for the same reason: without it a failed prompt records its
failure and returns its text, raising nothing to decide.

The markup did not run. Every stage passed props through expression props
as `agent={props.planner}`, which fails on main with `props is not
defined` — an expression prop reads the bare binding while text
interpolation reads the namespace. Unifying them is #305, whose
acceptance includes expression props reading `props.name`. 22 sites are
corrected to the spelling main supports, and the asymmetry is recorded
with the issue that removes it.

Vocabulary is collapsed onto the concepts #289, #291, and #298 authorize:
artifact ledger, artifact version, run identity, pinned source revision,
stop reason, terminal record, stage boundary, declared inputs, and
cross-process continuation, in place of the four names these files used
for a ledger and the three for a run. Missing capabilities now cite the
issue that supplies them rather than saying only "not implemented", and
replay is described as reaching the state execution resumes from, never
as the continuation itself.

Planning-loop exhaustion stays open. It is recorded against #290, which
pins the behavior; this change reports `verdict.passed` and does not call
an exhausted loop converged.

Evidence: `inspectDocument` parses all 9 frontmatters and compiles both
schema kinds; `compileParseSchema` compiles all 5 embedded draft-07
schemas; `inspectComponent` resolves 21 shipped and 5 repository names
and confirms 9 missing ones unresolved; `InstructionFiles` runs end to
end against the repository's own AGENTS.md.
taras added a commit that referenced this pull request Aug 10, 2026
The adversarial implementation workflow target was written against a main
that predates the error-model rules (#315), the error vocabulary rename
(#317), error-model semantics (#319), and operation-scoped state (#325).
It asserted `<CollectFailures>` as structural syntax, "the unresolved
diagnostic", and a durable layer that "replays recorded results" — none
of which describe main. This re-derives it on 7d7bdf2.

The error model reaches the documents. A stage component is split by its
`<Output>` boundary: the region inside runs under the `output` error mode,
everything outside is documentation and runs under `throw`, which no
`<PrintErrors>` region replaces. So a stage returns a complete validated
result or it fails, keeping only what it had already rendered — the final
`<Parse>` in each repair loop is a real gate. `throwOnError` is
load-bearing for the same reason: without it a failed prompt records its
failure and returns its text, raising nothing to decide.

The markup did not run. Every stage passed props through expression props
as `agent={props.planner}`, which fails on main with `props is not
defined` — an expression prop reads the bare binding while text
interpolation reads the namespace. Unifying them is #305, whose
acceptance includes expression props reading `props.name`. 22 sites are
corrected to the spelling main supports, and the asymmetry is recorded
with the issue that removes it.

Vocabulary is collapsed onto the concepts #289, #291, and #298 authorize:
artifact ledger, artifact version, run identity, pinned source revision,
stop reason, terminal record, stage boundary, declared inputs, and
cross-process continuation, in place of the four names these files used
for a ledger and the three for a run. Missing capabilities now cite the
issue that supplies them rather than saying only "not implemented", and
replay is described as reaching the state execution resumes from, never
as the continuation itself.

Planning-loop exhaustion stays open. It is recorded against #290, which
pins the behavior; this change reports `verdict.passed` and does not call
an exhausted loop converged.

Evidence: `inspectDocument` parses all 9 frontmatters and compiles both
schema kinds; `compileParseSchema` compiles all 5 embedded draft-07
schemas; `inspectComponent` resolves 21 shipped and 5 repository names
and confirms 9 missing ones unresolved; `InstructionFiles` runs end to
end against the repository's own AGENTS.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💥 Build the error propagation and partial output rules

1 participant