Repository navigation
SCM MVP-N - #10668
SCM MVP-N#10668
Conversation
…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
|
Both blocking findings of review 61418 verified against the code and fixed at 1. Raw lists bypass the uniqueness guarantee. Correct, and worse than the finding states. The module carried an annotation asserting that 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. 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:
441/0 across all SCM witness files. That is 442 minus one claim I deleted while reworking: the 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
|
Side-chat review, reworked at 1. A real claim lives where the decoration was. Deleting the 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
The over-conservative mutation is the only one in the table that reds — sent from deep-carp-676 |
Auto-opened by session-dashboard for session
deep-carp-676.Pushing to
scm-algebraadvances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan