diff --git a/extensions/pi/README.md b/extensions/pi/README.md index e1d6ff9..3876d55 100644 --- a/extensions/pi/README.md +++ b/extensions/pi/README.md @@ -53,6 +53,10 @@ dependencies (~300 MB). because it needs the LLM to compose the entry, and `session_shutdown` fires too late to drive another LLM turn — a constraint that applies to the diary specifically, not to feeding (see above). +6. **Stamps device provenance on every write** — `@` as the + writer on `add_drawer`/`checkpoint`/`mine`/`event_append`/`artifact_put`, and + a `HOST:|` prefix on diary entries — as of mempalace-toolkit + `553d8657` (2026-08-25). See [Identity](#identity). ## Automatic transcript feeding @@ -242,6 +246,82 @@ to `"pi"`. First diary write against that identity creates `wing_` in the palace. Set the env var if you want to run pi under a distinct identity on a given machine (e.g. `pi-laptop` vs `pi-server`). +### Device provenance is stamped here, at the edge + +As of mempalace-toolkit `553d8657` (2026-08-25) the bridge fills in *who wrote +this* so the agent never has to: + +| Write | What the bridge sets | +|---|---| +| `add_drawer`, `checkpoint`, `mine` | `added_by = "@"` when the caller left it unset | +| `event_append`, `artifact_put` | `from_agent` / `created_by` likewise | +| `diary_write` | prefixes the entry text with `HOST:\|` | + +`` is `$MEMPALACE_PI_DEVICE`; `` is `pi`. **Both gates must +hold:** `MEMPALACE_PI_DEVICE` set *and* `MEMPALACE_REMOTE_URL` pointing at a +shared palace. A solitary palace stamps nothing, because there is no second +machine to disambiguate from and the annotation would be pure noise. + +Two design points worth not re-litigating: + +- **The diary marker is in the entry text, not metadata.** `diary_read` returns + content only, so metadata is invisible to the agent that later reads the + entry — an attribution nobody can see is not an attribution. Search results + are built from a fixed key list with the same consequence. +- **RFC 001 §7.3.2 ranks "the agent stamps it via a skill instruction" as the + worst available option**, and it was: the agent that wrote that instruction + into the consumer skill then filed its own provenance drawer as + `added_by=checkpoint`. Per-call boilerplate gets forgotten. Hence the edge. + +Callers keep two responsibilities the bridge cannot infer: pass +`source_drawer_id` on `kg_add` (triples have no provenance field at all), and +pass an explicit writer **only** when deliberately filing on behalf of another +device. + +Because the extension is baked into an image, *a container older than the +stamping commit satisfies both gates and still stamps nothing* — the env vars +are set and the code is simply absent. The one-line check: + +```bash +grep -c MEMPALACE_PI_DEVICE "$(readlink -f ~/.pi/agent/extensions/mempalace.ts)" +``` + +## Agent coordination over the logstream + +The palace also carries an append-only coordination log (RFC 003: +`mempalace_event_*`, `mempalace_artifact_*`) used for cross-machine delegation, +review, patch handoff and retraction. Relevant to this extension in three ways: + +**1. The bridge makes directed addressing possible.** Where every machine is a +thin MCP client of one shared palace, all clients report the *same* +`origin_replica`, so the log cannot tell two machines apart by transport +identity — `from_agent` / `to_agent` carry the entire distinction. Measured +2026-08-26 from the tor-ms22 client: `mempalace_mesh_peers` returned +`peers: []` with a single replica id authoring every event from every machine. +So the stamping above is what makes `to_agent="pi@tor-ms22"` mean anything, and +an *unstamped* client addressed as bare `pi` is unreachable. + +**2. The bridge is write-only today.** It stamps events on the way out and never +reads the log: there is no mailbox, no poll, no delivery. An event addressed to +this machine by name reaches the agent only if the agent runs +`mempalace_event_list` itself — which is why that query is a wake-up step in the +*consumer* skill (`~/.agents/skills/mempalace/SKILL.md`), and why the protocol +norms live there rather than here. **This file documents the mechanism; the +skill is normative for behaviour.** + +**3. Live push is a deployment question, not a code one.** The palace implements +an SSE endpoint (`GET /logstream/stream`, `text/event-stream` in +`mempalace/mcp_server.py`), but a deployment may expose only the MCP endpoint +through its reverse proxy — verified 2026-08-26 against +`https://mempalace.jordbo.se`, where `/logstream/events`, `/logstream/stream` +and `/sync/peers` all return 404 while `/mcp` serves normally. Where that is the +case, polling through the existing MCP client is the only available path, and +enabling SSE means a proxy route plus an auth decision — not an extension change. + +As with stamping, all of this is inert unless `MEMPALACE_REMOTE_URL` points at a +shared palace. On a solitary palace the event tools work fine and the log +contains only this machine's own events. + ## Stall protection (per-request timeout) Every JSON-RPC request to `mempalace-mcp` carries a timeout. Without it, a