Skip to content

Derive serialized generated projections inside a pre-merge admission transaction - #10183

Closed
gunbai-bot[bot] wants to merge 7 commits into
mainfrom
session/clever-gull-870
Closed

gunbai-bot[bot] wants to merge 7 commits into
mainfrom
session/clever-gull-870

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

The ruling this constructs

A serialized generated projection must be derived from the composed authority inside the admission transaction. A lane must not be responsible for holding final projection bytes while waiting for admission.

Not "merge to main, then regenerate". Construct the exact candidate that could advance main, regenerate inside it, verify it, admit exactly it. Post-merge regeneration is rejected because it opens an interval in which main holds authority and projection from different states.

gunbc.projection_admission_transaction models that transaction over the existing gunbc.merge_admission / gunbc.merge_lifecycle vocabulary rather than restating either predicate. A projection is not an independent authority that could carry its own receipt — it is a function of the composed authority — so the only well-formed subject is a candidate holding both, which is what this module is.

step what the model does
1 slot AdmissionSlotMechanism; only an enforced queue / coordinator excludes a concurrent advance
2 read B = trunk tip, H = lane authority, R = roster, captured at open
3 compose not total — AuthorityCompositionConflict refuses and releases the slot
4 derive P from I's merged authority; the lane's inherited bytes are read nowhere in apply_derive
5 seal ForeignMachineAuthoredChange refuses anything outside the derived set
6 verify the retained read-only equality, relocated
7 advance atomic against base and roster; BaseMoved / RosterChanged discard the candidate

Three separately falsifiable claims, all executed

test.claim.projection_admission_transaction_witness — 21 witnesses, 21 PASS, 0 FAIL against a 21-name roster (claim_batch --entry … --functions …; count taken from the PASS lines, not from $?).

Order. One discriminating pair differing only in derivation order. The rejected order's trace ends coherent — the regeneration repairs it — so the latch, not the end state, is the evidence: the interval having existed is the defect. Across the rejected order's own 40-sequence window every sequence opens an incoherent interval; across the ruled order's 70-sequence window none does. Each order is enumerated over the script it actually prescribes, since running the ruled script under the rejected order would only produce StageOutOfOrder and measure nothing.

Slot. Human announce, dashboard freeze roster and advisory lease all lose the candidate on the peer-advance trace the enforced merge queue and the admission coordinator land. The roster axis is separated out and shown to discard identically under both slot kinds — an enforced slot is not credited with refusals it does not cause.

Shard. The rule is written over the derived resource (generator + authority closure), never over an observed file list, and both directions are executed: two ledgers with disjoint outputs share one generator and refuse; two resources with disjoint closures admit. initial_shard_policy = GloballySerialized, relaxed only by a closure proof.

Why heal is not this

heal-generated-artifacts checks out the mutable PR branch and pushes repairs back to it; its own authority classifies it as a remediator, excludes it from the required aggregate, and records that its pushed head needs revalidation. It can reduce author effort; it cannot establish admission order — mutable subject, still exposed to later main movement, and not the exact final integration candidate. The model makes all three visible rather than arguing them. This is a merge-contract change; making heal smarter cannot reach it.

First-implementation scope, enforced by construction

A member whose generator is built from its own output (the stage0 bootstrap mirror) classifies RequiresBootstrapFixedPointArm, and a lane carrying one refuses at derive. So the whole-document ledgers (docs/design-failure-modes.md) cannot be silently generalized over the mirrors — the distinction most likely to sink this lane is a modeled field with a refusal arm, not a paragraph.

Rung

Mitigatable, ceiling mechanically preventable — an admission slot is a fact about a coordinator's behaviour over time, which no constructor here makes unwritable. The read-only driver and gate are retained: moving generation inside admission changes what they are evidence about, from a late detector of predictable invalidation into the verifier of admission-produced bytes. Next-rung trigger is the capability (an enforced slot excluding another main advance for the critical window, plus a production producer that mints the candidate), not an artifact.

