0069: Pipeline Utilities — Fan-Out Degrade Contribution Refinements¶
- Status: Accepted
- Author: Chris Colinsky
- Created: 2026-06-15
- Accepted: 2026-06-16
- Targets: spec/pipeline-utilities/spec.md (§9.3 — three refinements to the fan-out degrade-contribution rules 0066 introduced: (1) correct the omitted-
extra_outputsrule from "not contributed / like a skipped heterogeneous branch field" to null at the instance's positional slot, mirroringcollect_field's null slot and preserving index-alignment withtarget_field; (2) add a SHOULD that a non-degradeinstance_middlewarereturn coverscollect_field, with an absentcollect_fieldon any fan-in path yielding a null slot and the fan-in MUST NOT raise on it — generalizing §9.8's degrade-never-raises to all paths; (3) a new conformance fixture pinning a degraded instance's slot across a checkpoint + resume round-trip) - Related: 0066 (the §9.3 degrade-contribution model + §9.8 compile check these refine), 0050 (
FailureIsolationMiddleware, §6.3), 0009 / 0036 (fan-out collection +instance_middleware, §9.3 / §9.7), 0008 (checkpointing — the resume path the new fixture exercises), graph-engine §2 (reducers —extra_outputsmerge in instance-index order) - Supersedes: 0066 (the §9.3 omitted-
extra_outputs"not contributed" clause only; the rest of 0066 — the degrade-is-the-contribution model, the §9.8collect_fieldcompile check, and the §11.7 branch skip — stands unchanged)
Summary¶
0066 settled what a FailureIsolation-degraded fan-out instance contributes:
the degraded instance is a §9.3 success whose contribution is its
degraded_update, with collect_field and extra_outputs read from it by
subgraph field name; a static degraded_update omitting collect_field is a
compile error (§9.8); a callable one yields a graceful null slot. Wiring a
reference implementation against that contract surfaced three small follow-on
points 0066 left imprecise or unpinned. This proposal resolves all three:
extra_outputsomission is a null value, not a "skip." 0066's §9.3 says an omittedextra_outputssource is "not contributed … the same shape as a skipped heterogeneous branch field." That analogy is wrong for fan-out: a degraded instance contributes its value in instance-index order like any other (§9.4), so an omission should be a null value (matchingcollect_field's null), not absence. For an extending reducer, this keeps the field aligned withtarget_field; under other reducers a null and an absent contribution merge alike — sonullis the uniform, safe rule. A "skip" would shorten an extending-reducer field and misalign it.- An absent
collect_fieldnever raises, on any path. §9.8 pins the callable-degrade case to a graceful null slot. The same gracefulness should hold uniformly: a non-degradeinstance_middlewarereturn SHOULD covercollect_field, and an absent one — by any route — yields a null slot rather than a fan-in raise (a raise would stop the graph underfail_fast, the outcome 0066 deliberately avoided). - Degrade survives resume. A degraded instance is a completed instance, so its slot (including a null slot) rolls forward on a checkpoint resume — but no conformance case exercises it. A fixture pins it.
Motivation¶
All three are about a single principle 0066 established but did not carry all
the way through: a fan-out collection is homogeneous and positional, and the
degrade path is graceful (never raises). 0066 applied that to collect_field
(null slot, compile-guarded for the static footgun) but described extra_outputs
with a heterogeneous-branch analogy that contradicts it, left the
non-degrade-absent-collect_field case unstated, and pinned neither the
extra_outputs slot nor the resume round-trip with a fixture. The result is that
two conformant implementations could disagree on extra_outputs shape
(null-at-slot vs. shortened list) or on whether an absent collect_field off the
degrade path raises. These are the cross-impl-consistency gaps that surface only
when an implementation wires the contract end-to-end.
Detailed design¶
Anticipated bump: MINOR (pre-1.0); concrete version assigned at acceptance. All changes are in pipeline-utilities §9.3 plus one conformance fixture.
1. extra_outputs omission → null value, not a skip (§9.3)¶
0066's §9.3 currently ends:
When the
degraded_updatedoes not supply anextra_outputssubgraph_field, that field is simply not contributed by this instance — the same partial-contribution shape as a skipped heterogeneous branch field (§11.7).
This is superseded. §9.3 merges each extra_outputs field across instances
via the parent's reducer in instance-index order (§9.4), the reducer
accepting the value type the subgraph produces — so a degraded instance
contributes a value in its index position like any other, not a
presence/absence flag. An omission should therefore be a null value,
mirroring collect_field's null, not a dropped contribution. The replacement
clause:
When the
degraded_updatedoes not supply anextra_outputssubgraph_field, that instance contributes null as its value for the mapped parent field — rather than being absent — merged in instance-index order like any other instance's value (§9.4), the same null treatment as an omittedcollect_field(above). For an extending reducer (e.g.append) this keeps the field index-aligned withtarget_field; under other reducers a null and an absent contribution merge alike, sonullis the uniform rule. (This is not a heterogeneous branch skip: parallel-branchoutputsare distinct parent fields with no per-instance value, §11.7.)
2. Absent collect_field is graceful on every path (§9.3)¶
§9.8 specifies that a callable degraded_update omitting collect_field
yields a null slot at runtime and MUST NOT raise. §9.3 gains a clause
generalizing that gracefulness and stating the success-path expectation:
A non-degrade
instance_middlewarereturn SHOULD covercollect_field(it is the homogeneous collection slot; the §9.7 success contribution carries it). The fan-in readscollect_fieldsuch that an absent value — whether from a callable degrade that omits it (§9.8) or a non-conformant middleware return — yields a null slot; the fan-in MUST NOT raise on an absentcollect_field. A runtime raise here would stop the graph underfail_fast, defeating isolation exactly as a degrade-time raise would (§9.8). The null is visible intarget_field, so a non-conformant return surfaces without halting the run.
The static-mapping footgun remains compile-guarded (§9.8, unchanged); a code middleware return is not compile-checkable, so — like the callable form — it is handled gracefully at runtime.
3. Degrade survives checkpoint resume (fixture, below)¶
No normative change: a degraded instance completes (§9.3), so the existing §10 checkpoint/resume rules already roll its recorded slot forward. This proposal adds the conformance fixture that pins it.
Conformance test impact¶
New fixture¶
A fixture under pipeline-utilities/conformance/ (number assigned at
acceptance) — degrade + extra_outputs slot + checkpoint resume:
- A single-instance (or small) fan-out with
instance_middleware: [failure_isolation],collect_field, and anextra_outputsmapping, under a checkpointer. - Case —
extra_outputsnull slot. A degrade whosedegraded_updatesuppliescollect_fieldbut omits anextra_outputssource; assert the mapped parent field holds null at that instance's slot (index-aligned withtarget_field), not a shortened list. - Case — absent
collect_fielddoes not raise. A callable degrade omittingcollect_field; assert the slot is null and the graph does not stop (re-affirming §9.8 from the §9.3 generalization angle). - Case — degrade survives resume. Checkpoint after the degraded instance
completes, then resume; assert the degraded slot (the null
collect_fieldslot and/or the degrade values, plus the nullextra_outputsslot) is preserved on the rolled-forward fan-in — not recomputed or dropped.
Fixture 065 (0066) continues to pass: this proposal refines the extra_outputs
omission shape and the resume path, neither of which 065's supplied-value
cases exercise.
Versioning¶
MINOR bump (pre-1.0):
- §9.3's omitted-
extra_outputsrule changes from "not contributed" to a positional null slot (a conformance-expectation change), and gains the absent-collect_fieldSHOULD + never-raise clause. - A new conformance fixture pins the
extra_outputsnull slot, the never-raise behavior, and the degrade-survives-resume round-trip.
Behavior-change note. Correctly-configured graphs (degrades that supply
their mapped fields) are unchanged. The pinned changes are: an omitted
extra_outputs source now contributes a positional null (vs. an ambiguous
"not contributed"), and an absent collect_field is uniformly a null slot
rather than a possible raise. Catch/degrade and graph-execution outcomes are
otherwise untouched.
Out of scope¶
- The 0066 degrade-contribution model, §9.8 compile check, and §11.7 branch
skip. Unchanged; this proposal only refines the
extra_outputsomission shape, the absent-collect_fieldgracefulness, and the resume fixture. - Per-branch (heterogeneous)
outputsomission. §11.7's skip is correct for branches and stands — the asymmetry with fan-out's positional null is deliberate (homogeneous slot vs. distinct parent fields). - Promoting
extra_outputsto a compile-checked slot.collect_fieldis the homogeneous slot the compile check guards (§9.8);extra_outputsare secondary reducer-merged contributions with a graceful runtime null, no compile requirement. - The general
extra_outputsmerge mechanics. §9.3's exact reducer-input shape forextra_outputs(per-value vs. list-at-once) is a pre-existing looseness; this proposal pins only the degrade omission (a null value in instance-index order) and does not otherwise redefine how non-degradeextra_outputsmerge.
Alternatives considered¶
extra_outputsomission as a shortened list ("skip"). The literal reading of 0066's current wording. Rejected: it misalignsextra_outputsfromtarget_field(a consumer can no longer tell which result a givenextra_outputsvalue belongs to), breaking the homogeneous positional contract.- Raise on an absent
collect_fieldoff the degrade path (strict). Rejected: it reintroduces a fan-in raise that stops the graph underfail_fast— the outcome 0066 removed — and would require the fan-in to distinguish degrade returns from normal returns. A visible null slot surfaces the non-conformant return without halting the run. - No fixture for resume. Rejected: the degrade-survives-resume path is composition of two accepted features but is exactly the checkpoint-adjacent resume path otherwise covered only by keying consistency, not a conformance case; a fixture pins it.
- Fold these into 0066. Not possible — 0066 is Accepted (immutable); these land as a superseding refinement per governance.