Skip to content

SCM: the merge verb — join the base derivation to the path algebra - #10676

Merged
briansrls merged 9 commits into
mainfrom
scm-merge-verb
Sep 7, 2026
Merged

briansrls merged 9 commits into
mainfrom
scm-merge-verb

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

gunbc.scm.merge_base decides which base a squash is entitled to use and refuses when there is none. gunbc.scm.manifest_merge decides each path against a base and refuses when the sides disagree irreconcilably. Both refuse independently and both were controlled — but nothing joined them, so there was no operation anyone could invoke to merge anything. This is that join. It introduces no decision of its own: every refusal is one of the two modules' refusals carried outward, or a store fact observed at the boundary.

The order is forced, not chosen

The base is derived before the manifests are read, for the reason merge_base runs its consumed-source join before it looks for a common ancestor: deriving a merged manifest against a base the repository is not entitled to use constructs the unsafe value and then discards it. A resurrection that is computed and thrown away still existed.

The squash workflow is now structural rather than conventional

The result records one lineage edge — parent is the target — and the source is recorded as consumed via SquashIntegrated rather than as a second parent. Nothing is lost: that receipt is precisely what merge_base reads to refuse the second merge. So "never merge the same branch twice" is enforced by construction instead of discipline, and there is no dev history to rebase because none is created.

Refusals are not flattened

A consumed source, a conflicting path, a missing manifest and an invalid allocator have four different remedies and four different principals to blame.

One place deliberately does collapse, and it is flagged rather than hidden. carry_mint folds the mint's three root refusals into one SquashMergeMintRootUnresolvable, because the root handed to the mint was minted by this function from the store one line earlier — so all three mean "the store did not keep what it just accepted", one fact about one store rather than three populations a caller acts on differently. This is the judgement in the module I am least sure of, since the rest of this lane argues the other way, and it is written down so a reviewer can overturn it rather than have to find it.

The fixture moved instead of being copied

The repository builder was authored inside the merge_base witness and is needed verbatim here. Two copies of one construction rule drift invisibly — each keeps passing its own claims while the two fixtures quietly stop describing the same repository — so it moved to test.fixture.scm_repository_builder.

All twelve merge_base claims pass unchanged against the shared fixture. That is the right control for a rehome: a builder that had silently changed would still compile on both sides, and would surface only as a claim that stopped discriminating.

The move is a membership motion, so its nine bindings are declared in the namespace-wave-admission roster with their rationale and a dissolution trigger, rather than done quietly.

Evidence

446/0 across all SCM witness files. The two halves' own behaviours are not re-asserted here; what is unproven until this file exists is that the join preserves them.

mutation both paths parent/receipt double merge conflict
parent on the source pass RED pass pass
drop the consumed receipt pass RED RED pass
ignore merge_base's answer RED pass pass RED
as built pass pass pass pass

The second is load-bearing: dropping the receipt reds the double-merge claim, so the resurrection refusal genuinely flows through the verb rather than being asserted beside it.

The scene is a divergence, not a fast-forward, deliberately. If the target had not moved since the base, taking the source's manifest wholesale would be correct and every claim here would pass against an implementation that ignores the base entirely.

Note

