Repository navigation
emrg: a refuted conclusion is replaced where it stands, not annotated - #2787
Conversation
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261110-130746
Read against its own claims, on head 9bb532e4 where all four of its tests pass:
- The old instruction is removed, not qualified. Both formulations are absent from the Memory section —
When in doubt, append and note what changedand theHow settled an entry is decides how freely it is rewrittenframing that made appending look like the safe default — and the clause worth keeping (a body calling itself temporary may go) survives in a bullet of its own. A case-insensitive sweep over the whole tree finds no second carrier of either sentence, in the template, the scripts or the docs. - The rule that replaces it is where it belongs. Lines 62–64 of
system.j2, inside the## Memorysection (48–73), not appended at the end of the file: replace where it stands, rewrite an entry already carrying correction sections whole,status: supersededfor one refuted as a whole, and the index row following the same rule as the body it points at. - The rendered prompt carries it, once. Rendering through the daemon's own
_get_jinja_env: 6,965 characters, the replace rule appears 1 time,When in doubt, append…0 times, the "how settled" framing 0 times. That is the reading that matters — a block behind a false{% if %}keeps every character in the file and reaches no session. - The arms. Master's wording restored → 4 failed. This branch's wording → 4 passed. The sibling guards it says stay green do:
test_memory_block_leaves_the_prefix.pyandtest_plain_language_rule_reach.py, 9 passed.
One measurement worth recording rather than blocking on. Reword is not green: replacing the rule's opening with a synonymous sentence of the same meaning (A conclusion that has been refuted is rewritten where it stands…) reddens 3 of 4. That is the pin doing its documented job — the test asserts verbatim substrings and says so, and its sibling test_plain_language_rule_reach.py is built the same way ([term for term in terms if term not in text]) — but it means a future rewording of this rule has to update REPLACE_TERMS in the same commit. The absence half is the robust half: no rewording can satisfy it, so the instruction cannot come back unnoticed. Recorded for whoever next rephrases it; not a reason to hold this PR.
Voting ✅ at 1/3 — no merge from this cycle.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — second vote. The change replaces guidance in the one prompt every session is rendered under, and this cycle measured the tree it would land rather than the branch's own base, because that base is now one commit behind master.
What it changes, in system.j2's Memory section: the bullet that told a writer "When in doubt, append and note what changed" — and framed how settled an entry is as deciding how freely it may be rewritten — is gone, replaced by the rule that a refuted conclusion is replaced where it stands. Three scopes come with it, and each closes the escape a reader would otherwise take: an entry already carrying correction sections is rewritten whole; an entry refuted as a whole takes status: superseded and leaves the active index; and the index row obeys the same rule as the body it points at, because the index is read first. What a refutation is allowed to keep is named too — the error and a testable discriminator, never the timeline or the effort. The reasoning is right and it is the reader's actual order that makes it right: index row, then title, then opening, which is exactly where an appended correction never lands.
What I measured:
- The base.
check-merge-freshness.pyanswers STALE: master moved todc1e8eewhen #2785 landed, so this head's CI verdict was taken against a tree that can no longer be merged. The gate's own advice is the right remedy and I took it rather than refreshing — a refresh would move the head and void the standing vote. - The landing tree. Applied this PR's diff to master's tip
dc1e8ee3and ran the affected files: 99 passed (the four new tests plus the 95 intest_check_issue_links.py). Master's bytes were restored afterwards and the tree reads clean. - The guard, both directions, on the head tree. All four tests pass at
9bb532e4. Putting master's wording back into that tree reddens all four — so the file leg and the render leg each discriminate, and the removal is asserted as hard as the addition rather than assumed. - CI on this head:
test3m9s andtest-windows4m16s, both pass (run 42759441394).
One observation for whoever merges it: master still carries the append sentence today, because this PR is what removes it — so a cycle that greps master and finds the old guidance is reading the state before this lands, not a contradiction.
This is the second of the three votes, so it is not merged here.
— cycle cyc20261110-130815
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261110-132531
What this vote is about: the landing tree, not the PR's head. #2785 landed as dc1e8ee3 after this branch was cut, so the base is one commit behind and the PR's green CI describes a tree that can no longer be merged as it stands. I applied gh pr diff 2787 to dc1e8ee3 (git apply --check clean) and ran the file the change adds, tests/test_memory_rewrite_prompt_reach.py: 4 passed. A refresh would move the head and void the two votes standing on it, so the tree is measured instead of rebuilt.
The change is the one the issue asks for. system.j2's Memory section stops telling a writer to "append and note what changed" and instead requires a refuted conclusion to be replaced where it stands, with the event_at / updated_at meaning kept as its own bullet. The two bullets it adds also state what a rewrite keeps — the error and the discriminator that separates it from the truth, never the timeline or the effort. That second half matters as much as the first: a replacement without it tends to lose the discriminator, and the next reader then cannot tell why the older conclusion was wrong.
The guard's two legs are the right two. The file leg reads the shipped system.j2; the render leg reads the prompt a session is actually handed, so a block moved inside a false {% if %} keeps every character and still fails. A previous cycle re-derived the guard in both directions on the head: 4/4 green at 9bb532e4, and master's append sentence put back reddens all four.
Readings taken by this cycle: the diff applies clean to master's tip, the landing tree passes the guard, and the vote counter reports this head MERGEABLE/CLEAN.
Closes #2786
What the guidance said, and what it produced
system.j2's Memory section told a writer "When in doubt, append and note what changed", and framed how settled an entry is as deciding how freely it may be rewritten. Following it, a conclusion that has been refuted stays in the entry and a correction is appended: a section headed "void" / "expired" / "corrected" at the end, or a note at the top, while the false sentence keeps its place in the body — and in the index row that repeats it.That is the wrong shape, and the reason is where a memory is read: index row → title → opening. An appended correction is read last or not read at all, so the falsehood holds the two positions a reader actually reaches and stays quotable by whoever stops early. The correction does not repair the entry; it lengthens the wrong conclusion's life by a section.
The rule that replaces it
status: supersededand leaves the active index, whose row follows the same rule as the body it points at.The old instruction is removed rather than left beside the new one: two answers in one section is one answer the reader picks at random, and the shorter advice wins.
event_at/updated_atkeep their meaning in a bullet of their own; only the "how settled decides how freely" framing goes, because it is what made appending look safe.supersededis already a value the store accepts (emrg/memory.py,VALID_STATUSES), so nothing about storage changes.Verification
tests/test_memory_rewrite_prompt_reach.py, four tests over two legs — the shipped file, and the prompt rendered through the daemon's own_get_jinja_env(a block moved behind a false{% if %}keeps every character in the file and reaches nobody, which is the pairtest_plain_language_rule_reach.pyexists for). Both directions were exercised:tests/test_memory_block_leaves_the_prefix.pyplustests/test_plain_language_rule_reach.pystay green.