Compare commits

...

1 Commits

Author SHA1 Message Date
joakimp bf265f6d54 release(v1.11.2): smoke esbuild check present-iff-declared; pi-studio 0.10.1
Lint / hadolint (push) Successful in 8s
Lint / skill-floor (push) Successful in 13s
Lint / doc-drift (push) Successful in 17s
Lint / actionlint (push) Successful in 30s
Publish Docker Image / lint-gate (push) Successful in 26s
Publish Docker Image / resolve-versions (push) Successful in 16s
Publish Docker Image / base-decide (push) Successful in 15s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke (push) Successful in 5m25s
Publish Docker Image / smoke-studio (push) Successful in 8m40s
Publish Docker Image / build-variant (push) Successful in 16m48s
Publish Docker Image / update-description (push) Successful in 8s
Publish Docker Image / promote-base-latest (push) Successful in 9s
Publish Docker Image / build-variant-studio (push) Successful in 21m23s
v1.11.1 (run 724) was tagged and never published: the smoke assertion
"esbuild works at every install site" required esbuild to exist, and pi
1.2.0 dropped @earendil-works/chord, its only source. The image was right.

- smoke-test.sh: esbuild must be present iff an installed package root or
  /opt repo root declares it, and compile at every site. Body tested 8 ways
  (v1.11.0-studio ok; sites removed / natives pruned / residue / bare
  declarer fail; fixture decoy ignored; v1.11.2 candidates ok). Full smoke
  on local arm64 candidates with run 724's args: old script 102/1 (CI
  reproduced), new core 103/0, studio 106/0 (0.9.63 and 0.10.1).
- pi-studio v0.9.63 -> v0.10.1 (tagged 2026-10-14; CI takes the newest
  tag): devbox contract unchanged (manifest, client bundle, 11 tools, 6
  commands, --no-browser/--port/--listen-all, env vars); exercised under pi
  1.2.0 in headless Chromium (render, Run Prompt via ERDC, Files ->
  Document, port freed); in-image TUI starts with no extension issues.
- CHANGELOG v1.11.1 -> v1.11.2; Dockerfile.variant/workflow cycle notes.
  No base input changed: base-6f3b20266de1 (built by run 724) is reused.
