From 495b7e38590c78b1611cfcf969d8a79ce3c73baa Mon Sep 17 00:00:00 2001 From: Joakim Persson Date: Thu, 27 Aug 2026 23:36:03 +0200 Subject: [PATCH] vendor: resync mempalace skill snapshot to skillset a12fe5e scripts/vendor-mempalace-skill.sh, real refresh not --check: skillset moved 6eb20af -> a12fe5e (mermaid-diagrams cutU normalisation, and the from_agent identity rule this same release ships in RFC 003). --check reported stale-but-truthful (exit 0, the sanctioned skip) but this release's point is getting today's fixes live fleet-wide, and the base rebuild is already forced by the entrypoint change and the floating mempalace-toolkit ref moving -- so the incremental cost of also bumping this pin is zero. Phrase canary in scripts/smoke-test.sh unaffected: neither pinned phrase ('Provenance is stamped for you' present, 'Attribute what you file yourself' absent) is in the section that changed; both verified still correct in the new snapshot. --- Dockerfile.variant | 2 +- .../share/pi-devbox/skills/mempalace/SKILL.md | 61 ++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Dockerfile.variant b/Dockerfile.variant index 76716ef..4cb5e42 100644 --- a/Dockerfile.variant +++ b/Dockerfile.variant @@ -305,7 +305,7 @@ ARG MEMPALACE_TOOLKIT_REF=main # no ~67-minute base rebuild. (scripts/check-base-hash.sh scans only # Dockerfile.base, so no folding into the base hash is required — nor would # it be correct, since this ARG changes nothing about the base's contents.) -ARG SKILLSET_SNAPSHOT_REF=6eb20af181f0147cb8c1377f6e36a6a47a68e8e5 +ARG SKILLSET_SNAPSHOT_REF=a12fe5ecc71e60feb24791e3e33571105f1afba7 # Dockerfile.base sets description="pi-devbox — base image (variant-independent)" # and every variant INHERITS it, so both published images used to advertise diff --git a/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md b/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md index 44b0a0e..192d93a 100644 --- a/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md +++ b/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md @@ -428,10 +428,56 @@ An obligation you never agreed to is noise, so the sender states it: | `to_agent="*"` (any status) | broadcast FYI | nothing | | any other status (`ready`, `applied`, `blocked`, …) | a statement of fact | nothing | +**That table says what you *owe*. Delivery is stricter, and the difference bites: +the mailbox is an obligation channel, not a news channel.** Mailbox candidates are +drawn with `status="open"`, so an event carrying any **terminal** status +(`applied`, `superseded`, `failed`, `blocked`) is never a candidate — *whoever it +is addressed to*. A `task.reply` written to a named machine to share a finding is +delivered to nobody, ever, and neither is any `event_ack`. It sits in the log +until somebody reads the log. + +So the most natural inter-machine message — *"here is something you should +know"* — is exactly the shape that gets no delivery. Pick deliberately: + +| You want the peer to… | Write | +|---|---| +| **do something**, and you need it tracked until done | directed `status="open"` ask, with a `correlation_id` | +| **know something**, no response needed | terminal-status event **plus a drawer** — the drawer is what actually reaches them, via search | + +What does **not** work is a terminal report plus an expectation of attention. +Measured 2026-08-26: a detailed report addressed to `pi@` with +`status="applied"` went unread for two and a half hours until the operator quoted +the event id by hand, with the mailbox working correctly the whole time. Full +mechanism in the toolkit's `docs/rfc-003-coordination-log.md` §7.12. + +One more timing fact, because it looks like negligence and is not: a delivered +ask is queued into the agent's **next turn** (`deliverAs: "steer"`, deliberately +no `triggerTurn`), and the poll fires when the agent is *idle*. Between delivery +and the next turn no inference runs, so **a human starting a turn is the +trigger** (§7.11). An agent that "has not reacted" has usually not been running. + Ack with `mempalace_event_ack(event_id=…, from_agent="", status=…)`. It **appends a new event** and never mutates the original; the correlation id is copied for you, and `metadata.ack_of` is set to the event you answered. +**Claiming, and what it does not do.** `status="claimed"` announces that you have +picked work up. Nothing requires it — a directed open ask owes "an ack *or* a +reply", and finishing the work is a complete answer. Do it anyway when the work is +long or the machine is unreliable, because it is the only thing that later +distinguishes *nobody started this* from *someone started and their container +died mid-task*. Be clear about its limits, both of which follow from candidacy +requiring exactly `status="open"`: + +- **It does not notify the requester.** `claimed` is not `open`, so a claim is no + more deliverable than a finished report is (see the delivery table above). Its + reader is whoever pulls the log. +- **It does not quiet your own mailbox.** The ask stays owed until a *terminal* + event of yours joins it, so a claimed-then-silent thread keeps resurfacing — + correctly. + +Prefer a prompt terminal reply over a claim plus a long silence; claim *in +addition*, when the gap between pickup and finish is where a machine might die. + #### What you actually owe — derive it, do not read it off `status` The log is append-only and `status` is written **once**, so it is an honest @@ -502,6 +548,17 @@ Two consequences worth internalising: - **Address the stamped name you actually saw** in a `from_agent` field, e.g. `pi@tor-ms22`. A bare `pi` reaches nobody's mailbox once stamping is live, and older events in the log still carry bare names — do not copy them. +- **The rule runs in reverse too: what you put in YOUR OWN `from_agent` decides + where every reply to your event goes.** Nothing stops you writing a synthetic + or borrowed identity there, and a reply is always addressed back to exactly + that string — so if no live session ever runs as it, the reply is stored, + searchable, and delivered to no one. Measured cost: a directed ask sent under + a synthetic sender got two correct replies, one of them an urgent security + finding, and both sat unread for ~2h20m because nobody's mailbox was that + identity (RFC 003 §7.13). Authoring under a synthetic name is fine for a + deliberate control experiment — this fleet does it on purpose — but then + **name the real identity to reply to inside the body**, because the address + line is not a safe place to also carry provenance. - **Use `status="open"` only when you truly need an answer.** It places an obligation on another machine. - **Never broadcast an ask.** `to_agent="*"` + `status="open"` obliges everyone @@ -513,7 +570,8 @@ Two consequences worth internalising: be matched to it at all. - **Corrections are new events, never edits.** Say explicitly what you retract and name the id — drawer or event — that carried the withdrawn claim. -- **Put a retraction where the reader will look.** An event reaches a live agent; +- **Put a retraction where the reader will look.** A *directed open ask* reaches a + live agent's mailbox; a **terminal-status event reaches no mailbox at all**, and a *drawer* is what a future semantic search finds. If you filed advice as a drawer and later withdraw it, file the withdrawal as a drawer too — otherwise the next agent finds your original confident advice and no trace of the @@ -600,4 +658,5 @@ Entity-relationship triples with temporal validity. Query with `mempalace_kg_que - **Don't treat the palace as a task list.** It's for knowledge and context, not todos. - **Don't broadcast an ask, and don't leave one unanswered.** On a shared palace, `to_agent="*"` + `status="open"` obliges every machine and therefore none of them. And don't expect acking to tidy your mailbox: `status` is immutable, so the event keeps matching either way — what a terminal reply buys you is that the *derived* owed set (see *What you actually owe*) stops counting it. Leave asks unanswered and that set only grows, until everyone learns to stop looking. "Seen, not doing it" is a complete answer — silence is not. - **Don't assume you would have heard.** Nothing pushes another machine's message into your session. If you did not run the mailbox query at wake-up, a correction addressed to you by name can sit unread while you confidently rebuild the thing it warned you about. +- **Don't author an ask under an identity nobody runs as, including your own throwaway labels.** The failure is symmetric to the one above: it is not that you missed a message, it is that nothing could ever have delivered the reply to you, because you addressed it at a name instead of an agent. If you must use a synthetic sender for a control or an experiment, say inside the body who should actually receive the reply. - **Don't invent provenance metadata, and don't hand-stamp it either.** An earlier version of this list told you to set `added_by="@"` by hand; that instruction has been withdrawn, because RFC 001 §7.3.2 places provenance at the client/server boundary and the pi bridge now does it uniformly (see *Provenance is stamped for you* above) — but the withdrawal only holds where the bridge is live, so run the one-line check in that bullet first; on an older image hand-stamping is still the only signal a hand-filed drawer gets. DO NOT invent values for the palace's own metadata fields (`device`, `agent_kind`, `origin_device`): those are stamped by infrastructure that also records *how* each was determined, and a fabricated value is worse than none because it silently corrupts a future merge. DO pass `source_drawer_id` on `kg_add`. And never put a machine name in a diary's `agent_name` — it becomes the wing name and hides your entries from `diary_read`.