Skip to content

SCM note: describe the operation that shipped, and measure the scenario corpus - #8794

Merged
briansrls merged 1 commit into
mainfrom
session/scm-note-truth-and-coverage
Aug 21, 2026
Merged

briansrls merged 1 commit into
mainfrom
session/scm-note-truth-and-coverage

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Two corrections to docs/plans/dag-native-scm-design.md, both about it having stopped describing reality.

§7 specified an operation nobody can invoke

It read merge(store, first, second) -> Merged | ChoiceRequired | CouldNotLand. What shipped in #8719 is merge(store, target, target_dependencies, proposals) -> Merged | MergeRolesContested | MergeRefused.

A stale authority is the premise-contamination failure DESIGN records against its own CI paragraph — where a session planned an afternoon against a command that didn't exist. Rewritten to the shipped signature with the reasoning that produced it: why the target arrived, why proposals became a list, and why both outcome arms are deliberately named for less than they conclude.

§8 said "no merge kernel should be built without these" — and one was built

Measured by joining the 15 witness claims against the rows one at a time: 4 of 13 covered.

The table gains a blocked on column, because "uncovered" was collapsing three different situations:

count
sub-node recursion 4 the headline win; the kernel cannot express them
rename/move vocabulary 3 the model has none
positional merge 2 explicit non-goal of this slice
closable now 1 duplicate named siblings

That last row is the useful find. Measured: nothing in gunbc.scm.* or v2.std.node refuses a node carrying two children under one name — the nearest live behaviour is content-hash canonicalization sorting named edges, which orders duplicates rather than refusing them. It's the only structural scenario needing neither new grain nor new vocabulary.

My earlier estimate from reading was "roughly five" — close enough to feel safe, wrong enough to have mis-scoped the next slice. The note now says coverage claims must be measured, not recalled.

Direction recorded

A CLI vertical before the depth recursion, reversing the note's own "headline win is the next slice" ordering. The reason is DESIGN §5, not a change of view about value: nothing has ever consumed this kernel, and a witness suite authored alongside the code is not a consumer.

Safe to reorder because depth changes how merge combines, not how add authors. The cost is designed for rather than discovered — a CLI makes the top-level-binding limit user-visible, and that refusal must say so plainly instead of reading as a defect.

Doc-only; no .dag or Rust changes.

…io corpus

Two corrections to the design note, both about it being an authority that had stopped
describing reality.

SECTION 7 SPECIFIED AN OPERATION NOBODY CAN INVOKE. It read merge(store, first, second) ->
Merged | ChoiceRequired | CouldNotLand. What shipped in #8719 is merge(store, target,
target_dependencies, proposals) -> Merged | MergeRolesContested | MergeRefused. A stale
authority is not a cosmetic problem: it is the premise contamination DESIGN records against
its own CI paragraph, where a session planned an afternoon against a command that did not
exist. Rewritten to the shipped signature, with the reasoning that produced it -- why the
target arrived (the two-proposal form had nothing to preserve FROM, so it could only union
bindings, which ships a program referencing a deleted node), why proposals became a list (a
contest is a property of the population, not of a pair), and why both outcome arms are named
for less than they conclude.

SECTION 8 SAID "no merge kernel should be built without these" AND A KERNEL WAS BUILT. The
honest thing is to state which of the thirteen it answers rather than let a reader assume.
Measured by joining the 15 witness claims against the rows one at a time: FOUR ARE COVERED.

The table now carries a blocked-on column, because "uncovered" was collapsing three different
situations:

  4 gaps  sub-node recursion (the headline win; the kernel cannot express them)
  3 gaps  rename/move vocabulary the model does not have at all
  2 gaps  positional merge, an explicit non-goal of this slice
  1 gap   CLOSABLE NOW at the current grain -- duplicate named siblings

That last one is the useful find. Measured: nothing in gunbc.scm.* or v2.std.node refuses a
node carrying two children under one name, and the nearest live behaviour is content-hash
canonicalization SORTING named edges, which orders duplicates rather than refusing them. It is
the only structural scenario needing neither new grain nor new vocabulary.

