Skip to content

emrg: render the prompt's memory limits from the store's constants - #1220

Closed
argszero wants to merge 1 commit into
masterfrom
feature/the-prompt-states-the-memory-limits
Closed

argszero wants to merge 1 commit into
masterfrom
feature/the-prompt-states-the-memory-limits

Conversation

@argszero

Copy link
Copy Markdown
Owner

What

emrg/server/open_source_prompt.md told the agent:

keep MEMORY.md a pure index — one short line per entry (title ≤512 chars, …); if the
index exceeds ~50 entries, merge/consolidate instead of appending.

The memory store warns at INDEX_TITLE_MAX_CHARS = 512 and INDEX_COUNT_WARN = **100**.
The entry number was simply wrong. This is the same defect class as #1217 (a merge verdict
read from a tree that is not the merge), #1218 (the embed cap re-spelling the store's
threshold) and #1219 (the reflection reminder naming ~50-entry): a stated number that is
not the one that fires.
An agent following the prompt consolidates at 50 while the store
only starts warning at 100 — and, worse, learns that the threshold in its own instructions
is not the threshold it is measured against.

The change

  • emrg/server/scheduler.py — the builder offers the store's numbers to every template:
    index_count_warn, index_size_warn_kb, index_title_max_chars, imported from
    emrg.memory rather than re-typed.
  • emrg/server/open_source_prompt.md — the line renders them
    (title ≤{{ index_title_max_chars }} chars, exceeds {{ index_count_warn }} entries), with
    a note naming the constants so the next reader does not have to guess where the numbers
    come from.
  • tests/test_prompt_templates.py — a new arm renders the prompt through the real
    builder
    (real context, real template path), asserts the shipped text states both
    constants and that ~50 entries is gone, then tunes both constants (512→256, 100→42)
    and asserts the rendered text follows.

Why the tuned arm is the load-bearing one

The discriminator is the one #1218/#1219 settled: if the text reads the constant, tuning the
constant has to move the text
. A prompt that hard-codes 512 / 100 passes the shipped
assertions and fails the tuned ones, which is exactly the state this PR is fixing — so
without the tuned arm the test would pin the current numbers instead of the derivation.

Mutants, each applied to the working tree and restored by sha256 (verified
byte-for-byte):

mutant result
the prompt states both numbers as literals again killed (1 test)
the old ~50 entries comes back killed (1 test)
the title limit is stated from the count constant (a swapped knob) killed (1 test)
the builder stops offering the constants killed (2 tests — the tuned arm and the strict-undefined guard in the same file)

Verification

  • full suite: 1956 passed, 1 skipped
  • import emrg.client.app green; emrg --help green
  • scripts/check-doc-count.pyOK: no tracked file states the Python test count
  • the real builder's output, measured: … (title ≤512 chars, …) … if the index exceeds 100 entries, …

Note on the previous cycle's reasoning

cyc20260914-122910 found this line and deliberately left it, recording that the twin text
lives in emrg/server/evolution_prompt.md, which this task is forbidden to edit — so a
one-sided change would be worse than none. Measured this cycle, that reasoning does not
hold: the twin phrase in evolution_prompt.md is ≤512 chars, which is correct
(INDEX_TITLE_MAX_CHARS is 512), and the only false number lived in the file I may edit.
So the one-sided change is safe and complete, and this is it. The general form is worth
keeping: "the twin is elsewhere" is a reason to check the twin, not to stop.

open_source_prompt.md told the agent to keep MEMORY.md a pure index "title <=512
chars ... if the index exceeds ~50 entries". The store warns at
INDEX_TITLE_MAX_CHARS = 512 and INDEX_COUNT_WARN = 100, so the entry number was
wrong: an agent reading it would consolidate an index the store is not warning
about, and the same defect class as #1217/#1218/#1219 - a stated number that is
not the one that fires.

The builder now offers index_count_warn / index_size_warn_kb /
index_title_max_chars to every template, and the prompt renders them, so the
numbers cannot drift from the store again.

The earlier cycle that found this (cyc20260914-122910) left it alone on the
grounds that the twin text lives in evolution_prompt.md, which this task is
forbidden to edit, and a one-sided change would be worse than none. Measuring it
this cycle: the twin phrase in evolution_prompt.md ("<=512 chars") is *correct*
(the title limit is 512), and only open_source_prompt.md carried the false
number. So the one-sided change is safe, and this is it.

