diff --git a/memory-fixer.prose.md b/memory-fixer.prose.md index 48ebf6d..ee2c8bb 100644 --- a/memory-fixer.prose.md +++ b/memory-fixer.prose.md @@ -6,13 +6,19 @@ name: memory-fixer description: > Auto-fix low-hanging fruit in the RA-H OS knowledge graph. No judgment calls — only deterministic Level 1 operations. Escalate anything that needs Kwame's input. Executes confirmed Kwame decisions to completion (state + updated_at). -version: 2.1.0 +version: 2.2.0 --- --- # Memory Fixer -> **Canonical copy:** `/root/.hermes/contracts/memory-fixer-v3.md` (used by the `memory-fixer-daily` cron job). This file is the institutional record of the same contract. When the two diverge, treat the v3 source in `/root/.hermes/contracts/` as executable truth. +> **Executable copy:** the `okyeame-memory-fixer` cron job on kagentz (`hermes cron list`) holds its instruction +> set **inline in `~/.hermes/cron/jobs.json`** (`hermes cron edit --prompt …`; there is no `--prompt-file`, and +> `~/.hermes/cron/memory-fixer-prompt.md` is a synced draft, not the live instruction). This file is the institutional +> record of the same contract; when the two diverge, the job prompt is what actually runs — diff it against this file +> before claiming a prompt change landed. +> ⚠️ Corrected 2026-09-26: the previous pointer (`/root/.hermes/contracts/memory-fixer-v3.md`) does not exist on +> kagentz — no `/root` access from this container — and was verified unreachable, not merely stale. ## Purpose Auto-fix low-hanging fruit in the graph. No judgment calls — only deterministic Level 1 operations. Escalate anything that needs Kwame's input. When Kwame replies to an escalation, **execute the decision to completion** (update state and timestamps), never leaving a node in review-pending forever. @@ -125,15 +131,44 @@ updateNode(id, { **Archive candidates are identified by the fix 3 query's `suggested_action = 'archive'` branch** (the `ELSE 'archive'` case: anything not an infrastructure/skill/documentation/strategic/audit type). +### 5. Duplicate-Node Detection (Level 1 — read-only, every run) + +The graph's duplicate problem is rarely an agent mistyping a title: it is **recurring writers creating a new +node per run instead of updating one**. This phase detects that class and reports it. It is read-only and +**never merges**. + +```bash +python3 /home/hermes/.hermes/scripts/memory_dup_detect.py --json +``` +Read-only, ~15s over the whole graph, exit 0. That script is the source of truth for the clustering logic — +do not re-implement it in the prompt or hand-count "duplicates" from titles. + +Consume each `items[]` entry's `verdict` field; do not invent your own: + +| `verdict` | Meaning | Required action | +|---|---|---| +| `WRITER-DEFECT` (`run_family: true`) | ONE scheduled task writes a new node per run | Report the ids, the `agents` (the writer) and `span_days`. **Never merge** — each node is that run's audit record. If the family grew since the last report, say `UNFIXED` and name the writer. | +| `SAFE-MERGE` | Bodies identical | Still requires an explicit `merge #A into #B` decision from Kwame. | +| `HUMAN-DECISION` | Same subject, bodies differ | Propose **connect (an edge)**, never merge. | + +- **Title overlap alone is not duplication.** Four distinct client workflows of one family (#357-#361) and two + different machines' migrations (#1792/#1793) both score high on title tokens while their bodies sit 0.1-0.3 + apart. Confirm against body similarity before calling anything a duplicate. +- Report clusters as **candidates for Kwame's decision**, never as established duplicates — a wrong auto-merge + destroys distinct content irrecoverably. +- Per-run history nodes are kept deliberately. Bulk-merging a run family destroys the audit trail the family exists for. + ## Level 2 Escalations (Kwame Decision Required) 1. **Refresh-suggested stale nodes** flagged with `[REVIEW: refresh]` — refresh or keep? (Archive-suggested nodes are auto-archived under fix 4 and are not escalated.) -2. **Duplicate Nodes** (same title or >70% title overlap) — Merge or keep? +2. **Duplicate Nodes** — as detected by fix 5, by `verdict`, never by raw title overlap. `WRITER-DEFECT` is a writer fix (update one canonical node), not a merge decision; `SAFE-MERGE` and `HUMAN-DECISION` clusters are escalated for merge-or-connect. 3. **Orphan Nodes >90 days old** — Archive or connect? ## Reporting Format -The fixer reports to Kwame via this Zulip DM: +The fixer does **not** send anything. Under the single-egress model (2026-09-21) every report leaves the node +through Mumuni's gate (`comms_drop.py` for the queue, `comms_gate.py` to release and read-back verify), so +exit 0 means QUEUED, never delivered. A report body is written to a file and handed to the outbox helper: ``` 🦅 Memory Fixer — [HH:MM UTC] @@ -147,8 +182,10 @@ Stale nodes needing review (max 10): 2. [Node #YYY] Title — Y days stale, SUGGEST: archive ... -Duplicates needing decision: -1. [Node #AAA] vs [Node #BBB] — Same title +Duplicate clusters (candidates — Kwame decides; the fixer never merges unilaterally): +1. [WRITER-DEFECT] #AAA/#BBB/#CCC — writer , N nodes, span Nd (UNFIXED if it grew since the last report) +2. [HUMAN-DECISION] #DDD/#EEE — same subject, bodies differ, SUGGEST: connect +3. "none" when the scan returned no clusters Orphans >90 days: 1. [Node #EEE] Title — X days stale, orphaned @@ -195,6 +232,7 @@ The result must be 0 rows when all decisions are executed. Report what was done. - **State integrity:** archived nodes have `state: archived` + `[ARCHIVED]` prefix; kept nodes are `state: active` without a `[REVIEW:]` tag. - **Auto-archive applied:** no node should ever be left tagged `[REVIEW: archive]` — that tag is retired. Any `[REVIEW: archive]` found means fix 4 was skipped; archive it and report. - **No review-pending forever:** after executing Kwame's decisions, `[REVIEW:%` node count must be 0. +- **Duplicate scan ran:** every report carries the fix 5 block (`none` when there were no clusters). A report with no duplicate section means phase 5 was skipped — a silently skipped detection phase is the failure this phase exists to prevent. - **Timestamps:** every executed decision (and every auto-archive) bumps `updated_at`, so the node exits the stale window on the next run. ## Logging