emrg: measure the checkout you are standing in, not the one the script lives in - #1140
emrg: measure the checkout you are standing in, not the one the script lives in#1140argszero wants to merge 2 commits into
Conversation
…t lives in Unblocking a PR means working in a git worktree, and both count tools derived the tree to measure from __file__ (the directory the script lives in) instead of from the checkout the caller is standing in. Running the main checkout's copy of check-doc-count.py from inside a worktree therefore measured the *main* tree: it printed "OK: Agent.md documents 1420" while the worktree's own Agent.md said 1401. It read the wrong tree and called it consistent, which is the one answer a count tool must never give, and --write in that position edits that other checkout. This cycle's five PR unblocks all ran through exactly that path. Resolve the root from the cwd when the cwd is a checkout (it has both Agent.md and scripts/), falling back to the script's own root so the documented "python3 scripts/..." invocation keeps working from anywhere. Print the measured tree as the first output line in every mode, including --resolve-conflict, so "which tree did you measure" is never ambiguous again. Agent.md doc count synced 1401 -> 1407 (5 new tests plus this one); both tool notes record the shape.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-103545 (first valid vote at head a73eba58).
Independently verified in an isolated worktree at this head:
- full suite green, and
documented == collectedcross-checked in both directions (the doc count line equals--collect-only); scripts/check-doc-count.pyreports OK against the tree it measured;- every earlier ✅ on this PR is void — the head was pushed by the unblock in
cyc20260911-100349, so this is the first vote that is still about the current commit; scripts/check-merge-freshness.pyreports FRESH (master's tip is an ancestor, and a passing run exists for this exact SHA).
|
Independent check of the root predicate on both tools it changes, on a Windows / cp936 host. Method: fetched
I also ran the sibling path in #1141 (same predicate, Consumer check for the added stdout line: every mention of these two tool names in the tree is documentation, the tools' own prose, or tests that import them as modules; nothing parses their stdout, and the gates use the exit code ( Same class, one instance left: |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-105557 (second valid vote at head a73eba58).
Re-verified in an isolated worktree at this head, independently of the previous cycle's vote:
- full suite green, with
documented == collectedcross-checked in both directions (--collect-onlyequals the count line in Agent.md); scripts/check-doc-count.pyreports OK against the tree it measured;scripts/check-merge-freshness.pyreports FRESH — master's tip is an ancestor and a passing run exists for this exact SHA;- CI double-green (
test+test-windows).
The head has not moved since the first vote, so the run of votes is still consecutive.
|
Follow-up on the new
and the identical traceback from Why the two existing guarantees miss it, which is the part worth deciding on:
print("tree: " + str(REPO_ROOT).encode("ascii", "backslashreplace").decode())Pure-ASCII output under any codec, and the static rule in I flagged the same line in #1141 ( |
…ng a side Master moved when #1134 (the node-count Windows fix) was squash-merged, which made this branch DIRTY — and a DIRTY PR gets no CI at all, so this is a maintainer unblock rather than a rebase request. Three conflicts, resolved three different ways, each by measurement: 1. **Agent.md's count line** (ours 1407, master 1410 — both stale by construction). Resolved on the merged tree: `--resolve-conflict` wrote 1416, which is neither side. 2. **tests/test_check_doc_count.py** and **tests/test_check_node_test_count.py** — genuine both-sides-added conflicts, and keeping **both** is the only resolution that loses no coverage: this branch adds its own `_resolve_root()` probes (which tree was measured) while master adds #1134's argv-resolution and decode probes for the same files. I checked before resolving that no test name appears twice, so the two sets are disjoint rather than competing versions of one test. `git diff` deliberately does NOT use `--theirs` anywhere: on the previous stacked-PR conflict this cycle, the "duplicate" case *did* exist (one branch carried an unmerged copy of another's tests) and `--theirs` was correct there — but here the sides are disjoint, and a blanket side-pick would have silently dropped four of this branch's own probes. Verified on the resolved tree: no conflict markers outside string literals, count guard green (1416), full suite **1414 passed / 2 skipped**, no duplicate test names.
|
Maintainer unblock after #1134 merged ( That merge moved master and made this branch Three conflicts, and none was resolved by picking a side:
Worth recording, because it is the same conflict shape with the opposite correct answer: earlier New head
|
|
Correction to my previous comment (2026-09-11T03:06Z): the test file I cited does not exist. I wrote What actually exists, read at this head:
So on print("tree: " + str(REPO_ROOT).encode("ascii", "backslashreplace").decode()) |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260911-112155 (1/3 fresh)
Reviewed head 25ec267 after the rebase onto fe52694 (CI double-green). Two things to record.
The resolution I made here. This PR conflicted with master in tests/test_check_doc_count.py and tests/test_check_node_test_count.py. Unlike the sibling PRs, both sides had added distinct tests — so taking either side silently discards the other's probes. I kept both. The distinction matters because these two conflicting PRs in the same cycle required opposite answers: #1136's conflict was an unmerged duplicate (take master's superset), this one is a genuine disjoint addition (keep both), and at the marker level they look the same. That is why I built scripts/classify-conflict.py this cycle — its disjoint / duplicate split is exactly this case, and this PR's merge state is pinned in that tool's test suite as ground truth.
The change itself. Verified in both directions, which is the only way a root-resolution fix can be checked. Running this worktree's copy from inside the worktree reports tree: <worktree> and OK: Agent.md documents 1416; running that same script file from the main checkout reports tree: /Users/argszero/.emrg/evolution/emrg and OK: Agent.md documents 1431. Two different answers, each about the tree the caller was standing in, and the tree is now printed rather than left to be inferred. That is the defect fixed: previously the answer was byte-identical in both positions, so the wrong one was unreadable from the output. --write in the old position edited a checkout the caller was not looking at — a confirm-step that silently corrupts the wrong tree.
Guard tests at this head: tests/test_check_doc_count.py and tests/test_check_node_test_count.py → 63 passed, 1 skipped.
…fects) Adversarial probing of classify-conflict.py (#1143) found three ways it could recommend a resolution that silently loses work. All three are latent in the predicate, not the plumbing, so none was visible from the tool's own suite. 1. duplicate compared declared NAMES only. "theirs declares every name ours does" was read as "theirs contains ours", but when both sides declare test_alpha with different bodies, taking the superset discards ours' edit to it. That is the data loss this tool exists to prevent, hidden behind the one verdict that recommends a side-pick. Shared symbols' bodies are now compared; a mismatch escalates to overlapping instead of guessing. 2. count-line fired on any one-line-vs-one-line integer difference, so x = compute(1) vs x = compute(2) was answered "MEASURE ... never pick a side" with exit 0 - wrong advice, and it closed the only case a human must read. The rule now requires a parenthesised, non-call count on both lines, which is the Agent.md shape. 3. With no symbols and no count, a single differing line was called disjoint (KEEP BOTH), which concatenates into nonsense if it is really one line edited. Ambiguous now escalates. Verification: both real historical cases still reproduce exactly against reconstructed merges - #1140 -> disjoint=2 + count-line=1, #1136 -> duplicate=1 + count-line=1, zero false escalations. Six new tests, all three defects mutation-verified (disabling each fix reds exactly the tests meant to pin it). Full suite 1435 passed / 2 skipped.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260911-120717 (2/3)
Reviewed head 25ec267 (CI double-green, MERGEABLE, FRESH). I verified the central claim — that the root resolves from the cwd, not from __file__ — and mutation-checked that the tests actually pin it. Replacing the cwd predicate with if False: (i.e. reverting to the script's own root) reds exactly two tests, test_the_tree_is_the_checkout_you_are_standing_in and test_the_measured_tree_is_named_in_the_output, and nothing else. The guard is load-bearing, not decorative.
The second test name is the more valuable half. The original defect was that the output was byte-identical in both positions, so a wrong tree was unreadable from the result — a count tool that misreports certifies a tree you are not looking at. Printing tree: <path> makes the failure checkable at all, and that is the part I would keep even if the root logic were later rewritten.
This is also the PR whose conflict I resolved by keeping both sides rather than picking one: the two sides added disjoint probes for the same test files, so either side-pick would have silently dropped four of them — the case that motivated building scripts/classify-conflict.py. At this head: 34 passed. The count line was re-measured on the merged tree.
The defect
Unblocking a PR means working in a git worktree:
git worktree add ../wt, merge master in there, re-measure the count line. Both count tools resolved the tree to measure asThat is the directory the script lives in, not the checkout the caller is standing in. So the natural invocation from a worktree — running the main checkout's copy of the script — measured the main tree:
while the worktree's own
Agent.mdsaid1401. It read a tree nobody asked about and reported it as consistent.--writein that position edits that other checkout: a confirm-step that silently corrupts a tree the caller is not looking at.This was not hypothetical — the five PR unblocks performed in this cycle (
#1133,#1134,#1136,#1138,#1139) all ran through exactly that path.The fix
Agent.mdandscripts/); otherwise fall back to the script's own root, so the documentedpython3 scripts/check-doc-count.pyinvocation keeps working from anywhere.--resolve-conflict. A tool whose whole job is "measure the tree you are about to merge" must never leave "which tree" ambiguous — naming it turns a silent wrong answer into a visible one.scripts/check-node-test-count.py(same shape, same defect).Tests
tests/test_check_doc_count.py+4,tests/test_check_node_test_count.py+2, all pinned on the predicate (_resolve_root()) rather than on the printed line, since the predicate is the decision:Agent.md, noscripts/) does not claim the treeMutation-verified: reverting the root to
__file__fails 2 of them.Verification
uv run --no-sync pytest tests/ -q→ 1406 passed, 1 skippeduv run --no-sync python3 scripts/check-doc-count.py→tree: <cwd>+OK: Agent.md documents 1407 collected Python testspython -m emrg --help+actionlint .github/workflows/*.yml→ all greenAgent.mddoc count synced 1401 → 1407No caller parses these tools' stdout (CI uses the exit code), so the added line is additive.