diff --git a/CHANGELOG.md b/CHANGELOG.md index 2104c9a..b1c5fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,175 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). --- -## Unreleased +## v1.8.6 — 2026-08-25 + +Patch release. Adopts the drift that accumulated in the ~2 days since v1.8.5 +(pi `0.84.3`, mempalace core `3.8.0`), then closes the documentation and +observability gaps that v1.8.5 itself listed as "Still open". No component +was adopted without an audit note recording *why* it is safe. + +All moving refs re-resolved immediately before tagging (2026-08-25T13:28Z): +pi-toolkit `0e1369e6`, pi-extensions `20228878`, mempalace-toolkit `0fe64c48` +and pi-observational-memory `ce9fc982` all unchanged since v1.8.5; +pi-fork `f1ff8087` → `bf702b4c`; pi-atelier holds at `v0.8.2` (floor for +pi ≥0.84 satisfied); pi-studio's CI-resolved newest tag has moved again to +`v0.9.51`. Base rebuild is forced (Dockerfile.base changed), so the 16 +floating base-tooling ARGs re-roll — expect ~67 min as for v1.8.5. + +### Changed + +- **`mempalace` core `3.7.1` → `3.8.0`.** Released 2026-08-23T21:19Z, hours + after this project's own v1.8.5 tag the same day. Additive/reliability only + — reviewed for MCP tool-schema changes before bumping, as always: none. + `sync --apply` (PR #2320/#2322) no longer deletes a drawer solely because + its `source_file` was unreachable *at that moment* — it asks for + corroboration first. **This does not relax the standing landmine** against + running `mempalace_sync` / `mempalace_delete_by_source` beyond dry-run on + the shared central palace: that failure mode is paths *permanently* absent + from whichever host runs the sync, not transient unavailability, and 3.8.0 + doesn't touch it. Server-side perf fix PR #2307 (long-running Chroma servers + no longer invalidate their own HNSW cache on their own writes) likewise does + not make `mempalace_reconnect` unnecessary — that tool covers *external* + writes bypassing the in-process client, a different scenario. Full reasoning + lives in the `Dockerfile.base` comment above `ARG MEMPALACE_VERSION`. + **Deployment note:** synlig's central palace currently serves `3.7.1` + server-side via `docker-compose.mempalace.yml` (which reuses this image) — + this client bump introduces version skew until that stack is separately + redeployed; sequence accordingly. + +- **`pi` `0.84.2` → `0.84.3`.** Published 2026-08-24T11:09Z. Release notes + carry one "Breaking Changes" line — `GoogleThinkingLevel` renamed to + `GoogleApiThinkingLevel` — checked against all four vendored packages + (`pi-fork`, `pi-observational-memory`, `pi-atelier`, `pi-studio`): zero + references, inert here. 0.84.3 also fixes two skill-discovery bugs that + land directly on this repo's own vendored-skill work: nested Markdown + skills inside `.agents/skills/` grouping directories not being discovered, + and root Markdown files (`README.md`/`AGENTS.md`) in skill directories being + wrongly reported as broken skills. + +### Added + +- **Browser automation is now documented to humans, not just to agents.** + `agent-browser` + Playwright + a headless Chromium (~625 MB — the single + largest addition in the image) previously had zero mentions in `README.md`, + `DOCKER_HUB.md` or `THIRD_PARTY.md`; it existed only in the agent-facing + `AGENTS.md` managed block. Added a `README.md` "Browser automation" + subsection, a `DOCKER_HUB.md` feature entry, and `THIRD_PARTY.md` license + rows for `agent-browser` (Apache-2.0), Playwright (Apache-2.0), and Chromium + (BSD-3-Clause for Chromium's own code plus a large set of bundled + third-party components under their own licenses; the binary here is not + compiled by this repo — it's Playwright's own "Chrome for Testing" download + via `playwright install --with-deps chromium`). +- **`THIRD_PARTY.md` gains rows for `pi-atelier` (MIT) and `mempalace` core + (MIT per the GitHub repo; noted that the PyPI package's own metadata omits + a license classifier, so verify against the repo's `LICENSE` rather than + sdist/wheel metadata if clearance is needed from the artifact alone).** +- **`typst` and `socat` added to `README.md`'s tooling inventory.** Both were + already used in prose (typst as pandoc's `--pdf-engine`, socat by + `studio-expose`) but missing from the "What's inside" lists, so the + inventory didn't match what the image actually ships. +- **`mempalace` core version recorded in `/etc/pi-devbox/build-manifest.json`.** + Previously absent — a published image couldn't answer "which palace version + shipped?", and a palace bug couldn't be correlated to an image version. + Derived from the live installed binary (matching the manifest's existing + ground-truth-not-build-args philosophy), degrading to `null` rather than + failing the build if the binary is missing or its output format changes. + Verified landed: new top-level `"mempalace_version"` key, sibling to + `pi_version` rather than a member of `components{}` (that map is rendered + truncated to 12 chars by `pi-devbox-version`, which would mangle a longer + version string). +- **New smoke assertions**, all landed in `scripts/smoke-test.sh`: (1) the + `pi-observational-memory` clone is checked for the actual `ce9fc98` + auth-fix markers pinned to their fix site, `src/runtime.ts` + (`availability_recheck`, `providerCredentialConfigured`, + `hasConfiguredAuth`) — not merely clone existence, and deliberately not a + repo-wide grep: all three identifiers also appear under `tests/`, so a + repo-wide search would stay green even with the fix reverted in + `src/runtime.ts` alone; (2) the manifest's new `mempalace_version` field is + asserted present, non-null, and equal to what `mempalace --version` reports + live, so the manifest can't silently drift from the installed package — + expected to fail against any pre-v1.8.6 image, by design; (3) a + behavioural check for the mempalace-toolkit feeder's `--agent` default + (see below — this one turned out to be possible after all). + +### Fixed + +- **A false claim was being published to Docker Hub on every release.** + `DOCKER_HUB.md` advertised "neovim (LazyVim defaults)". Nothing in this + repo installs LazyVim — the only nvim configuration is a 19-line + `sysinit.vim` that sets `termguicolors`. `update-description` pushes this + file verbatim (with `{{PI_VERSION}}` substituted) to the Hub description, so + the error was public, not internal. Corrected to describe what's actually + there. + +### Component audit for this release + +Checked against upstream 2026-08-25 (two days after v1.8.5's own audit): +`mempalace` core moved `3.7.1` → `3.8.0` (see Changed, above — timing is +notable: released *hours after* v1.8.5 tagged, so v1.8.5 could not have caught +it no matter how carefully it was audited). `pi` moved `0.84.2` → `0.84.3` +(see Changed). `pi-toolkit` `0e1369e6`, `pi-extensions` `20228878`, +`pi-observational-memory` `ce9fc982`, and `pi-atelier` `v0.8.2` are all +**unchanged** from v1.8.5 — in particular `pi-observational-memory` still sits +exactly at the auth-fix commit with nothing landed upstream since, and +`pi-atelier` is still the newest tag with the `≥0.7.1` floor for `pi ≥ 0.84` +trivially satisfied. `pi-fork` has one upstream commit not adopted this +release: `f1ff8087` → `bf702b4c`, a text-only rewording of the fork task +preamble (no code-path change) — **left un-pulled** for this release since it +is a moving ref CI resolves fresh at every build anyway; it will be adopted +automatically on the next build regardless of this entry. `pi-studio` (studio +variant) has drifted two tags upstream, `v0.9.48` (pinned at build time via +CI's newest-semver-tag resolution) → `v0.9.51` at tag time, purely additive +(watched PDF previews, opening PDFs directly in Studio, Studio header +hide) — nothing to bump in this repo since studio-tag resolution happens in +CI, not the Dockerfile, but note it **will** auto-adopt `v0.9.51` on the next +studio-variant build. `mempalace-toolkit` unchanged — this release's manifest +and pi-bump work in `Dockerfile.variant` stayed within that file's ownership +and did not require a toolkit-side change. + +### Still open + +- **`MEMPALACE_VERSION` has no CI-side audit equivalent to `PI_VERSION`'s.** + `PI_VERSION` is verified published-on-npm and warns (never silently adopts) + on drift; `MEMPALACE_VERSION` is a literal Dockerfile string with zero + references in `.gitea/workflows/docker-publish.yml`. Flagged in v1.8.5's + audit as a gap; still a gap. +- **`pi-devbox-version`'s human-readable output does not display + `mempalace_version`.** Its render path is a fixed sequence + (`release_tag`, `build_date`, `source_revision`, `pi`, then `components{}`) + and the new top-level field isn't in it — only `--json` mode (which `cat`s + the manifest directly) surfaces it today. One line in + `rootfs/usr/local/bin/pi-devbox-version` would fix this; deferred since the + field's stated purpose (correlating a palace bug to an image) is already + served by `--json`, but worth doing in a follow-up if this becomes a + routine manual check. + +**Resolved during this release, not left open:** the feeder `--agent` +default behavioural hook initially looked like it might need a +mempalace-toolkit change (a `--print-config` flag that doesn't exist). It +didn't — `mempalace-pi-session` assigns `AGENT` before argument parsing and +`--help` exits 0 with no side effects, so `bash -x mempalace-pi-session +--help` observes the real resolution (env interpolation and fallback) +without needing a source change. The new smoke assertion exploits exactly +that, checked both ways: with `MEMPALACE_PI_DEVICE` set it must resolve to +`pi@`; with it unset it must NOT be `pi@*` (catches a regression to +the old unconditional `$USER`/`mempalace` default). + `mempalace-toolkit` commit `c64ffa1` changed the feeder's `--agent` default + from `$USER` to `pi@`, but there is still no way for smoke to assert + this default is actually in effect from this repo alone, since + `mempalace-toolkit` is a separate repo this release does not modify. If the + concurrent smoke-test work could not find an honest assertion from the + existing `/opt/mempalace-toolkit` surface (help text, `--self-test`), this + remains open pending a toolkit-side `--print-config`-style hook — a + toolkit-repo change, not a pi-devbox one. +- **16 base-tooling `ARG *_VERSION=latest` pins remain unrecorded.** (Corrected + count — v1.8.5's entry said "~14"; the actual count from `Dockerfile.base` + is 16, plus 5 more that float with no ARG at all: `rustup-init`, AWS CLI v2, + Chromium-via-Playwright, Node's minor version via `setup_22.x`, and + `DEBIAN_VERSION=trixie-slim` itself.) None of these are recorded anywhere + once the build completes — not in the manifest, not in a label — so a + published image cannot answer "which nvim/uv/chromium shipped?" without + exec-ing in and asking the binary. ### Documentation diff --git a/DOCKER_HUB.md b/DOCKER_HUB.md index 65db460..83139e8 100644 --- a/DOCKER_HUB.md +++ b/DOCKER_HUB.md @@ -65,12 +65,19 @@ The entrypoint deploys/registers all of these on first container start. Re-runni ### Document and image tooling - **pandoc** — universal Markdown↔HTML/Org/RST/etc. conversion. Useful well beyond pi: agent-driven doc exports, format conversion, etc. +- **Typst** — markup-based typesetting, used as pandoc's `--pdf-engine` - **graphviz** (`dot`) — diagram rendering pipelines - **imagemagick** (`magick`) — image conversion / resizing +### Browser automation + +- **agent-browser** — CLI for driving a real browser (open pages, click/fill/`eval`, snapshot the DOM, screenshots) so agents can verify front-end work instead of guessing +- **Playwright** + a headless **Chromium** are pre-installed and pinned together; `AGENT_BROWSER_EXECUTABLE_PATH` is preset to the baked browser, so `agent-browser open ` works out of the box with no setup +- **socat** — TCP bridge used to expose the pi-studio server outside the container's loopback + ### Modern CLI tooling -- **Editor**: neovim (LazyVim defaults), tmux (configured for 0-indexed sessions) +- **Editor**: neovim (system-wide `termguicolors` default; bring your own config/plugins), tmux (configured for 0-indexed sessions) - **Search/nav**: ripgrep, fd, fzf, zoxide - **Display**: bat, eza, htop, tree - **Data**: jq, yq diff --git a/Dockerfile.base b/Dockerfile.base index 8ebfd73..dbbea58 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -396,7 +396,44 @@ ARG INSTALL_MEMPALACE=true # (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 +# +# 3.8.0 (2026-08-23, PyPI, released hours after this project's own v1.8.5 tag +# the same day) is additive/reliability only — reviewed for MCP tool-schema +# changes before bumping, as always: there are NONE. Two PRs matter: +# - PR #2320/#2322: `sync --apply` no longer deletes a drawer solely because +# its source_file was unreachable AT THAT MOMENT — it now asks for +# corroboration first. This fixes losing a whole mined project to one +# `sync --apply` while its volume happened to be unmounted. +# IMPORTANT — do not over-read this fix: it addresses TRANSIENT +# unreachability, not the standing landmine (documented in the operator's +# global AGENTS.md) against running `mempalace_sync` / `mempalace_delete_by_source` +# beyond dry-run on the SHARED central palace. On that palace most +# source_file paths are PERMANENTLY absent from whichever host runs the +# sync — a different machine's paths simply do not exist here, ever, not +# merely "right now". That is a different failure shape than #2320/#2322 +# fixes. The landmine still stands; this bump does not relax it. +# - PR #2307: long-running Chroma servers no longer invalidate their own +# HNSW cache on their own writes (server-side perf fix). This does NOT +# make `mempalace_reconnect` unnecessary — that tool exists for EXTERNAL +# writes bypassing the in-process client (e.g. direct sqlite backfills, +# CLI commands against a running server), a different scenario #2307 +# does not touch. +# +# Known gap, carried forward (flagged in prior release notes, not fixed here): +# unlike PI_VERSION, which CI's resolve-versions job verifies is published and +# warns — never silently adopts — on npm drift, MEMPALACE_VERSION has NO +# equivalent CI-side audit (confirmed: zero references to MEMPALACE_VERSION in +# .gitea/workflows/docker-publish.yml). This is a literal Dockerfile string +# with no automated freshness or publish check. +# +# Deployment sequencing note for whoever ships this bump: synlig (the shared +# central palace host) currently serves mempalace 3.7.1 SERVER-SIDE via +# docker-compose.mempalace.yml, which reuses this same devbox image. Bumping +# this ARG changes only the CLIENT version baked into pi-devbox images: it +# introduces client/server skew until synlig's compose stack is separately +# rebuilt/redeployed with the new pin. Not something to code around here — +# just sequence the redeploy. +ARG MEMPALACE_VERSION=3.8.0 ENV UV_TOOL_DIR=/opt/uv-tools ENV UV_TOOL_BIN_DIR=/usr/local/bin RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \ diff --git a/Dockerfile.variant b/Dockerfile.variant index 74ee775..671a565 100644 --- a/Dockerfile.variant +++ b/Dockerfile.variant @@ -56,7 +56,22 @@ ARG USER_NAME=developer # current when it was first populated (shipped the same bytes for pi-devbox # v0.74.0..v0.75.5; discovered + fixed in v0.75.5b, 2026-05-23). The `latest` # branch below is kept only for a deliberate local `docker build` override. -ARG PI_VERSION=0.84.2 +# +# AUDITED AT 0.84.3 (2026-08-25, was 0.84.2): upstream's notes carry a +# "Breaking Changes" heading — `GoogleThinkingLevel` renamed to +# `GoogleApiThinkingLevel`. INERT FOR THIS IMAGE: all four vendored companions +# (/opt/pi-fork, /opt/pi-observational-memory, /opt/pi-atelier, /opt/pi-studio) +# were grepped for that symbol and reference it ZERO times, so nothing here +# couples to the renamed type. Recorded because the heading will look alarming +# to the next reader doing step 1 above — the audit is done, don't redo it. +# Adopted for two fixes that land squarely on this repo's own vendored-skill +# wiring (see devbox-skill-reconcile, v1.8.5): nested Markdown skills inside +# `.agents/skills//` directories were not discovered, and root Markdown +# files such as README.md / AGENTS.md inside a skill dir were reported as +# broken skills unless they declared valid skill frontmatter. +# pi-atelier needs no companion bump: v0.8.2 clears the >=0.7.1 floor that +# pi >= 0.84 requires (see PI_ATELIER_REF below). +ARG PI_VERSION=0.84.3 ARG PI_TOOLKIT_REF=main ARG PI_EXTENSIONS_REF=main # Repo URLs default to the canonical gitea origin but are overridable so a @@ -297,6 +312,19 @@ RUN set -e; \ mkdir -p /etc/pi-devbox; \ rev() { git -C "$1" rev-parse HEAD 2>/dev/null || echo "unknown"; }; \ PI_V="$(pi --version 2>/dev/null | head -n1 | tr -d '\r\n')"; \ + # mempalace CORE (the PyPI package behind the MCP tools) is installed in + # Dockerfile.base via `uv tool install`, so no /opt clone reveals it and + # until v1.8.6 the manifest could not answer "which palace shipped here?" — + # a palace bug could not be correlated to an image, which is precisely the + # correlation this file exists to provide. Read from the INSTALLED BINARY, + # not from ARG MEMPALACE_VERSION, per the ground-truth rule above: that is + # what catches an install which resolved to something other than the pin. + # `mempalace --version` prints "MemPalace 3.7.1" — NAME-PREFIXED, unlike + # pi's bare "0.84.2" — hence the $NF pick rather than a straight read. The + # leading-digit test then rejects usage/error text (a renamed flag prints a + # usage block) and degrades to JSON null, so this can never fail the build. + MP_V="$(mempalace --version 2>/dev/null | head -n1 | tr -d '\r' | awk '{print $NF}')"; \ + case "$MP_V" in [0-9]*) MP_CORE="\"${MP_V}\"" ;; *) MP_CORE='null' ;; esac; \ STUDIO_REV='null'; \ if [ -d /opt/pi-studio/.git ]; then STUDIO_REV="\"$(rev /opt/pi-studio)\""; fi; \ { \ @@ -305,6 +333,10 @@ RUN set -e; \ echo " \"build_date\": \"${BUILD_DATE}\","; \ echo " \"source_revision\": \"${SOURCE_REVISION}\","; \ echo " \"pi_version\": \"${PI_V}\","; \ + # Sibling of pi_version, NOT a member of components{}: that map holds git + # SHAs and `pi-devbox-version` renders it with .value[0:12], which would + # silently truncate a longer version string. + echo " \"mempalace_version\": ${MP_CORE},"; \ echo " \"components\": {"; \ echo " \"pi-toolkit\": \"$(rev /opt/pi-toolkit)\","; \ echo " \"pi-extensions\": \"$(rev /opt/pi-extensions)\","; \ diff --git a/README.md b/README.md index 7b3b95e..cba0b16 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,27 @@ so `TERM=xterm-kitty` is understood. Override either in your own ### Document and image tooling - `pandoc` — universal Markdown↔HTML/Org/RST/etc. converter +- `typst` — markup-based typesetting, wired up as pandoc's `--pdf-engine` (see + [Generating a PDF with pandoc + typst](#generating-a-pdf-with-pandoc--typst)) - `graphviz` — `dot` rendering for diagram pipelines - `imagemagick` — image conversion / resizing (invoked as `magick`) +### Browser automation + +- `agent-browser` — CLI for driving a real headless browser: open pages, + click/fill/`eval`, snapshot the DOM, take screenshots. Useful whenever a task + involves a web UI or verifying how a page actually renders (live DOM, WebGL, + layout, popup positioning) instead of guessing from source. +- `playwright` + a pre-installed headless **Chromium** back it. + `AGENT_BROWSER_EXECUTABLE_PATH` is preset to the baked browser via a stable + `/usr/local/bin/agent-chrome` symlink (insulated from Playwright's + per-version/arch install directory), so `agent-browser open ` works + out of the box with no setup. Run `agent-browser skills get core --full` + for the command set and workflow patterns. +- `socat` — TCP bridge used by `studio-expose` to reach pi-studio's + loopback-bound server from outside the container (see + [Using pi-studio](#using-pi-studio--studio-variant)) + ### Language toolchains - `python3` + `python3-venv` + `python3-pip` (system Python) @@ -787,8 +805,9 @@ docker inspect --format '{{json .Config.Labels}}' joakimp/pi-devbox:latest | jq `org.opencontainers.image.{version,revision,created}` plus `se.jordbo.pi-devbox.*-ref` record the intended pi version and companion refs. The on-disk `/etc/pi-devbox/build-manifest.json` records **ground -truth** — the actual checked-out commit of each `/opt` clone and the live -`pi --version` — so a tag is reconstructable after CI logs rotate: +truth** — the actual checked-out commit of each `/opt` clone, the live +`pi --version`, and (from v1.8.6) the live `mempalace --version` of the +installed palace core — so a tag is reconstructable after CI logs rotate: ```bash docker run --rm --entrypoint= joakimp/pi-devbox:latest cat /etc/pi-devbox/build-manifest.json diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md index 8ee3ec2..5672440 100644 --- a/THIRD_PARTY.md +++ b/THIRD_PARTY.md @@ -18,8 +18,23 @@ for OS packages, the per-package copyright files inside the image at | pi-fork | github.com/elpapi42/pi-fork | MIT | | pi-observational-memory | github.com/elpapi42/pi-observational-memory | MIT | | pi-studio *(`-studio` variant only)* | github.com/omaclaren/pi-studio | MIT | +| pi-atelier | github.com/michaelmjhhhh/pi-atelier | MIT | | pi-toolkit, pi-extensions, mempalace-toolkit | authored by the maintainer (Joakim Persson) | MIT | +## MemPalace (AI memory) + +| Component | Upstream | License | +| --- | --- | --- | +| mempalace (core, MCP server) | github.com/MemPalace/mempalace (PyPI: `mempalace`) | MIT — the GitHub repo declares MIT; the PyPI package's own metadata omits a license classifier, so if you need clearance from the package artifact alone, verify against the repo's `LICENSE` file rather than the sdist/wheel metadata | + +## Browser automation + +| Component | Upstream | License | +| --- | --- | --- | +| agent-browser | github.com/vercel-labs/agent-browser (npm: `agent-browser`) | Apache-2.0 | +| Playwright | github.com/microsoft/playwright (npm: `playwright`) | Apache-2.0 | +| Chromium | chromium.googlesource.com/chromium/src | BSD-3-Clause for Chromium's own code, plus a large set of bundled third-party components each under their own license (see Chromium's own `LICENSE`/`about:credits`). The binary in this image is **not compiled here** — it is the build Playwright downloads for its pinned version ("Chrome for Testing"), installed via `playwright install --with-deps chromium` at `/usr/local/share/ms-playwright/`. Treat Playwright's own distribution terms for that build as authoritative over any summary here. | + ## Tooling baked into the base image | Component | Upstream | License (best effort) | diff --git a/rootfs/usr/local/bin/pi-devbox-version b/rootfs/usr/local/bin/pi-devbox-version index 1505527..3e655b8 100755 --- a/rootfs/usr/local/bin/pi-devbox-version +++ b/rootfs/usr/local/bin/pi-devbox-version @@ -55,6 +55,10 @@ release_tag=$(jq -r '.release_tag' "$MANIFEST") build_date=$(jq -r '.build_date' "$MANIFEST") source_rev=$(jq -r '.source_revision' "$MANIFEST") pi_version_baked=$(jq -r '.pi_version' "$MANIFEST") +# `// empty` matters: images built before v1.8.6 have no such field, and +# `jq -r` renders a JSON null as the 4-char string "null" — which would +# print as a bogus version rather than being treated as absent. +mp_version_baked=$(jq -r '.mempalace_version // empty' "$MANIFEST") if [ "$MODE" = "quiet" ]; then printf '%s (%s)\n' "$release_tag" "${source_rev:0:7}" @@ -71,6 +75,16 @@ if command -v pi >/dev/null 2>&1; then pi_version_live=$(pi --version 2>/dev/null | head -n1 | tr -d '\r\n') fi +# Same check for the palace, which matters more than it looks: mempalace is +# the one component that is BOTH client (here) and server (synlig runs this +# same image), so a skew between the two is a real failure mode rather than +# cosmetic. `mempalace --version` prints "MemPalace 3.8.0" — name-prefixed, +# unlike pi's bare "0.84.3" — hence $NF rather than reading the whole line. +mp_version_live="" +if command -v mempalace >/dev/null 2>&1; then + mp_version_live=$(mempalace --version 2>/dev/null | head -n1 | awk '{print $NF}' | tr -d '\r\n') +fi + printf 'pi-devbox %s\n' "$release_tag" printf ' built: %s (source %s)\n' "$build_date" "${source_rev:0:12}" if [ -n "$pi_version_live" ] && [ "$pi_version_live" != "$pi_version_baked" ]; then @@ -79,5 +93,15 @@ else printf ' pi: %s\n' "${pi_version_live:-$pi_version_baked}" fi +# Printed only when known, so this degrades quietly on pre-v1.8.6 images +# instead of showing an empty or "null" palace line. +if [ -n "$mp_version_live" ] || [ -n "$mp_version_baked" ]; then + if [ -n "$mp_version_live" ] && [ -n "$mp_version_baked" ] && [ "$mp_version_live" != "$mp_version_baked" ]; then + printf ' palace: %s \033[33m(baked as %s — drift detected)\033[0m\n' "$mp_version_live" "$mp_version_baked" + else + printf ' palace: %s\n' "${mp_version_live:-$mp_version_baked}" + fi +fi + printf ' components:\n' jq -r '.components | to_entries[] | select(.value != null) | " \(.key): \(.value[0:12])"' "$MANIFEST" diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index b743a9d..07dbcc9 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -151,6 +151,33 @@ run "pi stage follows MEMPALACE_PALACE_PATH" ' mempalace-pi-session --dry-run --reason smoke --sessions-dir "$(mktemp -d)" 2>&1) || true echo "$out" | grep -q "stage=/tmp/alt/.mempalace/pi-stage/" ' +# The feeder's --agent default is WHO a drawer is attributed to. mempalace core +# records neither the machine nor the harness on a write, and one shared bearer +# token means the server cannot tell clients apart, so toolkit c64ffa1 changed +# this default from $USER to pi@$MEMPALACE_PI_DEVICE — the one string that makes +# a write attributable to both. Nothing ever PRINTED the resolved value (the +# banner shows mode= and stage= only), so an image built from a pre-c64ffa1 +# toolkit ref would ship unattributed writes with every check still green. +# +# `--help` assigns AGENT (script top) before it parses args, then exits 0 with +# no side effects — so `bash -x` observes the REAL resolution, env interpolation +# and fallback included, rather than grepping the source for a literal line that +# any reformat would break. Two-sided on purpose: device set => pi@; +# device UNSET => must not be pi@anything. The second half is what fails against +# the old unconditional $USER default, which ignored the device entirely. +# +# Probes the PATH entry (a symlink into the /opt clone) rather than that clone +# path directly: this is the invocation the systemd/launchd timers and +# entrypoint-user.sh actually use, so it is the default that reaches the palace. +run "feeder resolves --agent to pi@ (drawer attribution)" ' + f=$(command -v mempalace-pi-session) || { echo "feeder not on PATH" >&2; exit 1; } + with=$(MEMPALACE_PI_DEVICE=smoke-device bash -x $f --help 2>&1 | sed -n "s/^+* *AGENT=//p" | tail -n1) + without=$(env -u MEMPALACE_PI_DEVICE bash -x $f --help 2>&1 | sed -n "s/^+* *AGENT=//p" | tail -n1) + echo "resolved with-device=[$with] without-device=[$without]" >&2 + [ "$with" = "pi@smoke-device" ] || exit 1 + case "$without" in pi@*) exit 1 ;; esac + echo ok +' # Regression guard for the pi transcript exporter. If pi ever changes its # session JSONL shape, the exporter stops recognising sessions and the palace # silently gets nothing (or, worse, raw JSON chunked as prose). Feed it a @@ -263,6 +290,26 @@ run "pi-fork clone + node_modules" \ "test -f /opt/pi-fork/package.json && test -d /opt/pi-fork/node_modules" run "pi-observational-memory clone + node_modules" \ "test -f /opt/pi-observational-memory/package.json && test -d /opt/pi-observational-memory/node_modules" +# ...and that the clone carries the AUTH FIX, not merely that it exists. om's +# pre-flight hasUsableAuth() check silently disabled `recall` for ~8 weeks once +# pi moved to request-time SigV4 signing and stopped exposing a static Bedrock +# key; upstream fixed it in ce9fc98, adopted in v1.8.4. PI_OBSMEM_REF tracks +# master, so an upstream revert or force-push would ship a dead `recall` with +# the clone assertion above still green — the exact gap flagged as open in the +# v1.8.5 changelog. +# +# Pin the markers to src/runtime.ts, the fix SITE, rather than grepping the +# repo: two of these three strings also appear under tests/, so a repo-wide +# grep stays green with runtime.ts itself reverted. That is a false green of the +# same family as the old skill-snapshot canary. +run "pi-observational-memory carries the ce9fc98 auth fix (recall stays alive)" ' + f=/opt/pi-observational-memory/src/runtime.ts + test -f "$f" || { echo "fix site missing: $f" >&2; exit 1; } + for m in availability_recheck providerCredentialConfigured hasConfiguredAuth; do + grep -q "$m" "$f" || { echo "marker absent from runtime.ts: $m" >&2; exit 1; } + done + echo ok +' # pi-atelier: deliberately NO node_modules assertion, unlike its siblings — # it declares zero runtime dependencies (only peerDeps, satisfied by the baked # pi) and has no build step, so Dockerfile.variant skips `npm install` for it. @@ -307,6 +354,20 @@ run_expect "manifest records pi-atelier" \ "cat /etc/pi-devbox/build-manifest.json" '"pi-atelier"' run_expect "manifest records pi_version" \ "cat /etc/pi-devbox/build-manifest.json" '"pi_version"' +# mempalace CORE was absent from the manifest through v1.8.5: the toolkit SHA +# was recorded but the palace version behind the MCP tools was not, so a palace +# bug could not be correlated to an image version. Assert the field exists AND +# equals the installed binary — recording it from ARG MEMPALACE_VERSION instead +# would look identical here yet drift silently the first time an install +# resolved to something other than the pin, which is the whole reason this file +# is built from ground truth. `// empty` matters: jq -r prints the 4-char +# string "null" for a JSON null, which would satisfy a naive -n test. +run "manifest mempalace_version matches the installed core" ' + m=$(jq -r ".mempalace_version // empty" /etc/pi-devbox/build-manifest.json) + b=$(mempalace --version 2>/dev/null | head -n1 | tr -d "\r"); b=${b##* } + echo "manifest=[$m] installed=[$b]" >&2 + [ -n "$m" ] && [ "$m" = "$b" ] +' # Every component must be a resolved commit (or null for pi-studio in the # non-studio variant) — 'unknown' means a clone silently failed to resolve. run "manifest has no unresolved ('unknown') components" \