release: audited bumps (pi 0.85.1, mempalace 3.9.0, atelier v0.10.1) + two guards
Lint / hadolint (push) Successful in 12s
Lint / actionlint (push) Successful in 17s

Version audit for the next release. pi 0.84.4 -> 0.85.1, deliberately skipping
0.85.0 (it published internal experimental code and broke SDK imports,
upstream #9132). mempalace 3.8.0 -> 3.9.0. pi-atelier v0.10.0 -> v0.10.1.
PI_STUDIO_VERSION relabelled none -> v0.9.60-rc.0 so the floating main ref's
RC status is visible at docker-inspect time instead of discovered later.
PI_FORK_REF stays floating and adopts e69725c.

The pi bump was verified by running it under a pty in five combinations rather
than by reading the changelog, because this repo has already shipped a version
pair no changelog flagged (atelier < 0.7.1 hangs pi >= 0.84). CPU delta
0.00-0.01s over 5s against a ~5s sustained-CPU hang signature, two-sided via
the atelier sidebar painting identically to the 0.84.4 control.

NODE_VERSION stays 22 on purpose: node 24 is technically safe (pi's five
prebuilt addons are all NAPI, nothing declares a ceiling, agent-browser's
engines.node >=24 is vestigial for the shipped aarch64 ELF), but this release
already moves two minors and bakes an RC, and a node major would leave four
suspects if the image misbehaves. Own release, smoke suite as the gate.

Also corrects a stale claim at the mempalace ARG: synlig serves 3.8.0
server-side, not 3.7.1 (measured over ssh 2026-09-06).

agent-browser volume shadowing: the image has shipped 0.35.2, but every
session on mbp-m1-2020 ran 0.27.0 from a 2026-07-17 hand-install in
~/.pi/npm-global (a VOLUME, at PATH position 2 vs /usr/bin at 8). Third
package hit by this hazard after pi and pi-atelier, so the guard is now
generalised: entrypoint-user.sh retires the copy by moving it aside
(reversible, only when the image ships its own), recreate-sanity-check.sh
asserts resolution under /usr where the volume is real, smoke-test.sh carries
the build-time half and says in the source why it is weak. The real damage was
the stale BUNDLED SKILL (3 skillsets/17.6 KB vs 8/31.5 KB, ten subcommands
undocumented to the agent) - a stale tool errors, a stale skill quietly
teaches wrong commands.

pi-fork capability floor (extensions: []): forks were measured across four
dispatches ignoring their brief, answering in the user's voice, fabricating
self-referential measurements, and once filing a diary entry as agent_name=pi.
Cause is upstream by design - the child gets getHeader()+getBranch(), the
whole active session branch, with the brief as the final user message. Not a
model-capability problem: the same model as the fast profile obeyed the
identical brief perfectly with a fresh session and no inherited context.
extensions: [] runs children with --no-extensions, so the mempalace bridge is
absent and palace writes are impossible by construction (verified by asking a
child to enumerate its tools: read, bash, edit, write). Removes palace writes,
not filesystem writes.
This commit is contained in:
2026-09-06 20:40:02 +02:00
parent c8622ece9d
commit adcf56f829
6 changed files with 253 additions and 11 deletions
+108
View File
@@ -13,6 +13,114 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
## Unreleased
**Version audit + three pins moved, one deliberately not moved.** `pi`
0.84.4 -> 0.85.1, `mempalace` 3.8.0 -> 3.9.0, `pi-atelier` v0.10.0 -> v0.10.1,
and `PI_STUDIO_VERSION` relabelled `none` -> `v0.9.60-rc.0` to record what the
floating `main` ref actually resolves to. `PI_FORK_REF=master` stays floating
and therefore adopts e69725c. Each rationale is written at the ARG itself
rather than only here, because that is where the next person doing the audit
will be standing.
0.85.0 is SKIPPED on purpose: it shipped internal experimental code and extra
subpaths that broke SDK imports (upstream #9132), and 0.85.1 exists to undo
exactly that. Neither release has a Breaking/Removed changelog heading, the
engine floor is unchanged (>=22.19.0 against the container's 22.23.2), and
runtime deps drop 20 -> 19.
The pi bump was verified by RUNNING it, not by reading about it, because this
repo has already been burned by a version pair that no changelog flagged
(pi-atelier < 0.7.1 hangs pi >= 0.84 at startup with no error). 0.85.1 was
side-installed and driven under a pty in five combinations — each companion
extension plus atelier v0.10.0 AND v0.10.1 — with a CPU delta of 0.00-0.01s
over a 5s window where the known hang signature is ~5s of sustained CPU. The
check was two-sided: the atelier sidebar painted ACTIVITY+WORKSPACE markers
identically to the 0.84.4 control, so "alive" could be distinguished from
"silently absent".
**NODE_VERSION stays 22 — audited, not overlooked.** node 24 is technically
safe: all five prebuilt native addons in pi use NAPI (ABI-stable, no
NODE_MODULE_VERSION lock, no binding.gyp), nothing in the image declares a node
CEILING, and the install is one token (`setup_${NODE_VERSION}.x`). agent-browser
0.36.0 declares `engines.node >=24`, but that field is vestigial for the
artifact actually shipped: the image's own 0.35.2 declares the same floor and
runs fine on 22.23.2 as a prebuilt aarch64 ELF. The reason to wait is
attribution, not compatibility — this release already moves pi a minor,
mempalace a minor and bakes a Studio RC, so adding a node major would leave four
suspects if the image misbehaves. Worth doing as its own release with the smoke
suite as the gate. (v22 is in maintenance until 2027-04-30; v24 is Active LTS
to 2026-10-20 and maintained to 2028-04-30, so there is real headroom.)
mempalace's client bump carries a sequencing note that is now also CORRECT: the
comment at the ARG claimed synlig serves 3.7.1 server-side, which was stale.
Measured 2026-09-06 over ssh, synlig's uv tool entry last changed 2026-08-25
and serves 3.8.0. Client 3.9.0 against server 3.8.0 is accepted skew until
synlig's compose stack is redeployed; 3.9.0's headline additions (release
awareness, `task create`/`task launch`) are SERVER-side and stay dark until
then — a client bump alone cannot light them up.
**agent-browser was running 7 weeks stale, and the interesting part is why
nothing noticed.** The image has shipped 0.35.2 since the last base rebuild,
but every session on mbp-m1-2020 was executing 0.27.0 from a 2026-07-17
hand-install: `npm i -g` writes into `~/.pi/npm-global`, which is the
devbox-pi-config VOLUME, and PATH puts that at position 2 against /usr/bin at
position 8. This is the third package hit by that exact hazard (pi itself and
pi-atelier already have guards), so the guard is now generalised instead of
re-invented a fourth time.
The damage was not the binary. It was the BUNDLED SKILL, which is the part an
agent reads: 3 skillsets / 17.6 KB core in 0.27.0 versus 8 skillsets / 31.5 KB
core in 0.35.2, with ten subcommands present in the image and entirely
undocumented to the agent (a11y, browser, data, mcp, page, plugin, read,
selectors, to, webmcp). A stale tool announces itself with an error; a stale
skill just quietly teaches the wrong commands and everything looks fine.
Three changes, at the three places this can be caught:
- `entrypoint-user.sh` retires a volume copy by MOVING it aside (reversible,
same instinct as the settings backups) and only when the image ships its own
copy, so a machine that deliberately hand-installs on an image without one
keeps it. The `bin/` shim is removed too — a dangling symlink would be a
worse failure than a stale version.
- `scripts/recreate-sanity-check.sh` asserts `agent-browser` resolves under
/usr. This is the check that matters, because it runs where the volume is
real.
- `scripts/smoke-test.sh` gets the build-time half, labelled WEAK in the source
for an honest reason: a `docker run` container has an empty config volume, so
it can never see the shadowing it is nominally testing for.
**pi-fork gets a capability floor: `extensions: []`.** Forks were measured
twice (2026-09-01, 2026-09-06, four dispatches) ignoring their brief, answering
in the USER's voice, fabricating self-referential measurements, and once filing
a diary entry as `agent_name=pi` — which landed in `wing_pi`, where a
wing-scoped `diary_read` never sees it.
The cause is upstream and by design, so there is nothing to wait for: the child
is handed `getHeader()+getBranch()`, i.e. the WHOLE active session branch, with
the brief appended as the final user message and the system prompt untouched
(pi-fork `src/index.ts`). In a long session the parent narrative simply
outweighs the task, and the child does the statistically obvious thing — it
continues the story it finds itself inside. Config offers no context knob
(extensions, environment, offline, costFooter, effort profiles only).
Falsified the tempting explanation before acting on it: the failures are NOT a
too-small model. The same model as the `fast` profile (haiku, thinking off)
obeyed the identical brief perfectly when run as
`pi -p --mode json --session-id <fresh> --no-extensions` — correct values,
exact format, no session recap, 3 seconds, $0.012. Model held constant, context
inheritance removed, failure gone.
`extensions: []` is therefore a mechanical guarantee rather than an
instruction: the mempalace bridge is a pi EXTENSION, so a fork child now runs
with `--no-extensions` and cannot write to the shared palace under the parent's
identity. Verified by asking a child to enumerate its own tools: `read, bash,
edit, write` — no `mempalace_*`, no `recall`, no nested `fork`. Two honest
limits, stated so nobody over-trusts this: it removes PALACE writes, not
FILESYSTEM writes (`edit`/`write` remain), and it costs forks their palace
search and recall. Set the key to `null` to restore normal loading.
Smoke asserts the floor is `[]` specifically, not merely falsy — `null` is the
unguarded state, so a "truthy or not" test would pass on exactly the
configuration being guarded against.
**`credential-incident-response` §5/§6 corrected — a stated mechanism was wrong,
and this is the second time in three days this section named a wrong reason
for a zero.** Docs only.