Compare commits

..

2 Commits

Author SHA1 Message Date
Joakim Persson 3a509077c2 v1.8.3: mempalace 3.7.1, refreshed skill snapshot, census on PATH
Lint / actionlint (push) Successful in 23s
Lint / hadolint (push) Successful in 8s
Publish Docker Image / resolve-versions (push) Successful in 13s
Publish Docker Image / base-decide (push) Successful in 11s
Publish Docker Image / build-base (push) Successful in 53m49s
Publish Docker Image / smoke (push) Successful in 7m22s
Publish Docker Image / smoke-studio (push) Successful in 18m6s
Publish Docker Image / build-variant (push) Successful in 19m6s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / build-variant-studio (push) Successful in 27m5s
mempalace 3.6.0 -> 3.7.1. Verified against the 3.7.1 source rather than its
changelog, because the risk lands on palaces users cannot reconstruct: legacy
drawers lack the new chunk_total marker and both decision sites trust them, so
no mass re-mine; NORMALIZE_VERSION is 2 in both; chromadb stays <2 so no
index-format migration; no auto-migration exists; logstream.sqlite3 is created
lazily. Downgrade remains possible (3.6.0 has zero references to chunk_total).

Two behaviour changes documented in the CHANGELOG: ALLOW_PEER_WRITER no longer
works on local/chroma palaces, and writer-lock setup failures fail closed.
Neither affects this image's MCP-server-plus-CLI-feeder pattern, which already
serialised on the same lock under 3.6.0 -- the upstream "process-lifetime
single-writer" entry describes tightened escape hatches, not a new lease.

