Skip to content

emrg: a refuted conclusion is replaced where it stands, not annotated - #2787

Merged
argszero merged 1 commit into
masterfrom
fix/a-refuted-conclusion-is-replaced-in-place
Nov 10, 2026
Merged

argszero merged 1 commit into
masterfrom
fix/a-refuted-conclusion-is-replaced-in-place

Conversation

@argszero

Copy link
Copy Markdown
Owner

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

  • A refuted conclusion is replaced where it stands, never annotated — the wrong sentence deleted, the right one written in its place. An entry already carrying correction sections is rewritten whole, and one refuted as a whole takes status: superseded and leaves the active index, whose row follows the same rule as the body it points at.
  • What a refutation keeps is the error and its discriminator — what was thought, what is true, and the test that separates them. Not the timeline and not the effort, which the next round reads as fresh fact.

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_at keep 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.

superseded is 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 pair test_plain_language_rule_reach.py exists for). Both directions were exercised:

  • Arm A — master's wording, on this branch's test: all four redden.
  • Arm B — this branch's wording with the append line put back: the three tests that carry the removal half redden, and the one that asserts only the replace rule on the shipped file stays green. So the guard is not satisfied by adding the new rule while the old one survives, and each assertion is exercised by the arm aimed at it.
  • Baseline — all four green, and tests/test_memory_block_leaves_the_prefix.py plus tests/test_plain_language_rule_reach.py stay green.

@how2how2how2-arch how2how2how2-arch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 changed and the How settled an entry is decides how freely it is rewritten framing 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 ## Memory section (48–73), not appended at the end of the file: replace where it stands, rewrite an entry already carrying correction sections whole, status: superseded for 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.py and test_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 argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.py answers STALE: master moved to dc1e8ee when #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 dc1e8ee3 and ran the affected files: 99 passed (the four new tests plus the 95 in test_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: test 3m9s and test-windows 4m16s, 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 argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@argszero
argszero merged commit 5fdcac2 into master Nov 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

emrg: a refuted conclusion is replaced where it stands, not annotated with a correction

2 participants