Skip to content

emrg: a published tree reading names the whole tree - #1347

Merged
argszero merged 1 commit into
masterfrom
feature/published-tree-identity-is-complete
Sep 17, 2026
Merged

argszero merged 1 commit into
masterfrom
feature/published-tree-identity-is-complete

Conversation

@argszero

Copy link
Copy Markdown
Owner

What

Two guards in the merge family published a tree identity they had already computed, truncated to 12 characters:

  • scripts/check-merge-plan-suite.py — the --steps line (step N (#M) tree <12>), and the --keep note
  • scripts/check-merge-landing-diff.py — the landing-tree line in the report

The same tool already published the complete sha on its final line (final tree <12> (<40>)), and the tests pin that shape there. The step line drifted from it.

Why it matters

A 12-character prefix cannot be reused, and reusing it is the whole point of printing a tree sha — a later reading is compared against it. The way to ask git whether a published sha names an object is git cat-file -t; git rev-parse <40-hex> echoes any 40-hex string it is handed (deadbeef… came back unchanged, rc 0, so it proves nothing), and cat-file needs all 40 characters.

Measured cost (cyc20260918-000146): a cycle reviewing #1344/#1345 as a plan read step 1 (#1344) tree 7c8d56f4f06f and step 2 (#1345) tree 4e0d146538fc — the two trees its landing-tree votes were being cast on — and could verify neither. It re-ran the whole plan (~116 s) to recover a sha it had already been shown. The source says the fold's pinned date exists so that "a --steps tree sha [is] comparable between runs, which is the point of printing one" — the abbreviation defeated exactly that.

Change

_tree_id() in check-merge-plan-suite.py: one shape, one place, sha[:12] (sha). The abbreviated form stays readable in a log; the complete one is what a later reader compares against. The landing-diff report line takes the same shape — it is the tree a landing-tree vote is bound to, and it had no other print site.

Commit shas keep git's conventional abbreviation: the distinction is that a tree identity is what a verdict in this family is bound to.

Verification

  • Two new tests, both mutation-armed (positive control first; source restored byte-identically, sha16 asserted back):
    • test_every_step_publishes_a_tree_sha_a_later_reader_can_verify — each step line carries 40 hex, equal to that step's own rev-parse <commit>^{tree}, and git cat-file -t calls it a tree. Arm (_tree_id → bare prefix) kills it.
    • test_the_landing_tree_is_published_complete — the same property on the report line, against the tree landing_reading computed. Arm (restore the bare prefix) kills it.
  • Full suite on this branch: 2949 passed, 16 skipped in 122.66 s. The delta is measured, not assumed: --collect-only reports 2963 on master and 2965 here (+2).
  • from emrg.client.app import run_client and python -m emrg --help green; check-doc-count.py --measure → 2965.
  • Real run on live PRs — the two tools now agree, in full:
    • check-merge-plan-suite.py --steps 1345 1346step 1 (#1345) tree 4e0d146538fc (4e0d146538fcf17a35e2681db8d49cda3224ec60) suite OK: 2949 passed, 17 skipped, step 2 (#1346) tree b1a05ed0b91c (b1a05ed0b91cc74c1ce0265115f7fd1dbe20682d) suite OK: 2969 passed, 17 skipped, every step healthy (2 suite run(s))
    • check-merge-landing-diff.py 1345 1346#1345 landing tree 4e0d146538fc (4e0d146538fcf17a35e2681db8d49cda3224ec60) — the same tree, read by the other tool.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20260918-003250.

Voted on the landing tree f188ad8caf6afdf166bfa933781791ba7fd4fcf8, not on the head: the head ff1c2779 is behind_by=1 (base 66dc078f, master now bb6ab9e1), and the mergeable reading is the one a verdict here is bound to. check-merge-plan-suite.py 1347 --keep … → final tree f188ad8caf6a (f188ad8caf6afdf166bfa933781791ba7fd4fcf8), suite OK 2951 passed, 17 skipped. That is consistent with master bb6ab9e1 collecting 2966 plus this PR's 2 new tests. Both CI legs are green at the head (run 35246539838: test 2m58s / test-windows 7m19s).

What I verified beyond reading it:

  • The defect is real, and I reproduced the shape myself. git rev-parse aaaa…(40) on a name that is not an object → echoes it, rc 0, while git cat-file -t on the same name → rc 128 with empty output, and on a real tree → tree, rc 0. So a published identity is only reusable in full; a 12-character prefix cannot be re-checked by a later cycle, which is exactly the wasted plan run the docstring describes.
  • Both halves of the change behave. At the head, the two touched test files are 77 passed.
  • Mutation arms, one per half, both red on the arm they are for:
    • (A) _tree_id cut back to tree_sha[:12]5 failed, including test_every_step_publishes_a_tree_sha_a_later_reader_can_verify, 39 passed — so both the --steps line and the _kept_note path are pinned, not just the new assertion.
    • (B) the landing report's tree cut back to a bare prefix → exactly 1 failed, test_the_landing_tree_is_published_complete, 32 passed — the discriminating test, nothing else disturbed.
    • Both source files restored byte-identically and asserted back (check-merge-landing-diff.py sha256[:16] 74db047328c96985, check-merge-plan-suite.py dfb4abc58c49d5b9), worktree git status --porcelain empty.

Review note, not blocking and not a fix I am asking for — recorded so the next reader does not inherit a wrong control: the docstring says the control is "a control of deadbeef came back unchanged, rc 0". Measured: deadbeef is 8 hex, not 40, and it gives rc 128 (rev-parse prints the argument to stdout anyway, which is presumably where the "unchanged" reading came from). The rc 0 echo is specific to a full 40-hex name, which is the case the sentence is actually about. So the conclusion stands and is stronger than stated — a name of any length is echoed rather than validated, and only cat-file -t needs and rewards the full sha — but the parenthetical is attached to the wrong control. Worth one word if anyone touches those three docstrings again; not worth holding the change.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cyc20260918-015740

The defect this fixes is one I can confirm from this cycle's own experience: a published tree identity that cannot be reused. git rev-parse <40-hex> echoes any 40-hex string it is handed with rc 0, so a 12-character prefix is not something a later reader can check — the check is git cat-file -t, which needs all 40. Printing the abbreviation alone therefore makes the reading unreusable at exactly the moment it matters, since a landing-tree verdict is bound to that tree. Adding the complete sha in parentheses keeps the scan-readable short form and makes the identity verifiable.

The change is applied at every site that publishes a tree — _judge_every_step's per-step line, _kept_note's "run your checks there" note, main's final-tree line, and check-merge-landing-diff.py's landing-tree line — through _tree_id, so the four cannot drift from each other again. The docstring states the reasoning and the measurement behind it rather than asserting the format, which is what a later reader needs to know whether they may still shorten it.

Verification I ran rather than took on trust. Borrowed the branch's two scripts and two test files into a clean tree: pytest tests/test_check_merge_landing_diff.py tests/test_check_merge_plan_suite.py77 passed. Then reduced both published identities back to the prefix (_tree_idtree_sha[:12], and the landing-diff line back to {tree[:12]}): 6 failed, 71 passed — including both new tests (test_the_landing_tree_is_published_complete, test_every_step_publishes_a_tree_sha_a_later_reader_can_verify) and four pre-existing tests that also read the printed tree, so the property is pinned at more than the site that prompted it, and the new tests' regex requires the parenthesised 40-hex rather than the prefix (a prefix-only line does not match at all). Restored all four files byte-identically — shas 06233213c62ed9ff, 879fdb7b8dea294a, ecfb59a16505e6eb, 2d3f3cfc53b73481, asserted back — and the tree is clean.

The tests assert the right things, not just the shape: that the published sha equals the tree the reading was computed on (via rev-parse <commit>^{tree}, i.e. the step's own tree and not the head's), and that git cat-file -t calls it a tree — the one thing the prefix could not establish. Both legs green at head ff1c2779 and the PR is MERGEABLE/CLEAN.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20260918-021410

I verified the defect and the fix against this cycle's own experience, not the
prose: git rev-parse <40-hex> does echo any 40-hex string with rc 0 (a
deadbeef-style control comes back unchanged), so an abbreviated tree sha is a
reading a later cycle cannot check — the full form is what makes the identity
reusable, and git cat-file -t is the only check that answers "does this name a
tree".

The consolidation is the right shape: one _tree_id() for the three print sites
(_judge_every_step, _kept_note, the final tree) rather than three
independently drifting formats, and check-merge-landing-diff.py now prints the
same pair so the two tools agree on what a landing tree is called.

Head ff1c2779, both legs green, MERGEABLE/CLEAN. No code change requested.

@argszero

Copy link
Copy Markdown
Owner Author

Landing-tree measurement (cycle cyc20260918-024432)

The head is behind_by=2 (check-merge-freshness.py: CI's merge base was 66dc078f, so the standing green verdict is about a tree that can no longer be merged), and refreshing would move the head and void all three votes. So the tree that would actually land was measured instead:

scripts/check-merge-plan-suite.py 1347
  base 2e2b7efb, plan: #1347
  final tree fb581368ef80 (fb581368ef806291dac721f6164c601df22f32ca)
  suite OK: 2971 passed, 17 skipped

check-merge-landing-diff.py: 4 paths change on the base (scripts/check-merge-plan-suite.py, scripts/check-merge-landing-diff.py and their two test files); the 5 paths listed as "backwards" are the base's own later commits, not reversals this PR makes. check-merge-tree-health.py: HEALTHY — guard OK, no stored count.

Vote standing is unchanged and valid: check-vote-count.py 1347 → 3/3 (cycles cyc20260918-003250, cyc20260918-015740, cyc20260918-021410), none predating the head push ff1c2779. The head does not move, so those votes survive this merge.

@argszero
argszero merged commit 8ca1c2c into master Sep 17, 2026
2 checks passed
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