The motivation is the shared central palace: 3.7.1 drops the stale chromadb
SharedSystemClient cache on reconnect (3.6.0 could let a stale in-memory HNSW
segment overwrite a peer's writes, "index count going backwards"), releases the
writer lease on SIGTERM/SIGHUP, and stops treating an interrupted mine as
complete. The fleet primary was upgraded to 3.7.1 and restarted before this tag,
because 3.7.1 refuses writes when the served library drifts and reconnect cannot
clear that. opencode-devbox still pins 3.6.0, so the lockstep is broken until it
cuts its own release.

Vendored mempalace skill snapshot refreshed to skillset 936fed8 (was 63f3bf5).
This closes a gap that had been invisible for two commits: ~/.agents/skills/
mempalace symlinks to the IMAGE-BAKED copy, entrypoint-user.sh creates that link
first, and the skillset deploy never clobbers an existing name -- so in a devbox
container the vendored snapshot always wins and editing skillset alone changes
nothing a container reads. Brings the multi-machine shared-palace section and
the hand-crafted-provenance guard.

pi-global-AGENTS.append.md already carried the three shared-palace damage rules
(a55f636); this tags them into a release.

mempalace-census gets the /usr/local/bin symlink its three siblings have had all
along, plus chmod and a build-time --help smoke check, so RFC-002 Phase A
censuses no longer need an absolute path.

Two new smoke assertions, both confirmed to FAIL against a v1.8.2 container so
they are not tautological: the vendored snapshot must contain the multi-machine
section (a stale manual snapshot is otherwise invisible), and mempalace-census
must be on PATH.

No other pins move: pi stays 0.84.2 (npm latest), pi-atelier v0.8.1, and every
git-ref component was checked against its upstream head and is unchanged.
2026-08-16 23:30:01 +02:00
Joakim Persson a55f6369b3 AGENTS.md: three damage-prevention rules for a shared central palace
Lint / hadolint (push) Successful in 13s
Lint / actionlint (push) Successful in 28s
The managed block already tells agents to load the mempalace skill, but said
nothing about the palace being shared with other machines. Three failure modes
observed tonight while onboarding tor-ms22's feeders, all of which do damage
rather than merely confuse:

- `mempalace sync` prunes drawers whose source files look gitignored, deleted
  or moved. On a central palace that describes most of the content, including
  every other machine's. Compounded by RFC-001 7.2: feeders now stage inside
  the palace root, so a scoped sync can delete the drawers it just filed.
- A client-side timeout is not a failure. The palace is single-writer and one
  large mine blocks every client for minutes, so
  `[mempalace ext] feed (tick) failed: mine timed out after 30000ms` usually
  means the mine COMPLETED. Verified: drawers from a timed-out tick were
  present 35 s after the client gave up. A blind retry files a duplicate.
- The `mempalace` CLI has zero references to MEMPALACE_REMOTE_URL, so it always
  opens a palace on local disk and can silently disagree with the MCP tools.

Orientation depth stays in the skill; only damage-prevention belongs here,
because this file is always read and the skill's later sections often are not.
2026-08-16 22:32:09 +02:00
6 changed files with 148 additions and 6 deletions
+78
View File
@@ -11,6 +11,84 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
---
## v1.8.3 — 2026-08-16
Patch release. **Bumps mempalace to `3.7.1`** and closes the gap that made the
baked mempalace skill go stale for four commits. `pi` stays `0.84.2` (still the
npm latest) and pi-atelier stays `v0.8.1`; every git-ref component
(pi-toolkit, pi-extensions, pi-fork, pi-observational-memory, pi-studio,
mempalace-toolkit) was checked against its upstream head and is unchanged.
- **`MEMPALACE_VERSION` `3.6.0``3.7.1`.** Verified against the 3.7.1 source
rather than its changelog, because the risk is to palaces users cannot
reconstruct: legacy drawers lack the new `chunk_total` completion marker and
**both** decision sites trust them (`if chunk_total is None: ... trust the
match as before`), so there is **no mass re-mine**; `NORMALIZE_VERSION` is `2`
in both versions, so the "pre-v2 drawers are stale" gate does not fire either;
`chromadb<2,>=1.5.4` keeps the same major, so no index-format migration; there
is no auto-migration (the source says *"We do NOT auto-migrate"* twice) and
`rebuild_index` has exactly one call site, the explicit `repair rebuild`; the
single new palace file (`logstream.sqlite3`) is created lazily on first
logstream use. Downgrade stays possible — 3.6.0 has zero references to
`chunk_total` and ignores it as unknown metadata.
Two behaviour changes worth knowing, both turning a silent condition into a
hard refusal: `MEMPALACE_MCP_ALLOW_PEER_WRITER` **no longer works on
local/chroma palaces** (it is now gated on `backend_requires_single_writer()`,
and `_MULTI_PROCESS_WRITER_BACKENDS` is `{pgvector, qdrant}`), and writer-lock
*setup* failures now **fail closed** (`refusing this mutating tool`) instead of
proceeding with a warning. Neither affects this image's normal
MCP-server-plus-CLI-feeder pattern, which already serialised on the same
`mine_palace_*.lock` under 3.6.0 — "process-lifetime single-writer ownership"
in the upstream changelog describes tightened escape hatches, not a new lease.
What 3.7.1 buys a **shared central** palace is the real motivation: the stale
chromadb `SharedSystemClient` cache is now dropped on reconnect (under 3.6.0 a
peer's writes could be overwritten by a stale in-memory HNSW segment, *"index
count going backwards"*), the writer lease is released on SIGTERM/SIGHUP
instead of leaking a lock naming a dead PID, and an interrupted mine is no
longer permanently skipped as though complete.
**Upgrading a server requires restarting it** — 3.7.1 refuses mutating tools
when the served library drifts from what is installed, and `mempalace_reconnect`
cannot clear that (it reopens the database but cannot reload Python modules).
The fleet primary was upgraded and restarted before this image was tagged.
Note: opencode-devbox still pins `3.6.0`. The two images are meant to move in
lockstep, so that pin diverges until opencode-devbox cuts its own release.
- **Vendored `mempalace` skill snapshot refreshed** to skillset `936fed8` (was
`63f3bf5`). This is the gap worth naming: `~/.agents/skills/mempalace`
symlinks to the **image-baked** copy under
`/usr/local/share/pi-devbox/skills/`, and `entrypoint-user.sh` creates that
link *first* while the skillset deploy never clobbers an existing name — so in
a devbox container the vendored snapshot always wins, and editing the skillset
repo alone changes nothing a container reads. Two commits' worth of guidance
had been invisible here: the multi-machine shared-palace section (device
provenance in `source_path`, mined drawers carrying the *mine* date with
UUIDv7 recovery, the naive-local vs UTC timestamp mismatch, `agent_name` not
being device-scoped, single-writer/no-queue semantics) and the
hand-crafted-provenance guard.
- **`pi-global-AGENTS.append.md`** gains `### If the palace is central, it is
shared — three rules`: never run `mempalace sync` against a shared palace (it
prunes drawers whose sources look missing, which on a central palace is most
of the content, including other machines' — compounded by RFC-001 §7.2, since
feeders stage *inside* the palace root); a client-side timeout is not a
failure (single writer, one large mine blocks everyone, so
`mine timed out after 30000ms` usually means the mine completed — verify
before retrying or you file a duplicate); and the `mempalace` CLI is not
remote-aware, so it always opens a local-disk palace and can silently
disagree with the MCP tools.
- **`mempalace-census` is now on `PATH`.** It shipped inside the image at
`/opt/mempalace-toolkit/bin/` but was never symlinked into `/usr/local/bin`
like its three siblings, so RFC-002 Phase A censuses had to be invoked by
absolute path. Added to the symlink set, the `chmod +x` set, and the
build-time `--help` smoke chain.
---
## v1.8.2 — 2026-08-16
Patch release. **Ships the fix for a silent transcript-feed failure**, plus the
+17 -2
View File
@@ -384,7 +384,19 @@ ARG INSTALL_MEMPALACE=true
# mempalace_checkpoint (#2023/#2034).
#
# Keep in lockstep with opencode-devbox when bumping.
ARG MEMPALACE_VERSION=3.6.0
#
# 3.7.1 (from 3.6.0) is safe for anyone with an EXISTING LOCAL palace: verified
# against the 3.7.1 source, not the changelog. Legacy drawers lack the new
# `chunk_total` marker and both decision sites trust them ("trust the match as
# before"), NORMALIZE_VERSION is 2 in both, chromadb stays <2 (no index-format
# migration), there is no auto-migration ("We do NOT auto-migrate"), and the one
# new palace file (logstream.sqlite3) is created lazily on first logstream use.
# Two behaviour changes to know: MEMPALACE_MCP_ALLOW_PEER_WRITER no longer works
# on local/chroma palaces, and writer-lock setup failures now fail CLOSED
# (refuse the write) rather than fail open. Neither affects the container's
# normal MCP-server-plus-CLI-feeder pattern, which already serialised on the
# same lock under 3.6.0.
ARG MEMPALACE_VERSION=3.7.1
ENV UV_TOOL_DIR=/opt/uv-tools
ENV UV_TOOL_BIN_DIR=/usr/local/bin
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
@@ -425,11 +437,14 @@ RUN if [ "${INSTALL_MEMPALACE}" = "true" ] && [ "${INSTALL_MEMPALACE_TOOLKIT}" =
ln -sf /opt/mempalace-toolkit/bin/mempalace-session /usr/local/bin/mempalace-session && \
ln -sf /opt/mempalace-toolkit/bin/mempalace-docs /usr/local/bin/mempalace-docs && \
ln -sf /opt/mempalace-toolkit/bin/mempalace-pi-session /usr/local/bin/mempalace-pi-session && \
ln -sf /opt/mempalace-toolkit/bin/mempalace-census /usr/local/bin/mempalace-census && \
chmod +x /opt/mempalace-toolkit/bin/mempalace-session /opt/mempalace-toolkit/bin/mempalace-docs \
/opt/mempalace-toolkit/bin/mempalace-pi-session && \
/opt/mempalace-toolkit/bin/mempalace-pi-session \
/opt/mempalace-toolkit/bin/mempalace-census && \
mempalace-session --help >/dev/null && \
mempalace-docs --help >/dev/null && \
mempalace-pi-session --help >/dev/null && \
mempalace-census --help >/dev/null && \
echo "mempalace-toolkit installed at $(cd /opt/mempalace-toolkit && git rev-parse --short HEAD)" ; \
fi
@@ -41,3 +41,32 @@ especially load-bearing here — a pi-devbox container is frequently recreated,
the palace is your only memory across recreates. Without the habit it is just
storage, not memory. (The skill is the consumer side; feeding the palace is the
separate `opencode-mempalace-bridge` skill, if present.)
### If the palace is central, it is shared — three rules
If `MEMPALACE_REMOTE_URL` is set, the MCP tools write to a **central palace
shared with other machines**, not to a local one. Your drawers are not the only
ones in there, and most drawers' `source_file` paths do not exist on this host.
The skill covers the orientation side (provenance, chronology, whose diary is
whose); these three are here instead because getting them wrong does *damage*
rather than merely confusing you:
- **Never run `mempalace sync` / `mempalace_sync` against a shared palace.** It
prunes drawers whose source files look gitignored, deleted, or moved — and on
a shared palace that describes most of the content, including every other
machine's. Compounding it (RFC-001 §7.2): feeders now stage *inside* the
palace root, so a scoped sync can delete the very drawers it just filed.
`mempalace_delete_by_source` is exact-match rather than existence-based, but
its blast radius is now the whole fleet's palace — leave it on its default
`dry_run=true` and confirm the match count before committing.
- **A timeout is not a failure.** The palace is single-writer, and one large
mine can block every client for minutes, so a write or mine that exceeds the
client's deadline has usually *completed* server-side. Verify with
`mempalace_get_drawer` or `mempalace_search` before retrying — a blind retry
files a duplicate. `[mempalace ext] feed (tick) failed: mine timed out after
30000ms` is the common benign instance: the transcript is already in the
server's inbox and the mine is idempotent, so nothing is lost either way.
- **The `mempalace` CLI is not remote-aware.** It always opens a palace on
local disk, so `mempalace search` can return older and different results than
the MCP tools while both look correct. Use the MCP tools for the central
palace; the CLI only for a local one.
@@ -50,4 +50,4 @@ also carries a copy, but it is a downstream duplicate and can lag), and
`mempalace` from `skillset`. Copying `pi-extensions` from `skillset` would
regress the snapshot to whatever that repo last mirrored.
Snapshot provenance at last refresh: skillset `63f3bf5`, pi-extensions pkg `e73cb9f`.
Snapshot provenance at last refresh: skillset `936fed8`, pi-extensions pkg `e73cb9f`.
@@ -275,18 +275,29 @@ Wings are top-level categories, typically one per project or domain:
- Named after the project directory (e.g., `cli_utils`, `opencode_devbox`)
- Agent diaries live in `wing_<agent_name>` (e.g., `wing_orchestrator`, `wing_pi`)
#### Multi-harness palace
#### Shared palace: multiple harnesses, and possibly multiple machines
A single palace can be fed by multiple coding-agent harnesses. On this machine the palace is shared between **opencode** and **pi** (Mario Zechner's pi-coding-agent). Implications:
A single palace can be fed by multiple coding-agent harnesses, and — when
`MEMPALACE_REMOTE_URL` points at a central palace — by multiple *machines*. On
this machine the palace is shared between **opencode** and **pi** (Mario
Zechner's pi-coding-agent). Implications:
- **`wing_conversations` mixes sources.** Both harnesses' session feeders write into the same wing. To tell them apart, look at the `source_file` metadata on each drawer:
- `pi_<uuid>.jsonl` → pi session
- `<slug>_ses_<id>.jsonl` → opencode session
- The first chunk of each session also carries a `| source: opencode` or `| source: pi` marker in the synthetic header line.
- **Other wings may belong to other harnesses.** For example `wing_pi` is pi's diary, not opencode's. Don't assume every diary entry was written by you — check `agent_name` on the entry.
- **Session feeders run on different schedules.** Pi sessions are fed Tue 03:00, opencode sessions Mon 03:00. 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.
When the palace is **central** (shared across machines), five more 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.
- **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.
- **`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.
- **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 are aspects within a wing:
@@ -324,3 +335,4 @@ Entity-relationship triples with temporal validity. Query with `mempalace_kg_que
- **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 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.
+8
View File
@@ -368,6 +368,14 @@ exec_test "settings.json bootstrapped" 'test -f $HOME/.pi/agent/sett
exec_test "pi-devbox-environment skill linked" 'test -L $HOME/.agents/skills/pi-devbox-environment && test -f $HOME/.agents/skills/pi-devbox-environment/SKILL.md && echo ok'
exec_test "pi-extensions skill linked (fallback)" 'test -L $HOME/.agents/skills/pi-extensions && test -f $HOME/.agents/skills/pi-extensions/SKILL.md && echo ok'
exec_test "mempalace skill linked (fallback)" 'test -L $HOME/.agents/skills/mempalace && test -f $HOME/.agents/skills/mempalace/SKILL.md && echo ok'
# The vendored mempalace snapshot is refreshed MANUALLY per release (see
# rootfs/usr/local/share/pi-devbox/skills/VENDORED.md). It silently shadows the
# skillset copy in a devbox container, so a stale snapshot is invisible: assert
# the multi-machine shared-palace guidance is actually present, not just the file.
exec_test "mempalace skill snapshot is current" 'grep -q "Shared palace: multiple harnesses" $HOME/.agents/skills/mempalace/SKILL.md && echo ok'
# mempalace-census gained a /usr/local/bin symlink in v1.8.3; its three siblings
# had one since they were added, so this asserts the set stays complete.
exec_test "mempalace-census on PATH" 'command -v mempalace-census >/dev/null && mempalace-census --help >/dev/null && echo ok'
# pi-fork + pi-observational-memory are registered by entrypoint-user.sh via
# `pi install /opt/<pkg>`, which runs slightly after the keybindings marker.