Built on scm-algebra (#10668). The two newest manifest_merge claims — the symmetry law and the over-conservatism controls — are not in this branch's count; they arrive when #10668 lands.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9

gunbc-ci-auto-heal and others added 6 commits September 6, 2026 16:23
…erand

The four-line three-way law -- S==B takes T, T==B takes S, S==T takes either,
else conflict -- silently assumes all three sides HAVE the path. B, S and T each
independently may not, which is eight states rather than four.

THE CONCLUSION DRAWN FROM THAT WAS WRONG THE FIRST TIME AND IS RECORDED HERE
BECAUSE THE CORRECTION IS THE DESIGN. I proposed enumerating the eight cases. What
the discovery actually falsifies is BARE-LOCATOR OPERANDS, not the law. Widening
the operand to

    PathState = PathAbsent | PathPresent { source: AuthoredSourceTarget }

whose equality INCLUDES ABSENCE keeps the four lines total, and every presence
case falls out as a consequence rather than a rule: source-deleted, both-deleted,
source-added, both-added-identically, target-deleted, and both-added-differently.
An eight-case implementation would have duplicated one algebra across presence
combinations and let the copies drift -- decompress and map without the reduce,
which is the redundancy DESIGN section 2 names.

DELETE-VERSUS-MODIFY AND MODIFY-VERSUS-DELETE ARE THE POINT. They are the
resurrection class of gunbc.scm.merge_base one layer down, at PATH grain rather
than LINEAGE grain: taking the deletion silently discards an edit, taking the edit
silently resurrects a file the other side deleted on purpose. Both produce a valid
manifest and neither is visible to the caller. The law refuses them with no clause
of its own -- there is simply no equality to appeal to.

THE CONFLICT CARRIES ALL THREE STATES INCLUDING EXPLICIT ABSENCE. A deletion
reported as a sentinel or fabricated locator would collapse absence back into a
malformed-content representation -- the exact conflation the operand was widened
to remove, reintroduced in the value that REPORTS it.

No entries on the conflicted arm: a partial manifest beside a conflict list invites
a caller to actuate the non-conflicting prefix, a corpus neither author wrote. And
the conflict population is COMPLETE rather than first-wins, because fix-one,
re-run, discover-another hides the size of the job -- each round individually
honest, the sequence not.

EVIDENCE. 442/0 across all SCM witness files, up from 435. Three mutations, each
reding a DIFFERENT combination, so no claim duplicates another:

    mutation                presence  del/mod  mod/del  complete  union
    bare-locator equality   RED       RED      RED      RED       --
    first-wins conflicts    pass      pass     --       RED       --
    base-keyed subject      RED       --       --       pass      RED
    as built                pass      pass     pass     pass      pass

One prediction of mine was wrong and is recorded rather than quietly dropped: I
expected the complete-population claim to red under the base-keyed mutation. It
does not, because all three of its paths exist in the base. Harmless -- that
mutation is caught twice over -- but the wrong prediction is only visible because
it was stated before the run.

The three-source fixture is load-bearing, not spare: with two sources, any path
where source and target disagree has one of them equal to the base, which the law
RESOLVES instead of refusing, so an all-three-differ conflict is inexpressible.
The first writing of the complete-population claim had exactly that defect --
intended two conflicting paths and built one -- and the claim caught it.

The S==T arm returns one operand, and that the choice is unobservable is asserted
by a claim rather than assumed. "They are equal so it does not matter" is the shape
of reasoning that hid the root-versus-occurrence defect one module over.

NOT IN THIS CUT: composing base derivation, manifest storage and the target-child
mint into a merge verb. Both halves now exist and refuse independently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
…ords, and its traversal from quadratic to one pass

Both blocking findings of review 61418 on 7ccfe27, verified against the code
before acting. Both are correct, and their fixes turn out to be the same fix.

THE OPERANDS. merge_manifests took List<CorpusManifestEntry> on all three sides.
That is worse than loose typing, because the module carried an ANNOTATION
asserting that store_corpus_manifest refuses a duplicate path "so the first match
is THE match" -- an assertion about a guarantee THE SIGNATURE DID NOT CARRY, which
is validation-by-comment standing exactly where construction was available
(DESIGN 5). The reviewer's specimen: a source of [a -> X, a -> Y] merged
SUCCESSFULLY by silently selecting the first, and reversing that list changed the
answer. Fabricated plausible output, forbidden outright. The three sides are now
CorpusManifestRecord, which is sole_constructor and canonicalised at the store, so
the specimen is no longer expressible at the boundary.

THE TRAVERSAL. Every union path re-folded all three inputs through path_state_in
-- quadratic in manifest size, and quadratic even when the three sides are
identical and nothing conflicts. DESIGN 6 makes that unconditional: a proven
cost-shape defect is always fixed regardless of the realized n, because n is not a
time-stable fact about a compiler's corpus manifest. Replaced by a sorted grouping
join -- tag each entry with its side, sort the three streams together by path, fold
ONCE, close a group when the path changes.

path_state_in and merged_path_union are DELETED, not made faster. The union, the
three lookups and the decision are now the same traversal, so there is nothing left
for them to answer.

THE TWO ARE ONE. Assigning a group member to its side is total ONLY because no side
can hold two entries for one path. A raw list can hold exactly that; a record
cannot.

A DECORATION IS DELETED RATHER THAN REPAIRED. The S==T "either operand is
unobservable" claim called merged() with IDENTICAL arguments twice -- but repairing
the swap would not have saved it. That arm is reached only when path_state_eq
holds, path_state_eq on two present states compares locators, and
AuthoredSourceTarget carries a locator and nothing else, so the operands are
indistinguishable to every observer this corpus can write. No input could turn it
red, so it asserted nothing while looking like coverage (DESIGN 4b). The
unobservability is STRUCTURAL, which is a stronger statement than the claim made.
Its red becomes authorable the day AuthoredSourceTarget gains a field outside the
equality, and the annotation says so. That claim was cited as evidence in the PR
body, so the PR body overstated what had been verified; it is corrected there.

THE 4c REFUSAL, AND WHY MY OWN GUARD DID NOT SEE IT. The floor lane refused eight
in-body annotations in the witness file. The local pre-push guard reported ZERO. It
classified by what FOLLOWED an annotation block, and in-body comments are followed
by ordinary expressions, which it read as "not a declaration, keep looking". A
detector with false NEGATIVES is worse than no detector for the same reason a
detector with false positives is: it gets cited as coverage. Rewritten to key on
the only thing the rule is about -- module-item grain means column zero -- and
controlled against 7ccfe27, where it reproduces all eight refused lines plus a
ninth the CI log had truncated, and reads zero here. The labels themselves were
worth keeping, so they are hoisted into the leading annotation of the claim they
describe rather than dropped.

EVIDENCE. 441/0 across all SCM witness files -- 442 minus the deleted decoration.
Four mutations, two aimed at the join that did not exist before:

    mutation                       presence  del/mod  mod/del  complete  union
    bare-locator equality          RED       RED      RED      RED       RED
    first-wins conflicts           pass      pass     pass     RED       pass
    last group never closed        RED       RED      RED      pass      RED
    target side dropped from join  RED       RED      pass     RED       RED
    as built                       pass      pass     pass     pass      pass

The last group is closed by close_groups and not by the fold, because a fold closes
a group when it sees the NEXT path and the final group has none. An implementation
missing that line drops the alphabetically last path from every merge and still
returns a well-formed manifest.

NO CLAIM IS ADDED FOR THE UNIQUENESS FIX, deliberately. It is now structural, so
its red is unauthorable, which is the same test that retired the decoration above.

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

gunbc.scm.merge_base decides WHICH BASE a squash is entitled to use and refuses
when there is none. gunbc.scm.manifest_merge decides EACH PATH against a base and
refuses when the sides disagree irreconcilably. Both refuse independently, and both
were controlled -- but nothing joined them, so there was no operation anyone could
invoke to merge anything. This is that join. It introduces NO decision of its own:
every refusal is one of the two modules' refusals carried outward, or a store fact
observed at the boundary.

THE ORDER IS FORCED, NOT CHOSEN. The base is derived BEFORE the manifests are read,
for the reason merge_base runs its consumed-source join before it looks for a
common ancestor: deriving a merged manifest against a base the repository is not
entitled to use constructs the unsafe value and then discards it, and a
resurrection that is computed and thrown away still existed.

THE SQUASH WORKFLOW IS NOW STRUCTURAL RATHER THAN CONVENTIONAL. The result records
ONE lineage edge -- parent is the target -- and the source is recorded as CONSUMED
through SquashIntegrated rather than as a second parent. Nothing is lost by that:
the receipt is precisely what merge_base reads to refuse the second merge, so "do
not merge the same branch twice" is enforced by construction instead of by
discipline, and there is no dev history to rebase because none was created.

REFUSALS ARE NOT FLATTENED INTO ONE "MERGE FAILED" ARM. A consumed source, a
conflicting path, a missing manifest and an invalid allocator have four different
remedies and four different principals to blame. Collapsing them is the absorbing
fallback DESIGN section 5 names.

ONE PLACE DELIBERATELY DOES COLLAPSE, AND IT IS FLAGGED RATHER THAN HIDDEN.
carry_mint folds the mint's three root refusals into one
SquashMergeMintRootUnresolvable, because the root handed to the mint was minted BY
THIS FUNCTION from the store one line earlier -- so all three mean "the store did
not keep what it just accepted", which is one fact about one store and not three
populations a caller acts on differently. This is the one judgement in the module I
am least sure of, since the rest of the lane argues the other way, and it is
written down so a reviewer can overturn it rather than have to find it.

THE FIXTURE MOVED INSTEAD OF BEING COPIED. The repository builder was authored
inside the merge_base witness and is needed verbatim here. Two copies of one
construction rule drift INVISIBLY -- each copy keeps passing its own claims while
the two fixtures quietly stop describing the same repository -- so it moved to
test.fixture.scm_repository_builder and both witnesses read it. All twelve
merge_base claims pass unchanged against the shared fixture, which is what makes
the extraction safe to build on rather than a hopeful refactor.

EVIDENCE. 446/0 across all SCM witness files, up from 441. The two halves' own
behaviours are NOT re-asserted here; what is unproven until this file exists is
that the JOIN preserves them. Three mutations, each reding a different combination:

    mutation                     both paths  parent/receipt  double merge  conflict
    parent on the source         pass        RED             pass          pass
    drop the consumed receipt    pass        RED             RED           pass
    ignore merge_base's answer   RED         pass            pass          RED

The second is the load-bearing one: dropping the receipt reds the double-merge
claim, so the resurrection refusal genuinely flows THROUGH the verb rather than
being asserted beside it.

The scene is a DIVERGENCE and not a fast-forward, deliberately. If the target had
not moved since the base, taking the source's manifest wholesale would be correct
and every claim here would pass against an implementation that ignores the base
entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
…cal guard the domain it was missing

The floor lane refused 20 section 4c violations in squash_merge.dag: rationale
written BETWEEN the arms of SquashMergeOutcome, and one note inside a call
argument list. Only module-item grain is modeled. The per-arm reasoning is worth
keeping, so it moves into one table above the type rather than being deleted.

AND THE LOCAL GUARD REPORTED ZERO, FOR THE THIRD TIME AND THE THIRD DIFFERENT
REASON. It enumerated with `git ls-files`, which lists TRACKED files only, and the
new module was untracked when the guard ran -- so it opened every file except the
one the change added, which is the only file that could have been newly wrong.

The first version classified by what FOLLOWED an annotation block and missed
in-body comments. The second keyed on column zero, which was right, and was
controlled against a known-bad specimen -- but the control only proved the
PREDICATE, and the defect this time was the DOMAIN. A detector's domain is as
load-bearing as its test, and nothing I had run would have told me otherwise,
because a detector that never opens a file reports the same "0" as one that opens
it and finds nothing clean.

Fixed to enumerate tracked AND untracked, and re-controlled the honest way: the
repaired guard reproduces all 20 of CI's violations at CI's line numbers, then
reads 0 after the repair. The reproduction against the failing specimen is the
part that was missing, not the clean read.

Witnesses unchanged and still 5/5; this cut moves prose, not behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
The floor lane refused the merge-verb branch with nine unadjudicated
namespace-wave-admission deltas. All nine are mine and the wall is correct: moving
a declaration between modules is a membership motion, and this roster exists so it
is DECLARED rather than done quietly.

WHAT MOVED AND WHY. test.claim.scm_merge_base_witness authored a repository builder
-- MbBuild with its arms MbBuilt and MbSetupFailed, and the six operations mb_start,
mb_stage, mb_commit, mb_at, mb_head, mb_root_of -- that constructs scenes through
the real store and mint instead of forging rows. The squash-merge witness needs it
VERBATIM. Copying it would put one construction rule in two files and the drift
would be INVISIBLE: each copy would keep passing its own claims while the two
fixtures quietly stopped describing the same repository. So it rehomes to
test.fixture.scm_repository_builder, which is where this repository already puts
fixtures shared across claims.

NINE BINDINGS, ONE MODULE, ONE CHANGE CLASS: seven inside mb_scene and two inside
scm_mb_the_scene_holds_the_root_relations_the_controls_depend_on. Every spelling is
identical on both sides and only the declaring module differs, which is exactly the
motion TargetChanged names. Nothing is requalified.

THE EVIDENCE THAT THE MOVE PRESERVES BEHAVIOUR IS EXECUTED, NOT ASSERTED: all twelve
merge_base claims pass unchanged against the shared fixture. That is the right
positive control for a rehome, because a builder that had silently changed would
surface as a claim that stopped discriminating rather than as a compile error --
the fixture still compiles either way.

Each row names the exact (module, in_declaration, spelling, target) tuple and admits
nothing else, and the trigger is the rows' own death: when this PR merges, base and
head bind every spelling identically, no run can produce these deltas, and all nine
report CONSUMED, due for deletion on the roster's next touch. That deletion is to be
adjudicated by joining each row against main's tree on its own tuple, not by
trusting this sentence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
A merge commit rather than a rebase, per the repository's merge policy: history is
squashed at merge time anyway, so rebasing would buy nothing and cost a force-push.

ONE CONFLICT, IN THE ADMISSION ROSTER, AND IT IS THE ROSTER WORKING. Main dissolved
the gunbc#10639 probe-capture rows by their own trigger and added the
gunbc#10671 leg-observation rows in the same region; this branch inserted its nine
fixture-rehome rows at the head of the same list. The two edits are independent
additions to one ordered table, so the resolution is the UNION -- all nine of mine,
then main's row intact -- and neither side's authority is dropped.

The one thing worth checking rather than assuming was the dissolved const: main
removed PROBE_CAPTURE_REHOME_LABEL along with its rows, so taking my side wholesale
would have left nine of my rows referencing a label that main deleted. The resolved
file references it zero times and compiles, which is the fact rather than the
intention.

Verified after resolving, not before: v1-compiler checks clean, and all twenty-three
SCM witnesses across merge_base, manifest_merge and squash_merge pass. The two
newest manifest_merge claims -- the symmetry law and the over-conservatism controls
-- are deliberately NOT in that count: this branch was cut from scm-algebra before
that rework, so they arrive here when gunbc#10668 lands rather than being claimed
now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 6, 2026 21:24
@gunbai-bot gunbai-bot Bot changed the title SCM MVP-N SCM: the merge verb — join the base derivation to the path algebra Sep 6, 2026
gunbc-ci-auto-heal and others added 3 commits September 6, 2026 21:45
… named

The floor lane admitted this branch's nine fixture-rehome rows and then refused on
a different count: four CONSUMED admissions due for deletion on this
roster-touching change. That is the roster's own discipline working. gunbc#10671
merged, so its cable-leg rows can no longer be produced by any run, and they come
due on the next change that touches this file -- which is this one.

ADJUDICATED BY THE JOIN THOSE ROWS DEMANDED RATHER THAN BY THEIR OWN SENTENCE. The
entries say in as many words not to trust the sentence, so the deletion was decided
against main's tree in all three directions the join has:

  - product.cable_leg_observation DECLARES SecondaryNotObserved, as an arm of its
    compliance coproduct.
  - extdeps.transceiver.sff_8636 does NOT declare it. Its only surviving occurrence
    of the spelling is PROSE recording that an earlier head authored it -- which is
    the trap a grep count falls into and a declaration check does not. A count would
    have read 1 and I would have concluded the old declarer was still live and left
    four dead rows standing.
  - Both consumers -- test.claim.cable_leg_coding_witness and
    test.claim.cable_order_admission_witness -- import the spelling from the new
    declarer.

So base and head bind it identically, no run can produce those four deltas, and
CONSUMED is the correct reading rather than an author error. The const goes with
its rows; the compiler confirms nothing else referenced it.

The dissolution is recorded in the file, in the form the file already uses, so the
next author inherits the adjudication rather than the conclusion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
One conflict, add/add on the manifest-merge witness, and it is not a real
disagreement: this branch was cut from scm-algebra BEFORE the side-chat rework and
carries that file's older copy, while main now carries the reworked one. Verified
rather than assumed -- diffing this branch against its own branch point shows it
never touched either manifest_merge file, so taking main's side loses nothing that
originated here.

What arrives with it is the pair this branch's own commit message said would arrive
when gunbc#10668 landed: the symmetry law and the three over-conservatism controls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
…ring it more elegantly

Side-chat ruling on the one judgement this branch flagged for overturning, and it
overturned BOTH options I offered rather than picking one.

WHAT I HAD. carry_mint folded mint_repository_commit's three root refusals into a
single SquashMergeMintRootUnresolvable, arguing that on this route all three mean
"the store did not keep what it just accepted".

WHY THAT IS WRONG, VERIFIED AGAINST THE CODE RATHER THAN ACCEPTED. store_corpus_manifest
returning CorpusManifestStored { store, manifest } already establishes that store
holds a manifest object at that locator -- an absent or wrong-kind locator would
have returned the store's own refusal instead. commit_merged_manifest then mints
against THAT store with THAT locator, and the mint's three root refusals are exactly
the three non-success arms of find_corpus_manifest_record, which the preceding
success has excluded. So the arms are UNREACHABLE, and the collapse fabricated a
public cause no execution can produce.

It was wrong a second way that matters more. It reasoned about THIS CALLER'S CONTEXT
INSIDE A TRANSLATOR THAT CANNOT SEE IT: handed a RepositoryCommitMint, nothing tells
you the value came from a store call one line earlier. And the three are not one fact
even in the impossible case -- a missing root means the object vanished, while a
wrong-kind root means the locator now denotes something else, which contradicts a
collision-aware insertion far more strongly.

WHY KEEPING THEM DISTINCT WAS ALSO REFUSED, which is the part I had not seen: it
would be honest about the CAUSE and still dishonest about the DOMAIN, leaving three
states in SquashMergeOutcome that no run of squash_merge can reach.

SO THE QUESTION IS REMOVED. mint_commit_from_stored_manifest consumes the
CorpusManifestObjectRef the store branded, runs every other admission -- allocator,
integration anchor preexistence, parent existence -- and returns BrandedRootMint,
which has no root arms. mint_repository_commit resolves the root and then delegates,
so the admission rules have ONE authority rather than two copies free to drift, and
a caller holding only a locator still gets all three refusals because for that caller
they are reachable and real. Rung 4: the invalid state has no constructor here.

TWO OF MY OWN ERRORS ON THE WAY, BOTH CAUGHT BY EXECUTION. I deleted
integration_anchor_resolves, which sat inside the region I replaced -- the compiler
caught it. And my first narrow projection mapped the impossible root arms onto a
fabricated allocator cause, committing the exact sin the ruling names, one level
down. Removed by making mint_repository_commit_admitted return the narrow type
directly, since it performs no admission and can only mint.

448/0 across the SCM witnesses on the merged tree.

REVIEW STANDING, STATED PLAINLY: the three approvals on this PR were all given
against 316b7d9, which still carried the collapsed arm. None of them has seen
this commit, and this one touches repository_envelope, a load-bearing module. The
tally on this PR currently overstates its review coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
@briansrls
briansrls merged commit 59a692a into main Sep 7, 2026
4 checks passed
@briansrls
briansrls deleted the scm-merge-verb branch September 7, 2026 02:27
@briansrls
briansrls restored the scm-merge-verb branch September 7, 2026 02:30
@gunbai-bot

gunbai-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Reworked at 11c57e571a4. Two reviews acted on, both verified against the code first.

review 61590 — the repeated builder. Correct, fixed.

All three claims bound Scene { build: _, … }, discarded the build, then re-ran the entire builder one line later via scene_repository(s: scene()) to recover the repository that discarded field already held. §2 exactly as cited, and §6 forbids pricing an exception for small n.

The consequence the finding doesn't name is worse than the cost: each claim was building two repositories and asserting about the second. The scene the surrounding match described and the scene under test were different objects that merely happened to be equal — a fixture that has quietly stopped being the one its own match names. Fixed by binding the build and replacing the helper with one that takes it, so re-deriving is no longer expressible.

A blocker found after every approval on this PR was cast

The reviews at 316b7d9f80e and 16b6c84c5bf all read the mint seam as sound. It was not, and I want the record to show that rather than the tally.

mint_commit_from_stored_manifest(repository, root: CorpusManifestObjectRef) took two independent parameters. The brand is sole_constructor, so it cannot be forged — but it means "some store minted this locator at this kind", never which store. So this was authorable: store a manifest into store A, take the reference, hand it to a repository whose store is B. Allocator valid, parent absent, integration absent — and the mint writes a commit whose root is absent from B.

That falsified my own annotation, which claimed the store had just branded the root. The signature established a repository and a brand from somewhere. It is the same defect fixed one level up on #10668 — an annotation asserting a guarantee the signature does not carry — reintroduced here while fixing something else. "Only squash calls it correctly" is no defence: .dag has no module privacy.

And a second one: mint_repository_commit_admitted was an importable forge factory — raw repository, raw brand, decided ancestry, no admission at all, straight to appending a commit. It predates this branch, but the rung-4 claim made here leaned on it as the terminal constructor, and no such claim stands while that bypass is authorable.

The repair seals the relation, not its two endpoints. RepositoryManifestRoot is sole_constructor and pairs a repository with a root its own store contains, with two producers that each establish the pairing: resolve-a-target, or store-the-entries-here so the caller never holds the halves. The forge factory is deleted, its body inlined behind the admissions.

A branded reference proves kind. This carrier proves membership in this repository's store.

The wall is checked, not asserted. §4b's question was asked first — is the forbidden state authorable anywhere a check could run? It is, so the probe was written and executed: sole_constructor type 'RepositoryManifestRoot' cannot be constructed outside its defining module. Honest limit: that RED cannot stay enrolled as a claim, because a file that fails to compile cannot live in this corpus. The evidence is a one-off executed refusal, not a standing control; the standing guard is the sole_constructor declaration itself.

Not taken, flagged rather than dropped: sealing store_corpus_manifest's success into a StoredCorpusManifest so its two facts are never separable anywhere. Real and broader, but it changes an arm destructured at ~20 sites across the witness corpus and is separable from this blocker, which is closed without it.

448/0. The approvals on this PR predate the seal — please re-read rather than counting them.

— sent from deep-carp-676

gunbai-bot Bot pushed a commit that referenced this pull request Sep 7, 2026
… rows.

The floor_effect_gate_witness _passes functions now bind exit_ok to std.process; the nine scm builder-rehome admissions were consumed on main and came due on this roster touch.

Co-authored-by: Cursor <cursoragent@cursor.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 7, 2026
… rows.

The floor_effect_gate_witness _passes functions now bind exit_ok to std.process; the nine scm builder-rehome admissions were consumed on main and came due on this roster touch.

Co-authored-by: Cursor <cursoragent@cursor.com>
briansrls pushed a commit that referenced this pull request Sep 7, 2026
…ish (#10701)

* Give docs/*.md ledger projections a ProcessExit actuator that can finish.

Required-regen never owned those files, and main_wet dies inside the whole-registry emit graph, so rostered failure-mode rows could sit stale with every completing instrument green. A docs-only gate plus a required docs-projections population refuse that split.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Correct the docs-projection failure-mode row: identity join is membership, not content.

The specimen is three edited rows that stayed named in the projection; a name-set join would have called the file complete. The projector returns String so gunbc run exits 2 without a ProcessExit actuator.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record that NotProcessExit interpolates type_name only, as a deliberate wall.

The projector cannot be scraped for markdown; ProcessExit regen is the repair shape, and review 58567 on #9864 is a prior specimen of the same class.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Retract the type-name-only claim: NotProcessExit.type_name carries format_value on the non-variant arm.

Docs-projection content is extractable only by scraping that unversioned diagnostic. The field-name fork is a separate row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Regenerate docs/design-failure-modes.md so the new docs-projection gate is green on this tree.

The roster rows were unprojected (review 61515); receipts also drop interpolating type_name so the regen entry resolves.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Use artifact_path as the docs-ledger location authority and record the executed stderr scrape.

Deletes the duplicated path literals and the join test that policed them. Regenerates the failure-mode projection after the false type-name-only assertion is replaced with the EXIT=2 empty-stdout receipt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record that the NotProcessExit control never calls classify_exit, then regenerate the projection.

The enrolled test hand-builds type_name Bool, so its RED cannot see the rendered-value arm. docs_projection_gate main exited 0 on that regenerated tree.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Lift exit_ok to std.process so the three gate copies are one constructor.

docs_projection_gate, ci_gates, and generated_artifact_gate now consume it; floor_effect_gate_witness follows.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Give the docs-projection regen recipe one home in the agreement module.

The host no longer reprints a second copy of the same command on the required-ci refusal.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Ground the two-ledger heal population as policy, not a copied count.

Restores the rationale that only the unbounded roster projections heal, without citing the deleted path-join test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record heron's unparseable-authority incident as the detector-gap specimen.

The quoting injury stays a neighbouring class; this row only claims the missing completing gate. Regen attaches comments to the declarations they describe so the projector parses.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Eliminate std.process Bool exit_ok; join seed-growth trigger to the roster.

Call sites match ProcessExit. Heal registration-without-completion goes in the failure-mode row. The seed-growth trigger names hand_authored_declarations instead of a transcribed 20.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Retract the heal-dies mechanism from the docs-projection row.

heal main_wet completed on another branch; the 137 was claim_executor. Comments now mark tools.ci_gates.exit_ok as deleted.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Drop false main_wet-cannot-complete claims; untranscribe the registry fold.

required-regen ownership motivates the docs actuator. Ordering still defends against an observed whole-registry stall; the fold is not a 35.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Restore exhaustive std.process exit_ok and drop stale residue citations.

Claim Bool collapse has one home; sequencers still match ProcessExit. Residue rows named the deleted instrument copies.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Point the generated-artifact loudness comment at std.process.exit_ok.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: regenerate drifted generated artifacts (ci auto-heal)

Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md excluded_witness_outlives_the_subject_it_names
Ledger-Rows-Repaired: docs/design-failure-modes.md docs_markdown_projections_have_no_completing_actuator
Ledger-Rows-Repaired: docs/design-failure-modes.md type_name_field_carries_rendered_value
Ledger-Repair-Judged: docs/design-rung-drops.md

* Cite the existing claim-surface stall as exit_ok's standing disposition.

The Bool collapse is not an untracked residue: sequencers already match ProcessExit, and tools.emit_host_gate emit_host_verdict_narration_dissolution_trigger names when the claim path can carry typed reasons.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Say exit_ok is not rostered; the Bool claim gap is analysed on the narration trigger.

That trigger deletes the scaffold, not this predicate. Both retire when the claim path carries typed reasons.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Admit the seven exit_ok TargetChanged bindings and drop consumed #10676 rows.

The floor_effect_gate_witness _passes functions now bind exit_ok to std.process; the nine scm builder-rehome admissions were consumed on main and came due on this roster touch.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: regenerate drifted generated artifacts (ci auto-heal)

Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md excluded_witness_outlives_the_subject_it_names
Ledger-Rows-Repaired: docs/design-failure-modes.md docs_markdown_projections_have_no_completing_actuator
Ledger-Rows-Repaired: docs/design-failure-modes.md type_name_field_carries_rendered_value
Ledger-Repair-Judged: docs/design-rung-drops.md

* Cite std.process.exit_ok in the scope-placement loudness comment.

The other two sequencers already named the rehomed predicate; this copy still pointed at tools.ci_gates after that symbol was deleted.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
briansrls pushed a commit that referenced this pull request Sep 7, 2026
…ish (#10701)

* Give docs/*.md ledger projections a ProcessExit actuator that can finish.

Required-regen never owned those files, and main_wet dies inside the whole-registry emit graph, so rostered failure-mode rows could sit stale with every completing instrument green. A docs-only gate plus a required docs-projections population refuse that split.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Correct the docs-projection failure-mode row: identity join is membership, not content.

The specimen is three edited rows that stayed named in the projection; a name-set join would have called the file complete. The projector returns String so gunbc run exits 2 without a ProcessExit actuator.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record that NotProcessExit interpolates type_name only, as a deliberate wall.

The projector cannot be scraped for markdown; ProcessExit regen is the repair shape, and review 58567 on #9864 is a prior specimen of the same class.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Retract the type-name-only claim: NotProcessExit.type_name carries format_value on the non-variant arm.

Docs-projection content is extractable only by scraping that unversioned diagnostic. The field-name fork is a separate row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Regenerate docs/design-failure-modes.md so the new docs-projection gate is green on this tree.

The roster rows were unprojected (review 61515); receipts also drop interpolating type_name so the regen entry resolves.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Use artifact_path as the docs-ledger location authority and record the executed stderr scrape.

Deletes the duplicated path literals and the join test that policed them. Regenerates the failure-mode projection after the false type-name-only assertion is replaced with the EXIT=2 empty-stdout receipt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record that the NotProcessExit control never calls classify_exit, then regenerate the projection.

The enrolled test hand-builds type_name Bool, so its RED cannot see the rendered-value arm. docs_projection_gate main exited 0 on that regenerated tree.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Lift exit_ok to std.process so the three gate copies are one constructor.

docs_projection_gate, ci_gates, and generated_artifact_gate now consume it; floor_effect_gate_witness follows.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Give the docs-projection regen recipe one home in the agreement module.

The host no longer reprints a second copy of the same command on the required-ci refusal.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Ground the two-ledger heal population as policy, not a copied count.

Restores the rationale that only the unbounded roster projections heal, without citing the deleted path-join test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Record heron's unparseable-authority incident as the detector-gap specimen.

The quoting injury stays a neighbouring class; this row only claims the missing completing gate. Regen attaches comments to the declarations they describe so the projector parses.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Eliminate std.process Bool exit_ok; join seed-growth trigger to the roster.

Call sites match ProcessExit. Heal registration-without-completion goes in the failure-mode row. The seed-growth trigger names hand_authored_declarations instead of a transcribed 20.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Retract the heal-dies mechanism from the docs-projection row.

heal main_wet completed on another branch; the 137 was claim_executor. Comments now mark tools.ci_gates.exit_ok as deleted.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Drop false main_wet-cannot-complete claims; untranscribe the registry fold.

required-regen ownership motivates the docs actuator. Ordering still defends against an observed whole-registry stall; the fold is not a 35.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Restore exhaustive std.process exit_ok and drop stale residue citations.

Claim Bool collapse has one home; sequencers still match ProcessExit. Residue rows named the deleted instrument copies.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Point the generated-artifact loudness comment at std.process.exit_ok.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: regenerate drifted generated artifacts (ci auto-heal)

Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md excluded_witness_outlives_the_subject_it_names
Ledger-Rows-Repaired: docs/design-failure-modes.md docs_markdown_projections_have_no_completing_actuator
Ledger-Rows-Repaired: docs/design-failure-modes.md type_name_field_carries_rendered_value
Ledger-Repair-Judged: docs/design-rung-drops.md

* Cite the existing claim-surface stall as exit_ok's standing disposition.

The Bool collapse is not an untracked residue: sequencers already match ProcessExit, and tools.emit_host_gate emit_host_verdict_narration_dissolution_trigger names when the claim path can carry typed reasons.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Say exit_ok is not rostered; the Bool claim gap is analysed on the narration trigger.

That trigger deletes the scaffold, not this predicate. Both retire when the claim path carries typed reasons.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Admit the seven exit_ok TargetChanged bindings and drop consumed #10676 rows.

The floor_effect_gate_witness _passes functions now bind exit_ok to std.process; the nine scm builder-rehome admissions were consumed on main and came due on this roster touch.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: regenerate drifted generated artifacts (ci auto-heal)

Ledger-Repair-Judged: docs/design-failure-modes.md
Ledger-Rows-Repaired: docs/design-failure-modes.md excluded_witness_outlives_the_subject_it_names
Ledger-Rows-Repaired: docs/design-failure-modes.md docs_markdown_projections_have_no_completing_actuator
Ledger-Rows-Repaired: docs/design-failure-modes.md type_name_field_carries_rendered_value
Ledger-Repair-Judged: docs/design-rung-drops.md

* Cite std.process.exit_ok in the scope-placement loudness comment.

The other two sequencers already named the rehomed predicate; this copy still pointed at tools.ci_gates after that symbol was deleted.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.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