My earlier estimate from reading was "roughly five", close enough to feel safe and wrong enough
to have mis-scoped the next slice. The note now says coverage claims must be measured.

DIRECTION RECORDED: a CLI vertical before the depth recursion, reversing the note's own
"headline win is the next slice" ordering. The reason is DESIGN section 5 rather than a change
of view about value -- nothing has ever CONSUMED this kernel, and a witness suite authored
alongside the code is not a consumer. Safe to reorder because depth changes how merge combines,
not how add authors. The cost is designed for rather than discovered: a CLI makes the
top-level-binding limit user-visible, and that refusal must say so plainly instead of reading
as a defect.
@briansrls
briansrls merged commit b1df371 into main Aug 21, 2026
2 checks passed
@briansrls
briansrls deleted the session/scm-note-truth-and-coverage branch August 21, 2026 21:25
gunbai-bot Bot pushed a commit that referenced this pull request Aug 21, 2026
13 conflicted paths resolved per-hunk to main's semantics; 286 import
lines cut from 10 files by the brace-balanced cutter. Guards pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 4, 2026
…ened to rename

The reviewer's point is structural: renaming a module re-qualifies EVERY claim it
declares. The four renamed locals were a subset of the migration, and reporting them
as the migration understated it by fifteen. The complete population is 20 identities
-- all 19 in the renamed module (4 of which also change local name), plus one pure
local rename in scm_commit_closure_json_v2_witness, whose module identity is unchanged.

Checked against that population rather than against the four: five of the nineteen are
cited in the SCM design document, none of them among the renamed four, and no workflow
YAML, roster or coverage join enrolls the module by name -- so the re-qualification has
no enrollment consequence to migrate. `git grep scm_merge_witness` is empty.

