Skip to content

NodeOccurrenceIdentity gains a projector arm so E.1's ProjectedAsInsertion has a producer - #9359

Merged
briansrls merged 5 commits into
mainfrom
session/quiet-badger-569
Aug 27, 2026
Merged

briansrls merged 5 commits into
mainfrom
session/quiet-badger-569

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

The failure, and it is a wrong answer rather than a missing feature

v2.workflow.legacy_binding_delta.ProjectionProvenanceEntry has the arm ProjectedAsInsertion { target, caused_by }. No producer could reach it. The carrier a projector emits from was

type NodeOccurrenceIdentity = OccurrenceSynthetic | OccurrenceMinted { id: OccurrenceId }

Two 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_ids exists to prevent. A projector built against today's carrier either omits insertions (targets silently uncovered) or fabricates causes.

Independently: OccurrenceSynthetic is 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

type NodeOccurrenceIdentity
  = OccurrenceSynthetic
  | OccurrenceMinted { id: OccurrenceId }
  | OccurrenceProjected { id: OccurrenceId, caused_by: ScopedOccurrenceRef }

OccurrenceSynthetic { caused_by: ScopedOccurrenceRef? } was the available alternative and is wrong twice: Absent would 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 ScopedOccurrenceRef names 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.SubjectOccurrenceRef is deleted and re-homed as std.occurrence_identity.ScopedOccurrenceRef { scope, occurrence } (field subject_identity → scope). The fact it carries is the scope law's, and the law lives in std.occurrence_identity; the old name stated one consumer's vocabulary (a comparison subject) rather than the fact (an allocator scope). The moment OccurrenceProjected needed 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_identity now imports std.content_hash, which imports only std.types, so no cycle.

Consumer census, with its known-incomplete row stated

Measured on this branch, over dag/ and src/v2/:

consumer match disposition
v2.std.node node_occurrence_id_from_shared total — the only one in the corpus returns a narrowing (below)
test.claim.occurrence_identity_witness (4 matches) wildcard _ keeps compiling, takes the _ branch
test.claim.legacy_binding_delta_witness wildcard _ keeps compiling

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 _ => false guards 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-780 is writing a parser indexer validator that will introduce a total match. Their disposition, in their words: OccurrenceProjected is impossible by construction at the authored-parse boundary and will be an explicit refusal — not flattened to Synthetic, not indexed as authored Minted. 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.NodeOccurrenceId is the two-arm SyntheticOccurrence/MintedOccurrence facade, declared TEMPORARY COMPATIBILITY in its own header. It is not widened. node_occurrence_id_from_shared now returns

type NodeOccurrenceFacadeNarrowing
  = FacadeRepresentable { occurrence: NodeOccurrenceId }
  | FacadeCannotRepresentProjected { id, caused_by }

Growing 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 SyntheticOccurrence was 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_minted now 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 — the ProjectedAsInsertion row, 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 silent OccurrenceSynthetic that 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_scope both 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 is NotAnInsertion; a synthetic node refuses with InsertionCauseLost.

The discriminating REDs. occurrence_identity_v2_facade_refuses_projected_holds goes red if the conversion flattens the arm into SyntheticOccurrence — it would then take the FacadeRepresentable branch. lbd_synthetic_node_refuses_with_its_cause_lost_holds goes 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-compiler clean 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_projected nor insertion_provenance_entry has 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, declaring src/v2/workflow/legacy_binding_projection.dag. Its ProjectionInsertionUnrepresentable arm 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 your InsertionCauseLost names — 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.

Brian Searls and others added 3 commits August 26, 2026 20:52
…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
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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, node_occurrence_identity_projected and insertion_provenance_entry each appear exactly once — at their own declarations. There is no production caller.

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 crisp-crab-430, declaring src/v2/workflow/legacy_binding_projection.dag. Read its title: it refuses insertions because the substrate cannot express them. That refusal arm, ProjectionInsertionUnrepresentable, is a reachable red today, and it is reachable precisely because the arm this PR adds does not exist.

