M5c spec: the owner's answers
A turn without complete speaker information is not sent; every peer in the fleet workspace is a member, so fleet notes are messages that one search finds, with a retraction message for an undone note (Honcho cannot delete a message); the fleet workspace derives nothing (reasoning, summaries and dreaming off); API keys per workspace are first-class, with a runbook entry for a refused key. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# M5c design: Honcho
|
||||
|
||||
Status: draft for the owner's review, 2026-09-27. Designed with the owner the same day (the
|
||||
decisions are marked "owner"). Supersedes P23 where they differ (section 14).
|
||||
Status: draft for the owner's review, 2026-09-27; section 15's answers folded in. Designed with the
|
||||
owner the same day (the decisions are marked "owner"). Supersedes P23 where they differ (section
|
||||
14).
|
||||
|
||||
## 1. What M5c proves
|
||||
|
||||
@@ -44,7 +45,7 @@ Projects in memory stay out of M5c (open row in `docs/decisions.md`).
|
||||
|---|---|---|---|---|
|
||||
| its **trusted** one | turns from clean sessions; conclusions mirroring its trusted memory writes | the agent (through `ask_memory`); other agents, as the owner labels it | trusted | `private` |
|
||||
| its **untrusted** one | turns from sessions that read untrusted content | the agent only (through `ask_memory`) | untrusted, fixed | `private` |
|
||||
| the **fleet** one, shared by every agent | conclusions from untrusted notes, each agent under its own peer | every agent | untrusted, fixed in code | `private` |
|
||||
| the **fleet** one, shared by every agent | untrusted notes, as messages from each agent's own peer | every agent | untrusted, fixed in code | `private` |
|
||||
|
||||
A workspace's class is also its ceiling for writes: nothing above `private` is written to Honcho
|
||||
(owner). A `public` turn is written as `private`, since `public` records where a result came
|
||||
@@ -56,6 +57,8 @@ workspace (owner).
|
||||
- the owner: one peer, the same name in every workspace of the fleet (`[honcho] owner`); observed;
|
||||
- other people who speak in a channel the agent answers: a peer each, named from their
|
||||
Mattermost username (a `.` becomes `_`: Honcho allows `[a-zA-Z0-9_-]`); observed;
|
||||
- every peer in the fleet workspace is a member of the fleet, whether or not this agent's
|
||||
configuration names it (owner);
|
||||
- the agent: a peer under its own name (`[honcho] agent`), **not observed** (`observe_me = false`).
|
||||
Its replies are written as messages and serve as context when Honcho reasons about the people
|
||||
it talks to, but Honcho draws no conclusions about the agent from them: they are the agent's
|
||||
@@ -80,7 +83,7 @@ limit) is written as several messages of at most 25,000, in order.
|
||||
| clean, class `public` or `private` | the trusted workspace |
|
||||
| untrusted (read untrusted content), class `public` or `private` | the untrusted workspace |
|
||||
| class `secret` | nowhere |
|
||||
| no single speaker (a burst joined from several people) | nowhere |
|
||||
| no speaker, or incomplete speaker information (a burst joined from several people) | nowhere (owner) |
|
||||
| the text holds a known-prefix secret (`sanitize::refuse`'s secret rules) | nowhere |
|
||||
|
||||
**Memory writes.** Every write is mirrored as a conclusion the moment it is journalled (owner),
|
||||
@@ -94,9 +97,10 @@ spotting the phrase:
|
||||
| an entry dropped from identity (`remove`, or dropped by `consolidate`) | its conclusion deleted |
|
||||
| an entry moved out of identity by `consolidate` | nothing: its conclusion is already there |
|
||||
| a trusted note | a conclusion in the trusted workspace |
|
||||
| an untrusted note | a conclusion in the fleet workspace |
|
||||
| an untrusted note | a message in the fleet workspace, from the agent's peer |
|
||||
| a flagged note (a sanitizer flag rule matched) | nothing: it stays local (owner) |
|
||||
| `bxctl memory undo` of an edit whose conclusions were written | those conclusions deleted; any it had deleted, written again |
|
||||
| `bxctl memory undo` of an untrusted note already in the fleet | a retraction message beside it, `retracted: <the note>`, from the agent's peer; the original stays, since Honcho cannot delete a message (owner) |
|
||||
|
||||
A conclusion's observer and observed are the same peer, which is Honcho's own picture of that
|
||||
peer: a `user.md` entry is the owner's (it is about the owner); a `memory.md` entry or a note is
|
||||
@@ -124,7 +128,8 @@ rebuilt from them.
|
||||
ledger.
|
||||
5. **Send.** One worker thread in `memoryd` sends what is decided and not yet sent, oldest first:
|
||||
for a turn it creates the Honcho workspace, peers and session on first use (section 6), then
|
||||
adds the two messages as one batch; for a memory write it creates or deletes the conclusion.
|
||||
adds the two messages as one batch; for a memory write it creates or deletes the conclusion,
|
||||
or, for an untrusted note, adds the message to the fleet workspace (or its retraction).
|
||||
Each send is recorded in the ledger with Honcho's ids.
|
||||
6. **Honcho down** (a connection error, a timeout, a 5xx): the worker waits and tries again,
|
||||
doubling the wait from 5 s to 10 minutes. It logs the outage once, when it starts, and once
|
||||
@@ -160,7 +165,10 @@ passes (checked 2026-09-27).
|
||||
- `roncho`'s `Status` and `Decode` errors carry up to 4 KiB of the response, which can be memory
|
||||
text: `memoryd` logs only the error's kind and status, never its `Display`.
|
||||
- On creation, `memoryd` configures each workspace: the trusted and untrusted ones with reasoning,
|
||||
summaries and dreaming on (Honcho's defaults); the fleet one holds conclusions only.
|
||||
summaries and dreaming on (Honcho's defaults); the fleet one with `reasoning`, `summary` and
|
||||
`dream` all `enabled = false`, and every agent's peer in it unobserved, so no model reads the
|
||||
fleet's notes to derive anything (Honcho enqueues summaries before it checks reasoning, and
|
||||
schedules dreams by their own setting, so all three are set).
|
||||
- Honcho ids allow `[a-zA-Z0-9_-]`, at most 512 characters. Boxmaker session ids fit; names from
|
||||
configuration are checked at start.
|
||||
|
||||
@@ -179,7 +187,7 @@ One tool, as today, searching (owner):
|
||||
|---|---|---|---|
|
||||
| the local index | FTS5, as today | as today (`identity`, `memory`, `memory-tainted`) | as recorded |
|
||||
| each other agent's trusted workspace in `[[honcho.read]]` | message search, and a conclusion query for that agent's peer and the owner's | `memory` if the owner labelled it trusted, else `memory-tainted`; the agent's name | the workspace's `class` |
|
||||
| the fleet workspace | a conclusion query for this agent's peer and each agent's in `[[honcho.read]]` | `memory-tainted`; the writing agent's name | `private` |
|
||||
| the fleet workspace | message search over the whole workspace | `memory-tainted`; the writing peer's name | `private` |
|
||||
|
||||
The agent's own workspaces are not searched: the local index holds every turn and note of this
|
||||
agent with exact labels, and would return the same text twice. Each source runs on its own thread
|
||||
@@ -189,12 +197,12 @@ ranked within their source and interleaved to `k`, the local index first. Text f
|
||||
neutralised on arrival and size-capped like every recall snippet. Any `memory-tainted` hit marks
|
||||
the session untrusted, and every hit raises the session's class to its label, as local hits do.
|
||||
|
||||
Honcho's search endpoints find messages only, and the fleet workspace holds only conclusions, as
|
||||
do the mirrored memory writes in another agent's trusted workspace: so `recall` also queries
|
||||
conclusions (`POST /conclusions/query`, which needs the observer and the observed; for a
|
||||
conclusion `memoryd` wrote, both are the same peer). `roncho`'s blocking client cannot list a
|
||||
workspace's peers, so the agents whose fleet notes an agent reads are itself and those in
|
||||
`[[honcho.read]]`.
|
||||
Honcho's search endpoints find messages only, and the mirrored memory writes in another agent's
|
||||
trusted workspace are conclusions: so for those `recall` also queries conclusions
|
||||
(`POST /conclusions/query`, which needs the observer and the observed; for a conclusion `memoryd`
|
||||
wrote, both are the same peer). The fleet workspace holds messages, so one search finds the notes
|
||||
of every peer in it, including agents this one was never told about (owner); a retracted note is
|
||||
found beside its retraction.
|
||||
|
||||
## 9. `ask_memory`
|
||||
|
||||
@@ -240,9 +248,15 @@ trust = "untrusted" # the default; "trusted" must be written out
|
||||
class = "private"
|
||||
```
|
||||
|
||||
Keys come from `bxcore::secrets` (M5b). An invalid `[honcho]` section stops `memoryd` at start
|
||||
with the reason (runbook `honcho-config-invalid`). `docs/egress.md` gains rift: `memoryd` →
|
||||
the configured URL, for writes, searches and chat.
|
||||
**API keys** (owner: the design must support them): each workspace this agent writes or reads has
|
||||
its key, `honcho_trusted`, `honcho_untrusted`, `honcho_fleet` and `honcho_read_<agent>`, from
|
||||
`bxcore::secrets` (M5b), and `memoryd` sends it as a bearer token on every call to that workspace
|
||||
and never logs it. A workspace without a key configured is called without one; rift has auth off
|
||||
today (2026-09-27), and turning it on needs only the keys. A refused key (401 or 403) stops the
|
||||
sends to that workspace, is logged once with the workspace's name (runbook `honcho-key-refused`),
|
||||
and is shown by `bxctl memory sync`; the others go on. An invalid `[honcho]` section stops
|
||||
`memoryd` at start with the reason (runbook `honcho-config-invalid`). `docs/egress.md` gains rift:
|
||||
`memoryd` → the configured URL, for writes, searches and chat.
|
||||
|
||||
## 11. Links between the roles
|
||||
|
||||
@@ -259,11 +273,11 @@ Wire changes (fixtures change): `Turn` gains `speaker: Option<String>`, which `g
|
||||
the Mattermost username when every post of the burst is from one person, and `bxctl chat` sets
|
||||
to the owner's name from its configuration; `loopd` logs it on the `User` record (a new optional
|
||||
field: older logs read as `None`); `SessionUpdated` gains `turn: u64`. A turn whose speaker is
|
||||
`None` is not synced.
|
||||
`None`, or not a valid peer name, is not synced (owner).
|
||||
|
||||
## 12. Runbook
|
||||
|
||||
New entries, given as fragments: `honcho-unreachable`, `honcho-send-refused`,
|
||||
New entries, given as fragments: `honcho-unreachable`, `honcho-send-refused`, `honcho-key-refused`,
|
||||
`broker-state-unavailable`, `honcho-config-invalid`. Each names what `bxctl memory sync` shows.
|
||||
|
||||
## 13. Testing
|
||||
@@ -281,6 +295,12 @@ that records requests and answers from fixtures taken from rift's OpenAPI docume
|
||||
- **Peers and sessions**: the agent's peer not observed; a new speaker starts the next Honcho
|
||||
session; names fit Honcho's pattern.
|
||||
- **`roncho`**: a `roncho.toml` with an `api_key` in the test's `HOME` is not read.
|
||||
- **Keys**: a configured key is sent as a bearer token to its workspace only, and appears in no
|
||||
log line or error; without a key, no `Authorization` header; a 401 stops that workspace's sends
|
||||
only, once logged.
|
||||
- **The fleet**: an untrusted note is a message from the agent's peer; its undo is a retraction
|
||||
message; the fleet workspace is created with reasoning, summaries and dreaming off; a note
|
||||
from a peer this agent's configuration does not name is found by `recall`.
|
||||
- **`recall`**: the fleet sources with their tags and classes; a source that times out is one
|
||||
`unavailable` line and the rest still answer; the agent's own workspaces are not searched.
|
||||
- **`ask_memory`**: two at a time; the per-workspace limit; labels; unknown workspace names.
|
||||
@@ -309,17 +329,16 @@ With this spec, as their own commits:
|
||||
rebuild); rows for the class ceiling, the peers, mirroring, the ledger, and `recall`'s sources.
|
||||
- `docs/milestones.md`, M5c: its check "Honcho's contents are rebuilt from the logs" goes.
|
||||
|
||||
## 15. For the owner's review
|
||||
## 15. The owner's answers (2026-09-27)
|
||||
|
||||
1. **The speaker on each turn** (section 11) is new: Honcho needs a peer per message, and today's
|
||||
turn text carries no name. A burst joined from several people is not synced at all, rather
|
||||
than guessed at.
|
||||
2. **Which fleet notes an agent reads** (section 8): its own and those of the agents in
|
||||
`[[honcho.read]]`, since the client cannot list the fleet's peers. An agent the owner has not
|
||||
listed contributes to the fleet but is not read.
|
||||
3. **For `roncho`'s agent** (found 2026-09-27, none blocking): make the config file opt-in (it
|
||||
supplies an `api_key` today; the README's "reads no environment variables" is wrong); evidence
|
||||
decoding requires `observer_id` and `observed_id`, which rift's schema lacks; a message's
|
||||
`configuration` (reasoning per message) is not exposed; the tag `v1.0.0` names `b4e7864` on
|
||||
`origin` and `1f5ea42` on crates.io.
|
||||
4. **Rift**: auth is still off; the keys in section 10 are ready for it.
|
||||
1. **The speaker**: a turn with no speaker, or incomplete speaker information, is not sent
|
||||
(sections 4 and 11).
|
||||
2. **The fleet**: every peer in the fleet workspace is a member of the fleet. Fleet notes are
|
||||
therefore messages, which one search finds whoever wrote them; an undone note gets a
|
||||
retraction message, since Honcho cannot delete a message (sections 3, 4, 8).
|
||||
3. **For `roncho`'s agent**, accepted (found 2026-09-27, none blocking): make the config file
|
||||
opt-in (it supplies an `api_key` today; the README's "reads no environment variables" is
|
||||
wrong); evidence decoding requires `observer_id` and `observed_id`, which rift's schema lacks;
|
||||
a message's `configuration` (reasoning per message) is not exposed; the tag `v1.0.0` names
|
||||
`b4e7864` on `origin` and `1f5ea42` on crates.io.
|
||||
4. **API keys**: the design supports them per workspace (section 10); rift has auth off today.
|
||||
|
||||
Reference in New Issue
Block a user