Repairing the memory
An append-only note store cannot be checked, because there is nothing to check
it against. This memory has a schema, so it can be — and this is the job that
does it.
Read $HERMES_HOME/schema.md first. It is the contract; everything below
is how to enforce it.
Start with the mechanical pass
Run the deterministic checker first and work from its output:
python3 "$HERMES_HOME/scripts/memory_check.py"
It returns JSON: every finding with a kind, a path, and a detail, plus a
clean flag. It never writes. Deciding what to do about each finding is your
job; detecting them is not, and a check you perform by reading is a check that
drifts.
What the findings mean, in this order
Cheap and mechanical first, so a run that is going to find nothing finds it
quickly.
- Index against filesystem. Every page has an index entry, every entry
points at a page that exists, and each entry sits under the
## Section
for its own page type. Resolve a mismatch in the direction that loses
nothing: for unindexed, add the missing entry; for index-dangling,
remove an entry only when its target is genuinely gone; for
index-misfiled, move the existing line into its own type's section
rather than adding a second entry — the page already has one, it is just
filed under the wrong heading. For index-unparseable, preserve the index,
report the ambiguity, and make no index edits: the checker deliberately
withholds derived findings when it cannot prove which content is top-level.
- Index sections.
index-section-missing means a validated page type
has no ## Section to be filed under, so the first page of that type
would be reported unindexed with no entry that could clear it. Add the
heading in the position schema.md fixes — immediately before whichever
required section, already present, comes next in that order. Then reconcile
every page of that type in the same pass: move each existing entry named by
index-misfiled, and add an entry for each page named by unindexed. Do
not leave the section empty: this finding is emitted only because at least
one page of the type already exists. This is what reaches a memory installed
before the type was added: the seed only supplies a fresh install, and
bootstrap never overwrites an index the user owns.
index-out-of-order means a ## Section heading exists but not where
schema.md puts it relative to the others. Move the heading — and
everything filed under it — to its correct position; do not add a second
heading for the same section.
- Links resolve. Every relative link between pages lands somewhere. A
broken link to a person becomes a stub page with
importance: low; a
broken link to anything else is removed and noted. Record which you chose.
- Frontmatter completeness. Every page carries the keys its type
requires. A missing
updated is filled from the newest dated content on
the page, never from today — today would assert a freshness the page has
not earned.
- Person identity. Every people page carries a
source_key, and no two
carry the same one. Never derive one from the page. For
missing-identity, take the value from the memory job's source_key for
that person and write it in; if the selector does not name them, leave the
field absent and note it — a page found by the wrong identity is worse than
one found only by its filename. For duplicate-identity, do not merge and
do not pick. Two pages claiming one identity means either that one of them
is about somebody else, or that a merge copied the content across and left
the emptied page behind — and nothing on disk tells those apart, because
the page that looks redundant is the correct one in the second case and
the victim in the first. The memory job knows: it reports the pages a
confirmed link has joined, under merge_into_slug. Leave this to that
job.
- Decay windows. Any page past its
decay window is flagged as stale in
the log. Do not delete it and do not silently refresh the date. A page
marked stale is still useful; a page whose date was quietly bumped is a
lie.
- Provenance. Claims on
patterns/ pages carry a footnote or an
(inferred) marker. A page with neither is flagged. Never invent a
footnote to satisfy the check — an unsupported claim should be visible, not
dressed up.
- Section ceilings. Pages past the limits in the schema's growth-control
table are reported for the consolidation job. Repair does not compact;
those are different jobs on purpose, because compaction needs judgment and
repair should be safe enough to run unattended.
What repair must never do
- Promote an inference to a sourced fact.
- Delete an unresolved commitment.
- Merge two pages on a name match alone. Identity needs evidence; a shared
first name is not evidence, and neither is a shared full name.
- Invent a
source_key, or copy one from another page to clear a finding.
- Rewrite a page wholesale when a bounded fix would do.
Log
Append one entry to $HERMES_HOME/workspace/memory/log.md every run, including runs that
changed nothing:
## [2026-08-18T09:14:00Z] repair
- created stub people/sam_ruiz.md for a link on projects/billing_migration
- flagged attention/current_priorities.md stale (updated 2026-08-11, decay daily)
- 2 pages over their section ceiling, reported for consolidation
A clean run is worth logging. It is the difference between "the memory is
healthy" and "nothing checked it".
1---2name: memory-repair3description: Check the memory against its schema and fix what can be fixed, so the store degrades visibly rather than silently.4license: Apache-2.05---67# Repairing the memory89An append-only note store cannot be checked, because there is nothing to check10it against. This memory has a schema, so it can be — and this is the job that11does it.1213Read `$HERMES_HOME/schema.md` first. It is the contract; everything below14is how to enforce it.1516## Start with the mechanical pass1718Run the deterministic checker first and work from its output:1920```bash21python3 "$HERMES_HOME/scripts/memory_check.py"22```2324It returns JSON: every finding with a kind, a path, and a detail, plus a25`clean` flag. It never writes. Deciding what to do about each finding is your26job; detecting them is not, and a check you perform by reading is a check that27drifts.2829## What the findings mean, in this order3031Cheap and mechanical first, so a run that is going to find nothing finds it32quickly.33341. **Index against filesystem.** Every page has an index entry, every entry35 points at a page that exists, and each entry sits under the `## Section`36 for its own page type. Resolve a mismatch in the direction that loses37 nothing: for `unindexed`, add the missing entry; for `index-dangling`,38 remove an entry only when its target is genuinely gone; for39 `index-misfiled`, **move** the existing line into its own type's section40 rather than adding a second entry — the page already has one, it is just41 filed under the wrong heading. For `index-unparseable`, preserve the index,42 report the ambiguity, and make no index edits: the checker deliberately43 withholds derived findings when it cannot prove which content is top-level.442. **Index sections.** `index-section-missing` means a validated page type45 has no `## Section` to be filed under, so the first page of that type46 would be reported unindexed with no entry that could clear it. Add the47 heading in the position `schema.md` fixes — immediately before whichever48 required section, already present, comes next in that order. Then reconcile49 every page of that type in the same pass: move each existing entry named by50 `index-misfiled`, and add an entry for each page named by `unindexed`. Do51 not leave the section empty: this finding is emitted only because at least52 one page of the type already exists. This is what reaches a memory installed53 before the type was added: the seed only supplies a fresh install, and54 bootstrap never overwrites an index the user owns.55 `index-out-of-order` means a `## Section` heading exists but not where56 `schema.md` puts it relative to the others. Move the heading — and57 everything filed under it — to its correct position; do not add a second58 heading for the same section.593. **Links resolve.** Every relative link between pages lands somewhere. A60 broken link to a person becomes a stub page with `importance: low`; a61 broken link to anything else is removed and noted. Record which you chose.624. **Frontmatter completeness.** Every page carries the keys its type63 requires. A missing `updated` is filled from the newest dated content on64 the page, never from today — today would assert a freshness the page has65 not earned.665. **Person identity.** Every people page carries a `source_key`, and no two67 carry the same one. **Never derive one from the page.** For68 `missing-identity`, take the value from the memory job's `source_key` for69 that person and write it in; if the selector does not name them, leave the70 field absent and note it — a page found by the wrong identity is worse than71 one found only by its filename. For `duplicate-identity`, do not merge and72 do not pick. Two pages claiming one identity means either that one of them73 is about somebody else, or that a merge copied the content across and left74 the emptied page behind — and nothing on disk tells those apart, because75 the page that looks redundant is the correct one in the second case and76 the victim in the first. The memory job knows: it reports the pages a77 confirmed link has joined, under `merge_into_slug`. Leave this to that78 job.796. **Decay windows.** Any page past its `decay` window is flagged as stale in80 the log. **Do not delete it and do not silently refresh the date.** A page81 marked stale is still useful; a page whose date was quietly bumped is a82 lie.837. **Provenance.** Claims on `patterns/` pages carry a footnote or an84 `(inferred)` marker. A page with neither is flagged. Never invent a85 footnote to satisfy the check — an unsupported claim should be visible, not86 dressed up.878. **Section ceilings.** Pages past the limits in the schema's growth-control88 table are reported for the consolidation job. Repair does not compact;89 those are different jobs on purpose, because compaction needs judgment and90 repair should be safe enough to run unattended.9192## What repair must never do9394- Promote an inference to a sourced fact.95- Delete an unresolved commitment.96- Merge two pages on a name match alone. Identity needs evidence; a shared97 first name is not evidence, and neither is a shared full name.98- Invent a `source_key`, or copy one from another page to clear a finding.99- Rewrite a page wholesale when a bounded fix would do.100101## Log102103Append one entry to `$HERMES_HOME/workspace/memory/log.md` every run, including runs that104changed nothing:105106```markdown107## [2026-08-18T09:14:00Z] repair108- created stub people/sam_ruiz.md for a link on projects/billing_migration109- flagged attention/current_priorities.md stale (updated 2026-08-11, decay daily)110- 2 pages over their section ceiling, reported for consolidation111```112113A clean run is worth logging. It is the difference between "the memory is114healthy" and "nothing checked it".