Task 04's session read the given test and the check.rs skeleton, both holding the tags the check refuses, and Ornith took them for its own template. It lost track of the file and made no change. The tags are now built with concat! and format! in check.rs and in the four given tests that held them, and the spec describes them in words. The rewritten tests pass against the reference, are rustfmt-clean, and still catch the case-folding mutation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
90 lines
6.1 KiB
Markdown
90 lines
6.1 KiB
Markdown
# M5a implementation plan: local memory
|
|
|
|
> **For the implementing model:** do not work from this file. The owner gives you one task file at
|
|
> a time (`01-…` to `15-…`). This file is the index for the owner and the reviewer.
|
|
|
|
**Goal:** a new role, `memoryd`, owns the agent's memory: identity (`soul.md`, `memory.md`,
|
|
`user.md`, in every new session's prompt) and experience (`notes.md` and the session logs, found
|
|
with `recall`). The agent writes memory with a `memory` tool without asking; a session that has
|
|
read untrusted content can only take notes. `brokerd` decides both tools under built-in grants and
|
|
hands them to `memoryd`; `loopd` takes the memory block from `memoryd` for each new session.
|
|
|
|
**Architecture:** `brokerd`'s two largest files are split first (01). `proto` gains the tools'
|
|
arguments and eight message kinds (02). `memoryd` is built bottom up: its crate, configuration and
|
|
sockets (03), entries and the check (04), the files and the block (05), notes and the edits (06),
|
|
the journal, undo and review (07), the index (08), `recall` (09), and serving (10). `brokerd`
|
|
gains the two tools and their built-in grants (11), then sends decided memory calls to `memoryd`
|
|
with the session's state, keeping the stricter label (12). `loopd` takes the block and declares
|
|
the two core tools (13); `bxctl` gains `reindex` and `memory` (14). Last, the starter `soul.md`
|
|
and the device checks with `memoryd` running (15).
|
|
|
|
**Spec:** `docs/specs/2026-09-24-m5a-local-memory.md` (approved; P17 to P21 applied to the brief).
|
|
Runbook: task 03 adds `memoryd-start-failed`, task 12 `memoryd-unreachable`, task 13 replaces
|
|
`core-memory-unreadable` with `memory-unavailable` and adds `memory-index-damaged`.
|
|
|
|
**New dependency:** `rusqlite` 0.40.2 with `bundled` (task 03; `deny.toml` gains Zlib for
|
|
`foldhash`). Nothing else.
|
|
|
|
## Global constraints
|
|
|
|
- Everything in `AGENTS.md`, including "Lessons from earlier reviews".
|
|
- Branch `m5a`. One task, one fresh OpenCode session, one commit. Run `cargo fmt --all` before the
|
|
gate. Review happens once, after task 15.
|
|
- **New files** come as skeletons: types, constants and the glue written, `todo!()` bodies with the
|
|
code to write in the comment above. **Existing files** are changed by the exact edits in the
|
|
task, never replaced whole.
|
|
- Some tasks hand over **new versions of given test files**, because the change is in what the
|
|
test must see. Copy them over the old ones as the task says; they are still not to be edited.
|
|
- Only task 15 needs straylight (`make verify-device`). Everything else runs offline against
|
|
temporary directories and fakes.
|
|
|
|
## Tasks
|
|
|
|
Every task had a reference implementation, built in this order from master with the gate at every
|
|
step and replayed on its own (tip T26), its given tests run against it, and mutations of it caught
|
|
by them (tip T28); then the reference was moved off this machine (tip T18). Every skeleton was
|
|
checked to compile at its task's end state and to fail its tests, and every comment above a
|
|
`todo!()` was read beside its signature (tip T29). The three largest functions were split before
|
|
the skeletons were cut (tip T25).
|
|
|
|
| # | File | Delivers | Tests | Check |
|
|
|---|---|---|---|---|
|
|
| 01 | `01-brokerd-split.md` | `policy/` and `ledger/` as modules; no behaviour change | the existing ones | reference |
|
|
| 02 | `02-proto-memory.md` | the tools' arguments, eight message kinds | `memory_args.rs`, `memory_wire.rs`, 8 fixtures | reference; 5 mutations caught |
|
|
| 03 | `03-memoryd-crate.md` | the crate, configuration, `listen`, `rusqlite` | `config.rs`, `socket.rs` | reference; `cargo deny` |
|
|
| 04 | `04-memoryd-entries-check.md` | entries, the check | `entries.rs`, `check.rs` | reference; 8 mutations caught |
|
|
| 05 | `05-memoryd-files.md` | the files, the block | `files.rs` | reference; 6 mutations caught |
|
|
| 06 | `06-memoryd-edits.md` | notes, the five actions, routing by trust | `edit.rs`, `notes.rs` | reference; 11 mutations caught |
|
|
| 07 | `07-memoryd-journal.md` | the journal, undo, review | `journal.rs` | reference; 5 mutations caught |
|
|
| 08 | `08-memoryd-index.md` | the FTS5 index, labels, catch-up, rebuild | `index.rs`, `support/logs.rs` | reference; 8 mutations caught |
|
|
| 09 | `09-memoryd-recall.md` | `recall`, source tags, the recall log | `recall.rs` | reference; 8 mutations caught |
|
|
| 10 | `10-memoryd-serve.md` | the three sockets; `memoryd serve` | `serve.rs` | reference; 10 mutations caught |
|
|
| 11 | `11-brokerd-memory-tools.md` | the two tools, built-in grants | `memory_tools.rs`; `grants.rs` updated | reference; 6 mutations caught |
|
|
| 12 | `12-brokerd-memory-port.md` | calls to `memoryd`, stricter labels | `memory_port.rs`; `rig.rs` updated | reference; 8 mutations caught |
|
|
| 13 | `13-loopd-memory.md` | the block, the core tools, `session_updated` | `memory.rs`; `baseline.rs`, `pointers.rs`, `channel.rs` updated | reference; 6 mutations caught |
|
|
| 14 | `14-bxctl-memory.md` | `bxctl reindex`, `bxctl memory` | `cli_memory.rs` | reference; 5 mutations caught |
|
|
| 15 | `15-soul-and-device.md` | the starter soul; device checks with `memoryd` | `device.rs` updated, `support/memoryd.rs` | reference; `make verify-device` on straylight |
|
|
|
|
At the end of task 15: 936 tests (854 before task 01). Each task's count was checked by running
|
|
the gate at its end state on its own.
|
|
|
|
## Changes during the run
|
|
|
|
- 2026-09-24, task 04: tasks 01 to 03 committed, each on its first session. Task 04's session read
|
|
the given test and the skeleton, which held chat-template tags literally (the opening and
|
|
closing think and tool-call tags, and the vertical-bar delimiters) because the check refuses
|
|
them. Ornith took them for its own template, its reasoning ended mid-thought, and it lost track
|
|
of the file; no change was made. The tags are now built from pieces in `check.rs` and in the four
|
|
given tests that held them (`check.rs`, `edit.rs`, `files.rs`, `serve.rs`), and the spec
|
|
describes them in words. The rewritten tests pass against the reference, are rustfmt-clean, and
|
|
still catch the case-folding mutation. Resume from task 04.
|
|
|
|
## Running it
|
|
|
|
```sh
|
|
git switch m5a
|
|
BOXMAKER_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M5a
|
|
```
|
|
|
|
Keep the OpenCode TUI closed while it runs.
|