What the host asked for
Rant 2026-09-14T20:14:56 (verbatim):
删除 evolution_prompt.md 中「每周期必须往内存索引插一行」的强制动作(宿主 2026-09-14 指示)
宿主原话:「'Every evolution cycle inserts one row into two memory indexes' 这个完全不需要。不需要有这个动作。agent session 自己会按需记忆的。」
建议的正确形态:索引由会话按需维护——需要时写入、需要时整理,不设每周期强制动作。周期记录本身(cycle-<ts>.md 详情文件)保留;只删掉「每周期必须在索引里插一行」这条强制要求与配套的周期级索引维护协议。
The mandate is one line of emrg/server/evolution_prompt.md (Step 6, line 386):
- Update the MEMORY.md index in the same directory (add one row, id linked to the filename) — this is the **single index** for cycle records
This issue carries the patch that removes it. It is a prompt edit, and the file may only be touched when a rant targets it — this rant does, which is why the change is here and not folded into another cycle's work.
Patch
Two files, 2 hunks, +9/−7, 4208 bytes / 4188 characters, sha256[:16] 6ad709d767ad350f, measured git apply --check rc=0 on master e6eaaee4 and byte-equal to the content that was verified.
| file |
before |
after |
content sha256[:16] |
emrg/server/evolution_prompt.md |
31759 chars |
31929 chars |
4632850aa960a6cf |
scripts/archive-memory-index.py |
— |
docstring only |
80c0b23c792d482b |
Two judgment calls, stated rather than assumed
1. The hygiene rules stay; they stop being a per-cycle duty. The rant asks for the mandate and "the accompanying cycle-level index maintenance protocol" to go. That protocol's rules — title-only rows, the 50-row cap, keep cycle-archive-*.md out of the prompt — were themselves mandated by an earlier host rant (2026-08-23T08:04:26 + 11:00:31, cited in the very text being edited) after a measured bloat incident: an index at 787KB / 2931 lines was 77% of a 452,972-char prompt. Deleting them would trade one host instruction for another. So they are re-scoped instead: they bind the write, not the cycle — whoever edits an index follows them, whether or not a cycle asked for the edit. If the host wants the rules gone as well, that is a further instruction; this patch does not take that step on its own.
2. The sentence the removal makes false is fixed in the same change. scripts/archive-memory-index.py opens with "Every evolution cycle curates two memory indexes…". Once the mandate is gone that is untrue, and leaving a knowingly-false sentence in the tree is the defect class this project keeps punishing. One docstring sentence, no behaviour change (the script still exits 2 — "could not measure" — on a missing index, on both arms).
The edit must not pay for itself in prompt growth
evolution_prompt.md is embedded in every cycle's context, so a change justified by "this ritual costs too much" has to be checked against its own cost. The first draft of this replacement added 553 characters to the prompt — a mandate removed, a paragraph added — which is the same bad trade in the other direction. The published version is trimmed to +170 characters (31759 → 31929). That number is reported rather than buried: what the change removes is the per-cycle ritual (insert a row, archive the overflow, and the index-script failure modes that motivated a 223-line tool plus 190 lines of tests in the closed #1230), not prompt bytes.
Verification
Both halves are driven, in opposite directions — a one-sided check here would pass for a patch that deleted the whole section:
- the mandate: present on control, absent on patched;
- the hygiene rules: 4/4 present on both arms (
Title-only rows, the 50-row cap, the archive-exclusion rule, the row-cap check);
- the stale sentence: present on control, absent on patched.
The failure mode a prompt edit actually has is that it stops rendering, so that is what is measured: tests/test_prompt_templates.py through the real builder (_build_evolution_prompt) — 6 passed / 6 passed, control and patched.
Nothing else pins the removed text: the phrases single index for cycle records, add one row, Index hygiene protocol, Title-only rows, Hard cap: keep at most the 50, Row-cap check appear in evolution_prompt.md only, in no test and in no other source file. dist/runtime/source/... also contains a copy, but /dist/ is gitignored (generated) — confirmed with git check-ignore.
Not claimed / not measured
- Not measured: whether the daemon's other surface (
emrg/server/daemon.py:4616, "after creating/updating .md files, update the MEMORY.md index in that directory") should also change. That is the memory-write tool's own instruction to the agent about its own writes, not a per-cycle mandate, so it is deliberately left alone.
- Not measured: the fate of
scripts/archive-memory-index.py itself. The rant leaves it open ("若上限保留,建议保留它"); since the cap is kept, the script is kept, and only its stale sentence is corrected.
- Not a general refactor of the prompt, and no other rant's wording is touched.
evolution-prompt-index.patch (4208 bytes, sha256[:16] 6ad709d767ad350f)
--- a/emrg/server/evolution_prompt.md
+++ b/emrg/server/evolution_prompt.md
@@ -383,12 +383,12 @@
- `event_at` / `created_at` / `updated_at`: ISO timestamps
- `type: task`, `scope: project`, `status: active` (cycle in progress) or `completed` (final)
- Body: findings, changes, verification results, expected effects (same content as before, just a memory file)
-- Update the `MEMORY.md` index in the same directory (add one row, id linked to the filename) — this is the **single index** for cycle records
-- ⚡ **Index hygiene protocol** (rants 2026-08-23T08:04:26 + 11:00:31 — the daemon embeds MEMORY.md into the system prompt raw, and evolution's direct file writes bypass memory_store's guards; an unbounded index once reached 787KB/2931 lines = 77% of a 452,972-char prompt, ~250K all-miss tokens per request). Apply to **every MEMORY.md you maintain** (evolution-level, source-project-level, session-level). Rules:
+- The `MEMORY.md` index in that directory is **not** a per-cycle obligation — it is kept on demand, by the session, when there is something worth indexing (rant 2026-09-14T20:14:56). The durable record is the `cycle-<ts>.md` detail file above
+- ⚡ **Index hygiene protocol — whenever you write to one of these indexes** (rants 2026-08-23T08:04:26 + 11:00:31 — the daemon embeds MEMORY.md into the system prompt raw, and evolution's direct file writes bypass memory_store's guards; an unbounded index once reached 787KB/2931 lines = 77% of a 452,972-char prompt, ~250K all-miss tokens per request). These rules bind **the write**, not the cycle — apply to **every MEMORY.md you maintain** (evolution-level, source-project-level, session-level). Rules:
- **Title-only rows**: each index row is a **one-line summary** (id linked to the filename). **Never embed a cycle's summary/NTE text into the index row** — that text lives in the `cycle-<ts>.md` detail file only.
- **Hard cap: keep at most the 50 most recent cycle rows** in each MEMORY.md. Before adding a row that would exceed 50: append the oldest cycle rows to `cycle-archive-YYYYMMDD.md` **in the same directory** (create-if-missing, append-only, never rewrite or dedupe), then remove those rows from MEMORY.md. **Detail files (`cycle-*.md`) are never deleted** — only index rows move.
- **Archive files are excluded from the system prompt** (the daemon embeds only `MEMORY.md`): never reference `cycle-archive-*.md` in MEMORY.md rows, never re-add archived rows to the index, never paste archive content into MEMORY.md or the prompt. Archived rows stay readable via the `read` tool.
- - **Row-cap check**: if a MEMORY.md already exceeds 50 cycle rows (e.g. after a missed cleanup), archive down to the latest 50 this cycle before adding the new row.
+ - **Row-cap check**: if a MEMORY.md already exceeds 50 cycle rows (e.g. a missed cleanup), archive down to the latest 50 before adding a row.
- Keep the file format identical to other memory entries (frontmatter + Markdown body)
> Transition note: legacy `evolution-cycle-*.md` files remain in place (readable, never deleted); only new records use the memory entry path.
--- a/scripts/archive-memory-index.py
+++ b/scripts/archive-memory-index.py
@@ -3,10 +3,12 @@
The class this exists for
-------------------------
-Every evolution cycle curates two memory indexes (the evolution project's and
-the session's), and the protocol is mechanical: keep at most 50 cycle rows,
-append the rows that fall off to `cycle-archive-<YYYYMMDD>.md`, never delete a
-detail file, never reference the archive from the index. It has been done by
+An evolution cycle used to curate two memory indexes (the evolution project's
+and the session's); that per-cycle mandate was removed (rant 2026-09-14T20:14:56),
+so an index is now maintained on demand. When it is maintained, the protocol is
+mechanical: keep at most 50 cycle rows, append the rows that fall off to
+`cycle-archive-<YYYYMMDD>.md`, never delete a detail file, never reference the
+archive from the index. It has been done by
hand-written scripts, and the hand-written scripts have failed twice:
* an empty shell variable turned ``sed -i '' "${n}d"`` into the sed script ``d``,
Generated by /private/tmp/emrgprobe/i1230/prompt_index_patch.py from master's own blob (git show e6eaaee4:…), with git apply output compared byte-for-byte against the content that was measured.
What the host asked for
Rant 2026-09-14T20:14:56 (verbatim):
The mandate is one line of
emrg/server/evolution_prompt.md(Step 6, line 386):This issue carries the patch that removes it. It is a prompt edit, and the file may only be touched when a rant targets it — this rant does, which is why the change is here and not folded into another cycle's work.
Patch
Two files, 2 hunks, +9/−7, 4208 bytes / 4188 characters, sha256[:16]
6ad709d767ad350f, measuredgit apply --checkrc=0 on mastere6eaaee4and byte-equal to the content that was verified.emrg/server/evolution_prompt.md4632850aa960a6cfscripts/archive-memory-index.py80c0b23c792d482bTwo judgment calls, stated rather than assumed
1. The hygiene rules stay; they stop being a per-cycle duty. The rant asks for the mandate and "the accompanying cycle-level index maintenance protocol" to go. That protocol's rules — title-only rows, the 50-row cap, keep
cycle-archive-*.mdout of the prompt — were themselves mandated by an earlier host rant (2026-08-23T08:04:26+11:00:31, cited in the very text being edited) after a measured bloat incident: an index at 787KB / 2931 lines was 77% of a 452,972-char prompt. Deleting them would trade one host instruction for another. So they are re-scoped instead: they bind the write, not the cycle — whoever edits an index follows them, whether or not a cycle asked for the edit. If the host wants the rules gone as well, that is a further instruction; this patch does not take that step on its own.2. The sentence the removal makes false is fixed in the same change.
scripts/archive-memory-index.pyopens with "Every evolution cycle curates two memory indexes…". Once the mandate is gone that is untrue, and leaving a knowingly-false sentence in the tree is the defect class this project keeps punishing. One docstring sentence, no behaviour change (the script still exits 2 — "could not measure" — on a missing index, on both arms).The edit must not pay for itself in prompt growth
evolution_prompt.mdis embedded in every cycle's context, so a change justified by "this ritual costs too much" has to be checked against its own cost. The first draft of this replacement added 553 characters to the prompt — a mandate removed, a paragraph added — which is the same bad trade in the other direction. The published version is trimmed to +170 characters (31759 → 31929). That number is reported rather than buried: what the change removes is the per-cycle ritual (insert a row, archive the overflow, and the index-script failure modes that motivated a 223-line tool plus 190 lines of tests in the closed #1230), not prompt bytes.Verification
Both halves are driven, in opposite directions — a one-sided check here would pass for a patch that deleted the whole section:
Title-only rows, the 50-row cap, the archive-exclusion rule, the row-cap check);The failure mode a prompt edit actually has is that it stops rendering, so that is what is measured:
tests/test_prompt_templates.pythrough the real builder (_build_evolution_prompt) — 6 passed / 6 passed, control and patched.Nothing else pins the removed text: the phrases
single index for cycle records,add one row,Index hygiene protocol,Title-only rows,Hard cap: keep at most the 50,Row-cap checkappear inevolution_prompt.mdonly, in no test and in no other source file.dist/runtime/source/...also contains a copy, but/dist/is gitignored (generated) — confirmed withgit check-ignore.Not claimed / not measured
emrg/server/daemon.py:4616, "after creating/updating .md files, update the MEMORY.md index in that directory") should also change. That is the memory-write tool's own instruction to the agent about its own writes, not a per-cycle mandate, so it is deliberately left alone.scripts/archive-memory-index.pyitself. The rant leaves it open ("若上限保留,建议保留它"); since the cap is kept, the script is kept, and only its stale sentence is corrected.evolution-prompt-index.patch (4208 bytes, sha256[:16]
6ad709d767ad350f)Generated by
/private/tmp/emrgprobe/i1230/prompt_index_patch.pyfrom master's own blob (git show e6eaaee4:…), withgit applyoutput compared byte-for-byte against the content that was measured.