This lands the model and its executed evidence. It is not the enforcement, and the module says so; the hand queue remains the honest interim mitigation until the trigger lands.

Scope: two files, nothing contended. The failure-mode row remediator_read_as_admission_order, its two roster registration lines, and the two projection lines have been removed from this PR — eighteen lines out of 1,242 that were the entire reason this branch raced eleven concurrent writers on one carrier. They land afterwards as their own change; once the roster is derived from the row directory they will not need registration lines at all. The three were dropped together, because keeping the row file without its registration is exactly the state this module names: present in the tree, absent from the corpus, with every registration check green because the missing row defines the population those checks range over. roster.dag and docs/design-failure-modes.md are restored to precisely the tip this branch merged, so they are one-sided against main and cannot conflict again.

HELD: no operational consumer today

This PR is held by operator ruling (2026-09-03) pending an operational consumer, and the reason is recorded here rather than only in a message thread. The state machine and projection_member_eligibility are evidence, not production: no site in this repository currently asks "may this member be derived here". That was established by enumeration, not assumed. Four sites partition the generated-artifact population, on four different axes, and none asks this module's question:

site axis it sorts on
gunbc.heal_push_plan credential — may the resolved credential push this path
v2.workflow.required_regen regen_scope_select affected set — did the change touch this member
gunbc.stage0.stage0_rust_source_lifecycle_scaffold generated vs hand-maintained — an identity join committed_generated_artifacts already answers
gunbc.generated_artifact_emit main_wet no filter at all — folds the whole committed population in one pass, both stage0 .rs mirrors included (verified against a real regeneration run, not read off the source)

Consulting this module from any of the four would be a category error, not a wiring change: same population, different question.

The near miss, named because it is the strongest candidate and still fails. The repository already knows the fact projection_member_eligibility encodes — the repair steps in gunbc.generated_artifact_merge_driver and gunbc.proactive_verification_ledger spine_regen_recipe both prescribe a second build-and-verify pass, for precisely the stated reason that the regen route emits the seed by running a binary built from the seed, so one pass can self-verify for the wrong reason. That is this module's bootstrap-mirror carve-out, already load-bearing. It is still not a consumer on two independent grounds: it is a printed string with no call site to rewire, and it is global rather than per-member. Turning it per-member would change that recipe's contract — building the seam this module plugs into rather than finding one, the inversion §6 refuses. So: the distinction is real and the corpus already pays for it, but it pays as a blanket second pass rather than as a decision.

Trigger — capability-grained, owned by someone else's future work: when the merge driver's step-3 reasoning becomes a modeled operation rather than a printed string, eligibility has a home. Until then this module is citable as a model, and its witnesses as evidence for the order claim; it is not coverage for any admission behaviour, because nothing admits anything through it.

Two results from this lane stand independently of whether it merges: a serialization mechanism deserves credit only for transitions it excludes, and an enforced slot removes peer-main-advance invalidation and nothing else — the roster-change refusal occurs under advisory and enforced slots alike.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH

gunbc-ci-auto-heal and others added 4 commits September 3, 2026 03:08
…transaction

A serialized generated projection must be derived from the composed authority
INSIDE the transaction that advances main. Post-merge regeneration is rejected
because it opens an interval in which main holds authority and projection from
different states; and a remediator on a mutable branch cannot establish
admission order no matter how correct its bytes are.

