nw.bodies.unproduced_output#
Body schema for unproduced-output records — nw#44.
URI: annot://schema/unproduced-output/v1
TransformResult.failed / .blocked (nw#25) carry a reason for a
planned output that was never produced, but only within the response that
produced them — reload the project and the hole is unexplained again. This
schema is the persisted record of that reason, written through
nw.graph.ProjectGraph.add_unproduced_output() at the same choke point
execute() writes successes through
(RenderStrategyTransform.execute self-stamps the same way, since it
overrides execute and bypasses the base implementation).
Why a sidecar tier rather than the output kind’s own schema#
Reading the live registry, most output kinds already validate as an empty
skeleton — so nothing on the write side forces a new tier. The read side
decides it: several consumers (e.g. a retry planner) compute “already
produced” from rows carrying the output kind’s body_schema_uri. A
tombstone written under that URI would mark the failed unit **already
produced**, and a retry would never be planned again — turning a transient
failure into a permanent one. So this is its own tier, never borrowing the
output kind’s URI, exactly like nw.bodies.verifying_trace never
borrows its target’s.
Identity and lifecycle#
A record’s retirement/dedup key is not just (transform_name,
upstream) — two units of the same fan-out (different mapping_key, e.g.
two panels of the same beat) can share an identical upstream set, and
keying on upstream alone let one unit’s success retire an unrelated unit’s
still-outstanding record. The real key is:
(transform_name, instance_id, call_index)wheninstance_idis known — the fan-out unit’s ownnw.transforms.fanout.work_item_instance_id()(a pure function of(transform_name, mapping_key)), threaded fromfan_out_execute()through anexecute()implementation that accepts the
unit_instance_idkeyword (the same accepts-it-or-not seam_accepts_keyword()already uses foron_failure).call_indexstill has to agree even here: a unit’s own plan can carry more than one call, and matching oninstance_idalone would collapse two of that unit’s own outputs into one key;
(transform_name, call_index, upstream)otherwise —call_indexis this output’s position within itsexecute()call’sskeletontuple, which disambiguates multiple outputs of one batch call that share identical upstream parents even with no fan-out involved. This fallback is still not a full identity: two DIFFERENT units run outside a fan-out (noinstance_idthreaded at all) with identical upstream parents and the samecall_indexstill alias, and one succeeding retires the other’s record too — the residual version of the original bug, scoped to callers that never passunit_instance_id.
add_unproduced_output() dedupes on this key:
a second record for the same identity replaces the first rather than
accumulating — a unit failing twice must not leave a first-run reason
readable as a live blocker after the unit has since failed differently (or
the record is stale but still there). add_annotation()
retires (removes) any record matching a real output’s identity the moment
that output is written — a successful retry clears its own record.
**A write that bypasses add_annotation — a raw store.add — leaves
its matching record in place.** It reads back as a live blocker after
reload even though the output was, in fact, produced. Route every derived
write through add_annotation(), as the module
docstring on nw.graph already requires for verifying traces.
reason never carries raw exception text#
The graph is exportable project data, and an exception’s str() can carry
a signed URL, a local path, or other operational detail that does not belong
in it. When the FailedOutput this record is built from carries an
error, add_unproduced_output() stores a
fixed sentence plus error_type here and logs the original reason instead
(logging.getLogger("nw.graph"), at WARNING). A reason with no
error (e.g. a "blocked" output’s falaw-supplied human string, the
whole point of nw#25 — “skipped: no dialogue in this panel”) is stored
as-is.
Classes
|
Body of an unproduced-output record. |
- class nw.bodies.unproduced_output.UnproducedOutputBodyV1(**data)[source]#
Bases:
BaseModelBody of an unproduced-output record.
statusmirrorsnw.transforms.fanout.UnitStatus’s two unproduced cases:"failed"(the call itself failed) or"blocked"(an upstream call in the same plan failed first).upstreamis stored for thecall_indexfallback identity (see the module docstring); it is not itself a sufficient key.