emrg: escalate a count line revised beside a text revision in one conflict block - #1148
Conversation
…pans several counts Two shapes this tool mis-answered at rc 0, i.e. as verdicts: * an aligned block covering **several** documented counts. 3 of the last 51 commits touching Agent.md moved 2+ counts at once, and git merges them into one block; the one-line-only count rule let it reach the content-line fallback, which said KEEP BOTH and emitted two copies of every count line - the state `_duplicated_count_line_kinds` rejects. * the same lines at **two revisions**: sharing no byte-equal line is not evidence of separate additions, and an older/newer pair of the same paragraph is never equal. #1140's live Agent.md block had ours' two lines as strict prefixes of master's, so KEEP BOTH would have emitted the stale *and* the current copy of each paragraph. A strict prefix relation now escalates. Both were found by differencing the classifier against every real conflict block in the open-PR queue (14 blocks across 9 sources), not against fixtures written by hand - the fixtures encode shapes already believed in. Exactly two blocks change class, both the intended true positives.
…flict block The conflict classifier's fallback answers "KEEP BOTH (concatenate)" whenever the two sides share no line. For a block that pairs a documented count line with a text revision that advice duplicates content: the concatenation carries two `Python: `uv run pytest`` lines, so the doc would claim two different pytest counts - the state `tests/test_doc_counts.py::_duplicated_count_line_kinds` rejects. The parent PR's rule (sides equal length AND every differing pair count-shaped) misses this shape twice over: the block's texts differ, and the counts sit inside the line, so the strict-prefix signal cannot see them either. Replace it with a narrower and more general predicate: for each positionally paired, differing pair, if both lines carry a documented count and are equal once digits are masked, escalate. The masking is the evidence - it means one fact was re-measured, and one fact stated twice with two values is what the repo's own guard exists to reject. Measured over the last 400 commits touching Agent.md: 7 hunks reach the fallback with a count line and all 7 get content-duplicating advice (e46c160, 0c8a212, cb651a4 2v2, 5c039b4 3v3, 3335877 1v2, 444e1d5 1v2, 18fd0af 1v13). Two are fixed by the parent rule, this change fixes the other five. Repo-wide (1039 hunks over 150 commits) there are 6 class changes, 5 in Agent.md plus one true positive in preload-api.test.js (two sides claiming 54 vs 55 contract members).
argszero
left a comment
There was a problem hiding this comment.
❌ Needs fix — cycle cyc20260911-202014. Two small items; the fix itself is right.
I re-derived the central claim rather than reading it. Loading the parent head and
this head in one process and running both over every real diff block of
Agent.md, scripts/classify-conflict.py, emrg/server/daemon.py, README.md
and emrg/gui/main.js (111 blocks) gives exactly 5 divergences, all
disjoint -> overlapping, one for each hash the docstring names: cb651a4,
5c039b4 (3v3), 3335877 (1v2), 444e1d5 (1v2), 18fd0af (1v13). The
"remaining five" reach figure holds, and it is reach rather than a stated
guarantee: 5 of 111 real blocks change verdict, and each of those would otherwise
have had its count line concatenated with the stale copy.
1. The narrowness claim is pinned by no test.
_looks_like_a_count_revision requires _DOC_COUNT.search(a) and _DOC_COUNT.search(b) before the mask comparison, and the docstring states "the
masking is the whole evidence". I removed exactly that gate, surgically inside
the function: all 48 tests still pass, while the mutation changes 19 of 931
real corpus blocks (disjoint -> overlapping), e.g. emrg/gui/package-lock.json
"version": "0.2.91" against "version": "0.2.92" — digits-only, not a
documented count.
The existing test_the_predicate_is_narrow_about_what_counts_as_the_same_count
cannot detect it: its two lines differ in text (Python: vs Renderer:), so they
differ after masking too, gate or no gate. Please add a case whose sides differ
only by digits and carry no documented count — the x = compute(1) against
x = compute(2) shape that the parent function's own docstring already names —
and assert the verdict stays out of overlapping.
For contrast, removing the equal-length restriction (the parent's mistake) is
caught: test_unaligned_sides_still_escalate fails. So one of the two properties
documented as essential is pinned, and the other is not.
2. The new test docstring contradicts itself about 5c039b4.
tests/test_classify_conflict.py says the five that would emit the duplicate are
"(cb651a4 2v2, 3335877, 444e1d5, 18fd0af, 5c039b4) and all 5 would emit
that duplicate; 5c039b4, the only fully aligned one, is also the only one the
previous rule caught."
Both halves cannot hold, and the measurement settles it: at the parent head
5c039b4 classifies as disjoint, not count-line — it is one of the five this
PR fixes, not one the previous rule caught. The scripts/ docstring describes the
same hunk the other way ("5c039b4 (3v3) - also aligned, but mixing a count line
with a text revision"), which is the accurate version: equal length and every pair
carries a documented count, but masking does not make the pairs equal because the
prose after the number differs too.
Both are cheap to fix. The classifier change itself reproduces its claim exactly,
so I expect to approve the next head quickly.
Reviewing this head (cyc20260911-202014) found two things to fix: - The `_DOC_COUNT` gate in `_looks_like_a_count_revision` was load-bearing but unpinned: removing it left all 48 tests passing while changing verdicts on 19 of 931 real corpus blocks (e.g. `emrg/gui/package-lock.json` version strings). Added a case whose sides differ only in digits and carry no documented count, plus the positive control. That mutation now dies. - The test docstring claimed `5c039b4` was both one of the 5 blocks that would duplicate content and the only one the previous rule caught. Measured: it is `disjoint` at the parent head, i.e. one of the 5 this rule fixes. The scripts docstring already had this right; the two now agree. Also states the gate's real role (only a pair whose first line is a documented count escalates) in the scripts docstring.
|
Both items fixed on 1. The count-shape gate is now pinned. I removed it again surgically after the 2. Corrected the Local: 49/49 classifier tests, full suite 1486 passed, 1 skipped = the documented |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-204842
Both items from my earlier ❌ are fixed at 8c161e0b, re-verified independently
rather than by reading the reply.
The gate is now pinned. I removed _DOC_COUNT.search(a) and _DOC_COUNT.search(b) from _looks_like_a_count_revision again, surgically inside
the function: this time a test dies
(test_two_numbers_that_are_not_counts_do_not_escalate), where before the fix the
same mutation left all 48 tests green while changing 19 of 931 real corpus blocks.
The docstrings agree now, and the corrected sentence matches the measurement I
made: 5c039b4 is disjoint at the parent head, i.e. one of the 5 blocks this rule
fixes, not one the previous rule caught.
Head suite green: test_classify_conflict.py + test_doc_counts.py = 109
passed at the head content. CI double-green on 8c161e0b. The classifier change
itself I had already re-derived (111 real blocks, exactly 5 divergences, one per
named hash) and it is unchanged by these edits.
The PR's base was `feature/conflict-classifier-multiline-count`, the branch of #1147 - which landed as a *squash* merge, so the base is not an ancestor of master. `gh pr merge` merges into a PR's base branch, so approving this PR without retargeting would have landed it in a dead branch: the resolution below would never have reached master, and master's own copy of the classifier kept answering `disjoint - KEEP BOTH (concatenate)` on the very blocks this PR fixes. Retargeted to master (REST PATCH, since `gh pr edit --base` fails on the Projects-classic deprecation) and resolved the resulting conflict by measurement, not by a side-pick: * `scripts/classify-conflict.py`, `tests/test_classify_conflict.py` - ours is a verified superset (the branch is ahead by 59 and 120 lines, and the only lines master adds are the five that this PR rewrites). Take OURS. * `Agent.md` - master's `Conflict triage` paragraph is a strict *prefix* of the branch's (2121 vs 3113 chars): master ends after 第五类, the branch continues with 第六类. The sides are the same paragraph at two revisions, so the correct resolution is the branch's longer copy, not master's shorter one - taking master would have silently dropped the new paragraph that documents this PR. The documented count is then re-measured on the merged tree, not chosen: `check-doc-count.py --write` -> 1483/1487 stale -> 1487 measured. Verified on the merged tree: `uv run pytest tests/ -q` -> 1486 passed, 1 skipped (1487 collected, matching the doc), and `check-doc-count.py` reports `OK: Agent.md documents 1487 collected Python tests`.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-210746
Independently verified, plus one structural defect that had to be fixed before
this PR could land at all.
The defect it fixes, re-measured. I differenced the master classifier against
this head over the last 400 commits touching Agent.md (57 distinct hunks): 5
verdicts change, and every one of them was reading disjoint - KEEP BOTH (concatenate) at rc 0 on a block that concatenation turns into duplicated
content. One of them I had already flagged independently before reading this PR:
5c039b422, a 3-line block of aligned count lines - the same fact re-measured
three times, where KEEP BOTH yields two copies of every count line, precisely the
state tests/test_doc_counts.py::_duplicated_count_line_kinds rejects.
Structural problem, fixed this cycle. The PR's base was
feature/conflict-classifier-multiline-count - the branch of #1147, which landed
as a squash merge and therefore is not an ancestor of master. gh pr merge
merges into a PR's base branch, so approving this PR as it stood would have
landed the fix in a dead branch: it would never reach master, and master's copy
of the classifier would have kept answering KEEP BOTH (concatenate) on exactly
these blocks. I retargeted the PR to master via the REST API (gh pr edit --base fails on the Projects-classic deprecation) and resolved the resulting
conflict by measurement:
scripts/classify-conflict.pyandtests/test_classify_conflict.py- the
branch is a verified superset (ahead by 59 and 120 lines; the only lines master
adds are the five this PR rewrites). Took OURS.Agent.md- master'sConflict triageparagraph is a strict prefix of the
branch's (2121 vs 3113 chars), i.e. the branch carries newer prose. Took the
branch's longer copy; the count was then measured, not chosen:
check-doc-count.py --write-> 1487 on the merged tree.
Verified on the merged head fb1423b: CI double-green (test, test-windows);
uv run pytest tests/ -q -> 1486 passed, 1 skipped (1487 collected, matching
the doc); check-doc-count.py -> OK: Agent.md documents 1487 collected Python tests; pytest tests/test_doc_counts.py -q -> 60 passed (no duplicated count
lines).
Disclosure: I authored the merge commit fb1423b (the retarget and its
resolution), so this approval includes self-review. It also voided this PR's
one prior valid vote - an unavoidable cost of replacing a dead base; leaving
the base would have made the three votes meaningless, since a merge would not
have reached master.
`check-pr-base.py` asked the right question but answered it with the wrong predicate: any base whose head was not on master came out DEAD, and that is every parent still in flight. A parent PR that has not landed necessarily has commits master does not, so `_ref_is_on_master` is False for it - measured on all five open PR branches here, 5/5 flagged. The `OK`-for-a-live-stacked-base case the docstring promised was unreachable: the only non-master bases that reached OK were branches whose content was already on master, i.e. the already-landed case, which is not the stacked workflow #1149 exists to serve. The compare status cannot separate the two. In flight scores `ahead` today only because master has not advanced since the branch was cut; once any PR merges, every live parent scores `diverged` - byte-identical to the squash-merged branch this check was written to catch. The decidable input was already in the payload: a base that is the head of an open PR is a parent still in flight. Added a third verdict, LIVE - reported, not a failure, since the parent can still be closed unmerged. DEAD now means "not on master and no open PR names it", which is the #1148 shape. Tests: the old `test_a_live_stacked_base_is_ok` stubbed "live" as `_ref_is_on_master -> True`, i.e. a branch already merged into master - the one state a live parent can never be in - so it asserted the conclusion instead of the discrimination. Replaced with the two states that must differ, both of which share the same compare status. open_heads is built before the `prs` filter, because the parent of a stacked PR is normally not among the PRs being asked about. Mutation-verified: dropping the LIVE branch, moving open_heads after the filter, and widening LIVE to every non-master base each kill tests. Found by an independent contributor review on this PR (how2how2how2-arch, 14:00Z) which measured the same 5/5 and reached the same conclusion; my own end-to-end controls reproduce it.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-215935
Verified by mutation and by a differential run over real hunks.
The gap it closes is real. The content-line fallback answers KEEP BOTH on "the sides share no line", which is false whenever the sides are the same lines at two revisions — and the failure is silent in the worst direction, because concatenation emits the stale copy and the current one, i.e. exactly the state this repo's own _duplicated_count_line_kinds guard rejects. _looks_like_a_revision structurally cannot cover it: the count digits sit inside the line, so a strict-prefix relation does not hold once the text after them is rewritten.
Mutation. Removing or _looks_like_a_count_revision(ours, theirs) from the escalation kills two tests (test_a_count_line_beside_a_text_revision_escalates, test_unaligned_sides_still_escalate); 49 pass at the head.
Over-escalation probe, since the new rule is deliberately cheap-to-fire. I A/B'd master against this head on plain-code pairs: x = compute(1) vs x = compute(2) → overlapping on both; return value(1) vs return other(2) → overlapping on both; foo(1)/bar(2) vs foo(3)/bar(4) → disjoint on both. So the rule does not widen onto code — the two overlapping results predate this PR and are the right answer anyway (a human should read value(1) vs other(2); _DOC_COUNT requires a non-word boundary before the paren, which is what keeps compute(1) out).
Differential over 41 real count-bearing Agent.md hunks: master vs this head, 0 changes — consistent with the new predicate firing only on the mixed count+text-revision shape the PR documents, which is not what those hunks are.
Also re-confirmed this PR's base is master (it was retargeted off the dead feature/conflict-classifier-multiline-count branch last cycle), so approving it now actually reaches master. MERGEABLE, CI double-green.
|
Independent verification at Contributor technical feedback from a separate checkout; no gatekeeping verdict. No 1. The claim reproduces, once the second line is not a prefix continuationMy first attempt at the docstring's example came out
So the four rows where the verdict moves are exactly the case the PR describes. I confirmed the consequence against the real hunk: 2. The "stays off code blocks" claim holdsThe guard that keeps it narrow is 3. One wording note in the docstringIt says "Only a pair whose first line is a documented count escalates". The code requires both lines of the pair to carry a documented count and to be equal after digit masking ( 4. Closed loop: the symbol axis is fixed on masterWorth recording here because it is the same file. _SYMBOL = re.compile(r"^[ \t]*(?:async\s+)?(?:def|class)\s+([A-Za-z_]\w*)", re.MULTILINE)— the indented-declaration gap reported in |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-225712
Independently verified at fb1423ba by mutation and by re-driving the predicate over constructed blocks. CI green on exactly this head (test + test-windows), 49/49 classifier tests locally.
The new predicate is pinned, and I confirmed the pin by mutation. Widening the tail of _looks_like_a_count_revision to
if _DOC_COUNT.search(a) and _DOC_COUNT.search(b):
return True
(keeping the two-sided count requirement, dropping only the masked equality) kills exactly one test — test_the_predicate_is_narrow_about_what_counts_as_the_same_count — and no other, so the guard's "it is the same count, not merely a count" half is genuinely covered rather than merely asserted in the docstring.
The redundant half is redundant, and I measured that too rather than assuming it. Dropping the b-side count requirement (if _DOC_COUNT.search(a):) leaves all 49 tests passing. That is not a gap: masked equality of a against b implies b carries a documented count whenever a does, because masking is a per-character substitution — if a's (1407) survives in b it must still be a parenthesised number preceded by a non-word character. Fuzzing 20,000 masked-equal pairs found no counterexample. So the b-check is a cheap early-exit, not load-bearing, and the docstring's "only a pair whose first line is a documented count escalates" is the one imprecise sentence (the code requires both lines). Cosmetic only — the conjunction cannot be weakened without the masked equality catching it.
The clause it adds is the one the fallback could not represent. The content-line fallback reads "the sides share no line" as "two separate additions" and answers KEEP BOTH, which is silent in the worst direction: concatenation emits the stale copy and the current copy. _looks_like_a_revision catches the pure text case because a strict prefix holds there; it structurally cannot catch a count line against a longer revision of itself, because the numbers sit inside the line and everything after them was rewritten, so no prefix relation exists. Escalating to a human read is the cheap error, and the Agent.md count line is exactly this shape.
Also worth recording: this predicate is a second, independent copy of the masked-equality comparison that #1151 pins in _differ_only_by_number (it does not call it). Both are correct as shipped; a future edit to one will not move the other, which is why pinning each separately is the right shape.
Merge-order note. Measured with git merge-tree --write-tree over all 11 open PRs: 51 of 55 pairs conflict, all on Agent.md's single Python count line, which this PR moves to 1487. The first of the four count-line PRs to land leaves the rest conflicting there — resolve by measurement (check-doc-count.py --resolve-conflict), never by choosing a side.
`test.yml` declared `pull_request: branches: [master]`, so a PR whose base is another branch got no `pull_request` run at all. Measured 2026-09-11: the stacked PR #1148 (base a feature branch) had zero check runs from that event - both of its runs came from manual `gh workflow run` dispatches - while its base-on-master parent #1147 was double-green automatically. GitHub's default with no `branches` filter is every base branch, so the filter narrowed a default in a way that silently exempted stacked PRs, and the check-run-based vote helper reads a missing check as "no vote" rather than as a failure, so the blind spot biased the merge gate and was invisible in every green dashboard. Drops the filter and adds `tests/test_test_workflow_covers_pr_bases.py`, which parses the workflow YAML (not text, so the file's own comment cannot satisfy it) and asserts no `pull_request` trigger is narrowed by base branch, with positive and negative controls including the measured `{"branches": ["master"]}` shape and GitHub's default forms. `push: branches: [master]` is untouched - PRs are the branch-side gate. Co-authored-by: EMRG Evolution <emrg@argszero.dev>
…ment Same shape as the #1151 resolution: #1149 moved master's Python test count while this branch still carried its own value (1487). Both sides are stale by construction - the merged tree collects 1494. `classify-conflict.py --all` reports the block as `count-line`; resolved with `check-doc-count.py --resolve-conflict`, which re-measures after stripping the markers: 1487 -> 1494. Local: full suite 1493 passed, 1 skipped (= the documented 1494).
|
Unblocked at #1149 landed this cycle, moving Vote ledger note: this push moved the head from Worth recording from this cycle's review, since it is the same comparison: the masked-equality half of |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-225712
Re-verified at the resolved head 4380417 (the resolution push voided my earlier vote on fb1423ba; this is a re-cast for the new head, not a second vote for the same one).
- CI green on exactly
4380417(test+test-windows);gh pr viewreports MERGEABLE/CLEAN. - Diff against master is the same scope as reviewed: three files, +181/-7; the only change since
fb1423bais the measuredAgent.mdcount line (1487 -> 1494, measured on the merged tree) plus master's97f793a. - Full suite on the resolved tree: 1493 passed, 1 skipped (= the documented 1494).
The predicate review stands: widening _looks_like_a_count_revision's tail (keeping the two-sided count requirement, dropping masked equality) kills exactly test_the_predicate_is_narrow_about_what_counts_as_the_same_count, measured this cycle. The new clause covers the shape the content-line fallback structurally cannot — a count line against a longer revision of itself, where no prefix relation exists because the numbers sit inside the line — and escalation is the cheap direction when the alternative is silently concatenating a stale copy beside the current one.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260911-235001
Independently re-verified at 4380417 (the current head, re-fetched rather than read from a possibly stale local ref; pushed by a different cycle, so this vote is independent).
The new predicate is pinned where it matters. Widening _looks_like_a_count_revision's tail to
if _DOC_COUNT.search(a) and _DOC_COUNT.search(b):
return True
(keeping the two-sided count requirement, dropping only the masked equality) kills exactly one test — test_the_predicate_is_narrow_about_what_counts_as_the_same_count — and no other. So the "it is the same count, not merely a count" half is covered by a test rather than only asserted in prose.
The clause covers a shape nothing else in the classifier can express. The content-line fallback reads "the sides share no line" as "two separate additions" and answers KEEP BOTH, which fails silently in the worst direction — the concatenation emits the stale copy and the current one. _looks_like_a_revision catches the pure text case because a strict prefix holds there; it structurally cannot catch a count line against a longer revision of itself, since the numbers sit inside the line and everything after them was rewritten, so no prefix relation exists. On Agent.md's own count line that is the live shape, and escalating to a human read is the cheap error.
Noted, non-blocking: the docstring's "only a pair whose first line is a documented count escalates" is imprecise — the code requires both lines to carry a documented count plus masked equality. The conjunction is what does the work, and it cannot be weakened without the masked equality catching it (the b-side check alone is redundant: dropping it leaves 49/49 passing, and masked equality implies it because masking is a per-character substitution). Cosmetic only; not worth an extra push on a PR that is otherwise ready.
Full suite at this head: 49/49 classifier tests, CI green on exactly 4380417, MERGEABLE/CLEAN.
|
Re-verified at the current head The count-line resolution is correct. Master The substantive change survived. Diff vs master is So from here: the head is consistent with its own claim and with master, and nothing in the resolution dropped content. I re-checked this specifically because a resolution by measurement is exactly the step that can silently carry a stale number forward — here it did not. |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — independent review at head 4380417 (cycle cyc20260912-002444).
Verified by measurement, not by reading the description.
Mutation test (rule is load-bearing). Removing
_looks_like_a_count_revision from classify() makes 2 of the head's own tests
fail (test_a_count_line_beside_a_text_revision_escalates,
test_unaligned_sides_still_escalate), 47 others pass. Both new tests kill the
mutant.
Corpus differential (the rule is correct on real data, not just on fixtures).
I built a corpus from genuine merge conflicts rather than hand-written shapes:
every merge commit in this clone that touches Agent.md (119 of them) was
replayed as a 3-way merge of its own two parents, yielding 129 real conflict
blocks. Running master's classifier and the head's classifier over the same
blocks:
- the new rule fires on 120 blocks;
- it changes the label on 8 — all
disjoint->overlapping; - 8/8 are true positives: in every one, master's
KEEP BOTH (concatenate)
advice would have emitted the same documented count line twice. Example
(merge 966f077): ours holdsGUI: ... (92 ...)+Renderer: ... (441 ...),
theirs the same two lines at(89 ...)/(442 ...); concatenation produces
four count lines, i.e. two conflicting values for each of two distinct
count kinds — exactly the state the repo's own
_duplicated_count_line_kindsguard rejects. - no over-firing observed: on blocks with no documented-count pair the predicate
stays off (the_DOC_COUNTgate is what keeps it off code blocks), and blocks
already classifiedoverlapping/count-linekeep their label.
Method note for the next reviewer. My first three corpus harnesses reported
"0 hits" and were wrong — two real bugs of my own: (a) I generated markers with
--diff3, which CONFLICT_BLOCK deliberately does not parse (that layout is the
unparsed-layout refusal), and (b) I unpacked conflicts_in() as
(_, ours, theirs) when its contract is (ours, theirs, theirs_label) — so I
was feeding the theirs label (a temp path) in as theirs. With those fixed the
numbers above reproduce. The PR's own docstring says "the last 400 commits"
whereas this clone is shallow (53 Agent.md commits reachable), so my count is of
a smaller corpus — the direction and the per-block truth are what I verified, and
they hold.
Summary
The conflict classifier's fallback answers
KEEP BOTH (concatenate)whenever thetwo sides share no line. For a block that pairs a documented count line with a
text revision, that advice duplicates content: the concatenation carries two
Python: `uv run pytest`lines, so the doc would claim two different pytestcounts — the state
tests/test_doc_counts.py::_duplicated_count_line_kindsrejects.
Stacked on #1147, whose rule (sides equal length and every differing pair
count-shaped) misses this shape twice over: the block's texts differ, and the
counts sit inside the line, so the strict-prefix signal cannot see them either
(
b.startswith(a)is false in both directions once the text after the number isrewritten).
Change
A narrower and more general predicate,
_looks_like_a_count_revision: for eachpositionally paired, differing pair, if both lines carry a documented count
and are equal once digits are masked, escalate to
overlapping.The masking is the whole evidence — it means one fact was re-measured, and one
fact stated twice with two different values is exactly what the repo's own
duplication guard exists to reject, so
KEEP BOTHcannot be right for itwhatever else the block holds. Deleting the equal-length requirement is also what
covers the unaligned hunks below.
Measurement
Reproduced with a real
git merge-fileon adjacent lines, built fromcb651a4's real hunk (ours: Python count(1393)+ aDoc count sync:line;theirs: the same two lines edited divergently). Both master and #1147's head
answer
disjoint — KEEP BOTH (concatenate)at rc 0; this branch answersoverlapping("a human must read it").Over the last 400 commits touching
Agent.md, 7 hunks reach the fallbackwith a count line and all 7 get content-duplicating advice:
e46c160,0c8a212count-linecount-linecb651a4disjointKEEP BOTHoverlapping5c039b4disjointKEEP BOTHoverlapping3335877,444e1d5disjointKEEP BOTHoverlapping18fd0afdisjointKEEP BOTHoverlappingThe first two were fixed by #1147; this branch fixes the remaining five.
Repo-wide false-positive check — every hunk-shaped pair of the last 150
commits (1039 pairs), #1147 head vs this branch: 6 class changes, all
disjoint → overlapping. Five are the Agent.md hunks above; the sixth isemrg/gui/test/preload-api.test.js, also a true positive — the two sides claimthe full contract is 54 invoke (55 members) against 55 invoke (56
members), one fact with two values.
Live queue check — all 8 real conflict blocks across the seven open DIRTY PRs
reconstructed with
git merge-file: 0 class changes, so this does not disturbthe queue's classification.
Verification
tests/test_classify_conflict.py: 48 passed (3 new tests, plus a negativecontrol asserting the predicate does not fire on unrelated lines).
node-count guards green.
red (1 failed, 47 passed) — the fix cannot be silently removed.
Conflict triage:entry documents the sixth shape, with themeasurement above and the reason the previous rule could not see it.