Compare commits

...

4 Commits

Author SHA1 Message Date
joakimp 16fddebd43 docs(v1.9.4): the client/server skew is narrower than the release commit said
Lint / hadolint (push) Successful in 8s
Lint / skill-floor (push) Successful in 11s
Lint / actionlint (push) Successful in 17s
Lint / doc-drift (push) Successful in 17s
Publish Docker Image / lint-gate (push) Successful in 21s
Publish Docker Image / resolve-versions (push) Successful in 15s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 1h4m42s
Publish Docker Image / smoke (push) Successful in 6m4s
Publish Docker Image / smoke-studio (push) Successful in 9m18s
Publish Docker Image / build-variant (push) Successful in 19m20s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / build-variant-studio (push) Successful in 24m15s
e3b38cd named "the feeder against the 3.9.0 hub" as the one path to exercise
before tagging, on the reasoning that 3.10.0's CLI writes now follow the daemon
write-routing policy. That was a reading of the mempalace changelog, not a
measurement of this image's feeder. Measured now:

  mempalace-pi-session in remote mode (MEMPALACE_REMOTE_URL set, which is how
  every fleet devbox runs) stages transcripts locally in python, rsyncs them to
  the hub host, and calls the hub's own `mempalace_mine` MCP tool over HTTP
  (run_remote_mine). The local `mempalace` CLI is required only in local mode
  (line 475) and invoked only on the local branch (line 1029). With a PATH shim
  logging every `mempalace` invocation, a 47-session `--dry-run` from this
  container logged ZERO calls; the shim's positive control logged one. The pi
  extension likewise speaks HTTP to the hub and spawns no local mempalace-mcp.

So the 3.10.0 CLI never runs against the 3.9.0 hub from this image. In remote
mode the client pin touches first-run `mempalace init` and the on-disk layout,
nothing else -- which is exactly what the ENV MEMPALACE_CONFIG_DIR change is
for. Both the Dockerfile.base comment and the CHANGELOG paragraph now say so,
and the "Still open" item becomes the thing that is actually unmeasured: a
first-boot acceptance of the built image from an empty ~/.mempalace volume.

Gates re-run on this tree: check-doc-drift.sh rc=0, lint-shell.sh rc=0,
hadolint 2.15.1 rc=0. lint.yml for e3b38cd: run 693, completed, success.
2026-09-22 14:51:22 +02:00
joakimp e3b38cdb0b release(v1.9.4): mempalace 3.10.0 with a pinned palace root, pi-atelier v0.10.3, pi held at 0.85.1
Lint / skill-floor (push) Successful in 8s
Lint / hadolint (push) Successful in 10s
Lint / doc-drift (push) Successful in 13s
Lint / actionlint (push) Successful in 22s
Three pins move or are deliberately held; the CHANGELOG's Unreleased section
becomes the v1.9.4 entry in this same commit, because since ee6cb9e the tag
build runs check-doc-drift.sh check 9 and every would-bake value must be named
above the last published heading.

