diff --git a/CHANGELOG.md b/CHANGELOG.md index 1afef25..0179b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Dockerfile.base b/Dockerfile.base index f1c54fb..89980c9 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -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 diff --git a/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md b/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md index 3a65f6b..17d952a 100644 --- a/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md +++ b/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md @@ -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`. 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 660ef46..e97e6eb 100644 --- a/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md +++ b/rootfs/usr/local/share/pi-devbox/skills/mempalace/SKILL.md @@ -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_` (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_.jsonl` → pi session - `_ses_.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//pi_.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_.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. diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 0c864e9..fab4522 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -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/`, which runs slightly after the keybindings marker.