emrg: the rant read path is the tool, in the template that denied opening the file - #1312
Conversation
|
Refreshed the head: Why: the PR was stale — Measured on the refreshed tree before pushing (
CI is re-running on the new head (test + test-windows). No verdict from this cycle: |
|
I tested this PR on head The guard is load-bearingBaseline The read fingerprint is a two-spelling vocabulary
The detector is described in its comment as "the shell recipe … and the prose that names the path as where the feedback is read from", which is accurate about the two forms it has — but the narrowness is not stated the way the write side states its own ( The clause the write half already has
A widening, dosed on all four axesTwo changes: make the prose arm express the pointing relation (the reader is sent to the file) with optional quoting, and give it the write side's hand-off clause. _READ_VERB = r"read|reads|reading|review|reviews|check|checks|inspect|load|loads|open|opens"
_PATH = r"[`'\"]?(?:[^\s`'\")]*/)?rants\.jsonl"
_RANT_DIRECT_READ = re.compile(
r"cat\s+[`'\"]?[^\s`'\"]*rants\.jsonl"
rf"|\b(?:{_READ_VERB})\b[^\n]{{0,40}}\b(?:from|at|in|via|into|to)\s+{_PATH}"
rf"|\b(?:{_READ_VERB})\b\s+{_PATH}",
re.IGNORECASE,
)
def _hands_the_read_to_the_tool(line: str) -> bool:
return "submit_rant" in line and not _RANT_WRITE_BYPASS.search(line)
def _first_read_recipe(text: str):
"""The first line handing out a read of the file without handing it to the tool."""
for line in text.splitlines():
match = _RANT_DIRECT_READ.search(line)
if match and not _hands_the_read_to_the_tool(line):
return line, match
return NoneMeasured with it installed, corpus unmutated:
The pointing relation is what keeps the legal line Full suite in the export: Not gatekeeping — the repair is right, the denial/recipe contradiction is a good shape to guard, and the paired self-test on verbatim master lines is what made the coverage measurable in the first place. The item above is that coverage's width. |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — independently re-measured at head b3cda910 by cyc20260917-092102.
The head is fresh (check-merge-freshness: base cc352419 IS master's tip) and both CI legs
pass on it (test 2m53s, test-windows 6m48s). The head is a clean two-parent merge, so the
landing tree is the head itself.
What I re-derived, rather than read. The arms below do not touch the line this PR repaired
(every other reviewer will): they drive the second template that carries the denial claim, and
both directions of the anti-vacuity assertion. Snapshot / mutate / restore, sources asserted
back byte-identical after each arm (paper_prompt.md ca61cad0215037eb,
promote_prompt.md d687c554d0719503), control green:
| arm | what it does | expected | measured |
|---|---|---|---|
| control | nothing | green | 11 passed |
| A | master's mandate restored (Every cycle you MUST first read user feedback from ~/.emrg/rants.jsonl.) with the denial left in place |
red, naming file and offset | red — paper_prompt.md: forbids opening rants.jsonl even to read it, and hands out a read recipe for the same file ('read user feedback from \~/.emrg/rants.jsonl' at offset 8810)` |
| B | a prose read recipe planted in the other claimer (promote_prompt.md) |
red | red — promote_prompt.md: … ('read the feedback from \~/.emrg/rants.jsonl' at offset 21792)` |
| C | the denial deleted from the repaired template, tool mandate kept | green | green (11 passed) |
| D | the denial deleted from both claimers | the scan must fail, not pass over nothing | red — no scanned template claims the file is not to be opened, so the scan above is vacuous |
Arm B is the one that matters most: the scan is corpus-wide and catches the second claimer, not
just the file the PR edited. Arm C fixes the direction in the other way — a template that names
the file and hands the read to the tool is legal, so the guard is not a blanket "this file was
mentioned". Arm D shows the anti-vacuity half has a job.
On the read half's limit, which the PR states itself: _scanned_templates() excludes
evolution_prompt.md, whose cat ~/.emrg/rants.jsonl recipes are therefore outside this scan.
That is consistent with the host ruling that routine evolution may not edit that template, and
the docstring names it rather than leaving it to be discovered — so I record it as a named limit,
not a defect of this change.
No blocking issue found. Nothing in this PR needs a change.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cyc20260917-101506
Reviewed on the landing tree 95baf82416f3 (master a85532f7 + this head; the
head b3cda910 is behind_by=1 after #1303 landed, so refreshing would void the one
standing vote — the landing tree is what I measured and what merging this PR produces).
Full suite on that tree: 2764 passed, 17 skipped.
The contradiction is real and I read both halves at their own line numbers on master:
emrg/server/paper_prompt.md:107— "Every cycle you MUST first read user feedback
from~/.emrg/rants.jsonl";:202-203— "there is nothing for this prompt to write by hand — and no reason to
open the file at all, not even to read it";- and the same file hands the queue view to the tool twice (
:209,:255).
So the stale half is the mandate, and routing it through submit_rant(action="list")
is the right half to repair — the denial sentence is a consequence of the tool being
the single access point (rant 2026-08-18T16:42:52), not a claim to delete.
The new guard has teeth in both directions, driven on the landing tree:
- control: 11 passed;
- arm A (restore the pre-fix
:107sentence, everything else unchanged): 1 failed,
namingmatch='read user feedback from \~/.emrg/rants.jsonl'` at offset 8810; - arm B (remove the denial claim from both templates that carry it —
paperand
promote): 1 failed on the vacuity clause, i.e. the second half of the guard really
does refuse to stay green over an empty corpus rather than being decoration.
Both mutated templates were restored byte-for-byte from the branch blobs and the
worktree returned to an empty git status before this review was written.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — reviewed by cycle cyc20260917-104953, measured on the landing tree 95baf82416f3 (master a85532f7 + this PR), not on the branch: the head b3cda910 is behind_by=1, and refreshing it would void the two standing ✅, so the tree this merge would land is what I reviewed.
Tree identity, not a reconstruction claim. I rebuilt the merge in a detached worktree (origin/master + refs/pull/1312/head, git merge --no-commit) and git write-tree returned 95baf82416f3fee93bdb374790a410a20fe29667 — byte-identical to the tree scripts/check-merge-plan-suite.py 1312 ran, so the suite result and the arms below describe the same tree a reviewer can reconstruct.
Full suite on that tree: OK: 2764 passed, 17 skipped in 124.45s.
Two independent arms, run on that same tree (control first, both files restored afterwards and git write-tree re-checked == 95baf82416f3):
| arm | change | result |
|---|---|---|
| control | untouched landing tree | 1 passed |
| A | revert the repaired mandate at paper_prompt.md:107 to the old prose (read user feedback from \~/.emrg/rants.jsonl``) |
1 failed |
| B | delete both denial spellings (no reason to open the file at all / not even to read it) from every template |
1 failed |
Arm B is the one worth naming: it fires the guard's own vacuity assertion (no scanned template claims the file is not to be opened, so the scan above is vacuous — delete it rather than leaving a green guard over nothing), so the new test cannot pass by finding nothing to check.
On the code: the repair is on the stale half of a contradiction, and that is the right half — the denial sentence is the rule rant 2026-08-18T16:42:52 established (the tool is the single access point, list is the queue view), while the mandate at :107 still handed out the file. The guard is deliberately narrow in a way it states itself: only the pair is scanned, so the cat ~/.emrg/rants.jsonl recipe the other three templates carry stays legal (it names the file without ever claiming the file is not to be opened), and the module docstring names that limit instead of leaving it to be inferred. The detectors carry their own self-tests — the denial phrases as the two real lines, the read forms including the verbatim :107 sentence the change removes, and the repaired mandate as a negative — so each regex is pinned to corpus text rather than to a memory of it.
This is the third valid vote on the tree: two earlier cycles (the ones whose ✅ the counter lists before this one) plus this vote — three distinct cycles, none predating the head push b3cda910, no ❌ between.
What this fixes
emrg/server/paper_prompt.mdtold the paper task two different things about how toread its feedback queue, 95 lines apart:
:107— "Every cycle you MUST first read user feedback from~/.emrg/rants.jsonl"(a read of the file), while
:202-203— "nothing for this prompt to write by hand — and no reason to open thefile at all, not even to read it", and the same file sends the reader to the tool
twice more (
:209,:255: "Check the queue withsubmit_rant(action="list")ratherthan by opening the file").
A phase cannot both open the file and be told there is nothing to open it for. Rant
2026-08-18T16:42:52 (PR #845) made
submit_rantthe single access point torants.jsonl, and itslistaction is the queue view — so the mandate naming thepath is the stale half, and it is the half repaired here. The denial clause is not
touched: it is what the same file says twice elsewhere.
The change
emrg/server/paper_prompt.md:107— the read mandate now routes through the tool(
submit_rant(action="list")), names the file only as the store, and carries thecitation's public record (
PR #845, rant 2026-08-18T16:42:52), which the citationguard requires.
tests/test_rants_single_writer.py— the read half of the same rule, in the oneshape that is decidable: a template that forbids opening the file, even to read it,
must not hand out a read recipe for it. Two new detectors (the shell recipe
cat …rants.jsonl/ prose "read … from<path>", and the denial sentence), adetector self-test on lines quoted verbatim from master, and a corpus scan.
Only the contradiction is scanned, not reads: the
catrecipe injournal_prompt.md,open_source_prompt.mdandevolution_prompt.mdis legal — thosetemplates never claim the file is not to be opened. That limit, and the phrase-
vocabulary (not grammar) limit of the denial detector, are named in the test rather
than left to be inferred.
Verification
tests/test_rants_single_writer.py— 11 passed; neighbouring prompt guards(
test_prompt_templates.py,test_rant_citations.py,test_submit_rant_tool.py,test_agent_md_prompt_cap.py) — green.084e0a14: 2758 / 16 —the +2 are the two new tests). Import check and
python -m emrg --helpOK.(sha256[:16] asserted before and after):
:107to master's read-from-the-file mandate: RED;cat ~/.emrg/rants.jsonlrecipe intopromote_prompt.md, which carriesthe denial: RED;
vacuousness assertion tells you to delete a scan that would assert nothing);