diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml index e7fbf2f..8d3722a 100644 --- a/.gitea/workflows/docker-publish.yml +++ b/.gitea/workflows/docker-publish.yml @@ -9,9 +9,13 @@ name: Publish Docker Image # Pipeline shape: # 1. base-decide compute base hash from Dockerfile.base + rootfs/ # + entrypoints; probe Docker Hub for existing tag. -# 2. resolve-versions resolve pi @ npm 'latest', pi-fork/pi-obsmem refs -# to commit SHAs (defeats registry-buildcache -# cache-hit footgun on byte-identical build args). +# 2. resolve-versions pi and mempalace from their pins (checked on npm/ +# PyPI); pi-obsmem and pi-atelier from their pins in +# Dockerfile.variant; pi-fork and the Gitea repos to +# their branch heads, pi-studio to its newest tag; +# all refs to commit SHAs (defeats +# registry-buildcache cache-hit footgun on +# byte-identical build args). # 3. build-base only if probe missed; multi-arch push of base-. # 4. smoke amd64-only build of the variant FROMing the base # tag; runs scripts/smoke-test.sh. @@ -186,7 +190,7 @@ jobs: # 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 + # watches (PI_FORK_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 -> @@ -386,15 +390,37 @@ jobs: fi echo "mempalace_version=${MEMPALACE_VERSION}" >> "$GITHUB_OUTPUT" - # pi-fork / pi-observational-memory (GitHub) → commit SHAs. + # pi-fork (GitHub) → master's commit SHA. Floats by design. FORK_REF=$(curl -sf -H "Accept: application/vnd.github.sha" \ "https://api.github.com/repos/elpapi42/pi-fork/commits/master" || true) require_sha PI_FORK_REF "$FORK_REF" - OBSMEM_REF=$(curl -sf -H "Accept: application/vnd.github.sha" \ - "https://api.github.com/repos/elpapi42/pi-observational-memory/commits/master" || true) - require_sha PI_OBSMEM_REF "$OBSMEM_REF" echo "fork_ref=${FORK_REF}" >> "$GITHUB_OUTPUT" + + # pi-observational-memory → the PINNED SHA in Dockerfile.variant. + # Until v1.11.1 this resolved `master` and passed it as --build-arg, + # which OVERRIDES the ARG default: the pin bound only local builds, + # and CI baked it only while master == pin (v1.9.5, v1.10.2, v1.11.0). + # The pin is a master SHA, not a tag, on purpose: obsmem tags rarely + # and lands what pi needs on master (PR #83, pi 0.87's finishTurn, + # never reached a tag). Its peerDependencies are `*`, so a mismatch + # with pi is silent at runtime. Adopting master is therefore a + # deliberate one-line bump after audit, never a side effect of the day + # CI ran. Read like atelier's tag, and require the full 40 chars: a + # short SHA would also downgrade check-doc-drift.sh's check to a SKIP. + OBSMEM_REF=$(sed -n 's/^ARG PI_OBSMEM_REF=\([^[:space:]]*\).*/\1/p' Dockerfile.variant | head -n1) + if ! printf '%s' "${OBSMEM_REF:-}" | grep -qiE '^[0-9a-f]{40}$'; then + echo "::error::ARG PI_OBSMEM_REF in Dockerfile.variant is not a full 40-char commit SHA (got '${OBSMEM_REF:-}'). pi-observational-memory must stay pinned to an audited commit — see the note above that ARG." + exit 1 + fi echo "obsmem_ref=${OBSMEM_REF}" >> "$GITHUB_OUTPUT" + # Informational only, like pi's npm drift warning: master moving past + # the pin is how a fix pi needs becomes visible. `|| true` so a GitHub + # hiccup cannot fail a release whose pin is already in the repo. + OBSMEM_MASTER=$(curl -sf -H "Accept: application/vnd.github.sha" \ + "https://api.github.com/repos/elpapi42/pi-observational-memory/commits/master" || true) + if printf '%s' "${OBSMEM_MASTER:-}" | grep -qiE '^[0-9a-f]{40}$' && [ "${OBSMEM_MASTER}" != "${OBSMEM_REF}" ]; then + echo "::warning::pi-observational-memory master is ${OBSMEM_MASTER:0:7}; this build ships the audited pin ${OBSMEM_REF:0:7}. To adopt it: read https://github.com/elpapi42/pi-observational-memory/compare/${OBSMEM_REF}...${OBSMEM_MASTER} (the observer/reflector/dropper workers and their pi API use), re-run the turn-cap probe against the pinned pi, then bump ARG PI_OBSMEM_REF in Dockerfile.variant and name the SHA in CHANGELOG.md." + fi # pi-atelier → the PINNED TAG's commit SHA. Unlike fork/obsmem # (which track a branch head) atelier wraps pi's private TUI @@ -483,7 +509,7 @@ jobs: echo "Resolved PI_VERSION=${PI_VERSION} (pinned in Dockerfile.variant; npm latest is ${PI_NPM_LATEST:-unknown})" echo "Resolved MEMPALACE_VERSION=${MEMPALACE_VERSION} (pinned in Dockerfile.base; PyPI latest is ${MEMPALACE_PYPI_LATEST:-unknown})" echo "Resolved PI_ATELIER_REF=${ATELIER_REF} (pi-atelier ${ATELIER_TAG}, pinned)" - echo "Resolved PI_FORK_REF=${FORK_REF}, PI_OBSMEM_REF=${OBSMEM_REF}" + echo "Resolved PI_FORK_REF=${FORK_REF}, PI_OBSMEM_REF=${OBSMEM_REF} (pinned; master is ${OBSMEM_MASTER:-unknown})" echo "Resolved PI_TOOLKIT_REF=${TOOLKIT_REF}, PI_EXTENSIONS_REF=${EXTENSIONS_REF}" echo "Resolved PI_STUDIO_REF=${STUDIO_REF} (pi-studio ${STUDIO_TAG})" echo "Resolved MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}" diff --git a/CHANGELOG.md b/CHANGELOG.md index a5754cc..b00a44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,17 +11,47 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). --- -## Unreleased +## v1.11.1 — 2026-10-14 -### Components that move with the next build +Patch release. Headlines: **pi 1.2.0**, **`pi -p` returns in seconds** on a +devbox instead of idling for five minutes (mempalace-toolkit), **MCP servers use +pi's built-in support** (pi-extensions' `mcp-loader.ts` is retired), and **CI +now builds the pinned pi-observational-memory commit** instead of whatever its +`master` is that day. Unchanged upstream, re-checked 2026-10-14: +pi-observational-memory `022da5e`, pi-atelier `v0.15.0`, pi-studio `v0.9.63`, +pi-fork `e69725c`, pi-toolkit `3760d90`, mempalace `3.10.0`, Node 24 (v24.21.0 +LTS). The skillset moved (`96b840e` → `1e02b44`), but the vendored mempalace +`SKILL.md` is byte-identical, so the snapshot is not refreshed. The base image is +rebuilt (the rootfs skill floor and the mempalace-toolkit ref both changed), so +the base tools pinned to `latest` re-resolve too. -| Component | Baked in v1.11.0 | Next build | Why | +### Components that move with this release + +| Component | Baked in v1.11.0 | v1.11.1 | Why | |---|---|---|---| +| **pi** | `1.1.0` | **`1.2.0`** | audited, see *Changed* below | | pi-extensions | `a88fa99` | **`750b2a8`** (or later: CI resolves `main` at build time) | `mcp-loader.ts` retired; `install.sh` prunes stale links (below) | | mempalace-toolkit | `975ab92` | **`d5a03f4`** (or later) | `pi -p` returns in seconds: `2f0edf5` clears the feed's leftover 5-min timer, `f590a3a` skips the feed in print/JSON mode, and `d5a03f4` skips the wake-up and mailbox poll there too (below). Also `a30af61`: the bridge's `RemoteMcpClient` is now the only copy, and `scripts/check-mcp-client-sync.sh` is removed (comment/README only) | ### Fixed +- **The pi-observational-memory pin now binds CI builds** (v1.11.0's known + issue). `resolve-versions` resolved the repo's `master` and passed it as + `--build-arg PI_OBSMEM_REF`, which overrides the Dockerfile's pinned default, + so CI baked the pin only while `master` happened to equal it (v1.9.5, v1.10.2, + v1.11.0). It now reads the pin from `Dockerfile.variant`, the way pi-atelier's + tag is read. It refuses anything but a full 40-char SHA, and prints a + `::warning::` with the compare link when `master` has moved past the pin. The + pin stays an untagged `master` commit on purpose. Upstream tags rarely (the + newest tag is still 3.1.4) and lands what pi needs on `master`: #83, the + migration to pi 0.87's `finishTurn`, never reached a tag. Its + `peerDependencies` are `*`, so a mismatch with pi fails silently at runtime. + Adopting a newer `master` is now a deliberate one-line bump, after the + turn-cap probe. The new step was run on its own against six inputs: the real + pin gives `022da5e` and no warning; a short SHA, `master` or a missing ARG + fails; an older pin gives that SHA plus the warning; GitHub unreachable gives + the pin and no warning. The workflow's pipeline comment, which still said pi + resolves from npm `latest`, is corrected too. - **`pi -p` on a devbox no longer sits idle for five minutes after answering.** It printed its answer, then held the terminal until a timer ran out: the feed's 300 s mine deadline (mempalace-toolkit `mempalace.ts`). @@ -40,9 +70,9 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). feed for a device that only runs `pi -p`. pi 1.0.0 behaved the same, so it predates v1.11.0. `pi-task` and `fork` children run `--no-extensions` and were never affected. `scripts/test-feed-exit.sh` in mempalace-toolkit pins - both changes, and each change's pre-fix source fails it. Until the next - build, scripts that call `pi -p` on a devbox should keep their `timeout` or - set `MEMPALACE_FEED=0`. + both changes, and each change's pre-fix source fails it. On images before + v1.11.1, scripts that call `pi -p` should keep their `timeout` or set + `MEMPALACE_FEED=0`. - **`pi -p` no longer gets the palace wake-up.** In print and JSON mode the mempalace bridge now skips the wake-up injection and the mid-session mailbox poll (mempalace-toolkit `d5a03f4`). The injection is status, diary, and the @@ -57,6 +87,38 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). ### Changed +- **pi `1.1.0` → `1.2.0`.** Audited 2026-10-14 on tor-ms22 from an isolated npm + prefix, by the same method as v1.11.0. 1.2.0 has four breaking changes, and + none reaches this image: + - `before_agent_start` also fires for runs started by + `pi.sendMessage(…, { triggerTurn: true })`, and its event is a union on + `messageType`. `systemPrompt` is on both members. Of the three handlers in + the image, ssh-controlmaster reads only `systemPrompt`, and aws-sso and + mempalace read no event field. Nothing in the image uses `triggerTurn`. + - The session counts as busy while a run starts, so `pi.sendUserMessage()` + then needs `deliverAs`. aws-sso already passes it. mempalace's + `/mempalace-diary` is a command, so it runs while pi is idle. + - Themes are schema-validated at startup, and the schema moved to `pi.dev`. + pi-studio's themes still name the old URL, but they load and apply on 1.2.0 + with no warning. + - `pi mcp list --json` reports `resources: true` instead of counts. Nothing + here parses it. + + Measured on 1.2.0. pi-agent-core's dist is byte-identical to 1.1.0's. The + turn-cap probe ran 19 turns uncapped and stopped at exactly 3 with + `maxTurns=3`. atelier paints in both renderers, with controls at 0/3. + pi-observational-memory registers `/om:status` and `/om:view`. A copy of + tor-ms22's real `~/.pi/agent` starts with the same extension list as on 1.1.0 + and no extension issues. `pi -p` answers in 1.0 s through ERDC and 5.2 s + through Bedrock. One change reaches scripts: `pi -p` now exits 1 when the + reply was cut off at the model's output limit. That is text mode only; + `pi-task` and `fork` run `--mode json`. The first start after the upgrade + shows 1.2.0's What's New once. Read from the code, not reproduced: pi-studio + marks pi busy on `agent_start`, which 1.2.0 fires after the session already + counts as busy. A Studio request sent in that gap (the `before_agent_start` + handlers) gets pi's "Agent is already processing" error instead of Studio's + own "pi is busy". Before 1.2.0 the same request started a second run that + failed, so retrying is the answer either way. - **MCP servers now use pi's built-in MCP support; pi-extensions' `mcp-loader.ts` is retired.** pi has shipped MCP as a built-in extension (`builtin:mcp`) since 0.99.0: servers go in `~/.pi/agent/mcp.json` or are @@ -88,10 +150,12 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). - **Vendored `pi-extensions` skill floor refreshed** to pi-extensions `750b2a8`. `check-skill-floor.sh` went red on the retirement commit and is green again. The skill now says MCP servers are the built-in's, not an extension. Because - `rootfs/` is in the base hash, the next tag rebuilds the base. + `rootfs/` is in the base hash, this release rebuilds the base. - DOCKER_HUB.md's pi-extensions list was three releases stale (`mcp-loader` listed; `task`, `fork-gate`, `secret-guard`, `aws-sso` missing). It now lists - the 10 that ship and has an MCP line. + the 10 that ship and has an MCP line. The vendored skills' `VENDORED.md` + pointed at "the Unreleased CHANGELOG entry", which had been stale since + v1.8.8; it now names v1.8.8. ## v1.11.0 — 2026-10-12 diff --git a/Dockerfile.base b/Dockerfile.base index 2faeb26..ab27649 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -14,7 +14,7 @@ # content-addressed over this file, so any byte change invalidates the # cache. Recommended cadence: once per release for security updates. # -# BASE_REBUILD_DATE: 2026-10-12 (v1.11.0 — rootfs gains link-pi-models.sh, entrypoint-user.sh links models.json; base tools re-resolve `latest` (upstream on 2026-10-12: uv 0.13.0, nvim 0.12.6, fzf 0.74.5); previous marker 2026-09-22 / v1.9.4) +# BASE_REBUILD_DATE: 2026-10-14 (v1.11.1 — rootfs skill floor to pi-extensions 750b2a8, mempalace-toolkit d5a03f4; apt and base tools re-resolve (upstream unchanged since 2026-10-12: uv 0.13.0, nvim 0.12.6, fzf 0.74.5); previous marker 2026-10-12 / v1.11.0) # # ── Lineage note ───────────────────────────────────────────────────── # Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2). diff --git a/Dockerfile.variant b/Dockerfile.variant index 10aeaec..379b38e 100644 --- a/Dockerfile.variant +++ b/Dockerfile.variant @@ -260,7 +260,36 @@ ARG USER_NAME=developer # ACTIVITY/READY/TOOLS in BOTH renderers (controls 0/3, every arm render-proofed); # obsmem 022da5e registers /om:status and /om:view (control: none); and all 11 # pi-extensions a88fa99 extensions load with no error (control: no /aws-sso). -ARG PI_VERSION=1.1.0 +# +# v1.11.1: 1.1.0 -> 1.2.0 (npm latest, 2026-10-12). MEASURED 2026-10-14 on +# tor-ms22 from an isolated npm prefix (nothing in /usr or ~/.pi touched): +# 1. FOUR `### Breaking Changes` in 1.2.0, none of which reaches us: +# - before_agent_start also fires for sendMessage(triggerTurn) runs and is +# a union on `messageType`; `systemPrompt` is on the shared base. Our 3 +# handlers: ssh-controlmaster reads only systemPrompt, aws-sso and +# mempalace read no event field. Nothing in the stack uses triggerTurn. +# - the session counts as busy while a run starts (isIdle() false, +# sendUserMessage needs deliverAs). aws-sso already passes deliverAs; +# mempalace's /mempalace-diary is a command and runs while idle. +# - themes are schema-validated at startup and the schema moved to +# pi.dev. pi-studio's themes still name the old pi-mono $schema URL, but +# `--use-theme pi-studio-dark` applies (palette differs from the +# default) and prints no warning. +# - `pi mcp list --json` reports `resources: true`. Nothing parses it. +# 2. pi-agent-core 1.1.0 -> 1.2.0: dist is BYTE-IDENTICAL (24 files, same +# tree hash). The turn-cap probe drove the real 1.2.0 agentLoop: uncapped +# ran its chosen 19 turns, maxTurns=3 stopped at exactly 3 + agent_end. +# 3. 1.2.0's `-p` now exits 1 on a reply cut off at the output limit, in TEXT +# mode only (print-mode.js). pi-task and pi-fork run `--mode json`. +# 4. engines.node is still `>=22.19.0`; the image ships 24.x. Peer ranges: +# atelier >=0.84.0, fork >=0.75.0, obsmem and studio `*`. +# Exercised before tagging, in a real pty with -ne controls: atelier v0.15.0 +# paints ACTIVITY/READY/TOOLS in BOTH renderers (controls 0/3, every arm +# render-proofed); obsmem 022da5e registers /om:status and /om:view (control: +# none); a copy of tor-ms22's real ~/.pi/agent (all packages, pi-extensions +# 750b2a8) starts with the same [Extensions] list as on 1.1.0 and no +# [Extension issues]; `pi -p` answers PONG via ERDC in 1.0 s, Bedrock in 5.2 s. +ARG PI_VERSION=1.2.0 ARG PI_TOOLKIT_REF=main ARG PI_EXTENSIONS_REF=main # Repo URLs default to the canonical gitea origin but are overridable so a @@ -289,10 +318,14 @@ ARG PI_OBSMEM_REPO=https://github.com/elpapi42/pi-observational-memory.git # dist/index.ts instead of 32 TS sources. The bundle is committed, so # `npm install --omit=dev` below needs no build step. src/agents/ is # UNCHANGED between the two SHAs, so the finishTurn migration is intact. -# KNOWN GAP, not fixed here: docker-publish.yml's resolve-versions still -# resolves obsmem from `master` and passes it as --build-arg, which overrides -# this default (verified two-sided on buildx 2026-10-12). CI builds therefore -# get the pin only while master == pin, as it is at this release. +# v1.11.1: unchanged (master is still 022da5e, 6 commits past the 3.1.4 tag). +# CI NOW BUILDS THIS PIN. Until v1.11.1 docker-publish.yml's resolve-versions +# resolved obsmem from `master` and passed it as --build-arg, which overrides +# this default (verified two-sided on buildx 2026-10-12), so CI got the pin only +# while master == pin. resolve-versions now reads this line, refuses anything +# but a 40-char SHA, and WARNS when master has moved past it. obsmem tags +# rarely and lands what pi needs on master (PR #83 never reached a tag), so +# adopting master is still the norm: bump this SHA, deliberately, after audit. ARG PI_OBSMEM_REF=022da5e5554d04dfdc876ed7dac4a42c6b83dfd4 # pi-atelier (TUI sidebar: ordered panels, split-pane, themes) is PINNED TO A # TAG, which CI resolves to that tag's commit SHA — same treatment as @@ -381,6 +414,8 @@ ARG PI_ATELIER_REPO=https://github.com/michaelmjhhhh/pi-atelier.git # panels and simplifies the Activity rows. The startup preference is still # `showSidebarOnStartup` (default true, src/config.ts:38). Painted in both # renderers on pi 1.1.0 before tagging (see the PI_VERSION note). +# v1.11.1: unchanged (still the newest tag); painted in both renderers on +# pi 1.2.0 before tagging. ARG PI_ATELIER_REF=v0.15.0 # Human-readable tag PI_ATELIER_REF was resolved from; recorded as a label. ARG PI_ATELIER_VERSION=v0.15.0 diff --git a/README.md b/README.md index 071d37b..8751ff3 100644 --- a/README.md +++ b/README.md @@ -1198,7 +1198,7 @@ persisted volumes survived, and pi runtime wiring is intact: ```bash ./scripts/recreate-sanity-check.sh # auto-detects variant ./scripts/recreate-sanity-check.sh --expected-image-version 1.8.9 # assert the pi-devbox release tag -./scripts/recreate-sanity-check.sh --expected-version 1.1.0 # assert the pi coding agent version +./scripts/recreate-sanity-check.sh --expected-version 1.2.0 # assert the pi coding agent version ``` Those are **two different versions**, and the flags are not interchangeable: @@ -1237,7 +1237,7 @@ resolved to `latest` at build time: | Component | Pin | Where | |---|---|---| -| pi | `1.1.0` | `ARG PI_VERSION` — `Dockerfile.variant` | +| pi | `1.2.0` | `ARG PI_VERSION` — `Dockerfile.variant` | | pi-obsmem | `022da5e5554d04dfdc876ed7dac4a42c6b83dfd4` | `ARG PI_OBSMEM_REF` — `Dockerfile.variant` | | pi-atelier | `v0.15.0` | `ARG PI_ATELIER_REF` — `Dockerfile.variant` | | mempalace | `3.10.0` | `ARG MEMPALACE_VERSION` — `Dockerfile.base` | diff --git a/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md b/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md index 90fe4b2..6d92f91 100644 --- a/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md +++ b/rootfs/usr/local/share/pi-devbox/skills/VENDORED.md @@ -61,7 +61,7 @@ its skill file needed baking. `docker build` records the same thing CI does. Verifying "is this snapshot current?" is **not** a CI job and was deliberately - not made one — see the Unreleased CHANGELOG entry for why (private repo; + not made one — see the v1.8.8 CHANGELOG entry for why (private repo; another repo's branch must not be able to fail this build; and the artefact it would guard is read by no host on this fleet). The check belongs where the skillset actually is: `vendor-mempalace-skill.sh --check` for a maintainer,