2026-10-14 07:28:38 +02:00
4 changed files with 121 additions and 19 deletions
+1 -1
View File
@@ -397,7 +397,7 @@ jobs:
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,
# Until v1.11.2 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
+80 -11
View File
@@ -11,15 +11,34 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
---
## v1.11.1 — 2026-10-14
## v1.11.2 — 2026-10-14
Patch release. Headlines: **pi 1.2.0**, **`pi -p` returns in seconds** on a
**v1.11.1 was tagged and never published.** Run 724 failed one smoke assertion
in each variant (`smoke` 102 passed / **1 failed**, `smoke-studio` 105 / **1**):
*esbuild works at every install site* reported "no esbuild install found at
all". The image was right and the assertion was wrong. esbuild was never a
direct pi dependency. It came through `@earendil-works/chord` (pi 1.1.0 → chord
→ esbuild 0.28.2, at the pi and pi-fork sites), and pi 1.2.0 dropped chord, so
a correct 1.2.0 image has no esbuild. pi 1.2.0's tree has no esbuild import;
1.1.0's chord bundle had one. This is the mistake the clipboard assertion made
at v1.10.0. Every publish job skipped, so no `v1.11.1`, `latest` or Hub
description was written. The one artefact pushed is the base `base-6f3b20266de1`.
It is content-addressed and this release reuses it, which is why
`Dockerfile.base`'s `BASE_REBUILD_DATE` comment still names v1.11.1: editing it
would change the hash and rebuild an identical base. **Everything below ships
here, with the assertion fixed** (*Fixed*, first entry). The tag moves rather
than being re-pointed, as at v1.10.2. One component moved between the two tags:
pi-studio published **v0.10.0 and v0.10.1** the same morning, CI builds the
newest pi-studio tag, and this release adopts 0.10.1 after testing it (*Changed*
below) rather than pinning 0.9.63.
Patch release. Headlines: **pi 1.2.0**, **pi-studio 0.10.1** (two editors, a
Prompt and a Document, by default), **`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
pi-observational-memory `022da5e`, pi-atelier `v0.15.0`, 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
@@ -27,14 +46,33 @@ the base tools pinned to `latest` re-resolve too.
### Components that move with this release
| Component | Baked in v1.11.0 | v1.11.1 | Why |
| Component | Baked in v1.11.0 | v1.11.2 | Why |
|---|---|---|---|
| **pi** | `1.1.0` | **`1.2.0`** | audited, see *Changed* below |
| **pi-studio** (studio variant) | `v0.9.63` (`fae3ad3`) | **`v0.10.1`** (`76db01f`) | tagged 2026-10-14, tested under pi 1.2.0, 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 smoke test's esbuild assertion is now "present iff something declares
it"**, like clipboard's. Because esbuild arrives transitively, "declared" is
not read from pi's `package.json`. It is derived from every installed package
root and every `/opt` repo root, counting dependencies, optional dependencies
and peer dependencies. Anchoring on package roots means a test fixture's
`package.json` cannot count. Both directions fail: declared but missing (the
prune removed function), and installed with nothing declaring it (residue).
The new body was run before tagging. On published v1.11.0-studio it reports
"declared (2 package(s)) and compiles at 2 site(s)". With every esbuild dir
removed it fails, naming both chord `package.json` files. With
`@esbuild/<platform>` pruned it fails with "esbuild broken at …". On the new
images it reports that nothing declares esbuild. It also fails a fake esbuild
dir that nothing declares, and a package root that declares esbuild with no
install, but ignores a test fixture's `package.json`. The whole
`smoke-test.sh` then ran against arm64 candidates, built locally with run
724's resolved build args on the same base. The old script reproduced CI's
102/1 on core. The new one passed core 103/0 and studio 106/0, with pi-studio
0.10.1 and with 0.9.63.
- **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,
@@ -71,7 +109,7 @@ the base tools pinned to `latest` re-resolve too.
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. On images before
v1.11.1, scripts that call `pi -p` should keep their `timeout` or set
v1.11.2, 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
@@ -114,11 +152,42 @@ the base tools pinned to `latest` re-resolve too.
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.
(0.9.63 and 0.10.1 alike) marks pi busy on `agent_start`, which 1.2.0 fires
after the session already counts as busy. A request that reaches pi 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. From the browser it is hard to reach: three **Run Prompt** clicks 30 ms
and 150 ms apart produced exactly one run in 0.10.1.
- **pi-studio `v0.9.63` → `v0.10.1`** (studio variant). 0.10.0 is the largest
Studio change in a while. By default there are two editors: a **Prompt** for
what you send to pi and a **Document** for the file you are working on. You
can switch with `/studio --single` / `--dual` or from the footer. Unsaved text
survives reloads, and `/studio` reopens your last workspace (`--blank`,
`--last`); its state lives under `~/.pi/agent/pi-studio`, on the preserved
volume. There is a new **Files** pane, a two-row toolbar, and **Add to
Prompt**. 0.10.1 fixes the Run button flickering while a run starts. Upstream
typechecks against pi 1.2.0; its peers stay `*`.
- **What pi-devbox relies on is unchanged:** the package manifest
(`./index.ts`, `./themes`), the committed `client/studio-client.js`, the 11
`studio_*` tools, the 6 commands, and the `--no-browser`, `--port` and
`--listen-all` flags that `studio-expose` and the README use. The set of
environment variables read is identical.
- **Tested under pi 1.2.0 before tagging**, in a copy of tor-ms22's real
`~/.pi/agent` with 0.10.1 swapped in:
- pi starts with no extension issues, and `/studio --no-browser --port
18765` prints its token URL.
- In headless Chromium the new UI renders with no console errors.
- A prompt typed into the Prompt and sent with **Run Prompt** came back
through ERDC in both Studio and the terminal.
- The Files pane lists the workspace, and **Open in Document** loads a file.
- Quitting pi frees the port.
- **In the image:** the 0.10.1 studio candidate passes the full smoke test,
106/0. It is 11 MB smaller than with 0.9.63, because the npm package shrank.
Inside it, pi's TUI starts with pi-studio among the extensions and no
extension issues.
- **To go back**, if 0.10 does not suit: v1.11.0-studio still has 0.9.63.
Inside a container, `/studio --single` restores the one-editor layout.
- **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
+4 -4
View File
@@ -261,7 +261,7 @@ ARG USER_NAME=developer
# 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).
#
# v1.11.1: 1.1.0 -> 1.2.0 (npm latest, 2026-10-12). MEASURED 2026-10-14 on
# v1.11.2: 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
@@ -318,8 +318,8 @@ 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.
# 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
# v1.11.2: unchanged (master is still 022da5e, 6 commits past the 3.1.4 tag).
# CI NOW BUILDS THIS PIN. Until v1.11.2 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
@@ -414,7 +414,7 @@ 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
# v1.11.2: 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.
+36 -3
View File
@@ -30,7 +30,8 @@
# client bundle present + registered via `pi install`
# - no foreign npm-11 platform packages (@esbuild, clipboard) beyond the host
# - no build-time npm cache (/root/.npm) shipped in the image
# - esbuild compiles everywhere; clipboard present iff pi declares it
# - esbuild present iff something declares it, and compiles everywhere;
# clipboard present iff pi declares it
# - image size within threshold
set -euo pipefail
@@ -985,8 +986,40 @@ run "no build-time npm cache shipped (/root/.npm)" \
# runbook as "if this fails, revert the release", and it duly failed for the
# wrong reason on the first machine that ran it. A check must fail only for the
# thing it is checking.
run "esbuild works at every install site (prune removed weight, not function)" \
'sites=$(find /usr/lib/node_modules /opt -type d -path "*/node_modules/esbuild" -prune 2>/dev/null); if [ -z "$sites" ]; then echo "no esbuild install found at all" >&2; exit 1; fi; for d in $sites; do node -e "require(\"$d\").transformSync(\"const x:number=1\",{loader:\"ts\"})" || { echo "esbuild broken at $d" >&2; exit 1; }; done; echo ok'
#
# PRESENT IFF DECLARED, like clipboard below. esbuild was never a direct pi
# dependency: it arrived through @earendil-works/chord (pi 1.1.0 > chord >
# esbuild 0.28.2, at both the pi and pi-fork sites). pi 1.2.0 dropped chord, so
# a correct 1.2.0 image has NO esbuild, and the old "must exist" form failed the
# v1.11.1 tag build (102/103 core, 105/106 studio, nothing published) against a
# good image — the same mistake the clipboard check made at v1.10.0. Because it
# is transitive, "declared" cannot be read from pi's package.json: it is
# derived from every INSTALLED package root (node_modules/<pkg>/package.json,
# node_modules/@scope/<pkg>/package.json) and every /opt repo root, counting
# dependencies, optionalDependencies and peerDependencies (devDependencies are
# not installed: --omit=dev). Anchoring on package roots keeps a test fixture's
# package.json from counting. Both directions fail: declared but missing (the
# prune removed function), and installed with nothing declaring it (residue).
run "esbuild: present iff something declares it, and works at every install site (prune removed weight, not function)" \
'declarers=$(find /usr/lib/node_modules /opt -regextype posix-extended -type f \( -regex ".*/node_modules/(@[^/]+/)?[^/]+/package\.json" -o -regex "/opt/[^/]+/package\.json" \) 2>/dev/null | node -e "
const fs = require(\"fs\"); const hit = [];
for (const f of fs.readFileSync(0, \"utf8\").split(String.fromCharCode(10)).filter(Boolean)) {
let j; try { j = JSON.parse(fs.readFileSync(f, \"utf8\")); } catch (e) { continue; }
if (!j || j.name === \"esbuild\") continue;
for (const k of [\"dependencies\", \"optionalDependencies\", \"peerDependencies\"]) {
if (j[k] && Object.prototype.hasOwnProperty.call(j[k], \"esbuild\")) { hit.push(f); break; }
}
}
console.log(hit.join(\" \"));") || { echo "declarer scan itself failed" >&2; exit 1; };
sites=$(find /usr/lib/node_modules /opt -type d -path "*/node_modules/esbuild" -prune 2>/dev/null);
if [ -n "$declarers" ]; then
[ -n "$sites" ] || { echo "declared by $declarers but NO install found - prune removed function" >&2; exit 1; };
for d in $sites; do node -e "require(\"$d\").transformSync(\"const x:number=1\",{loader:\"ts\"})" || { echo "esbuild broken at $d" >&2; exit 1; }; done;
echo "ok: declared ($(echo $declarers | wc -w) package(s)) and compiles at $(echo $sites | wc -w) site(s)";
else
[ -z "$sites" ] || { echo "nothing declares esbuild yet installs remain: $sites" >&2; exit 1; };
echo "ok: nothing declares esbuild (pi >= 1.2.0 dropped chord); no install expected";
fi'
# Clipboard is the family pruned second. Upstream pi 0.86.0 (#9163) REPLACED the
# external `@mariozechner/clipboard` dependency with bundled macOS/Windows/X11