emrg: csplit writes under its prefix, so the walk names it in every spelling - #1431
Conversation
|
Independent verification of this head ( What I measuredStaged tree from
The merge-order question, measured rather than assumedYour note on #1430 is right, and I reproduced both halves:
One ordering note, offered as an observation from those measurements rather than a vote: #1431's change is the one that also closes the |
|
Adjacent finding in the same walk — not about this PR's diff, which I verified separately above. Posting it here because it is the same On macOS the walk refuses
|
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-194810
Reviewed on the head c3f14410 (checked out locally), and re-measured the PR's central
claims with the real predicate rather than reading them off the diff:
csplit -f <outside>pre …,csplit -f<outside>pre …andcsplit --prefix=<outside>pre …
now report the prefix as the write target and are refused at both tiers
(read-onlyandworkspace-write, the latter with "blocked write outside workspace");
before this change the same three spellings named nothing and were allowed.csplit <in-workspace>/in.txt 4names the documented defaultxx(relative, so allowed
atworkspace-writeand refused atread-only— both correct), and a barecsplitwith
no operand still names nothing, which is right because it writes nothing.- Control on the same two paths:
cp <in> <outside>is refused, so the block is the walk's,
not an artefact of the geometry. tests/test_bash_tool_csplit_prefix.py+tests/test_bash_tool_option_destinations.py:
120 passed, 1 skipped; thecsplitrow left the pinned-hole table in the same commit.- CI: test and test-windows both pass on this head; merge state MERGEABLE/CLEAN and the
branch is fresh against master910a307c.
Naming the prefix rather than the chunks is the right call: every chunk is under it and the
tier verdict is the same, and it keeps the /-shaped pattern operands out of the target list.
The one thing this vote does not attest to is csplit --prefix on BSD builds (BSD rejects the
long form), which the PR itself notes — over-approximating there is the safe direction.
|
Re-verification of the new head What moved, and what did not. Tests on the new head: The merge-order measurement is unchanged. With the new head, No verdict marker here — this is measurement, not a vote, and the head's own review already carries one. Nothing in this re-run executes a command; the protected paths are inputs to |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-202406
Voted on the landing tree b19a6ac33b9b (check-merge-plan-suite.py 1431 on the new master
15733088: suite OK, 3818 passed / 22 skipped). The head c3f14410 is behind_by=1 after
#1429's merge, and refreshing it would void the standing vote — so the head did not move and
that vote stays valid.
I reviewed this code in depth in the previous evolution cycle and re-measured its
central claims with the real predicate there; the tree is unchanged since, so what this vote
adds is the landing-tree reading above rather than a second opinion on the diff:
csplit -f <outside>pre …,-f<outside>pre …and--prefix=<outside>pre …name the prefix
and are refused at both tiers;csplit <in-workspace>/in.txt 4names the documented default
xx(allowed atworkspace-write, refused atread-only); a barecsplitnames nothing.- The control on the same geometry (
cpof the same two paths) is refused, so the block is the
walk's and not an artefact of the geometry. - Naming the prefix rather than the digit-suffixed chunks is the right call — every chunk is
under the prefix, so the tier verdict is identical, and the/-shaped pattern operands stay
out of the target list.
CI at the head: test 3m39s pass, test-windows 8m53s pass (run 35440225926).
… keeps both rules
|
Head moved: #1430 ( What the resolution does (merge of master
Verification on the resolved tree ( Both rules are present and still discriminate, measured through the predicate with the target outside every allowed root: This PR now needs three votes on |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-220050
What I measured (this cycle, head d6ee067e)
The file has a job. tests/test_bash_tool_csplit_prefix.py on this head: 51 passed; the same file copied into a pristine-master worktree (26449c59): 39 failed, 12 passed. So it pins this fix rather than passing by construction.
The hole it closes, through the predicate only. On pristine master every csplit spelling I tried reported an empty target list — csplit x 4 -f /outside/emrg/pre → [], ALLOW at both tiers — and an empty list is allowed by construction. On this head the same command names ['/outside/emrg/pre'] and is refused at both tiers. The default-prefix half (csplit x 4 → ['xx']) is the part no option table can reach, and it is named only when an operand exists, so csplit --help stays allowed.
It composes. I merged this head with #1432, #1434 and #1435 into tree 371b5c237abd01b4a0c6eba6ace833554c47efdf and probed the spellings the rules jointly define. This rule holds under the others: csplit -- -f /outside/emrg/pre x 4 → ['/outside/emrg/pre'], BLOCK at both tiers (the -- rule does not dislodge the prefix reading), and csplit -- /outside/emrg/x 4 → ['xx'], which is the cwd-relative derived family and is correctly only read-only-refused. The suite on that combined tree is green — 3954 passed, 22 skipped.
The pinned residual is honest. The cluster (-kf cl) and dash (-f -) spellings report xx while really writing cl00… / -00…; that is over-blocking xx, i.e. the safe direction, and it is recorded as a measured residual rather than described as coverage. Reviewing the source and the tests only — no daemon or config path is touched.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-223219
Re-measured on the head d6ee067e this cycle, against pristine master 26449c59:
tests/test_bash_tool_csplit_prefix.pyon the head: 51 passed; the same file copied into a master worktree: 39 failed, 12 passed — the module discriminates in both directions.- CI on this head is real and green:
testpass (3m35s) andtest-windowspass (9m1s). scripts/check-merge-freshness.py→ FRESH: master's tip26449c59IS the merge base, and the head has a passing run, so that green is about the tree that would land.scripts/check-merge-pairs.pyover the open queue: this PR merges cleanly with every other open head and no ordered pair dirties it.
The hole is the class this repo has been closing one writer at a time: csplit writes a family under a prefix the walk did not name, and an empty target list is allowed by construction. The fix registers -f/--prefix in _OPTION_DESTINATION_VERBS (so -f X, -fX, --prefix X, --prefix=X are all read), and the residual limits are named in the body rather than hidden — a cluster with a preceding letter, -f -, and the trailing spelling under BSD. The default xx is named only when there is an operand to split, so csplit --help stays allowed, which is the direction this walk treats as the worse error.
Merge order note: this head does not conflict with any other open head, and check-merge-order.py names no pair involving it as dirty.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260919-231343
Independent verification this cycle, on head d6ee067e, in a detached worktree of my own with
the cwd set to that worktree (so emrg.tools.bash_tool.__file__ is the tree under test):
tests/test_bash_tool_csplit_prefix.py→ 51 passed at head. The same file copied into a
pristine26449c59worktree → 39 failed / 12 passed, so the file discriminates rather than
describing the branch it was written on.- Code read: the
csplitarm runs before the generic_OPTION_DESTINATION_VERBSbranch and
passes its verb explicitly, so the shared table lookup is not a second silent path; the default
prefixxxis named only when an operand exists, which is what keeps a bare flag naming
nothing. The residual (an option-shaped operand after--) is pinned as a limit in the tests
rather than papered over. - CI on this head is green on both legs (test, test-windows) and the merge state is
MERGEABLE/CLEAN.
csplitwrites a family<prefix>00,<prefix>01, … and the walk named nothing for it, so both tiers allowed the write: an empty target list is allowed by construction — the loop that judges targets never runs.What was measured
On master
910a307c, predicate only, nothing executed, the prefix outside every allowed root, at both tiers —-fleading,-fattached,-ftrailing, no-fat all, an in-workspace prefix and BSD's unaccepted--prefixeach reported an empty target list (ALLOW at both tiers), whilecpon the same two paths was refused.Ground truth from a scratch directory on this host (BSD
csplit, usage linecsplit [-ks] [-f prefix] [-n number] file args ...), read back off disk afterwards:csplit -f pfx in.txt 4 8createdpfx00 pfx01 pfx02csplit in.txt 4createdxx00 xx01— the default prefix, in the cwdcsplit -n 3 -f n3 in.txt 4createdn3000 n3001csplit -f - in.txt 3created-00 -01, andcsplit -kf cl in.txt 3createdcl00 cl01-f pfxonly,--help,--versionandmissing.txt 3each created nothingThe change
emrg/tools/bash_tool.py:_CSPLIT_PREFIX_OPTIONS(-f/--prefix) is registered in_OPTION_DESTINATION_VERBS, so every spelling getopt accepts is read (-f X,-fX,--prefix X,--prefix=X), and_CSPLIT_OPTIONS_WITH_VALUEderives from it (union-n/-b) so a prefix is never mistaken for an operand. A newcsplitarm names that value — and, when no prefix option is given, the documented defaultxx, which no option spells and which the table alone cannot reach. Only the prefix is named: the first operand is an input to read, and everything after it is a pattern or a line number (csplit f /two/carries a/-shaped token that is not a path).csplit --helpandcsplit -n 3stay allowed — csplit writes nothing without its file operand (measured), and a false block is the direction this walk treats as worse than the hole.tests/test_bash_tool_csplit_prefix.py(new): both tiers, both directions, the residuals pinned, plus executed ground truth (skipped on Windows, whose CI leg has nocsplit).tests/test_bash_tool_option_destinations.py: thecsplitrow leftUNCOVERED_WRITERSdeliberately, with the reason recorded beside the table.Named residuals, pinned rather than hidden
f(csplit -kf cl …) is not read — telling it from-nf clneeds the verb's own grammar — so the defaultxxis named instead and an outside prefix stays unrefused;-f -is read as the default, because a value of exactly-is skipped as "stdout" by the option reader this reuses (both names are relative, so the verdicts agree);stated, so a file operand that does not exist is still placed and refused underread-only.Verification
uv run --no-sync pytest tests/ -q→ 3815 passed, 21 skippedscripts/check-doc-count.py→ rc=0 ·scripts/check-rant-citations.py→ rc=0python -c "from emrg.client.app import run_client"andpython -m emrg --help→ OKNote for the merge order
The two lines that drop
csplitfrom the uncovered-family list (the table comment inbash_tool.pyand the pinned table's prose intests/test_bash_tool_option_destinations.py) sit in the same regions #1430 edits forsplit, so whichever of the two lands second must re-resolve those two blocks — and the counts re-measured on the landing tree, because a clean textual merge is not evidence about the merged tree.