Tests: tests/test_prompt_templates.py::test_the_prompt_states_the_memory_limits_the_store_enforces
- the shipped prompt states both constants, the old "~50 entries" is gone, and
tuning both constants moves the rendered text (a literal cannot follow, so the
tuned arm is what makes the shipped one load bearing). 4/4 mutants killed, files
restored by sha256. Full suite 1956 passed, 1 skipped.
@argszero

Copy link
Copy Markdown
Owner Author

Superseded by #1222, following the host's directive (rant 2026-09-14T13:23:04): static text must delete the memory-index thresholds, not render them from the constants — "应删除复述,而不是改成从常量渲染". With the numbers gone there is nothing left to drift, so no rendering plumbing (nor the prompt-side guard this PR added) is needed.

The diagnosis here was right and is cited in #1222's body: open_source_prompt.md stated an entry count the store never used. What changes is the cure, and the coverage: this PR handled one file, leaving system.j2 — injected every round, holding the same wrong number — and evolution_prompt.md unhandled, which is the "half-repaired master" the directive rules out. #1222 deletes every instance in one change.

Closing as superseded.

@argszero argszero closed this Sep 14, 2026
argszero added a commit that referenced this pull request Sep 14, 2026
`check-vote-count.py` stated its own default twice in code and twice in prose:
`Verdict.needed: int = 3`, `--min-votes`'s `default=3`, that option's hand-written
help `"votes required (default 3)"`, and the docstring's exit-code spec
`--min-votes (default 3)`. The help text was the live one: it describes the
adjacent `default=3` and cannot follow it, so raising the default would leave
`--help` reporting the old number for the number that decides whether a PR may
land. Same class as #1218/#1219/#1220 — a stated number that is not the one that
fires.

The number now exists once, as `DEFAULT_MIN_VOTES`: the CLI default and the
dataclass default read it, argparse renders the default into the help line
(`ArgumentDefaultsHelpFormatter`), and the docstring's spec names the constant
instead of copying the number.

Tests (both arms tunable, and each dies to its own mutant):

* the rendered `--help` line states the default the parser actually uses — the arm
  tunes `DEFAULT_MIN_VOTES` to 7 and requires the line to follow, because `main()`
  reads the constant while building the parser. Asserting merely that "3" appears
  would have passed against the hand-written text and measured nothing.
* `Verdict.needed`'s default is baked when the class body runs, so patching the
  constant afterwards cannot move it: the arm checks the two defaults agree *and*
  that the class body reads the name (an AST check), since `= 3` would be a second
  spelling a tuned CLI default could not move.

Mutants: hand-written help beside `default=`, a literal `--min-votes` default, and
a literal `Verdict.needed` default — each killed by one arm, files restored by
sha256. Full suite green; `--help` prints `votes required (default: 3)`.

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
argszero added a commit that referenced this pull request Sep 14, 2026
…1222)

The memory store owns the three numbers a MEMORY.md index is held to
(`emrg/memory.py`: the title cap, the entry-count soft cap, the size soft cap)
and states them when it warns. Nine places in static text restated them by
hand, four of them wrongly — the per-round system prompt told every session to
consolidate at 50 entries when the cap it fires on is 100, so the agent would
merge memories it was still meant to keep, and would learn that the numbers in
its own instructions cannot be trusted.

Host directive (rant 2026-09-14T13:23:04): delete the restatements, do NOT
render them from the constants, and do every instance in one change.

Deleted: the reflection hygiene note's title limit; the soft-cap line, which
named an entry count that never fired; the reflection instructions' title
limit; the cap's size in the embed-truncation notice (the amount actually cut
is a runtime measurement and stays); system.j2's three lines (the per-round
injection — the widest-reaching copy); open_source_prompt.md's hygiene bullet;
evolution_prompt.md's row rule. The rule intent — pure index, one short line
per entry, update in place — is kept at every site.

Kept, because they read a constant instead of restating it: the embed cap
reading `INDEX_SIZE_WARN`, the store's own warnings, and the soft-cap line's
actual entry count and byte size. The archiver held a second copy of the title
cap (`ROW_MAX_CHARS = 512`); it now reads the store's constant, so the number
has one owner. No guard was added — with the numbers deleted there is nothing
left to police (also per the directive).

Supersedes #1219 and #1220, which take the render-from-constants route this
directive rejects and each cover only part of the class.

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
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.

1 participant