Skip to content

emrg: an entry written without its index row is measured by nothing #2797

Open

Description

@argszero

scripts/check-memory-index.py measures an index in three directions: its line count, its character budget, and each row's length and links. The third resolves a row's ](target) against the directory beside the index, so a row pointing at a file that is gone is reported.

Nothing measures the opposite direction — a detail file that no row names. An entry can be written and never indexed, and the guard still prints OK with exit code 0.

Observed

2026-11-10, this host's agent memory store (~/.emrg/evolution/emrg/.emrg/memory/): a detail file was written at 17:52 with status: active. Running uv run --no-sync python3 scripts/check-memory-index.py .emrg/memory/MEMORY.md two minutes later printed

  lines 88 of 100 - within
  chars 40323 of 52224 - within
  rows 81, longest 512 chars, over 512: 0
  row links 102, unresolved: 0
OK: 1 index(es) within the three numbers the rule names ...

and exited 0, while the store held 97 files declaring status: active against 82 rows. One active entry was named nowhere in the index. The same reading over the whole directory: of the four active files carrying no ](target) link, three are still followed by a reader because a row names them inside an inline code span; exactly one was named by nothing at all.

Expected

emrg/server/prompts/system.j2:61 states the rule — "A write is two steps: the file, then its row in the index" — and emrg/server/evolution_prompt.md repeats it under R9. A rule the prompt states and no reading takes is prose that decays, and this failure is silent in the direction that matters: the file exists, the work was done, and the fact is unreachable, because the index is the only map of the store.

What the fix must satisfy

  1. A fourth reading in scripts/check-memory-index.py: every detail file beside the index whose frontmatter says status: active is named somewhere in the index. The directory is the one the existing resolution reading already assumes, since rows name their siblings.
  2. Named, not linked: a link and an inline code span both count. R9's compaction protocol requires a merged row to "still name every id and file that line replaces", and this host's index does exactly that inside backticks — so a link-only predicate would report a shape the rule prescribes.
  3. A file whose status is merged, superseded, or absent is out of scope: the index lists active entries only, so retiring an entry is a legitimate way to leave it unnamed.
  4. Exit 1 when an active detail file is named nowhere, with a line naming the file and the remedy. Exit 0 keeps its meaning only where the reading had a subject: an index with no active detail file beside it says so — in the per-index report and in the summary line — rather than closing with a pass it did not take.
  5. The docstring's exit codes, its scope section and its argparse description and epilog carry the new reading. A reading added without them is one a later reader cannot see, which is the defect this tool exists to prevent one level down.
  6. Tests in both directions, with mutation arms: a file named by a row (clean) · a file named nowhere (exit 1, naming it) · a file named only inside a code span (clean) · a file with status: merged (clean) · an index with no detail file beside it (no subject, exit 0).

Not in scope

  • Repairing any store's existing index. A row summarises its detail file's content, which is the agent's judgement, not a reading.
  • Any change to what emrg/memory.py writes, or to the store's own thresholds.

Activity

  1. argszero commented on Nov 10, 2026

    @argszero
    OwnerAuthor

    Handled by #2798

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions