Task 16: memoryd takes the day and the month of a time with str::get, never a slice. Task 17: repeated error lines in memoryd, proto, brokerd and bxctl written once. Neither changes behaviour. Both were applied to a scratch tree from 5f82260: the gate passes with 936 tests, and four mutations of the new helpers are caught by the existing tests. The README runs with OPENCODE_MODEL. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
105 lines
7.3 KiB
Markdown
105 lines
7.3 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 `17-…`). 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 given complete | `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 |
|
|
| 16 | `16-memoryd-no-slicing.md` | the day and the month with `get`, not a slice (review nit) | the existing ones | reference |
|
|
| 17 | `17-repeated-code.md` | repeated error lines written once (review nit) | the existing ones | reference; 4 mutations caught |
|
|
|
|
At the end of task 15: 936 tests (854 before task 01); tasks 16 and 17 change no behaviour and
|
|
keep 936. 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, first stop: 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,
|
|
because the check refuses them. Ornith took them for its own template. Its reasoning ended
|
|
mid-thought, it lost track of the file, and it made no change. The tags were then built from
|
|
pieces in `check.rs` and the four given tests that held them, and the spec describes them in
|
|
words (e40906f).
|
|
- 2026-09-24, task 04, second stop: this time Ornith copied the files and saw the tests fail, then
|
|
checked its planned code against the markup test cases in its reasoning, spelling out the tags as
|
|
it went. The tag it wrote ended its reasoning; what followed became its answer, and the turn
|
|
ended with no tool call. Building the tags from pieces keeps them out of files, not out of the
|
|
model's thoughts. So `check.rs` is now given complete (the reference's, tags from pieces), and
|
|
task 04 fills only `entries.rs` and says not to work through the markup cases. The three later
|
|
tests that used a tag only as something the check refuses (`edit.rs`, `files.rs`, `serve.rs`)
|
|
now use an instruction phrase or an invisible character. They pass against the reference and
|
|
are rustfmt-clean. The attempt is saved in `.state/runs/M5a/04-attempt-2/`. Resume from task
|
|
04. The measurements behind this are in `docs/research/2026-09-24-chat-template-tokens.md`,
|
|
added after the run.
|
|
- 2026-09-24, after the review: tasks 16 and 17 clean up the review's nits. Resume from task 16.
|
|
|
|
## Running it
|
|
|
|
```sh
|
|
git switch m5a
|
|
OPENCODE_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M5a
|
|
# the review's follow-ups:
|
|
OPENCODE_MODEL=straylight/ornith-1.5-35b-a3b tools/run-plan.sh docs/plans/M5a 16
|
|
```
|
|
|
|
Keep the OpenCode TUI closed while it runs.
|