skills: refresh the vendored mempalace snapshot (withdrawn hand-stamping)
Lint / hadolint (push) Successful in 9s
Lint / actionlint (push) Successful in 19s

VENDORED.md's freshness model for `mempalace` is "Option 2 only — refreshed
manually per release", and it had drifted since 2026-08-23. The stale snapshot
still carried the instruction to hand-stamp added_by="<harness>@<device>", which
skillset 73c7c8e withdrew: the pi bridge now stamps at the edge
(mempalace-toolkit 553d865), and RFC 001 §7.3.2 ranks agent-side stamping ❌
worst-possible.

That matters specifically for the fallback case this snapshot exists to serve — a
container started WITHOUT the private skillset mounted would otherwise be the
only kind of container still being taught to do it by hand.
This commit is contained in:
pi
2026-08-25 22:27:04 +02:00
parent 9e744d701f
commit 4f1aa0d0dd
@@ -79,6 +79,79 @@ mempalace_search(query="<keywords>", wing="<project>")
**Never guess about facts that might be in the palace.** Wrong is worse than slow. Say "let me check" and query. **Never guess about facts that might be in the palace.** Wrong is worse than slow. Say "let me check" and query.
#### Search Before You *Probe*
The rule above covers **questions**. This one covers **actions** — and it is the one
that actually gets skipped, because mid-task the impulse is to go and *look* rather
than to remember. The palace is a **fleet** record: another machine's agent has
usually already paid the cost of discovering how this environment is wired, and its
notes include the corrections that came afterwards, which a fresh probe cannot show
you.
**Before you SSH somewhere to find out how it is set up, enumerate infrastructure,
or derive a deployment — search.** Concrete triggers, all meaning *search first*:
- about to run `ssh <host> …`, `docker ps`, `systemctl list-units`, `ip addr` to
discover how something is deployed or connected
- about to establish topology: which hosts/runners/services exist, where they live,
which of them can reach which
- about to conclude "this isn't documented anywhere" or "there's no way to know"
- about to assert an environment fact you learned **earlier in this same session**
**That last trigger is the sharp edge.** A compacted session summary is lossy by
design, and a belief you formed 40 turns ago may already be *retracted* in the
palace by another machine. Trusting your own context over the shared record is how a
withdrawn claim gets re-published as fact.
Search broadly before narrowing — fleet knowledge often sits in another machine's
wing, or inside a mined conversation, not where you would file it yourself:
```
mempalace_search(query="<topic> <host> <mechanism>") # no wing filter first
mempalace_search(query="…", wing="<likely-wing>") # then narrow
```
Two or three searches cost seconds. Re-deriving infrastructure costs minutes **and
can be wrong**: a probe shows one host's present state, while the palace records
intent, history, and what was already disproved.
> **Worked example (real, 2026-08-25).** An agent evaluating whether to add an ARM
> CI runner probed hosts directly instead of searching. It concluded "the runner
> lives on synlig" — there are **four** — and that "synlig is on the home LAN" —
> it is an OpenStack VM with a public floating IP that cannot reach the home LAN at
> all. Both facts were already in the palace, the second one as an **explicit
> retraction of the very same mistake** made weeks earlier. The palace also held
> the runner labels and the deliberate `capacity: 1` setting, which the probe never
> revealed. Cost: a wrong recommendation written into the palace twice, then
> corrected twice.
**A search that comes back empty is not an answer — least of all about recent work.**
Semantic search is weakest exactly where the fleet record is freshest: a drawer filed
minutes ago is unranked against a keyword-shaped query, and the drawer you most need
is *by construction* the newest one, because the other machine files its release,
handoff and correction drawers at the **end** of its session. So a single miss proves
nothing. **If the work is 0-2 days old and the first search looks stale or empty,
enumerate before concluding:**
```
mempalace_list_drawers(wing="<wing>", since="<today>") # or room=, or no filter
mempalace_diary_read(agent_name="<you>", wing="<wing>") # the other machine's handoff
```
Enumeration is exact where embeddings are probabilistic. Treat "I searched and found
nothing" as a hypothesis you have not yet tested, and never as licence to go probing.
> **Worked example (real, 2026-08-25, same fleet as above).** An agent asked to
> orient on an in-flight release *did* search first — `"v1.8.6 release run 579
> Docker Hub verification"` — and got back only v1.6.4 / v0.78.0 era hits, because
> the release drawer it needed was **58 seconds old**. It accepted the miss and went
> off to probe Docker Hub and the Gitea API. The user had to prompt "maybe there is a
> note in mempalace"; `list_drawers(wing="pi-devbox", since=<today>)` then returned
> the drawer immediately, along with the diary entry naming the exact open item. The
> rule above was present and correct in this very file at the time — the failure was
> not knowing to *retry differently* after a bad first hit.
#### Mine New Projects #### Mine New Projects
When working on a new codebase for the first time: When working on a new codebase for the first time:
@@ -290,13 +363,14 @@ Zechner's pi-coding-agent). Implications:
- **Session feeders run on different schedules.** Pi sessions are fed Tue 03:00, opencode sessions Mon 03:00 (launchd `Weekday`: `0`/`7`=Sunday, `1`=Monday, `2`=Tuesday — misreading this by one day is easy). Recent sessions from either harness can lag the palace by up to a week, so absence-of-evidence in `wing_conversations` is not evidence-of-absence for recent work. - **Session feeders run on different schedules.** Pi sessions are fed Tue 03:00, opencode sessions Mon 03:00 (launchd `Weekday`: `0`/`7`=Sunday, `1`=Monday, `2`=Tuesday — misreading this by one day is easy). Recent sessions from either harness can lag the palace by up to a week, so absence-of-evidence in `wing_conversations` is not evidence-of-absence for recent work.
- **Reading another harness's diary is useful.** When orienting after a gap, `mempalace_diary_read agent_name=pi` (or whichever sibling agent has been active) often gives a fresher picture than waiting for the conversations feeder to catch up. - **Reading another harness's diary is useful.** When orienting after a gap, `mempalace_diary_read agent_name=pi` (or whichever sibling agent has been active) often gives a fresher picture than waiting for the conversations feeder to catch up.
When the palace is **central** (shared across machines), five more things apply: When the palace is **central** (shared across machines), these further things apply:
- **Check which machine a conversation came from.** Transcripts are fed per device, so `source_path` reads `…/mempalace-feed/<device>/pi_<uuid>.jsonl` while the displayed `source_file` is only the basename. One search can legitimately return hits from several machines at once — look at the device segment before attributing a decision to *this* project. - **Check which machine a conversation came from.** Transcripts are fed per device, so `source_path` reads `…/mempalace-feed/<device>/pi_<uuid>.jsonl` while the displayed `source_file` is only the basename. One search can legitimately return hits from several machines at once — look at the device segment before attributing a decision to *this* project.
- **Attribute what you file yourself.** Drawers now carry `device` and `agent_kind` metadata (plus `device_source`/`agent_kind_source` recording *how* each was determined, so an inference is never mistaken for a fact). Mined content gets these for free — the inbox path gives the device, the filename shape gives the harness — and a timer on the palace host re-stamps hourly, because live re-mining replaces metadata rows and silently drops earlier stamps. But for anything **you** file by hand, the only signal is what you pass: set `added_by="<harness>@<device>"` (e.g. `pi@emb-7kj4vr4g`, from `$MEMPALACE_PI_DEVICE`) on `add_drawer`/`checkpoint`/`mine`. Skip it and your drawer joins the ~16k historic `/workspace` project mines that are permanently unattributable, because `/workspace` exists identically on every devbox. Note the palace preserves `source_file` in full (see `source_path`) but *displays* only the basename — so a device prefix there survives storage even though it looks stripped. - **Provenance is stamped for you — leave it alone.** Drawers carry `device` and `agent_kind` metadata (plus `device_source`/`agent_kind_source` recording *how* each was determined, so an inference is never mistaken for a fact). You do **not** set these, and you no longer set `added_by` either: the pi bridge defaults the writer field to `<harness>@<device>` on `add_drawer`/`checkpoint`/`mine`/`event_append`/`artifact_put`, and prefixes diary entries with `HOST:<device>|`, from host-supplied `$MEMPALACE_PI_DEVICE`. RFC 001 §7.3.2 ranks "agent stamps it via a skill instruction" as the *worst possible* place for exactly the reason you would expect — it is per-call boilerplate that gets forgotten, and it did: the agent who wrote the previous version of this bullet then filed its own provenance drawer as `added_by=checkpoint`. Two things remain yours: pass `source_drawer_id` on `kg_add` (triples have no provenance field, so that pointer is the only path back to a device), and pass an explicit `added_by` **only** when deliberately filing on behalf of another device. Never invent values for `device`/`agent_kind`/`origin_device` — a fabricated value is worse than a blank, because it silently corrupts a future merge.
- **Metadata is invisible to search — so check the text, not the fields.** `search` results are built from a fixed key list and `diary_read` returns content, so neither ever shows `device`/`added_by`. Only `mempalace_get_drawer` reveals them. This is why diary entries carry an in-text `HOST:<device>` marker: it is the only attribution a reader actually sees. **A diary entry with no `HOST:` marker predates the convention and may be from any machine — do not assume it is this one's history.**
- **Mined drawers carry the MINE date, not the session date.** When history is imported, or re-mined on the palace host, `filed_at`/`created_at` is the *import* time — so sorting by them does not give chronological order. Real session time is recoverable from the UUIDv7 in `pi_<uuid>.jsonl`: the first 12 hex digits are milliseconds since the epoch (and UUIDv7 sorts lexicographically in time order, so a plain filename sort is already chronological). Agent-authored drawers and diaries have no such backdoor — for those `filed_at` is the only chronology, which is why it must never be restamped. - **Mined drawers carry the MINE date, not the session date.** When history is imported, or re-mined on the palace host, `filed_at`/`created_at` is the *import* time — so sorting by them does not give chronological order. Real session time is recoverable from the UUIDv7 in `pi_<uuid>.jsonl`: the first 12 hex digits are milliseconds since the epoch (and UUIDv7 sorts lexicographically in time order, so a plain filename sort is already chronological). Agent-authored drawers and diaries have no such backdoor — for those `filed_at` is the only chronology, which is why it must never be restamped.
- **Beware the timezone mismatch when you combine those.** Palace `filed_at`/`created_at` are naive timestamps in the palace host's local time, while a UUIDv7 decodes to UTC. Comparing them directly introduces a silent offset (2 h for a CEST host). Normalise before drawing conclusions about ordering. - **Beware the timezone mismatch when you combine those.** Palace `filed_at`/`created_at` are naive timestamps in the palace host's local time, while a UUIDv7 decodes to UTC. Comparing them directly introduces a silent offset (2 h for a CEST host). Normalise before drawing conclusions about ordering.
- **`agent_name` is not device-scoped.** `mempalace_diary_read(agent_name="pi")` returns *every* machine's `pi` diary, interleaved. Read the entry before assuming it is your own history. - **`agent_name` is not device-scoped.** `mempalace_diary_read(agent_name="pi")` returns *every* machine's `pi` diary, interleaved. Read the entry before assuming it is your own history — and note that a container cannot tell you which machine it is on (`hostname` is a docker hash, `$DEVBOX_HOST_ALIAS` is generic). `$MEMPALACE_PI_DEVICE` is the cheap answer; `ssh -F ~/.ssh-local/config host hostname` is the independent one.
- **One writer, no queue.** A concurrent mine returns a structured `already-running` error rather than waiting its turn, and one large mine can make the palace unresponsive to every client for minutes. After another client's mine, call `mempalace_reconnect` to see the new drawers. A client-side timeout is not evidence of failure — verify before retrying, or you file a duplicate. - **One writer, no queue.** A concurrent mine returns a structured `already-running` error rather than waiting its turn, and one large mine can make the palace unresponsive to every client for minutes. After another client's mine, call `mempalace_reconnect` to see the new drawers. A client-side timeout is not evidence of failure — verify before retrying, or you file a duplicate.
### Rooms ### Rooms
@@ -330,10 +404,13 @@ Entity-relationship triples with temporal validity. Query with `mempalace_kg_que
## Anti-Patterns ## Anti-Patterns
- **Don't guess when you can search.** If a question touches past work, search first. - **Don't guess when you can search.** If a question touches past work, search first.
- **Don't probe what the fleet already knows.** Before SSH-ing into a host, enumerating infrastructure, or deriving how something is deployed, search the palace. A probe reveals one host's present state; the palace holds intent, history and prior corrections — including the ones that contradict what you are about to conclude.
- **Don't trust this session's context over the palace.** A compacted summary is lossy, and another machine may have corrected the fact since. Verify load-bearing environment claims against the shared record before acting on them.
- **Don't take one empty search as proof the palace is silent.** Fresh drawers rank worst, and the drawer that matters is usually the newest one. For anything 0-2 days old, enumerate with `mempalace_list_drawers(since=…)` and read the other machine's diary before you go and probe.
- **Don't infer elapsed time from session or container boundaries.** A restart isn't a new day. Compare the actual timestamp (`timestamp` / `created_at`) against the current date/time before saying "yesterday", "last week", etc. - **Don't infer elapsed time from session or container boundaries.** A restart isn't a new day. Compare the actual timestamp (`timestamp` / `created_at`) against the current date/time before saying "yesterday", "last week", etc.
- **Don't skip the diary.** A session without a diary entry is a session forgotten. - **Don't skip the diary.** A session without a diary entry is a session forgotten.
- **Don't summarize drawer content.** File verbatim — the embedding model needs the original words. - **Don't summarize drawer content.** File verbatim — the embedding model needs the original words.
- **Don't mine .git directories or node_modules.** The CLI miner respects .gitignore by default. - **Don't mine .git directories or node_modules.** The CLI miner respects .gitignore by default.
- **Don't create duplicate drawers.** Use `mempalace_check_duplicate` before adding manually. - **Don't create duplicate drawers.** Use `mempalace_check_duplicate` before adding manually.
- **Don't treat the palace as a task list.** It's for knowledge and context, not todos. - **Don't treat the palace as a task list.** It's for knowledge and context, not todos.
- **Don't hand-craft provenance.** Leave `added_by` alone (and never put a machine name in a diary's `agent_name` — it becomes the wing name and hides your entries from `diary_read`). Recording *which device* wrote a record is client/server infrastructure, not your job: a hostname or container ID is not a stable identity, and an invented value is worse than none because it silently corrupts any future palace merge. If you find notes in the palace describing an `origin_device` scheme, that is a design for the client to implement — not an instruction for you to start stamping. - **Don't invent provenance metadata, and don't hand-stamp it either.** An earlier version of this list told you to set `added_by="<harness>@<device>"` 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). 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`.