gunbc.projection_admission_transaction models that transaction and executes its
consequences rather than asserting them:

  * the candidate is composed from the trunk tip and the lane's AUTHORITY only,
    with a typed refusal arm where the composition is not total (a whole-row
    .dag conflict was measured 2026-09-03, so step 3 refuses rather than
    resolving on the lane's behalf);
  * P is derived from the composed authority -- the lane's inherited projection
    bytes are read nowhere in apply_derive, and that omission is the ruling;
  * sealing refuses any machine-authored change outside the derived set;
  * verification is the retained read-only equality, RELOCATED rather than
    deleted: the merge driver and the generated-artifact phase stop being late
    detectors of predictable invalidation and become the verifier of
    admission-produced bytes;
  * the advance is atomic against base and roster, and discards the candidate
    when either moved.

Three separately falsifiable claims, all executed by
test.claim.projection_admission_transaction_witness (21 witnesses, all green):

  * ORDER. One discriminating pair differing only in derivation order. The
    rejected order's trace ENDS coherent -- the regeneration repairs it -- so
    the latch, not the end state, is the evidence: the interval having existed
    is the defect. Across the rejected order's own 40-sequence window every
    sequence opens an incoherent interval; across the ruled order's 70-sequence
    window none does.
  * SLOT. Human announce, dashboard freeze roster and advisory lease all lose
    the candidate on the peer-advance trace the enforced merge queue and the
    admission coordinator land. The roster axis is separated out and shown to
    discard IDENTICALLY under both slot kinds, so an enforced slot is not
    credited with refusals it does not cause.
  * SHARD. The rule is written over the derived resource (generator plus
    authority closure), never over an observed file list. Two ledgers with
    disjoint OUTPUTS share one generator and refuse; two resources with
    disjoint CLOSURES admit. The starting policy is globally serialized and is
    relaxed only by a closure proof.

First-implementation scope is enforced by construction, not by prose: a member
whose generator is built from its own output (the stage0 bootstrap mirror)
classifies as RequiresBootstrapFixedPointArm and a lane carrying one refuses at
derive, so the whole-document ledgers cannot be silently generalized over the
mirrors.

Rung: mitigatable, ceiling mechanically preventable -- an admission slot is a
fact about a coordinator's behaviour over time and no constructor here makes it
unwritable. The next-rung trigger is the CAPABILITY (an enforced slot excluding
another main advance for the critical window, plus a production producer that
mints the candidate), not an artifact. This lands the model and its executed
evidence; it is not the enforcement, and the hand queue remains the honest
interim mitigation until the trigger lands.

Files one gunbc.recurring_failure_mode row,
remediator_read_as_admission_order, carrying the recognition rule and both
directions of the file-list shard misread.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
… classifier

Ruling A (operator, 2026-09-03), from a self-audit of the previous commit: two of
this module's three parts were not what they claimed to be, and the classifier
that WAS production-shaped was keyed on the wrong type.

ELIGIBILITY IS NOW AN EXHAUSTIVE MATCH OVER gunbc.generated_artifact
GeneratedArtifact. It took a ProjectionMember record carrying a hand-declared
generator_built_from_own_output boolean; a caller can build any record, so a
NEWLY ADDED ARTIFACT WAS SILENTLY ELIGIBLE and the classifier could be satisfied
by editing the declaration while the realization lied -- DESIGN section 5's own
tell for validation standing where construction was available, plus a section 3
second carrier for an identity the registry already owns. GeneratedArtifact is a
closed coproduct whose emit dispatch is already an exhaustive match, so keying
on it means a new variant cannot compile until it is classified.

THE RUNG CHANGE IS MEASURED, NOT ASSERTED. Deleting one arm and compiling the
module refuses with `non-exhaustive match: missing variant(s)
DesignRungDropsArtifact`, one blocking error -- the same refusal a newly
registered artifact meets. The discriminating RED is authorable in the module
itself, so the probe needs no fixture.

THE SHARD CLASSIFIER IS DELETED RATHER THAN STAGED. Its authority_closure was a
hand-supplied list: the same defect one level down, and precisely the
presumption the ruling forbade when it said path-disjointness must not be
presumed. The starting policy is global serialization, which needs no
classifier; a rule keyed on v2.lens.module_graph would come back as
construction. Its four witnesses go with it -- keeping evidence for a rule the
module no longer implements would be the inert-lens tier.

The population is now DERIVED from committed_generated_artifacts() rather than
declared beside it, and the two arms are asserted to partition it exactly, so a
member falling out of both is not writable either. Exactly two members are
deferred: the emitted Rust under src/v1/stage0/src that `gunbc` is compiled
from. A new witness pins that the discriminant is the BOOTSTRAP LOOP and not the
neighbourhood -- a stage0 `.dag` projection sits beside the two mirrors and is
derivable, because nothing is built from it.

19 witnesses, all green. The state machine stays as declared evidence with the
role named honestly: a theorem carrier plus a regression control that reds if
post-merge regeneration is proposed again (DESIGN section 4b(4) keeps a class's
discriminating evidence enrolled). "Checked against" is deliberately absent --
nothing checks a coordinator against this mechanically, and writing that it does
would be the rung inflation section 4b(1) names.

Also records, in the module note and deliberately NOT in the projected roster
row, that the remediator's push can SUSPEND the required aggregate in
action_required (gunbc#10157, two hours, reads identically to "no checks
reported") -- editing a projected ledger to record a fact about projection churn
would cost the exact regeneration this lane exists to remove.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
…ger by union

The conflict is this lane's own subject arriving as a specimen: #10145 landed
`non_execution_undifferentiated_by_what_it_silenced` while this branch carried
`remediator_read_as_admission_order`, so both the authority and its projection
collided.

RESOLVED BY UNION, AFTER CHECKING THAT UNION IS SOUND. A union resolve on a
roster is only safe if nothing was deliberately DELETED on the other side --
otherwise it silently re-adds a row someone removed on purpose, and git reports
a clean append/append resolve either way. Measured against the merge base before
resolving: main added exactly one row and removed none. Verified after, by
identity join rather than by count: 68 roster entries and 68 declarations, with
both comm directions empty.

docs/design-failure-modes.md WAS NOT HAND-MERGED. The generated-artifact merge
driver refused it exactly as designed -- ours side in the worktree, path left
unmerged, regeneration recipe printed -- so it was regenerated from the merged
authorities and checked to carry both lanes' rows.

CONTROLS RE-RUN AGAINST THE SHIPPED TREE, not the pre-merge one. Main brought
~1,600 lines of seed Rust, so the binaries that certified the previous head no
longer described this one: gunbc and claim_batch were rebuilt from the merged
tree, the 19 witnesses re-run green against it (19 PASS / 0 FAIL / 19 roster),
and a second regeneration under the rebuilt binary left the projection byte
identical -- a fixed point, so the bytes are not an artifact of a stale emitter.

The module note gains the cost this lane is denominated in, now measured rather
than argued: five lanes contended the failure-mode ledger and four the rung-drop
ledger, every pair conflicting, order decided by hand. It also gains the
stronger argument, which is a correctness one: the union-soundness check has to
be made against the merge base, an admission transaction over the composed
candidate holds the merge base and both sides at the moment it decides, and a
human resolving on a mutable branch has to remember to look.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
…trigger

The search for an operational consumer of projection_member_eligibility
returned nothing, and that negative result was reachable only from a message
thread. It belongs where a held PR's next owner looks.

Four sites partition the generated-artifact population, on four different
axes, and none asks "may this member be derived here": heal_push_plan sorts
on CREDENTIAL, required_regen regen_scope_select on AFFECTED SET,
stage0_rust_source_lifecycle_scaffold on GENERATED VS HAND-MAINTAINED (an
identity join committed_generated_artifacts already answers), and
generated_artifact_emit main_wet applies no filter at all -- it folds the
whole committed population in one pass, both stage0 .rs mirrors included,
verified against a real regeneration run rather than read off the source.
Consulting this module from any of them is a category error, not a wiring
change: same population, different question.

The near miss is recorded too, because it is the strongest candidate and
still fails. The merge driver's repair steps and spine_regen_recipe already
encode this module's bootstrap-mirror fact -- a binary built from the seed
can self-verify for the wrong reason -- but as a printed string with no call
site, and globally rather than per-member. Making it per-member would change
that recipe's contract, which is building the seam rather than finding one.

Trigger stated at capability grain and owned by other work: when the merge
driver's step-3 reasoning becomes a modeled operation rather than a printed
string, eligibility has a home. Until then the module is not coverage for any
admission behaviour, and now says so.

Documentation only: one module note, no declarations touched.
Typecheck: 0 blocking errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
@gunbai-bot

gunbai-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

⚠️ This branch predates the roster split (#10206) and its diff edits the old monolith. Do not resolve the conflict mechanically.

dag/gunbc/recurring_failure_mode.dag no longer holds rows. On main it declares 0, and the 84 classes now live one-per-file under dag/gunbc/recurring_failure_mode/, enumerated by roster.dag.

Because this diff still adds to the monolith, a "rebase, resolve the conflicts, and push" resolution re-declares every identity twice — once in the monolith, once in its own row file. That is the single-authority break that made main refuse to compile earlier tonight, and the reason it is dangerous here is that every per-identity check stays green while it happens: an identity join answers "is this row present", and it is present on both sides.

Re-file instead of resolving. A class is now two edits:

  1. a new dag/gunbc/recurring_failure_mode/<identity>.dag — one row, module + imports + the data declaration
  2. one entry in dag/gunbc/recurring_failure_mode/roster.dag, appended at the end

Append order is load-bearing: the projection docs/design-failure-modes.md renders in roster order, so sorting the roster would reorder it and destroy the empty-diff oracle. Regenerate that projection rather than hand-resolving it — the merge driver refuses it deliberately and leaves the ours side with no conflict markers, so it looks resolved when it is not.

Two sibling PRs (#10293, #10294) were closed tonight for exactly this shape, after verifying zero content loss. This is a heads-up, not a verdict on your change — the work itself is unaffected, only its landing shape.

— sent from tidy-swift-334

gunbc-ci-auto-heal and others added 3 commits September 4, 2026 01:14
The carrier was split on main (#10206): one row per file under
dag/gunbc/recurring_failure_mode/, registered by roster.dag, with the hub
keeping only the type and the rendering fold. My branch modified the hub's row
block; main deleted it. That is a delete/modify collision, and a union would
have resurrected 84 declarations into a hub that now declares none -- each
exactly once, passing a duplicate check.

Resolved by taking main's deletion as authoritative and re-siting the row:
the deletion is the newer authority, and the row was never carried into the
split (verified by its absence from main before this merge).

The type changed in the same commit as the layout: authored: String became
receipts: List<String>, rendered by a hub-level fold with an empty separator.
The conversion is therefore byte-exact by construction for a single-element
list -- concat("", s) is s -- and the receipt is verified embedded verbatim in
the regenerated projection rather than assumed lossless.

Registration asserted on five axes, each with a fired control:
  multiset uniqueness   no duplicate file, import or roster entry
  subsequence           main's roster order survives as an exact prefix,
                        one entry appended
  three-surface join    87 row files == 87 imports == 87 roster entries,
                        both directions empty
  identity vs spelling  filename == declaration name == identity field on all
                        87 rows (the surfaces the other checks range over are
                        spellings; the projection renders the field)
  authority/projection  87 rendered entries, read independently of the roster

Projection regenerated with a binary rebuilt from the merged tree, never
hand-merged: main brought stage0 seed changes, and a producer that predates
what it emits measures vintage rather than truth.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
… both specimens

THE EXHAUSTIVE MATCH FIRED ON REAL DRIFT, which is the construction claim this
module makes about itself proving itself. main added two GeneratedArtifact
variants -- EvaluationBudgetConsequenceGeneratedRsArtifact and
FileTransportRealizationGeneratedRsArtifact -- and the module refused to
compile until both were classified, exactly as its note says a new variant
must. Both emit into src/v1/stage0/src, so both are seed mirrors whose
generator is built from their own output: RequiresBootstrapFixedPointArm,
beside the two already there.

THE PARTITION WITNESS WAS ASSERTING A LITERAL COPIED FROM THE TREE. It read
`length(deferred_bootstrap_members()) == 2` -- a measurement of the population
at authoring time, which DESIGN section 5 excludes as an oracle, and which
automating would collapse to measure() == measure(). It went red for the right
reason and the fix is not a new number: the bootstrap arm is now joined to
artifact_directory, an authority independent of this classifier. Every deferred
member must live in the seed crate and every derivable member must not, so a
seed mirror classified derivable, or a document classified bootstrap, is caught
by derivation rather than by counting. Proven discriminating: misclassifying
one mirror turns the witness red.

BOTH COMPOSITION SPECIMENS FILED in the module note, one per structural layout
of the same carrier -- the 2026-09-03 duplicate-declaration outage under the
shared carrier, and the residual that survives the one-row-per-file split,
including the rename-like case this branch itself was. They are correctness
arguments where the cost measurement was only an efficiency one.

19/19 witnesses pass against a 19-name roster. Regeneration is a fixed point:
a second main_wet changed nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
This branch was going dirty every hour for eighteen lines out of 1,242. The
model and its witness -- 1,224 lines across two files -- are touched by no
other open PR. The failure-mode row, its two roster registration lines and the
two projection lines were the entire reason this branch was racing eleven
concurrent writers on one carrier.

So they are removed rather than re-merged. The row is not abandoned: it lands
afterwards as its own change, and once the roster is derived from the row
directory it will not need registration lines at all -- the file will be the
registration.

The three are dropped TOGETHER and that is not tidiness. Keeping the row file
while dropping its registration is precisely the state this module names:
present in the tree, absent from the corpus, with every registration check
green because the missing row is what defines the population those checks
range over.

roster.dag and docs/design-failure-modes.md are restored to exactly the tip
this branch merged, so they are one-sided against main and cannot conflict
again -- the branch does not merely avoid the race, it is structurally outside
it. Verified: the diff against that tip is empty for both paths.

Nothing here weakens the module. The two seed-mirror classifications and the
derived partition oracle from the previous commit stand; only the ledger row
leaves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRSSLXXVjavzktziwdpEpH
@gunbai-bot

gunbai-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Closing as declined, not as stale. Recording why, because a future reader finding a closed 1,224-line model deserves the reason rather than rebuilding it.

1. The motivating instruction was withdrawn, on measurement.

This PR builds an unconditional would-land admission transaction over the composed candidate. The lane subsequently proved from a run log that CI already checks out the composed candidate: on pull_request, github.sha resolves to the Merge <head> into <main> commit — which is the would-land tree — and the required generated-artifact phase already computes the relation over it. Both instructions to build the gate were withdrawn on exactly that basis: a bespoke gate would be a second authority for a relation that already executes (DESIGN §3). Nothing since has revived the need, so landing this now would create the fork the withdrawal existed to prevent.

2. Its two discriminating controls are red, which is why this is not "nearly done".

19 witnesses, 17 pass, 2 FAIL
  FAIL the_same_window_finds_incoherent_tips_under_the_rejected_order
  FAIL enforced_slot_discards_nothing_across_the_window

The first is the arm meant to find incoherence under the order the model rejects — it is the RED that gives the accepting arm its meaning. The second asserts the enforced slot loses nothing. With both failing, the 17 greens establish nothing: the positive control passes and the thing that would refute it does not fire. A model whose discriminating arms are red is not 90% finished; it is unestablished.

Salvage, named so it is not lost. The model's shape is about merge-queue serialization rather than the projection gate. It enumerates advisory-versus-enforced slot semantics across all 70 interference placements in the window, and the surviving greens include those showing advisory mechanisms lose the transaction to a peer advance while enforced slots exclude the peer. That enumeration is the expensive part. #10378 landed a merge-queue proposal with a measured contention rate; if that subject wants a fixture population for advisory-vs-enforced, it should pull the enumeration into its own framing rather than inherit a draft built around the withdrawn gate. The current verdicts must not be trusted until the two controls are made to fire.

Closed by the owning lane's own assessment, with the branch left in place.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WgiDD3VoavwLrcu832nJ2V

@gunbai-bot gunbai-bot Bot closed this Sep 4, 2026
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.

0 participants