Skip to content

SCM MVP-N - #10668

Merged
briansrls merged 3 commits into
mainfrom
scm-algebra
Sep 7, 2026
Merged

SCM MVP-N#10668
briansrls merged 3 commits into
mainfrom
scm-algebra

Conversation

@gunbai-bot

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

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session deep-carp-676.
Pushing to scm-algebra advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

…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
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 6, 2026 16:26
…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
@gunbai-bot

gunbai-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Both blocking findings of review 61418 verified against the code and fixed at 2f32c9b45b2. Both correct, and they turned out to be one fix.

1. Raw lists bypass the uniqueness guarantee. Correct, and worse than the finding states. 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. That is validation-by-comment standing exactly where construction was available. Your [a→X, a→Y] specimen is the proof: it merged successfully by silently selecting the first, and reversing the list changed the answer. All three sides now take CorpusManifestRecord, so the specimen is not expressible at the boundary.

2. Θ(N²). Correct, and §6 makes it unconditional, so there was nothing to weigh. Replaced with 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 rather than made faster: the union, the three lookups and the decision are the same traversal now, so there is nothing left for them to answer.

The two fixes are one fix. 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. Your first finding is what makes the second one's implementation legal.

Two mutations aimed at the code the rework introduced, since the old table said nothing about it:

mutation presence del/mod mod/del complete pop 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

441/0 across all SCM witness files. That is 442 minus one claim I deleted while reworking: the S == T "either operand is unobservable" claim called the merge with identical arguments twice, and repairing the swap would not have saved it — the arm is reached only when path_state_eq holds, that compares locators, and AuthoredSourceTarget carries a locator and nothing else. Its RED was unauthorable, so it asserted nothing while looking like coverage. Deleted, with the structural argument recorded in its place and the condition that brings it back. It was cited as evidence in the PR body, which is corrected there.

I did not add a claim for the uniqueness fix, on the same test: it is structural now, so its RED is unauthorable too.

— sent from deep-carp-676

Side-chat review of the manifest algebra. Both findings verified by execution before
acting, and both are correct.

1. A REAL CLAIM LIVES WHERE THE DECORATION WAS. Deleting the "S == T returns either
operand" claim was right -- its two operands are indistinguishable, so no input
could red it. But the statement worth making was never about that arm's operand. It
is about the WHOLE LAW: read the four lines with source and target exchanged and
each maps onto another, so the merged result must not depend on which side was
handed in as source. That red IS authorable, because the tempting wrong law
resolves a conflict by preferring one side -- what a git user knows as `-X ours` --
and that is asymmetric by construction.

2. EVERY MUTATION I HAD TESTED THAT THE LAW REFUSES ENOUGH. None tested that it does
not refuse TOO MUCH. A law that conflicted on every path whose three sides were not
all equal would have passed the entire table and been useless. The three all-present
resolvable cases are now controlled -- only the target moved, only the source moved,
both moved to the same place -- which is exactly where a conflict wall over-reaches.

AND A DEFECT IN MY OWN NEW CLAIM, CAUGHT BY RUNNING THE MUTATION RATHER THAN BY
READING THE CODE. The first `outcomes_agree` compared merged entries by PATH ALONE
while its annotation claimed path AND locator -- weaker than its own description --
so the asymmetric-conflict mutation walked straight through the symmetry claim.
Both directions produce an entry at path "a"; they disagree only about which side's
content it holds, which is the entire defect the claim exists to catch. This is the
same lesson as the decoration it replaces: a claim is worth what its red proves,
not what its annotation asserts.

The repair reuses object_store's `manifest_comparands` rather than coining a private
path-and-locator tuple beside it. That function is already the authority for "are
these two manifests the same content" -- `manifest_entries_equal` consumes it -- and
a second answer to one question is DESIGN section 3.

    mutation                       presence del/mod mod/del complete union symmetry only-one-moved
    bare-locator equality          RED      RED     RED     RED      RED   RED      RED
    first-wins conflicts           pass     pass    pass    RED      pass  pass     pass
    last group never closed        RED      RED     RED     pass     RED   RED      pass
    target side dropped from join  RED      RED     pass    RED      RED   RED      pass
    prefer source on conflict      RED      RED     RED     RED      pass  RED      pass
    conflict unless all three equal RED     pass    pass    RED      pass  RED      RED
    as built                       pass     pass    pass    pass     pass  pass     pass

The two new mutations are the ones the two new claims exist for, and each reds a
combination no earlier mutation produced. The over-conservative mutation is the
only one in the table that reds `only-one-moved`, which is the point: nothing before
it could tell a correct wall from one that refuses everything.

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

gunbai-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Side-chat review, reworked at 6a13f0f24c1. Both findings verified by execution before acting; both correct.

1. A real claim lives where the decoration was. Deleting the S == T "either operand" claim was right — its operands are indistinguishable, so no input could red it. But the statement worth making was never about that arm's operand. It is about the whole law: read the four lines with source and target exchanged and each maps onto another, so the merged result must not depend on which side was handed in as source. That red is authorable — the tempting wrong law resolves a conflict by preferring one side (-X ours), which is asymmetric by construction.

2. Every mutation I had tested that the law refuses enough. None tested that it does not refuse too much. A law that conflicted on every path whose three sides were not all equal would have passed the entire previous table and been useless. The three all-present resolvable cases are now controlled — only the target moved, only the source moved, both moved to the same place.

And a defect in my own new claim, caught by running the mutation rather than reading the code. The first outcomes_agree compared merged entries by path alone while its annotation claimed path and locator — weaker than its own description — so the asymmetric-conflict mutation walked straight through the symmetry claim. Both directions produce an entry at a; they disagree only about which side's content it holds, which is the entire defect the claim exists to catch. Same lesson as the decoration it replaces: a claim is worth what its red proves, not what its annotation asserts. The repair reuses object_store's manifest_comparands rather than coining a private tuple beside the authority that already answers that question.

mutation presence del/mod mod/del complete union symmetry only-one-moved
bare-locator equality RED RED RED RED RED RED RED
first-wins conflicts pass pass pass RED pass pass pass
last group never closed RED RED RED pass RED RED pass
target side dropped from join RED RED pass RED RED RED pass
prefer source on conflict RED RED RED RED pass RED pass
conflict unless all three equal RED pass pass RED pass RED RED
as built pass pass pass pass pass pass pass

The over-conservative mutation is the only one in the table that reds only-one-moved — nothing before it could tell a correct wall from one that refuses everything.

— sent from deep-carp-676

@briansrls
briansrls merged commit 1ed3310 into main Sep 7, 2026
4 checks passed
@briansrls
briansrls deleted the scm-algebra branch September 7, 2026 00:04
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