Apply P21: identity and experience; memory writes without approval; built-in policy
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+2
-3
@@ -103,9 +103,8 @@ Later changes to the brief:
|
||||
| 2026-09-24 | P18, Roles and Network isolation: a sixth role, `memoryd`, owns `memory/` and `index/`, syncs turns to Honcho and runs memory tool calls; `loopd` has four sockets and no longer holds the memory files. | Owner's approval, 2026-09-24; see the row of that date under Decided. |
|
||||
| 2026-09-24 | P19, Memory 2 to 8: `recall` over a local FTS5 index (session logs, archive) and Honcho, `ask_memory`; Honcho writes only from clean sessions; per-workspace trust set by the owner; notes, the flush job and the embedding index go; memory text stays on the owner's hosts. | Owner's approval, 2026-09-24; see the row of that date under Decided. |
|
||||
| 2026-09-24 | P20, Milestones: M5 becomes M5a (local memory in `memoryd`), M5b (Honcho) and M5c (the scheduler and the tiering job); the embedding-index M5b goes. | Owner's approval, 2026-09-24; see the row of that date under Decided. |
|
||||
| 2026-09-24 | P21, Memory 1, 2, 6, 7, State and Milestones: memory is identity (`soul.md`, `memory.md`, `user.md`, in the prompt, edited only by clean sessions) and experience (`notes.md` and session logs, recalled with a source tag: `identity`, `memory`, `memory-tainted`); writes need no approval; `note` in any session; an untrusted session's identity `add` becomes a note; a check on every write, a journal and undo; the policy of the memory tools is built in. | Owner's approval, 2026-09-24; see the rows of that date under Decided. |
|
||||
|
||||
## Proposed changes to the design brief
|
||||
|
||||
| # | Change | Reason |
|
||||
|---|---|---|
|
||||
| P21 | Memory 1 to 8, State and Milestones: memory is **identity** and **experience** (owner, 2026-09-24). Identity is who the agent is and what it holds true about its work and owner: `soul.md` (the owner's), `memory.md`, `user.md`, in every prompt, written only by the owner or by sessions that have not read untrusted content. Experience is what it has seen or been told: `memory/notes.md` (replacing `archive.md`) and the session logs, never in the prompt, found with `recall`, each item labelled with its source and trust; not everything remembered is believed. Memory writes are not approved. The `memory` tool gains `note`, which records something seen in any session, labelled by the session's state. In a session that has read untrusted content an identity `add` becomes a `note` and the other identity edits are refused. Every write is checked (no control or invisible characters, no chat-template tokens, no instruction phrases from a short fixed list), and identity entries are checked again when the block is built. Every write is recorded in the edit journal and listed by `bxctl memory log` (not posted to the chat), and the latest edit of a file can be undone with `bxctl`. `consolidate` and `remove` put what they take out into `notes.md` as moved or dropped; nothing is deleted. An owner setting stages identity writes for review instead of applying them (off by default). The policy of `recall` and `memory` is built into `brokerd` and no grant file can change it. `recall` tells identity ("you knew") from notes ("you noted, from an untrusted session"). Milestones: M5a's row loses `ask` and gains notes; M5c's tiering job may promote only trusted notes into identity, and its writes are journalled like any other. | Owner, 2026-09-24, pointing to Hermes: writes without questions, a scan, a snapshot frozen per session, size limits; and memory is two things, who the agent is and what it has seen. Turning an untrusted session's writes into notes covers what a pattern scan cannot, a plausible fact planted by a page, without losing what was seen. |
|
||||
None pending.
|
||||
|
||||
+32
-22
@@ -176,39 +176,49 @@ a multi-turn tool conversation and tool parsing had no failures in 20 trials.
|
||||
Memory is what carries continuity across epoch compaction, so it is required. It must not break
|
||||
the inference contract, and it must not become a way for injected text to persist.
|
||||
|
||||
1. **Memory files.** Three files, read once per epoch into the baseline, in this order after the
|
||||
system prompt: `memory/soul.md`, the persona, written only by the owner (at most 4,000
|
||||
characters); `memory/memory.md`, the agent's core notes on its work (at most 2,200 characters);
|
||||
and `memory/user.md`, its core notes on the owner (at most 1,375 characters). Each file is shown
|
||||
with how full it is. The agent edits the last two with a `memory` tool: `add`, `replace` and
|
||||
`remove` an entry, and `consolidate`, one edit that marks every entry of a file kept,
|
||||
rewritten, moved or dropped. Moved entries are appended to `memory/archive.md` (append-only,
|
||||
each entry with the date and session it was first approved in), where `recall` finds them;
|
||||
nothing leaves `memory.md` silently. Every edit is `ask`, and the approval shows `consolidate`
|
||||
as a diff. An approved edit takes effect at the next epoch, never mid-session. A write over a
|
||||
file's limit is refused with a pointer to `consolidate`.
|
||||
1. **Identity and experience.** Memory is two things. **Identity** is who the agent is and what
|
||||
it holds true about its work and its owner: three files read once per epoch into the baseline,
|
||||
in this order after the system prompt: `memory/soul.md`, the persona, written only by the owner
|
||||
(at most 4,000 characters); `memory/memory.md`, the agent's core notes on its work (at most
|
||||
2,200 characters); and `memory/user.md`, its core notes on the owner (at most 1,375
|
||||
characters), each shown with how full it is. **Experience** is what it has seen or been told:
|
||||
`memory/notes.md` (append-only) and the session logs, never in the prompt, found with
|
||||
`recall`, each item labelled with its source and trust. Not everything remembered is believed.
|
||||
The agent writes memory with a `memory` tool, without asking: `note` records something seen,
|
||||
in any session; `add`, `replace`, `remove` and `consolidate` (one edit marking every entry of a
|
||||
file kept, rewritten, moved or dropped) edit identity, and only a session that has not read
|
||||
untrusted content may use them. In a session that has, an identity `add` becomes a `note` and
|
||||
the other edits are refused. What leaves an identity file goes to `notes.md` as moved or
|
||||
dropped; nothing is deleted. A write takes effect at the next epoch, never mid-session. A
|
||||
write over a file's limit is refused with a pointer to `consolidate`.
|
||||
2. **Everything else is retrieved, not injected.** A `recall(query, k)` tool returns size-capped
|
||||
snippets with their source (a file and line, or a Honcho workspace). An `ask_memory` tool asks
|
||||
Honcho's dialectic. Results are appended like any tool result, so the cache prefix is untouched.
|
||||
3. **Local index.** SQLite FTS5 over the session logs and `memory/archive.md`, kept by `memoryd`
|
||||
snippets, each with a source tag (`identity` for entries moved out of identity, `memory` for
|
||||
trusted experience, `memory-tainted` for experience from sessions or workspaces that are not
|
||||
trusted), where it came from, and its class. An `ask_memory` tool asks Honcho's dialectic.
|
||||
Results are appended like any tool result, so the cache prefix is untouched.
|
||||
3. **Local index.** SQLite FTS5 over the session logs and `memory/notes.md`, kept by `memoryd`
|
||||
and rebuildable with `bxctl reindex`. It needs no network and no model, so it still answers
|
||||
when Honcho is down. `memoryd` counts `recall` hits per archive entry and when each was last
|
||||
found. Retrieval sits behind a `Retriever` trait.
|
||||
when Honcho is down. `memoryd` counts `recall` hits per note and when each was last found. Retrieval sits behind a `Retriever` trait.
|
||||
4. **Honcho.** The fleet's memory service; it and the models it calls run on the owner's hosts. Each
|
||||
Boxmaker writes finished turns to its own workspace, and only turns from sessions that saw no
|
||||
untrusted content and are within a configured data class; `memoryd` enforces this itself. Entries
|
||||
moved by `consolidate` are written there too. Honcho's own extraction, summaries and
|
||||
consolidation replace a flush job of ours. What Honcho holds is derived from the session logs and
|
||||
`memory/archive.md`, and can be rebuilt from them.
|
||||
`memory/notes.md`, and can be rebuilt from them.
|
||||
5. **Workspaces and trust.** `memoryd` can read other workspaces. The owner marks each one
|
||||
trusted or untrusted; untrusted is the default. Results from an untrusted workspace are marked
|
||||
untrusted and taint the session. Honcho's conclusions carry no per-entry provenance, so the
|
||||
filter is applied when writing and the label is per workspace.
|
||||
6. **Provenance on recall.** Recalled text is presented as data with its source and data class,
|
||||
never placed in the system prompt, and taints the session with that class. Entries from
|
||||
`memory/archive.md` come back as approved memory.
|
||||
7. **The tools go through `brokerd`** under ordinary grants and run in `memoryd`: `recall` and
|
||||
`ask_memory` `auto`, every `memory` edit `ask`.
|
||||
never placed in the system prompt, and taints the session with that class; a `memory-tainted` hit
|
||||
marks the session untrusted.
|
||||
7. **The tools go through `brokerd`** and run in `memoryd`. Their policy is built in, and no grant
|
||||
file can change it: `recall`, `ask_memory` and `memory` are all `auto`. What protects the
|
||||
prompt is which sessions may edit identity (item 1), plus a check on every write (no control
|
||||
or invisible characters, no chat-template markup, a short list of instruction phrases), run
|
||||
again when the prompt is built. Every write is journalled in `memory/edits/`, listed by
|
||||
`bxctl memory log`, and the latest edit of a file can be undone. An owner setting stages
|
||||
identity writes for review instead (off by default).
|
||||
8. **No memory text leaves the owner's hosts.** Honcho and the models it calls run on the owner's
|
||||
hosts, and each is listed in `docs/egress.md`.
|
||||
|
||||
@@ -217,7 +227,7 @@ the inference contract, and it must not become a way for injected text to persis
|
||||
Files are the source of truth. SQLite is allowed only for rebuildable indexes and queues.
|
||||
|
||||
- `sessions/<id>/<epoch>.jsonl` — append-only session log
|
||||
- `memory/soul.md`, `memory/memory.md`, `memory/user.md`, `memory/archive.md` — owner-readable memory
|
||||
- `memory/soul.md`, `memory/memory.md`, `memory/user.md`, `memory/notes.md`, `memory/edits/` — owner-readable memory
|
||||
- `index/` — FTS5, kept by `memoryd`; disposable and rebuildable from `memory/` and `sessions/`
|
||||
- `grants/*.toml` — owner-written
|
||||
- `audit/*.jsonl` — hash-chained
|
||||
|
||||
+3
-3
@@ -13,9 +13,9 @@ One session per milestone. The design brief is `docs/design.md`.
|
||||
| M3b | `brokerd` runner | Rootless container runner, four tools (read file, write file, sandboxed shell, HTTP fetch with host allowlist), egress proxy | A container with no network grant cannot reach the network; fetch refused for hosts outside the grant |
|
||||
| M4a | `gatewayd` + Mattermost conversations | Bot connection over TLS, user-ID allowlist, threads as sessions, bursts as one turn, typing while working, catching up after downtime, the secret store, no listening port | A direct message answered on the real server; a non-allowlisted user gets no response |
|
||||
| M4b | Approvals over Mattermost | `gateway.sock`, approval posts, answers by `!approve`/`!deny` in the thread, approvers list, long approvals in parts | An `ask` call approved and refused from Mattermost; the audit log names the post |
|
||||
| M5a | Local memory | `memoryd` without a network: the three memory files served to `loopd` once per epoch, the `memory` tool (add, replace, remove, `consolidate`) at `ask`, `memory/archive.md`, FTS5 `recall` over session logs and the archive, `recall` hit counts, `bxctl reindex` | A fact from one session is recalled in another; an entry moved by `consolidate` is found by `recall`; a memory edit changes the prompt only in the next session (next epoch once M5c adds epochs); the baseline with the owner's files stays under 3,000 tokens |
|
||||
| M5b | Honcho and hardening | A sanitizer module for memory writes, from the prompt-injection research of 2026-09-24; `memoryd` syncs finished turns to its own Honcho workspace through `roncho`, `recall` and `ask_memory` over Honcho, other workspaces read with the owner's trust labels, moved entries written as conclusions | A turn from a tainted session never reaches Honcho; a result from an untrusted workspace taints the session; Honcho's contents are rebuilt from the logs |
|
||||
| M5c | Scheduler and compaction | Heartbeat and cron on their own slot in isolated sessions; compaction at idle, starting a new epoch from the baseline and a summary; summaries of tool output for the tools that opt in (a web fetch, say), indexed with the output's label; the tiering job, which proposes what stays at which tier | A scheduled run leaves the main slot's cache intact; a fact from epoch 1 is recalled in epoch 2, and a memory edit reaches the next epoch; the tiering job's proposals reach the owner as ordinary approvals and change nothing on their own |
|
||||
| M5a | Local memory | `memoryd` without a network: the three memory files served to `loopd` once per epoch, the `memory` tool (`note`, and add, replace, remove, `consolidate` for identity) without questions and its check, `memory/notes.md`, the journal and `bxctl memory log`/`undo`, FTS5 `recall` over session logs and notes with source tags, `recall` hit counts, `bxctl reindex` | A fact from one session is recalled in another; an entry moved by `consolidate` is found by `recall`; a write from a session that read untrusted content becomes an untrusted note, never identity; a memory edit changes the prompt only in the next session (next epoch once M5c adds epochs); the baseline with the owner's files stays under 3,000 tokens |
|
||||
| M5b | Honcho and hardening | A sanitizer module for memory writes, from the prompt-injection research of 2026-09-24; `memoryd` syncs finished turns to its own Honcho workspace through `roncho`, `recall` and `ask_memory` over Honcho, other workspaces read with the owner's trust labels, moved entries and trusted notes written as conclusions | A turn from a tainted session never reaches Honcho; a result from an untrusted workspace taints the session; Honcho's contents are rebuilt from the logs |
|
||||
| M5c | Scheduler and compaction | Heartbeat and cron on their own slot in isolated sessions; compaction at idle, starting a new epoch from the baseline and a summary; summaries of tool output for the tools that opt in (a web fetch, say), indexed with the output's label; the tiering job, which decides what stays at which tier | A scheduled run leaves the main slot's cache intact; a fact from epoch 1 is recalled in epoch 2, and a memory edit reaches the next epoch; the tiering job promotes only trusted notes into identity, and its writes are journalled like any other |
|
||||
| M6 | Subagents + cloud consult | Subagents on their slot returning summaries; consult tool gated by data-class grants with full payload log | A tainted session is refused; payload log matches what was sent |
|
||||
| M7 | Split deployment | Each role in its own rootless container; `loopd` with no network; socket volume permissions; egress proxy for tools; host ACL policy in `deploy/` | From inside the `loopd` container, requests to the internet, the tailnet and the host all fail; a tool container without a host grant cannot reach the tailnet |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user