While checking the coverage join I found a transcribed count that had rotted: the design
document said "the 15 claims in test.claim.<module>" while the module declares 19. It was
true when #8794 wrote it and has been wrong since. Per DESIGN section 6 the fix is not a
corrected number -- both sites now name the module and let the count be re-derived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ4iThiZ1B9LPB9ePr8qa9
briansrls pushed a commit that referenced this pull request Sep 4, 2026
…carrying two contracts (#10355)

* Give the proposal vocabulary its own authority and stop one spelling carrying two contracts

gunbc.scm.merge answered a bounded question -- one target commit plus N authored
proposals, folded at role grain into one resulting root -- but it held the name
"merge". Two-commit merge is a materially different contract: two commit
occurrences and a derived common base, a different failure population, a
different output meaning. Section 3 says a materially different contract needs a
materially different name, and a second operation called merge was about to
arrive. This cuts the existing authority over BEFORE that happens rather than
after, because the fork is cheap to remove while it is still latent.

THE MODULE WAS ALSO THE ACCIDENTAL HOME OF ITS OWN INPUTS, and that was measured
rather than asserted. Of the eight modules importing gunbc.scm.merge, SIX
consumed only the proposal nouns and never the operation -- and three of those
six are production modules (gunbc.scm.status, gunbc.scm.authoring,
gunbc.scm.read_command). Only the two witness modules consumed the operation. A
vocabulary with three times the consumers of the operation it was filed under is
not a detail of that operation.

  gunbc.scm.proposal                      Requirement, RequireBinding,
                                          RequireBindingAbsent, requirement_role,
                                          Proposal, RoleDependency
  gunbc.scm.role_requirement_integration  everything else, with the outcome
                                          renamed off "Merge" through to the
                                          externally meaningful result

DesiredRoleValue STAYS WITH THE OPERATION, which corrects my own first partition.
I had placed it with the nouns on the reasoning that a contested alternative is
authored intent. The module's own annotation says otherwise and I had read past
it: a Requirement is authored syntax, a RoleValue is result provenance, and
DesiredRoleValue is the REQUESTED STATE that both normalize into -- produced by
normalize_requirement, which does object-store work and can refuse with a locator
collision. Derived from authored intent is not the same fact as authored intent;
a proposer cannot write one. Keeping it operation-side also keeps ObjectStore and
ObjectId out of the noun layer, so the dependency direction is proposal nouns ->
integration -> object store, never back.

RoleDependency, NOT ProposalDependency. The integration consumes this carrier in
TWO populations -- the target's own dependencies and the ones a proposal declares
-- and combines them in effective_dependencies, so a proposal-flavoured name
would make every target-side use a lie. It is named for the relation. Which
kinds an integration supports, which propagate a removal, and how an unsupported
kind refuses stay with the operation: that is classifier policy, not a property
of the row.

RESIDUE THIS DOES NOT CLOSE, recorded on the carrier rather than left silent:
target and target_dependencies still arrive as independent arguments and nothing
proves the second was derived from the first, so a foreign or empty population
can admit an invalid removal or fabricate a refusal. That is gunbc#10295's
subject-binding class at role-dependency grain. Moving the carrier to its right
home does NOT bind the population, and the annotation says so explicitly with a
next-rung trigger naming the capability.

PROSE MOVED WITH THE SYMBOLS. A rename that leaves notes pointing at the deleted
module is the evidence-bearing-narrative failure this lane was corrected on four
times: gunbc.scm.ancestry's mechanism citation, gunbc.scm.authoring's
apply_requirement citation, scm_authoring_witness_test's two references, the
signature block in dag-native-scm-design.md, and the current-state claim in
scm-demo-cli-rebuild.md. The witness module and its merge_-prefixed cells are
renamed too, so "merge" is free for the witness that will actually need it. The
terminal sweep over old identities leaves exactly one match, in the CLI plan,
and it is explicitly historical and invalidated.

No alias, no umbrella re-export: the old root is deleted, so a future import
cannot keep choosing the ambiguous authority.

EVIDENCE. Semantically a no-op, which is the whole claim: 83 cells across the
five affected witness modules pass, zero fail, and all nine touched modules
resolve and typecheck. Ancestry is untouched semantically -- MergedFrom and the
commit-merge model are the second cut and wait on the ancestry result shape being
settled first.

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

* Rename the witness module identity too, which my own sweep pattern could not see

REQUEST_CHANGES on #10355 (review 59814). Correct, and it exposes a hole in the
verification I ran rather than only a missed line.

The file was renamed and the operation was renamed, but the module DECLARATION
still read:

  module test.claim.scm_merge_witness

so the witness authority kept the exact spelling this PR exists to free, against
the contract it no longer describes.

WHY MY SWEEP MISSED IT, which is the part worth recording. The terminal sweep ran
over `scm_merge_witness_test` -- the FILE stem. The module identity is
`scm_merge_witness`, with no `_test` suffix, because the corpus convention is
`test.claim.scm_<name>_witness` for a file named `scm_<name>_witness_test.dag`.
The pattern was strictly narrower than its subject, so it reported clean over a
tree that still contained five matches. A green instrument that cannot express
the defect is the decoration DESIGN section 4b calls worse than absent, and this
is the same class the SCM reviewer warned about one round earlier: a stale
identity survives a sweep keyed to the wrong spelling.

The corrected pattern drops the suffix, and it is the one in this commit's
verification: `scm_merge_witness` matches both forms, `scm_merge_witness_test`
matches only one.

FOUR MORE REFERENCES were behind that hole -- the finding named one:
  the module declaration itself
  scm_commit_closure_json_v2_witness_test's annotation about where the merge-arm
    claim lives, which additionally cited it WRONG as `test.claim.scm.scm_merge_witness`
    with a segment that never existed
  three citations in docs/plans/dag-native-scm-design.md, all naming the claim
    population that measures kernel coverage

Renamed to `test.claim.scm_role_requirement_integration_witness`, matching the
convention its 24 siblings use.

The corrected sweep now leaves three matches, all in the same commit's own prose,
all unmistakably historical and explicitly invalidated: "previously lived in",
"It was `MergeDependency`", "formerly `gunbc.scm.merge`".

62 cells pass across the two touched witness modules, zero fail.

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

* Retire the last merge tags, fix a citation that was wrong before I touched it, and withdraw "semantic no-op"

Three findings on this head. All correct.

1. FOUR LIVE MERGE TAGS SURVIVED the rename because they are not identities my
sweep pattern covers -- they are helper and tag functions, not module or type
names: outcome_is_merged, merged_roles, scm_image_merge_refusal_tag,
scm_image_merge_tag_for. Renamed to the integration vocabulary. The two witness
modules now contain no function whose name carries "merge".

2. A CITATION I REWROTE POINTED AT A SYMBOL THAT HAS NEVER EXISTED. The authoring
witness said its node helper was "built exactly as ... `atom_node`". No atom_node
has ever existed in the cited module -- it lives in
test.claim.scm_object_store_witness. The citation was ALREADY WRONG on main; what
this change did was rewrite its MODULE half while carrying the stale SYMBOL half
forward, which makes an unverified name look freshly checked. That is worse than
leaving it alone, because a rewritten citation reads as re-derived.

Both halves are now derived rather than copied: the symbol is
integration_witness_leaf, and the annotation states what makes the claim true --
the two bodies are byte-identical node_synthetic over an Atom connective. The
error's history is recorded because a future reader has no other way to know the
citation was stale rather than broken by this rename.

3. "SEMANTICALLY A NO-OP" WAS TOO STRONG AND I AM WITHDRAWING IT. Two different
facts were riding on one sentence:

  PRODUCTION BEHAVIOUR is unchanged. Every moved declaration is byte-identical
  modulo its name; no fold, arm, refusal or classifier changed. That part was
  true.

  THE CLAIM CORPUS IS NOT UNCHANGED. Five claim identities were renamed -- four
  in the integration witness and one in the JSON codec witness -- along with the
  module identity and six helpers. At identity grain a renamed claim is a
  deletion plus an addition, not the same claim, which is exactly the standard
  this lane applies to residues and rosters. Calling that a no-op used the
  passing count as evidence for a claim the count cannot support.

WHAT MAKES THE RENAMES SAFE IS A DIFFERENT CHECK, and it is the one that was
missing: every renamed identity was grepped across .dag, .md, .rs and .yml for a
citation outside its defining file. There are none, so no roster, coverage table
or coverage number is invalidated. The design doc's kernel coverage table names
claims -- an_independent_sibling_is_preserved_exactly,
two_distinct_requests_for_one_role_are_contested, dependent_add_and_delete_refuses,
the_same_request_authored_twice_is_not_a_contest -- and none of them is renamed
here, checked one at a time rather than by pattern.

67 cells pass across the three touched witness modules, zero fail.

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

* Derive the whole claim-identity population, not the four names I happened to rename

The reviewer's point is structural: renaming a module re-qualifies EVERY claim it
declares. The four renamed locals were a subset of the migration, and reporting them
as the migration understated it by fifteen. The complete population is 20 identities
-- all 19 in the renamed module (4 of which also change local name), plus one pure
local rename in scm_commit_closure_json_v2_witness, whose module identity is unchanged.

Checked against that population rather than against the four: five of the nineteen are
cited in the SCM design document, none of them among the renamed four, and no workflow
YAML, roster or coverage join enrolls the module by name -- so the re-qualification has
no enrollment consequence to migrate. `git grep scm_merge_witness` is empty.

While checking the coverage join I found a transcribed count that had rotted: the design
document said "the 15 claims in test.claim.<module>" while the module declares 19. It was
true when #8794 wrote it and has been wrong since. Per DESIGN section 6 the fix is not a
corrected number -- both sites now name the module and let the count be re-derived.

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

* Close the meaning fork where it actually lived, and pay the roster's sweep

Three things, all from review 5110471067 plus the required gate.

THE DESIGN DOCUMENT STILL DEFINED merge AS PROPOSAL-COMBINATION. I renamed the modules and
swept their prose, then wrote a vocabulary row saying the operation is no longer called
merge -- while the document below that row still carried "## 4. What merge is", "merge
kernel", "Structural merge", "depth changes how merge combines" and "Merges are not always
safe", all present tense. A new definition sitting above the old one does not replace it;
that is the same fork inside one artifact, and it was the fork I claimed to have closed.
Migrated at the root: proposal-combination is role-requirement integration throughout,
merge is now reserved for the two-commit operation, and section 4 says so in its own body.
Thirteen surviving uses are line-based merge, git's merge-as-invoked-event, the two-commit
contrast, or explicitly historical.

THE COMMON-ANCESTOR REFUSAL WAS MATHEMATICALLY WRONG AND IS WITHDRAWN. The refused-concepts
table said merge base / common ancestor "requires a total order that need not exist". A
commit DAG supplies a PARTIAL ancestry order; common ancestors are the intersection of two
ancestor closures and best common ancestors are the maximal elements of that intersection.
No total order is involved. Withdrawn in place rather than deleted, because it is a
prerequisite of the two-commit merge design this note does not yet model.

THE CENSUS HAD THE RIGHT ARITHMETIC AND THE WRONG UNIT. "20 claim identities" conflates
migrations with identity values: each transition removes one and adds one, so the symmetric
difference holds 40. It is 20 claim-identity MIGRATIONS. The population is unchanged.

NAMESPACE-WAVE-ADMISSION. The required run refused with 30 unadjudicated deltas -- exactly
my 30 binding sites retargeted from gunbc.scm.merge to gunbc.scm.proposal. Rows added.
Touching the roster sets roster_touched, which makes its 142 consumed rows due for deletion
on this change, so they are swept. Their consumption was READ FROM THE BASE rather than
taken from the run that reported it: for all 142, the row's own module imports the row's
spelling from extdeps.systems.nvidia_dgx_spark_setup by name. That check is what the
roster's own comment says costs a required run whenever it is guessed instead.

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

* A withdrawn row cannot sit inside the population it left, and a dated join cannot cite a renamed module

Two authority defects from review 5110979857, both of them mine and both introduced by
fixes rather than found under them.

THE WITHDRAWN ROW WAS STILL A MEMBER OF THE REFUSED SET. I marked "merge base / common
ancestor" as withdrawn but left it inside a table introduced by "Terms deliberately refused
-- every one", under a column literally named `refused`. The row and its container then
asserted opposite things about the same term, which is the fork I had just spent the branch
closing, reproduced one level up in the containing structure. The table is now a disposition
table: each term's standing is stated by its own cell, and membership no longer asserts
refusal.

A DATED MEASUREMENT CANNOT CITE A MODULE THAT HAS SINCE BEEN RENAMED. Removing the stale
"15" was right for the "witnesses are not consumers" sentence, which needs no cardinality. I
applied the same edit to the 2026-08-21 coverage join, and that sentence is a different
subject: it now claimed 4 of 13 came from joining "every claim declared in
test.claim.scm_role_requirement_integration_witness", a module identity and population that
did not exist on 2026-08-21. Naming a changing module does not re-run a historical join --
it makes a dated receipt look re-derived when nothing re-derived it, which is worse than the
stale number I replaced. Historicized: the figure is stated as the result of the join over
the population that existed that day, with no current-completeness claim, and the missing
current-head instrument is named as what would be needed to restate it.

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

* Take main's parse repair, and name my own bug class the way the reviewer named it

Main was red at parse: #10390 left a trailing annotation block at EOF of
emit_copy_qualification_witness_test.dag with no declaration after it, 16 section 4c errors,
which took the head index down and with it the namespace-wave-admission verdict this branch
actually needs. Repaired on main; merged here. I filed nothing against it -- three PRs were
already open on that file.

The ledger entry for my own three verification bugs is rewritten in the reviewer's framing,
which is sharper than what I had. I wrote that an index which can be incomplete must be able
to say so. The precise statement is that A PARTIAL OBSERVER RETURNED THE NEGATIVE VALUE OF A
TOTAL OBSERVER.

That is the part that makes these dangerous rather than merely incomplete. A parser that
cannot see continuation-joined literals does not report that it failed to read a row -- it
returns the row set without it, the same type and shape a complete parser returns. An index
that does not know coproduct variants does not report that it lacks the form -- it returns
the empty owner set, which is exactly what a total index returns for a spelling nothing
declares. The answer is well formed and of the right type; only the meaning is wrong, and the
caller has no way to tell.

So the obligation sits on the observer, not the caller: anything reading the corpus that can
be partial must be able to say it was partial, or refuse. Three instances on this branch, one
shape -- an empty read from a transliterated path, a non-match from the import path, an empty
owner set from a pattern that did not cover the language.

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

---------

Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.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.

2 participants