Skip to content

💥 Build the error propagation and partial output rules #318

Description

@taras

architecture.md (#314) locks the error model, and #316 aligned the vocabulary
to it. What the registry describes is still largely unbuilt. This issue builds
the Error propagation and Partial output sections.

Motivation

Three rules the architecture states are not true of main:

  • The error mode union has two members, not three. Every <Output> region
    installs print, so an undecided error in a region is printed and the run
    carries on — into the next region, the next <Elicit>, the next publish step.
    💥 Make <Output> fail fast and rename failure capture to CaptureErrors #309 has the operational account: a failed preview still reaches the step
    after it, and ✨ Bootstrap npm packages as a real xmd run: elicit the OTP #308 had to build a fail-closed protocol out of sentinel files
    and cat to say "show the operator what this stage produced, but stop".
  • A failing region renders nothing. Expansion hands its segments back only
    when it finishes, so everything a region produced before a failure is lost —
    it never reaches the output stream, and it is not in the journal either.
  • A run that fails is not a record. Nothing closes, so there is nothing to
    replay: the failure is an escape rather than an outcome.

What changes

1. The output error mode. The union becomes print | output | throw.
Every <Output> region installs output: an undecided error in a region fails
the run, and what the region rendered before the failure is preserved.
Documentation and value roots keep throw. The root default stays print.

2. <PrintErrors> under output. A printing boundary sets print for its
region — plain lexical context scoping — alongside the failure-translation
middleware it already installs. It does not override throw: documentation is
hidden, so a printed error there gives an author nothing to read.

No provenance marks, no decision-identity objects, no re-settlement at a
consumer boundary. Raise decides by value only, and the transitivity property
holds: wrapping <PrintErrors> — or any printErrors(fn) component, <File>
included — never changes what happens inside a nested <Output> region and
never resumes it past its stop. A failure that leaves a stopped region is a
propagating failure like any other, and the nearest printing boundary turns it
into exactly one printed error whose cause is the complete original failure.

3. Partial output. A failing region keeps what it rendered: everything
rendered before the failure stays in the document and reaches the output
stream, not only the journal — including when an earlier segment already
streamed. Only work the document was going to render reaches the output: a
failing as= binding, value production, or documentation adds nothing.

4. Outcomes. A run that fails is a complete record: replaying it restores
the output and the failure without re-executing anything. The journal is
parsed, and an unreadable record is refused with a message naming the
situation. Only a durability failure means the journal no longer describes the
run and escapes it. The state that supports this is scoped to the operation
that owns it.

5. A printed error is data in both directions. A printed error a child
produced, consumed by a parent's documentation, does not stop the rest of the
child's rendering and does not fail the parent. It was decided once, where it
was raised; the consumer reads data. A failure, uncaptured, still propagates.
This replaces the settle-again pass a component invocation runs today.

Out of scope

<Retry>, <Result>, suspension and the error middleware Js api stay defined
and unbuilt. So does the state-ownership sweep over the remaining
module-scoped registries.

Test discipline

Every behavior gets a test that discriminates — red on main, or red under a
stated mutation. Three scenarios that have hidden defects before are required:

  • <PrintErrors> wrapping a component with an <Output> region of its own:
    the nested region still stops, and nothing after its failure runs.
  • The same with <File> as the wrapper.
  • A streamed prefix before a failing component: the output arrives as chunks,
    not only in the close value.

Spec sections describing the old region behavior are rewritten in the same PR,
in registry vocabulary.

Context: #314, #316, and the closed #312.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions