Parity with opencode-devbox: PR #1735 (the diary_write root-anyOf fix) was
closed UNMERGED on 2026-06-11, so the old "remove once PR #1735 ships" TODO
pointed at a dead PR. Issue #1728 is still open; PR #1717 is the current live
candidate; mempalace PyPI latest is still 3.4.0 (== our pin), so the
workaround stays.
- Dockerfile.base: rewrite the upstream-tracking comment + TODO (#1735 dead,
watch #1717, removal trigger = a PyPI release > 3.4.0 stripping root anyOf).
- CHANGELOG: Unreleased Docs entry.
Docs-only; no behavior change.
mempalace-toolkit is the only companion cloned in Dockerfile.base (all
others live in Dockerfile.variant), so it bypassed the resolve-versions ->
build-arg plumbing and its ref stayed a literal `main`. Because the base
only rebuilds on a content hash of Dockerfile.base + rootfs/* + entrypoints,
a toolkit-only fix would silently fail to land unless Dockerfile.base itself
changed (as it incidentally did in v1.1.1).
Changes:
- resolve-versions: new mempalace_toolkit_ref output (gitea commits API,
mirrors pi-toolkit resolution; jq '.[0].sha // "main"' fallback).
- base-decide: needs resolve-versions; fold the resolved SHA into the
base-tag hash so a moved toolkit forces a base rebuild automatically.
- build-base: needs resolve-versions; pass --build-arg MEMPALACE_TOOLKIT_REF.
- Dockerfile.base: switch clone from `git clone --branch` to a SHA-capable
`git fetch <ref> + checkout FETCH_HEAD` (the --branch <SHA> footgun
already fixed in Dockerfile.variant, run 374).
base_tag now reflects a live gitea lookup; on API blip it falls back to
`main`, triggering one extra rebuild, never a missed one.
No new tag — lands on the next v* release or workflow_dispatch.
The per-request timeout + stall-kill landed in mempalace-toolkit's
mempalace.ts pi extension (commit a3b8829), which the base clones at
build via MEMPALACE_TOOLKIT_REF=main. A base rebuild picks it up.
- CHANGELOG: move from 'Known issues' to 'Fixed'; document the env knobs
(MEMPALACE_MCP_TIMEOUT_MS / MEMPALACE_MCP_INIT_TIMEOUT_MS) and why the
standalone stdio-watchdog shim was dropped.
- Dockerfile.base: replace the TODO with a note pointing at the fix.
Symptom: pi TUI blocks on a mempalace tool call, ESC does not abort.
Initial WAL-contention hypothesis ruled out (no other writer running).
Likely cause: virtiofs cold open of chroma.sqlite3 stalls the JSON-RPC
initialize handshake; pi has no per-call MCP timeout.
Recovery today: docker exec <ctr> pkill -9 -f mempalace-mcp, restart pi.
Planned fix (deferred until after opencode-devbox pi removal): stdio
watchdog shim with per-REQUEST timeout. A naive process-lifetime
timeout wrapper is wrong because mempalace-mcp is long-lived.
Sharing the palace across harnesses remains the goal.
pi-studio renders Mermaid natively but has no DOT renderer. Its markdown
preview displays local PNG/JPG/GIF/WEBP images, so dot-watch closes the
loop for Graphviz: edit .dot -> auto-render <name>.png -> Studio
refresh-from-disk shows the update. Uses mtime polling (no inotify dep).
- rootfs/usr/local/bin/dot-watch: the helper (executable)
- Dockerfile.base: COPY + chmod, following the studio-expose pattern
- README.md: 'Graphviz diagrams in Studio' subsection
- CHANGELOG.md: Unreleased entry
graphviz was already in the base image; no new package.
pi-studio binds the container's 127.0.0.1, which a published Docker port
can't reach. Add a robust, portable bridge rather than a doc-only one-liner:
- Dockerfile.base: add socat (~1 MB, generally useful TCP relay).
- rootfs/usr/local/bin/studio-expose: socat TCP relay listening on the
container's egress IPv4 (not 0.0.0.0 — that would EADDRINUSE against
Studio's loopback listener) forwarding to 127.0.0.1:PORT on the SAME
port, so Studio's printed token URL works verbatim. Robust egress-IP
detection (hostname -I, loopback-filtered; ip route get fallback),
--help, port validation, foreground.
- entrypoint-user.sh: opt-in STUDIO_EXPOSE=1 auto-starts the bridge in the
background (studio variant only). Default OFF — Studio stays loopback-only
(its secure default) unless explicitly opted in.
- README: 'Using pi-studio' now documents host-networking (A) and the
studio-expose/STUDIO_EXPOSE bridge (B) with a security note; ssh -L for
remote, mosh caveat retained.
- smoke-test: assert socat + studio-expose present (base-level).
- CHANGELOG/AGENTS updated.
No tag — stopping for review.
Anthropic's tools API rejects top-level anyOf/oneOf/allOf in
input_schema. Mempalace 3.3.x/3.4.0 advertise diary_write with
`anyOf: [{required:[entry]}, {required:[content]}]`, breaking pi
on first prompt with:
tools.<n>.custom.input_schema: input_schema does not support
oneOf, allOf, or anyOf at the top level
Patch the installed mcp_server.py after `uv tool install` to drop
the anyOf block and require ["agent_name", "entry"] instead. The
handler still accepts `content` server-side as a kwarg alias, so
callers using either name keep working.
The workaround is idempotent and self-deactivating: once upstream
ships the fix the regex no longer matches and the RUN is a silent
no-op.
Also pin mempalace to 3.4.0 via a new MEMPALACE_VERSION build arg
so future bumps are a deliberate, reviewable diff rather than an
implicit pull of latest (an implicit upgrade is what swept the
broken schema in unannounced).
Refs MemPalace/mempalace#1728, MemPalace/mempalace#1735
Self-contained build chain — own Dockerfile.base + Dockerfile.variant
+ entrypoint scripts + rootfs + CI pipeline. Previously v0.79.0 and
earlier were thin re-brands of opencode-devbox's pi-only variant
(joakimp/pi-devbox:base-pi-only built by opencode-devbox CI).
Architectural changes:
- Replace 5-line Dockerfile shim with full base+variant pair.
- Adapt CI workflow from opencode-devbox/docker-publish-split.yml,
simplified to a single variant. Includes content-addressed base hash,
PI_VERSION concrete-resolution to defeat registry-buildcache footgun,
crane-based base-latest promotion, and the c6f9d11 smoke-test gate.
- pi-devbox releases no longer require rebuilding opencode-devbox first.
Base image additions:
- pandoc, graphviz, imagemagick, yq — broadly useful, ~260 MB total.
- tldr (tealdeer) — Rust port replaces Node tldr global, saves 135 MB.
- /etc/tmux.conf with base-index 0 + pane-base-index 0 — required for
the planned :latest-studio variant; pi-studio hard-codes :0.0 target.
Smoke test:
- New checks for pandoc, graphviz, imagemagick, yq, tldr, tmux config,
/tmp/sshcm directory.
- Image-size measurement now sums docker history layers (the prior
inspect --format='{{.Size}}' returned only the variant-unique layer
with the new base/variant split, understating by 2+ GB).
- Threshold 2850 → 3500 MB to absorb base additions + arch margin.
Image size:
- Local arm64 build: 3.20 GB. ~390 MB up from prior pi-only equivalent.
- Will tighten threshold once amd64 actuals settle in CI.
Pre-1.0 history preserved at tag pre-v1.0.0-decouple-backup.
Future work:
- v1.1.0: :latest-studio variant (adds pi-studio).
- v1.2.0: :latest-studio-tex variant (adds texlive-xetex for PDF).
- opencode-devbox v2.0.0 will retire INSTALL_PI / pi-only paths.