mempalace 3.9.0 -> 3.10.0 (Dockerfile.base). Deferred at v1.9.3 for two
"Upgrade notes" items; both re-measured against the 3.10.0 wheel:

  - New installs resolve ~/.config/mempalace. The order is $MEMPALACE_CONFIG_DIR,
    then ~/.mempalace IF it holds config.json / people_map.json /
    palace/chroma.sqlite3, then XDG. An EMPTY ~/.mempalace fails that test, and
    an empty ~/.mempalace is what a freshly mounted devbox-palace volume (or
    entrypoint.sh's mkdir on a volume-less container) looks like at first boot.
    Measured with a fresh $HOME and `uvx mempalace==3.10.0 init`: config.json
    landed in ~/.config/mempalace, ~/.mempalace stayed empty, and
    entrypoint-user.sh's `[ ! -d ~/.mempalace/palace ]` would fire every start.
    Same run with MEMPALACE_CONFIG_DIR set: everything in ~/.mempalace.
    => ENV MEMPALACE_CONFIG_DIR=/home/${USER_NAME}/.mempalace, placed in the
    non-root-user section where USER_NAME is in scope, and entrypoint-user.sh
    reads PALACE_DIR from the same variable (old path as fallback). Existing
    volumes were safe either way; this is for first boots. MEMPALACE_PALACE_PATH
    is still honoured (config.py:927), so smoke-test.sh's stage test holds.
  - event_list defaults newest-first without a cursor. Server-side: the
    extension talks to synlig over MEMPALACE_REMOTE_URL, so it lands when the
    hub upgrades. mempalace-toolkit 2167a1b (floating main) makes every
    cursor-less call say order:"desc" so the mailbox reads the same window on
    either server version.

Corrected a stale sequencing comment while there: synlig serves the palace as
a `uv tool` under systemd (mempalace-serve.service; measured 2026-09-22:
mempalace 3.9.0, python 3.12.13, chromadb 1.5.9, no palace container), not via
docker-compose.mempalace.yml as the comment had said for two releases.

pi-atelier v0.10.1 -> v0.10.3 (Dockerfile.variant). Fixes only (both tags
released 2026-09-22); package.json at v0.10.3 still has zero runtime deps and no
build script, peerDeps still >=0.84.0. Annotated tags: the CHANGELOG names the
peeled SHA ed3837b, which is what resolve-versions bakes and check 9 compares.

pi HELD at 0.85.1 while 0.86.1 / 0.87.0 exist. 0.87.0 removed the
shouldStopAfterTurn agent option; pi-observational-memory 3.1.4 still uses it
and AgentContext.systemPrompt in observer/reflector/dropper (measured: 1 hit
per worker in src/agents on both the baked 3.1.3 tree and upstream master;
finishTurn 0). peerDeps are `*`, so it would break at runtime, not install.
Upstream #82, fix PR #83 open and unmerged. Bump pi and pi-obsmem together
once #83 ships. The other extensions are clean against the 0.86/0.87 lists.

Verified locally the way CI does: check-doc-drift.sh rc=0 with check 9
reporting all four moved components as named above the v1.9.3 heading;
lint-shell.sh rc=0; hadolint 2.15.1 (CI's pin, with .hadolint.yaml) rc=0 on
both Dockerfiles; PyPI 3.10.0 present, not yanked. Two claims caught by
re-measurement before commit and corrected in the text: a pi issue number
that does not exist on the 0.87.0 changelog line, and "8 shouldStopAfterTurn
hits" that was 3 (one per worker) in src/.

Still open before the tag: run the feeder from the built image against the
3.9.0 hub (dry-run first); recreate acceptance must show ~/.mempalace/palace
still passing and no ~/.config/mempalace appearing. After acceptance: upgrade
synlig's tool to 3.10.0.
2026-09-22 14:26:10 +02:00
joakimp ee6cb9e62a ci(release): a tag must not publish a component no CHANGELOG entry names
Lint / skill-floor (push) Successful in 9s
Lint / hadolint (push) Successful in 12s
Lint / actionlint (push) Successful in 18s
Lint / doc-drift (push) Successful in 16s
lint-gate already existed to enforce "do not RELEASE a tree whose lint failed",
because lint.yml does not run on tag pushes. scripts/check-doc-drift.sh had the
same gap and it was never extended to cover it: check 9 ran only in lint.yml, so
no tag build has ever evaluated it. Add it to lint-gate, which resolve-versions
already needs, so it fails in ~8 s ahead of the 46-minute base build.

For this check the gap is strictly worse than it is for shellcheck. Shellcheck
judges the tree, so green on main is still green at the tag -- the bytes did not
move. Check 9 judges the tree against upstream NOW, and the floating refs it
watches move with no commit here at all, so a green reading on main carries no
information about tag time. v1.9.3 is the worked example: pi-observational-memory
moved cba0334 -> e7d77dc the day AFTER the tag, nothing went red, and it
surfaced only because someone ran the gate by hand.

Measured, not assumed:
- Same file lint.yml calls (one reference in each workflow), not a second copy.
- Works on a CI-shaped checkout: cloned --depth 1 --no-tags (0 tags, 1 commit),
  rc=0. It needs no local tags because `last` comes from the Hub tags API, not
  `git tag`, so the plain actions/checkout@v4 above is sufficient.
- Has teeth: deleting the Unreleased section from that clone gives rc=1 and
  names the component; restoring it gives rc=0.
- ~8 s (7.8-8.3 s measured), vs ~1 s for lint-shell.sh.

Residual, accepted: the gate resolves the refs seconds before resolve-versions
resolves them again, so an upstream push inside that window still slips past.
Check 9 on the next release names it then.
2026-09-21 23:23:29 +02:00
joakimp 0d324f1855 changelog: name pi-obsmem 3.1.4 (e7d77dc) as the next rebuild's implicit adoption
Lint / skill-floor (push) Successful in 13s
Lint / hadolint (push) Successful in 15s
Lint / doc-drift (push) Successful in 14s
Lint / actionlint (push) Successful in 23s
check-doc-drift.sh was rc=1: pi-observational-memory moved cba0334 (3.1.3) ->
e7d77dc (3.1.4) upstream on 2026-09-20, the day after the v1.9.3 tag, and
nothing named it above the v1.9.3 heading.

No code change is needed or wanted here. PI_OBSMEM_REF defaults to master in
Dockerfile.variant and CI's resolve-versions turns that into a SHA at build
time, so the next rebuild adopts this whether or not anyone acts -- which is
precisely why it has to be written down. There is no pin in this repo to bump,
so the CHANGELOG is the only record a reader of the next tag would have.

v1.9.3 itself has no defect: its manifest, image labels and baked
/opt/pi-observational-memory clone all agree on cba0334. The drift is
post-tag, so a green drift reading taken on 2026-09-19 was correct when taken.

Measured, not assumed: the range cba0334...e7d77dc is 4 commits whose only
substance is one line in src/agents/worker-stream.ts (preserve registry
receiver when resolving stream, PR #78 / issue #77) plus a 19-line regression
test; the rest is the version bump and release merge. peerDependencies are
unchanged (all four @earendil-works/* peers still *) and there is no engines
block, so no pi or node floor to clear.

Gate now rc=0: "OK pi-obsmem cba0334 -> e7d77dc since v1.9.3, named above the
v1.9.3 heading".
2026-09-21 23:15:11 +02:00
6 changed files with 318 additions and 16 deletions
+26
View File
@@ -174,6 +174,29 @@ jobs:
# #
# ~40 s, ahead of everything expensive, and it runs scripts/lint-shell.sh -- # ~40 s, ahead of everything expensive, and it runs scripts/lint-shell.sh --
# the same file lint.yml calls, not a second copy that drifts. # the same file lint.yml calls, not a second copy that drifts.
#
# scripts/check-doc-drift.sh is here for the same reason and closes the same
# gap -- and for it the gap is strictly worse. Shellcheck judges the TREE:
# green on main is still green at the tag, because the bytes did not move.
# Check 9 judges the tree against UPSTREAM NOW, and the floating refs it
# watches (PI_OBSMEM_REF=master and friends, which resolve-versions below turns
# into SHAs) move with no commit in this repo at all -- so a green reading on
# main carries no information about tag time, and that window is exactly where
# releases live. Worked example: pi-observational-memory moved cba0334 ->
# e7d77dc the day AFTER v1.9.3 was tagged. Nothing went red; it surfaced only
# because someone ran the gate by hand. Without this step a tag can publish a
# component that no CHANGELOG entry names, and the floating ref means no other
# file in the repo would record it either.
#
# Adds ~8 s. No token and no built image: it takes the last published vX.Y.Z
# from the Hub tags API, that release's baked labels from the anonymous
# registry API, and `git ls-remote`s each upstream -- so a plain checkout is
# enough, with no tags or history to fetch. Offline it SKIPs loudly and
# counted rather than passing, so an outage degrades it to a visible skip
# instead of a false green. Residual, accepted: it resolves the refs seconds
# before resolve-versions resolves them again, so an upstream push landing
# inside that window still slips through -- and check 9 on the NEXT release
# would then name it.
lint-gate: lint-gate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
@@ -189,6 +212,9 @@ jobs:
- name: "Shellcheck + syntax-check repository scripts (severity: error)" - name: "Shellcheck + syntax-check repository scripts (severity: error)"
run: bash scripts/lint-shell.sh run: bash scripts/lint-shell.sh
- name: Components the next build would bake differently are named in the CHANGELOG
run: bash scripts/check-doc-drift.sh
resolve-versions: resolve-versions:
# Gated: a defective tree must not reach a 46-minute base build. # Gated: a defective tree must not reach a 46-minute base build.
needs: [lint-gate] needs: [lint-gate]
+171
View File
@@ -11,6 +11,177 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
--- ---
## v1.9.4 — 2026-09-22
### Dependency audit (2026-09-22)
Every component checked against upstream by direct command, not assumed. "Baked"
is v1.9.3's published labels or, for the floating `*_VERSION=latest` tools, the
binaries in a running v1.9.3 container. The 13 GitHub-`latest` tools were
resolved through the same `/releases/latest` redirect the Dockerfile follows.
| Component | Baked in v1.9.3 | Upstream now | Action |
|---|---|---|---|
| **pi** | `0.85.1` (pinned) | **`0.87.0`** (0.86.0, 0.86.1, 0.87.0 since) | **held — blocked by pi-observational-memory, see below** |
| **pi-atelier** | `v0.10.1` (`734258b`) | **`v0.10.3`** (`ed3837b`, released 2026-09-22) | **bumped** |
| **mempalace** | `3.9.0` (pinned) | **`3.10.0`** (changelog dated 2026-09-15) | **bumped, with one adaptation** |
| **mempalace-toolkit** | `817b3a8` | **`2167a1b`** | floating `main`; the commit is this release's own (below) |
| **pi-observational-memory** | `cba0334` (3.1.3) | **`e7d77dc`** (3.1.4) | adopted implicitly via `master` (below) |
| pi-toolkit | `9c87ee8` | `9c87ee8` | none |
| pi-extensions | `25c1265` | `25c1265` | none |
| pi-fork | `e69725c` | `e69725c` | none |
| pi-studio (studio variant) | `e04fc7a` | `e04fc7a` highest semver tag | none |
| skillset (mempalace fallback snapshot) | `e9e45f7` | skillset at `1c5f960`; `check-doc-drift.sh` rc=0 (snapshot still byte-identical) | none |
| gitea-mcp, agent-browser, node | `1.7.0`, `0.38.1`, `v24.21.0` | identical | none |
| 13 floating `*_VERSION=latest` tools | — | all 13 identical to the running v1.9.3 binaries | none |
### mempalace 3.9.0 → 3.10.0, and why one `ENV` line comes with it
v1.9.3 deferred 3.10.0 for two "Upgrade notes" items. Both were re-measured
against the 3.10.0 wheel rather than the changelog; one needed an adaptation, the
other turned out to be the other side's problem.
**"New installs keep config and palace under `~/.config/mempalace`."** The
resolution order (`config.py`, `_default_config_dir`) is `$MEMPALACE_CONFIG_DIR`,
then `~/.mempalace` *if* it holds `config.json`, `people_map.json` or
`palace/chroma.sqlite3`, then XDG. An **empty** `~/.mempalace` fails that test —
and an empty `~/.mempalace` is exactly what a freshly mounted `devbox-palace`
volume looks like at first boot (and what `entrypoint.sh`'s `mkdir` leaves on a
volume-less container). Measured with a fresh `$HOME` and `uvx mempalace==3.10.0`:
`mempalace init` wrote `config.json` to `~/.config/mempalace`, `~/.mempalace`
stayed empty, and `entrypoint-user.sh`'s first-run test `[ ! -d ~/.mempalace/palace ]`
would have stayed true on every start. Same run with `MEMPALACE_CONFIG_DIR` set:
everything landed in `~/.mempalace`.
So `Dockerfile.base` now sets `ENV MEMPALACE_CONFIG_DIR=/home/${USER_NAME}/.mempalace`
(in the non-root-user section, where `USER_NAME` is in scope), and
`entrypoint-user.sh` reads `PALACE_DIR` from the same variable with the old path
as fallback — the first-run test and mempalace's own resolution can no longer
disagree. Existing volumes were safe either way (`config.json` is a legacy
marker; this container's volume holds one); the `ENV` is for first boots.
`palace_path` still defaults to `<config_dir>/palace` and `MEMPALACE_PALACE_PATH`
is still honoured (`config.py:927`), so `smoke-test.sh`'s stage-path test keeps
its meaning, and `recreate-sanity-check.sh`'s hard-coded `~/.mempalace/palace/chroma.sqlite3`
stays correct.
**"MCP event listing returns the newest events first when no cursor is given."**
This is **server-side**. The pi extension talks to the fleet hub over
`MEMPALACE_REMOTE_URL`, so `mempalace_event_list`'s default flips when *synlig*
upgrades, not when this image does. `mempalace-toolkit` `2167a1b` (this release)
makes every cursor-less `event_list` call in the extension say `order: "desc"`
explicitly — three of five did not — so the mailbox's `deriveOwed` and
`deriveClosed` read the same newest-N window against either server version. Its
selection was already order-independent (`isStrictlyAfter`); what the fix changes
is *which* events are in the window: on a 3.9.0 hub the cursor-less calls were
returning the **oldest** N, the latent truncation `deriveOwed` had already fixed
for two of its calls in toolkit `e2b060a` (2026-09-09).
Also in the upgrade notes, neither reaching this image: `mempalace rules` dropped
`--agent` (zero callers in pi-devbox, mempalace-toolkit, skillset, myconfigs);
`get_collection()` refuses unknown collection names (library callers only). MCP
tool-schema review — the regression class this pin exists for: no tool removed or
renamed; additive fields on search results (`filed_at` / `content_date`
provenance), `limit`/`offset` on `kg_timeline`, `last_modified` on drawers.
**Server state, measured over ssh on 2026-09-22:** synlig serves mempalace
**3.9.0** as a `uv tool` under `systemd` (`mempalace-serve.service`, python
3.12.13, chromadb 1.5.9) — *not* via `docker-compose.mempalace.yml`, which the
`Dockerfile.base` sequencing comment had claimed for two releases (corrected in
this commit). Client and server are level today; this image reintroduces skew
until synlig runs `uv tool upgrade mempalace` and restarts the unit. That is the
step that lights up the server-side changes above. The skew meanwhile is narrower
than the release commit's message claimed: the pi extension speaks HTTP to the hub
(no local `mempalace-mcp` is spawned), and the feeder in remote mode stages
locally in python, rsyncs, and calls the hub's own `mempalace_mine` tool.
Measured with a PATH shim in front of `mempalace`: a 47-session
`mempalace-pi-session --dry-run` from this container made **zero** local CLI calls
(the shim's positive control logged one). So 3.10.0's new CLI write-routing
policy never runs against the hub from this image; in remote mode the client pin
touches first-run `mempalace init` and the on-disk layout, nothing else.
### pi-atelier v0.10.1 → v0.10.3 (`734258b` → `ed3837b`)
Fixes only per the release notes for v0.10.2 and v0.10.3 (both 2026-09-22):
sidebar text/borders preserved beside inline images (#53), transcript images
hidden while capturing overlays are open, Workspace Pulse skips redundant
HEAD/diff when nothing tracked changed (#61), sidebar height from row counts
(#59), git/usage scans suspended while disabled, Display Revert + Undo ordering.
Checked before bumping: `package.json` at v0.10.3 still declares zero runtime
dependencies and no build script (the no-`npm install` reasoning in
`Dockerfile.variant` holds), `peerDependencies` still `>=0.84.0` (spans the
pinned pi 0.85.1), 13 commits all under `src/ tests/ docs/ scripts/` plus
metadata, no entry-point move. Both tags are annotated: the SHA above is the
peeled commit, which is what `resolve-versions` bakes and what check 9 compares.
### pi held at 0.85.1 (0.86.1 and 0.87.0 exist)
Not an oversight. pi 0.87.0 *"Removed the inherited `shouldStopAfterTurn` agent
option. Use `finishTurn` and return `{ action: "end" }` instead"*, and 0.86.0
moved provider stream inputs to `TranscriptContext` with the system prompt read
from `context.messages`. pi-observational-memory 3.1.4 still uses both the
removed option and `AgentContext.systemPrompt` in its observer, reflector and
dropper workers — measured in `src/agents/*/agent.ts` on both the baked 3.1.3
tree and upstream `master`: `shouldStopAfterTurn` once per worker, `finishTurn`
zero. Its `peerDependencies` are `*`, so nothing at install time would refuse;
under 0.87 the workers would lose their turn caps and their specialised prompts
at runtime. Upstream tracks it as pi-observational-memory
[#82](https://github.com/elpapi42/pi-observational-memory/issues/82) with fix PR
[#83](https://github.com/elpapi42/pi-observational-memory/pull/83) — opened
2026-09-21, mergeable, **not merged** at this writing. Bump pi and pi-obsmem
together once #83 ships in a release.
The other extensions were checked against the 0.86.0 and 0.87.0 breaking lists
and are clean: `ssh-controlmaster`'s `user_bash` handler already returns
`undefined | { operations }` (0.86.0's fail-closed contract), and its four
`registerTool` calls spread the built-in tools so they carry parameter schemas
(#9300). 0.86.1 as an intermediate was not tested — not worth the pty matrix for
a stop that #83 makes moot.
### Still open
- **First-boot acceptance on the built image** — the palace-path adaptation was
measured with the wheel, not the image. Start a throwaway container from the
published v1.9.4 with an *empty* `~/.mempalace` volume and confirm the
entrypoint's `mempalace init` lands `config.json` there, that
`~/.config/mempalace` does not appear, and that `MEMPALACE_CONFIG_DIR` is in
the container environment. On a real device the recreate checklist's
`recreate-sanity-check.sh` should keep passing `~/.mempalace/palace/chroma.sqlite3`.
- **synlig upgrade to 3.10.0** after v1.9.4 is accepted on one device:
`uv tool upgrade mempalace`, restart `mempalace-serve.service`, verify one
search and one `event_list`.
- **pi 0.87.x + pi-observational-memory ≥ 3.1.5** together, when #83 has shipped.
### pi-observational-memory 3.1.3 → 3.1.4 (`cba0334` → `e7d77dc`)
No change in this repo. `PI_OBSMEM_REF` defaults to `master`
(`Dockerfile.variant`) and CI resolves it to a SHA at build time
(`docker-publish.yml`, `resolve-versions`), so the next rebuild bakes this
whether or not anyone acts — which is exactly why it is written down here. The
floating ref means this CHANGELOG is the only place a reader of the next tag can
learn that the component moved.
v1.9.3 shipped `cba0334` (3.1.3) and was internally consistent: its manifest,
its image labels and the baked `/opt/pi-observational-memory` clone all agree on
that SHA. Upstream `master` then moved to `e7d77dc` (3.1.4) on **2026-09-20**,
the day *after* the v1.9.3 tag — so the drift is real but v1.9.3 has no defect,
and a green `check-doc-drift.sh` reading taken before that date was correct when
it was taken.
| | |
|---|---|
| Upstream range | [`cba0334...e7d77dc`](https://github.com/elpapi42/pi-observational-memory/compare/cba03347a60af8b8afbb35677ade4d6bb05d4c5a...e7d77dc9a8305acb8054124e47662b3c766c2321) — 4 commits |
| Substance | one line in `src/agents/worker-stream.ts` — *preserve registry receiver when resolving stream* (PR #78, upstream issue #77) — plus a 19-line regression test |
| Remainder | `chore(release): prepare 3.1.4` (version bump) and the release merge (PR #79) |
| `peerDependencies` | unchanged — all four `@earendil-works/*` peers still `*`, so no pi floor to clear |
| `engines` | absent in both — no node floor |
Because the ref floats, the SHA this repo actually bakes is whatever `master`
resolves to **at build time**. If upstream moves again before the next tag, the
value above is stale and `check-doc-drift.sh` will say so; re-run it immediately
before tagging rather than trusting this row.
---
## v1.9.3 — 2026-09-19 ## v1.9.3 — 2026-09-19
### Dependency audit (2026-09-19) ### Dependency audit (2026-09-19)
+70 -10
View File
@@ -14,7 +14,7 @@
# content-addressed over this file, so any byte change invalidates the # content-addressed over this file, so any byte change invalidates the
# cache. Recommended cadence: once per release for security updates. # cache. Recommended cadence: once per release for security updates.
# #
# BASE_REBUILD_DATE: 2026-09-19 (v1.9.3 — mempalace-version label, mempalace-toolkit 817b3a8 mine deadline, pi-extensions skill floor 25c1265; the marker had been stale since 2026-07-13 through the v1.9.0 Node 24 and v1.9.2 npm-residue rebuilds, updated now because the base is rebuilding anyway) # BASE_REBUILD_DATE: 2026-09-22 (v1.9.4 — mempalace 3.9.0 -> 3.10.0 with ENV MEMPALACE_CONFIG_DIR pinning the layout, mempalace-toolkit 2167a1b explicit event_list order; previous marker 2026-09-19 / v1.9.3)
# #
# ── Lineage note ───────────────────────────────────────────────────── # ── Lineage note ─────────────────────────────────────────────────────
# Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2). # Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2).
@@ -532,14 +532,22 @@ ARG INSTALL_MEMPALACE=true
# the part that should stay manual. # the part that should stay manual.
# #
# Deployment sequencing note for whoever ships this bump: synlig (the shared # Deployment sequencing note for whoever ships this bump: synlig (the shared
# central palace host) serves mempalace 3.8.0 SERVER-SIDE via # central palace host) serves mempalace SERVER-SIDE as a `uv tool` install run
# docker-compose.mempalace.yml, which reuses this same devbox image. (Measured # by the systemd unit `mempalace-serve.service` (`python -m mempalace.mcp_server
# 2026-09-06 over ssh: synlig's UV_TOOL_DIR mempalace entry last changed # --transport http`), NOT via docker-compose.mempalace.yml — that compose file
# 2026-08-25 15:33 — this comment previously said 3.7.1, which was stale.) # exists in this repo but is not what runs there. (Measured 2026-09-22 over
# Bumping this ARG changes only the CLIENT version baked into pi-devbox # ssh: `uv tool list` -> mempalace v3.9.0, python 3.12.13, chromadb 1.5.9;
# images: it introduces client/server skew until synlig's compose stack is # `docker ps` matched no palace container. This comment previously said the
# separately rebuilt/redeployed with the new pin. Not something to code around # compose stack served 3.8.0, which was stale on both counts.) Bumping this ARG
# here — just sequence the redeploy. # changes only the CLIENT version baked into pi-devbox images: it introduces
# client/server skew until synlig's tool is upgraded (`uv tool upgrade
# mempalace` + restart the unit). Not something to code around here — just
# sequence the upgrade. And note which side OWNS what: MCP tool semantics
# (event_list ordering, kg_timeline pagination, search result fields) come
# from the SERVER the extension talks to over MEMPALACE_REMOTE_URL, so they
# change when synlig upgrades; only the local CLI (`mempalace init` at first
# run, the mempalace-pi-session feeder) and the on-disk layout under
# ~/.mempalace change when THIS pin does.
# #
# v1.8.13: 3.8.0 -> 3.9.0. Audited: no Breaking/Removed changelog headings. # v1.8.13: 3.8.0 -> 3.9.0. Audited: no Breaking/Removed changelog headings.
# Adopted mainly for #2281 (`mempalace_mine` accepts a single conversation # Adopted mainly for #2281 (`mempalace_mine` accepts a single conversation
@@ -551,7 +559,47 @@ ARG INSTALL_MEMPALACE=true
# (release awareness, `task create`/`task launch` MCP tools) are SERVER-side, # (release awareness, `task create`/`task launch` MCP tools) are SERVER-side,
# so they stay dark until synlig is redeployed — a client bump alone cannot # so they stay dark until synlig is redeployed — a client bump alone cannot
# light them up. # light them up.
ARG MEMPALACE_VERSION=3.9.0 #
# v1.9.4: 3.9.0 -> 3.10.0 (PyPI 2026-09-15). Deferred at v1.9.3 for two
# "Upgrade notes" items; both re-measured against the 3.10.0 wheel, one needed
# an adaptation:
# - "New installs keep config and palace under ~/.config/mempalace". The
# resolution order is $MEMPALACE_CONFIG_DIR, then ~/.mempalace IF it holds
# config.json / people_map.json / palace/chroma.sqlite3, then XDG. An
# EMPTY ~/.mempalace does not count — and an empty ~/.mempalace is exactly
# what a freshly mounted devbox-palace volume (or entrypoint.sh's mkdir on
# a volume-less container) looks like at first boot. Measured with a fresh
# $HOME: `mempalace init` wrote to ~/.config/mempalace, outside the
# persisted path, and entrypoint-user.sh's first-run test
# `[ ! -d ~/.mempalace/palace ]` would stay true on every start. With
# MEMPALACE_CONFIG_DIR set, everything landed in ~/.mempalace. Hence the
# ENV MEMPALACE_CONFIG_DIR below (in the non-root-user section, where
# ${USER_NAME} is in scope): first in the resolution order, so the
# heuristic never runs and the image's layout contract no longer depends
# on it. Existing volumes were safe either way (config.json is a legacy
# marker); the ENV is for first boots. palace_path still defaults to
# <config_dir>/palace and MEMPALACE_PALACE_PATH is still honoured (config.py
# :927), so scripts/smoke-test.sh's stage-path test keeps its meaning.
# - "MCP event listing returns the newest events first when no cursor is
# given". SERVER-side (see above), so it lands when synlig upgrades, not
# here. mempalace-toolkit 2167a1b made every cursor-less event_list call
# in the pi extension say `order: "desc"` explicitly, so the mailbox reads
# the same window against either server version.
# Also in the notes, neither reaching this image: `mempalace rules` dropped
# `--agent` (no caller in pi-devbox, mempalace-toolkit, skillset or myconfigs);
# `get_collection()` refuses unknown collection names (library callers only).
# MCP tool-schema review, as always: no tool removed or renamed; additive
# fields on search results (filed_at / content_date provenance), `limit` /
# `offset` on kg_timeline, `last_modified` on drawers. Skew while synlig stays
# on 3.9.0 is narrower than it looks: the pi extension speaks HTTP to the hub
# (no local mempalace-mcp is spawned), and the feeder in remote mode stages
# locally in python, rsyncs, and calls the hub's own `mempalace_mine` tool.
# Measured 2026-09-22 with a PATH shim in front of `mempalace`: a 47-session
# `mempalace-pi-session --dry-run` made ZERO local CLI calls (the shim's
# positive control logged one). So 3.10.0's new CLI write-routing policy never
# runs against the hub from this image; the client pin touches first-run
# `mempalace init` and the on-disk layout, nothing else in remote mode.
ARG MEMPALACE_VERSION=3.10.0
# Recorded as a label HERE, not in Dockerfile.variant, for three reasons: the # Recorded as a label HERE, not in Dockerfile.variant, for three reasons: the
# value lives next to the ARG that defines it (a second copy in the variant # value lives next to the ARG that defines it (a second copy in the variant
# would be one more pin able to drift, which is the class check-doc-drift.sh # would be one more pin able to drift, which is the class check-doc-drift.sh
@@ -832,6 +880,18 @@ print('chromadb embedding model warmed: all-MiniLM-L6-v2')" && \
ENV NPM_CONFIG_PREFIX=/home/${USER_NAME}/.pi/npm-global ENV NPM_CONFIG_PREFIX=/home/${USER_NAME}/.pi/npm-global
ENV PATH="/home/${USER_NAME}/.pi/npm-global/bin:${PATH}" ENV PATH="/home/${USER_NAME}/.pi/npm-global/bin:${PATH}"
# ── MemPalace config/palace root: pin it, do not let a heuristic pick it ──
# mempalace >= 3.10.0 resolves its config dir as $MEMPALACE_CONFIG_DIR, then
# ~/.mempalace ONLY if it already holds a config/palace, else ~/.config/mempalace
# (XDG). An empty ~/.mempalace — a fresh devbox-palace volume, or entrypoint.sh's
# mkdir on a volume-less container — fails that test, so a first boot would put
# the palace outside the persisted path and re-run first-run init forever. This
# ENV is first in the order, so the layout is what entrypoint.sh (mkdir),
# entrypoint-user.sh (first-run test), the feeder's <palace-root>/pi-stage and
# scripts/recreate-sanity-check.sh all already assume. Rationale and the
# measurement live with ARG MEMPALACE_VERSION above; keep the two in step.
ENV MEMPALACE_CONFIG_DIR=/home/${USER_NAME}/.mempalace
# ── Shell defaults (bash history, aliases, readline) ───────────────── # ── Shell defaults (bash history, aliases, readline) ─────────────────
RUN mkdir -p /etc/skel-devbox RUN mkdir -p /etc/skel-devbox
COPY rootfs/home/developer/.bash_aliases /etc/skel-devbox/.bash_aliases COPY rootfs/home/developer/.bash_aliases /etc/skel-devbox/.bash_aliases
+34 -2
View File
@@ -113,6 +113,27 @@ ARG USER_NAME=developer
# signature is ~5s of sustained CPU. Two-sided check: the atelier sidebar # signature is ~5s of sustained CPU. Two-sided check: the atelier sidebar
# painted ACTIVITY+WORKSPACE identically to the 0.84.4 control, so the test # painted ACTIVITY+WORKSPACE identically to the 0.84.4 control, so the test
# could distinguish "loaded" from "silently absent". # could distinguish "loaded" from "silently absent".
#
# v1.9.4: HELD at 0.85.1 while 0.86.1 and 0.87.0 exist upstream. 0.87.0's
# changelog: "Removed the inherited `shouldStopAfterTurn` agent option. Use
# `finishTurn` and return `{ action: "end" }` instead" (no issue number on
# that line); 0.86.0 moved provider stream inputs to `TranscriptContext`, with
# system prompts read from `context.messages`. pi-observational-memory 3.1.4
# (the `master` ref baked below) still uses both the old option and
# `AgentContext.systemPrompt` in its observer, reflector and dropper workers —
# measured 2026-09-22 in src/agents/*/agent.ts, both the baked 3.1.3 tree and
# upstream master 3.1.4: `shouldStopAfterTurn` 1 per worker (3), `finishTurn`
# 0, `systemPrompt` 1 per worker. Its peerDependencies are `*`,
# so nothing at install time would refuse; it would break at runtime (turn
# caps ignored, workers losing their specialised prompts). Upstream tracks it
# as pi-observational-memory #82 with fix PR #83 (opened 2026-09-21, mergeable,
# not merged at this writing). Bump pi and pi-obsmem TOGETHER once #83 has
# shipped in a release. The other extensions were checked against the 0.86.0
# and 0.87.0 breaking lists and are clean: ssh-controlmaster's `user_bash`
# handler already returns `undefined | { operations }` (0.86.0 fail-closed
# contract) and its registerTool calls spread the built-in tools so they
# carry parameter schemas (#9300). 0.86.1 as an intermediate is untested and
# not worth the pty matrix for a stop that #83 will make moot.
ARG PI_VERSION=0.85.1 ARG PI_VERSION=0.85.1
ARG PI_TOOLKIT_REF=main ARG PI_TOOLKIT_REF=main
ARG PI_EXTENSIONS_REF=main ARG PI_EXTENSIONS_REF=main
@@ -170,9 +191,20 @@ ARG PI_ATELIER_REPO=https://github.com/michaelmjhhhh/pi-atelier.git
# old and new pin. Included because it was already exercised: the pty matrix # old and new pin. Included because it was already exercised: the pty matrix
# for PI_VERSION above ran atelier v0.10.1 against pi 0.85.1 and painted the # for PI_VERSION above ran atelier v0.10.1 against pi 0.85.1 and painted the
# sidebar identically to v0.10.0. # sidebar identically to v0.10.0.
ARG PI_ATELIER_REF=v0.10.1 #
# v1.9.4: v0.10.1 -> v0.10.3 (both v0.10.2 and v0.10.3 released 2026-09-22).
# Fixes only per the release notes: sidebar text/borders preserved beside
# inline images (#53), transcript images hidden while capturing overlays are
# open, Workspace Pulse skips redundant HEAD/diff when nothing tracked changed
# (#61), sidebar height from row counts (#59), git/usage scans suspended while
# disabled, Display Revert + Undo ordering. Checked before bumping: package.json
# at v0.10.3 still declares zero runtime dependencies and no build script (so
# the no-`npm install` reasoning above holds) and peerDependencies are still
# pi >=0.84.0, so it still spans the pinned 0.85.1. 13 commits v0.10.1..v0.10.3,
# all under src/ tests/ docs/ scripts/ plus metadata; no entry-point move.
ARG PI_ATELIER_REF=v0.10.3
# Human-readable tag PI_ATELIER_REF was resolved from; recorded as a label. # Human-readable tag PI_ATELIER_REF was resolved from; recorded as a label.
ARG PI_ATELIER_VERSION=v0.10.1 ARG PI_ATELIER_VERSION=v0.10.3
RUN set -e && \ RUN set -e && \
# git_fetch_ref: clone-equivalent helper that accepts EITHER a branch name # git_fetch_ref: clone-equivalent helper that accepts EITHER a branch name
+9 -3
View File
@@ -572,7 +572,13 @@ ChromaDB ONNX embedding model so first-time semantic search is
instant. instant.
The palace data lives at `~/.mempalace/palace` on the host The palace data lives at `~/.mempalace/palace` on the host
(bind-mounted into the container). This means: (bind-mounted into the container). The image pins that layout with
`ENV MEMPALACE_CONFIG_DIR=/home/developer/.mempalace` (`Dockerfile.base`):
mempalace ≥ 3.10.0 would otherwise treat an *empty* `~/.mempalace` — a freshly
mounted volume at first boot — as "no install here" and put a new palace under
`~/.config/mempalace`, outside anything the compose files persist. With the
variable set, first in mempalace's resolution order, the location is a contract
rather than a heuristic. This means:
- A pi running on the host and a pi running inside this container see - A pi running on the host and a pi running inside this container see
the same palace. the same palace.
@@ -1140,8 +1146,8 @@ resolved to `latest` at build time:
| Component | Pin | Where | | Component | Pin | Where |
|---|---|---| |---|---|---|
| pi | `0.85.1` | `ARG PI_VERSION` — `Dockerfile.variant` | | pi | `0.85.1` | `ARG PI_VERSION` — `Dockerfile.variant` |
| pi-atelier | `v0.10.1` | `ARG PI_ATELIER_REF` — `Dockerfile.variant` | | pi-atelier | `v0.10.3` | `ARG PI_ATELIER_REF` — `Dockerfile.variant` |
| mempalace | `3.9.0` | `ARG MEMPALACE_VERSION` — `Dockerfile.base` | | mempalace | `3.10.0` | `ARG MEMPALACE_VERSION` — `Dockerfile.base` |
The objective is **not** to freeze versions. Bumping is routine — usually one The objective is **not** to freeze versions. Bumping is routine — usually one
line plus a changelog note. The objective is that adopting a new upstream line plus a changelog note. The objective is that adopting a new upstream
+8 -1
View File
@@ -100,7 +100,14 @@ fi
# existing data. `--yes` auto-accepts detected entities so the init is # existing data. `--yes` auto-accepts detected entities so the init is
# non-interactive. # non-interactive.
if command -v mempalace &>/dev/null && [ -d /workspace ]; then if command -v mempalace &>/dev/null && [ -d /workspace ]; then
PALACE_DIR="${HOME}/.mempalace" # Read the root from the same variable mempalace itself reads (set as an
# image ENV in Dockerfile.base since mempalace 3.10.0 started resolving
# ~/.config/mempalace for an EMPTY ~/.mempalace). The fallback keeps the
# historical location for anyone running this script with the ENV unset;
# the point of naming the variable here is that this test and mempalace's
# own resolution can no longer disagree about where the palace lives — a
# disagreement that would make this branch fire on every start.
PALACE_DIR="${MEMPALACE_CONFIG_DIR:-${HOME}/.mempalace}"
if [ ! -d "$PALACE_DIR/palace" ]; then if [ ! -d "$PALACE_DIR/palace" ]; then
echo "Initializing MemPalace for workspace (non-interactive)..." echo "Initializing MemPalace for workspace (non-interactive)..."
# </dev/null: mempalace init has an interactive "Mine this directory # </dev/null: mempalace init has an interactive "Mine this directory