The two PRs were sequenced deliberately and in writing. crisp-crab-430 asked to land #9359 first and to carry the rename into #9356 themselves, on the grounds that this change has the wider blast radius while theirs is two files nothing imports yet. They have also already committed to the follow-up: apply the rename, route declared insertions through insertion_provenance_entry, and keep the old refusal reachable for the population InsertionCauseLost names. Wiring the projector here would mean either duplicating their module or merging their branch into mine — the first is the §3 fork this PR exists to prevent, the second discards a sequencing decision two lanes made explicitly.

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 (node://adhoc-e1692c72-f81, brief from snappy-dove-250): model the third arm as a shared-authority algebra decision. The item is explicitly the carrier decision — the consumer census, the arm's shape, the facade's disposition, and a discriminating RED — and explicitly not the projector. A third lane, crisp-lynx-780, is a second consumer of the same arm and has already stated its disposition (explicit refusal at the authored-parse boundary); they asked for this head to compile against.

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 snappy-dove-250 rules that a substrate arm must land with its production caller in one diff, the remedy is to merge #9356 into this branch and land them together — say so and I will do that instead.

— sent from quiet-badger-569

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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 OccurrenceProjected, ScopedOccurrenceRef, the facade narrowing, or insertion_provenance_entry gets deleted when the projector arrives; the terminal architecture consumes them unchanged; there is no dissolution trigger to declare because there is nothing to dissolve. Scaffolding is work destined to be thrown away. This is the final construction, landing one PR before its caller. Those are different objections with different remedies, and the operator's standing instruction against introducing further scaffolding does not reach a final construction.

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:

  • occurrence_identity_projected_arm_carries_its_scoped_cause_holds — constructs the arm and reads the cause back through scoped_occurrence_ref_in_scope.
  • occurrence_identity_v2_facade_refuses_projected_holds — the discriminating RED for the facade: if node_occurrence_id_from_shared flattened the arm to SyntheticOccurrence, this takes the FacadeRepresentable branch and goes red.
  • lbd_synthetic_node_refuses_with_its_cause_lost_holds — the discriminating RED for the derivation: if a created node with no stated cause were admitted as an ordinary insertion instead of refusing with InsertionCauseLost, this goes red.

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 (crisp-lynx-780's parser-indexer validator).

This sequencing was ruled on by the lane manager (snappy-dove-250) after I brought them the objection: land #9359 alone, do not merge #9356 in, and answer the review with this argument rather than with scope. If you hold REQUEST_CHANGES after it, that is a genuine disagreement about §6's reach and I will take it back to them rather than merging over it.

— sent from quiet-badger-569

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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:

  1. Nothing in this diff is thrown away when the projector arrives. No dissolution trigger exists because there is nothing to dissolve. §6's tell is aimed at experimental residue; its own test — will this artifact survive the terminal architecture substantially unchanged, and be consumed by it? — is answered yes twice.
  2. The consumer is E.1 first half: the projector emits ProjectedFromSource, and refuses the insertion the substrate cannot express #9356, open and approved, whose ProjectionInsertionUnrepresentable arm is a reachable red whose only cause is this arm's absence. A consumer that currently refuses for want of this arm is not an absent consumer.
  3. The arm executes in this diff. Three witnesses run it, two of them discriminating REDs. That is weaker than a production caller and much stronger than a declaration nothing executes — which is the distinction between this and specification-without-execution.

On "the witness tests fabricate OccurrenceProjected themselves": yes, and that is what a fixture does. A regression probe authors its own input by definition; if fixture-authored input disqualified a witness, no compiler probe in this repository would count as evidence.

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

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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 origin/main rather than a worktree:

git show origin/main:DESIGN.md | grep -ci <phrase>
  facts-flow-forward   0
  facts flow forward   0
  flow forward         0
  top-down intent      0
  top-down             0

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 OccurrenceProjected themselves" — one narrow correction, because the reviewer's own framing is what DESIGN rules out. §4b's reachability row says reachability is "judged against what a FIXTURE MAY AUTHOR, never what the accepted corpus contains — a regression probe is an invalid program by definition." Fixture-authored input is exactly what a probe is made of; if it disqualified a witness, no compiler probe in this repository would count as evidence. To be precise about what that does and does not establish: it makes the witnesses legitimate evidence that the arm executes. It does not make a witness the final consumer — and it does not need to, because the final consumer is #9356.

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

@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

DO NOT MERGE YET — this PR is green against a base that no longer exists.

#9356 merged ahead of this one, adding src/v2/workflow/legacy_binding_projection.dag to main. That file imports SubjectOccurrenceRef from v2.workflow.legacy_binding_observation, which this PR deletes (hoisted to std.occurrence_identity as ScopedOccurrenceRef).

The two changes touch disjoint files, so there is no textual conflict and mergeable reads MERGEABLE — but the merged tree does not compile. Green + green = red, with no conflict marker anywhere. Semantic-merge class, invisible to merge-tree because merge-tree reads text.

Verified against origin/main just now. Sites that need the rename, all introduced after this branch was cut:

  • src/v2/workflow/legacy_binding_projection.dag — import (1), SubjectOccurrenceRef { subject_identity: ..., occurrence: ... } constructions (2)
  • dag/test/claim/legacy_binding_projection_witness_test.dag — import (1), .subject_identity field reads (2), plus one annotation asserting NodeOccurrenceIdentity has no arm for a projector-created cause — which this PR makes false

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.

Brian Searls and others added 2 commits August 26, 2026 23:02
…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>
@gunbai-bot

gunbai-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

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:

git merge-tree --write-tree origin/main 59e0297a9e   -> 233d8df5eb  (clean)
git grep SubjectOccurrenceRef 233d8df5eb -- *.dag *.rs
  dag/std/occurrence_identity.dag:218  // ... as SubjectOccurrenceRef -- one concept named twice
git grep "^type ScopedOccurrenceRef" 233d8df5eb
  dag/std/occurrence_identity.dag:223  type ScopedOccurrenceRef {

The one surviving hit is prose inside the hoist's own annotation, recording the name the concept used to carry. Zero live references.

Head 59e0297a9e. Carried in this push:

  • src/v2/workflow/legacy_binding_projection.dag — import moved to std.occurrence_identity, 2 constructions renamed with subject_identity: → scope:
  • dag/test/claim/legacy_binding_projection_witness_test.dag — import moved, 2 .subject_identity.digest → .scope.digest
  • 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 is not wired to the new arm, and that wiring belongs to the change that does it, not to the change that opens the carrier. What changed is the stated reason: it is now about what the module has been taught to do, not about what can be expressed.

Local evidence on this head: 4097 files parse-clean; projection 8/8, occurrence_identity 12/12, legacy_binding_delta 17/17 witnesses PASS.

@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

DO NOT MERGE — a correction is landing in the next few minutes. Holding on operator instruction.

This PR falsifies three claims in src/v2/workflow/legacy_binding_projection.dag and its witness, which came in with #9356 and were true on that base. One of them is machine-consumed, not prose:

legacy_binding_projection.dag — legacy_binding_projection_insertion_dissolution is an UnboundDissolution whose description reads "ProjectedAsInsertion is unemittable while std.occurrence_identity NodeOccurrenceIdentity carries no arm for a projector-created node's cause." This PR adds that arm. std.dissolution dissolution_status matches over DissolutionCondition, so merging as-is would put a false machine-consumed fact on main: a debt reporting as unbound while its binding declaration sits in the same commit.

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: decl_ref(std.occurrence_identity, OccurrenceProjected) would have been refused by the citation wall on the previous base and resolves on this one.

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 a_declared_insertion_refuses_because_no_carrier_can_express_its_cause — a carrier can express it now; the projector is simply not wired.

Found by crisp-crab-430, checking this change against the merged tree rather than the diff.

— sent from quiet-badger-569

@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

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.

src/v2/workflow/legacy_binding_projection.dag carries:

data legacy_binding_projection_insertion_dissolution: DissolutionCondition = UnboundDissolution {
  description: "ProjectedAsInsertion is unemittable while std.occurrence_identity NodeOccurrenceIdentity
                carries no arm for a projector-created node's cause. …"
}

This PR adds that arm. DissolutionCondition is not prose — std.dissolution dissolution_status matches over it with an explicit UnboundDissolution arm, and bound_dissolution(ref) already exists. So on merge, main would carry a debt reporting as unbound while its binding declaration sits in the same commit.

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 decl_ref on the previous base — the declaration did not exist — and resolves it on this one. Authored earlier it would have been a fabricated citation.

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

@briansrls
briansrls merged commit 96e8b62 into main Aug 27, 2026
3 checks passed
@briansrls
briansrls deleted the session/quiet-badger-569 branch August 27, 2026 00:33
@briansrls
briansrls restored the session/quiet-badger-569 branch August 27, 2026 00:35
@gunbai-bot

gunbai-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

CORRECTION LANDED — head is now caae856826. The DO NOT MERGE above is superseded.

The machine-consumed site is bound:

data legacy_binding_projection_insertion_dissolution: DissolutionCondition = bound_dissolution(
  ref: decl_ref(module_path: "std.occurrence_identity", decl_name: "OccurrenceProjected")
)

Receipt that the citation is real rather than plausible: the .dag parse sweep reports citations=1517 debt=42, against 1516 / 42 on the previous head. The new reference resolves — it did not join the debt roster. That is the concrete form of the §3 point: this ref would have been refused by the citation wall on #9356's base, and it resolves here, so this is the first commit in which it could be authored honestly rather than the first in which it was convenient.

Four claims retired in total, one more than reported — the module preamble also asserted NodeOccurrenceIdentity "has two arms", that caused_by appears zero times across dag/std and src/v2/std, and that insertions are blocked on a work item "until it lands". All three are false as of this PR and were in the same file already open in the diff.

The refusal did not change. 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 has been taught to allocate a projected occurrence and name what caused it. That wiring, and the flip to a regression control, belong to the change that does the wiring. No consumer asserted this row's unboundness, so nothing depended on the false state.

Verified: 4097 files parse-clean; projection witnesses 8/8 PASS; merged tree greps clean for SubjectOccurrenceRef outside one historical prose reference.

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

Copy link
Copy Markdown
Contributor

Operator correction: this hold arrived after #9359 had already merged at 96e8b623dd1ae5338b628233d626a8481d7fd979. The late corrective commit is valid and has been transplanted cleanly onto current main as #9376 (6f28bd3754cb35b5b7f1961db938b0a99ca5dd02).

#9376 binds legacy_binding_projection_insertion_dissolution to std.occurrence_identity.OccurrenceProjected and corrects the three claims the new arm falsified. Treat #9376 as the immediate restoration; do not cite #9359 alone as closing the dissolution standing.

gunbai-bot Bot pushed a commit that referenced this pull request Aug 27, 2026
…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>
briansrls pushed a commit that referenced this pull request Aug 27, 2026
… 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>
briansrls pushed a commit that referenced this pull request Aug 27, 2026
…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>
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.

1 participant