ebd0de0be2dfc4740c1f21837b34e34e8d583a48
102 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ebd0de0be2 |
changelog: v1.8.7 — device provenance reaches the fleet
Lint / hadolint (push) Successful in 9s
Lint / actionlint (push) Successful in 17s
Publish Docker Image / resolve-versions (push) Successful in 10s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 42m4s
Publish Docker Image / smoke (push) Failing after 4m44s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / smoke-studio (push) Failing after 7m46s
Publish Docker Image / build-variant-studio (push) Has been skipped
The provenance fix's client half lives in mempalace-toolkit, which the image clones at build time, so it only reaches the fleet through a tag. Records both routes (extension via MEMPALACE_TOOLKIT_REF folded into base_tag; vendored skill via rootfs), the three design points (stamp in the client not the agent; diary marker in TEXT because metadata is invisible to readers; solitary devbox stamps nothing), and why the allowlist is per tool (3.8.0 hard-fails -32602 on undeclared args). Carries the CI-hardening work already sitting in Unreleased, and a Still open block for the three known bounds. |
||
|
|
9e744d701f |
lint: shellcheck the repo's own shell scripts, not just workflow run: steps
lint.yml has shellchecked every workflow `run:` step since the dash-vs-bash incidents, but nothing had ever pointed shellcheck at entrypoint.sh, scripts/*.sh or the extensionless tools under rootfs/usr/local/bin/. That gap is not hypothetical: the skillset repo's ci-release-watcher template shipped `echo "$json" | python3 <<'EOF' ... json.load(sys.stdin)` for two months, where the heredoc IS python's stdin (no script arg) so the load hit EOF and the function silently returned nothing. shellcheck names exactly that at severity ERROR — SC2259, "This redirection overrides piped input" — and could have named it the whole time. New step in the existing actionlint job, so no second container pull: shellcheck -S error plus bash -n over every shell file, discovered as *.sh UNION a shebang scan (the glob alone misses pi-devbox-version, devbox-skill-reconcile, dot-watch and studio-expose; a shebang scan alone would miss a sourced fragment without one). Fails loudly on a zero-file match, because a green tick over an empty set is not a check. Severity chosen by measurement, not taste: -S error is 0 findings across all 11 shell files today, so the gate is green on arrival with no cleanup, while -S warning is NOT free (19x SC2088 tilde-in-quotes in recreate-sanity-check.sh plus assorted SC2016, all intentional) and would train everyone to ignore the job — the same reasoning as the SHELLCHECK_OPTS exclusions already on the actionlint step. |
||
|
|
2b8c3a4db4 |
ci: audit MEMPALACE_VERSION the way PI_VERSION is audited
Closes the item v1.8.6 (and v1.8.5 before it) listed as "Still open": the palace pin was a literal string in Dockerfile.base with zero references in docker-publish.yml, while PI_VERSION had a concreteness gate, a published-on-registry check and a never-silently-adopt drift warning. resolve-versions now applies all of those to MEMPALACE_VERSION, read from Dockerfile.base so a local `docker build` and CI install the same version by construction, plus one gate pi does not need: a YANKED release is refused, because an exact pin installs one silently under PEP 592 and would have shipped a withdrawn palace client to the whole fleet. smoke gains `installed mempalace matches CI's audited pin` via a new EXPECTED_MEMPALACE_VERSION threaded into both smoke jobs. It is not redundant with `manifest mempalace_version matches the installed core`: that compares two properties of one image and cannot notice that both are the wrong version. The case this covers is a variant built FROM a cached base carrying an older pin — internally consistent, silently stale. Mutation-tested by extracting the shipped block out of the YAML and stubbing curl: 9 cases covering every gate, then once end-to-end against live PyPI. That found a real defect in the first draft — the yank message inlined a jq program inside $(...) inside a double-quoted string, where the escaping broke the filter (jq compile error) while the surrounding `exit 1` still fired: a gate that looked correct and reported garbage. Note: correcting Dockerfile.base's now-false "known gap, carried forward" comment forces a base rebuild (~67 min) on the next tag. Leaving a comment asserting the audit does not exist was the worse option. |
||
|
|
cb7b8ad2ae |
smoke: assert manifest VALUES, not the presence of field names
Four build-provenance assertions grepped the manifest for a field name and
never looked at the value:
run_expect "manifest records pi_version" "cat …manifest.json" '"pi_version"'
which passes on {"pi_version": ""} and on {"pi_version": null}. The tell was
in its own passing output the whole time — `✅ manifest records pi_version (got
"pi_version")` echoes the key back as the thing it claims to have found. Found
while reading run 579's smoke log to confirm v1.8.6's new assertions had really
executed rather than merely gone green.
Now checked against values, and against ground truth where it exists:
- every required component key present, naming the one that vanished
- every component value a full 40-hex SHA (null allowed for pi-studio alone,
which is legitimately absent in the non-studio variant)
- pi_version equal to `pi --version`, mirroring the mempalace ground-truth check
- release_tag non-empty; source_revision 40-hex and build_date ISO-8601 *when
populated*, since both default empty on a plain local `docker build` and
demanding them would fail honest local smoke runs
- --json compared byte-for-byte with the file, which is assertable because that
mode is a verbatim cat; the old form grepped its output for "release_tag"
Key presence and value shape are deliberately SEPARATE assertions: a single
"all values are valid SHAs" loop passes vacuously on components:{}, because
jq's all() over an empty list is true. Combining them would reproduce the same
shape of hole as the three false greens already recorded in CHANGELOG.md.
Dropped `manifest has no unresolved ('unknown') components`: the 40-hex check
strictly subsumes it ("unknown" is not 40-hex, and only rev() emits it, feeding
components{} exclusively). Removed rather than kept, because a check that can
no longer fail independently is one more green tick that means nothing.
Mutation-tested twice rather than reasoned about: nine fabricated manifests
through the raw jq filters, then twelve through the shipped assertions using
the real `run` helper's `sh -c` quoting path — the quoting is load-bearing,
since a jq filter dying on a quoting error exits non-zero and looks exactly
like a caught defect. Measured on the same twelve defects: old caught 3, missed
9; new catches 12. Three legitimate variations stay green (empty
source_revision, empty build_date, null pi-studio).
Also corrects a factually wrong "Still open" bullet in the released v1.8.6
entry, which claimed pi-devbox-version's human output does not show
mempalace_version and that only --json surfaces it. Both halves are false: it
prints a `palace:` line with live-vs-baked drift annotation, verified against
fabricated manifests (match, skew, and pre-v1.8.6 absent-field cases). Left as
a struck-through correction rather than deleted, since v1.8.6 is published.
|
||
|
|
93f986e90e |
v1.8.6: adopt pi 0.84.3 + mempalace 3.8.0, close the v1.8.5 doc/observability gaps
Lint / hadolint (push) Successful in 9s
Publish Docker Image / resolve-versions (push) Successful in 15s
Publish Docker Image / base-decide (push) Successful in 8s
Lint / actionlint (push) Successful in 1m9s
Publish Docker Image / build-base (push) Successful in 41m23s
Publish Docker Image / smoke (push) Successful in 4m50s
Publish Docker Image / smoke-studio (push) Successful in 5m5s
Publish Docker Image / build-variant (push) Successful in 15m46s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / promote-base-latest (push) Successful in 15s
Publish Docker Image / build-variant-studio (push) Successful in 19m59s
Three coupled pieces of work, all of which ride on the base rebuild that the
mempalace bump forces anyway.
DRIFT ADOPTED
- pi 0.84.2 -> 0.84.3. Its release notes carry a "Breaking Changes" line
(GoogleThinkingLevel -> GoogleApiThinkingLevel). Audited before adopting:
zero references across all four vendored companions (pi-fork,
pi-observational-memory, pi-atelier, pi-studio), so it is inert for us. The
reason to adopt is two skill-discovery fixes that land directly on v1.8.5's
vendored-skill work: nested Markdown skills inside grouping directories were
not discovered, and root README.md/AGENTS.md in skill dirs were reported as
broken skills.
- mempalace core 3.7.1 -> 3.8.0. Additive/reliability only. Its sync fix
(#2320/#2322) stops sync --apply deleting drawers whose source_file was
unreachable *at that moment* -- which does NOT relax the standing landmine
against sync on the shared palace, because that landmine is about paths
permanently absent from whichever host runs the sync. Different failure
shape; the caution stands.
DOCS -- three defects, one of them public
- DOCKER_HUB.md advertised "neovim (LazyVim defaults)". Nothing in the image
installs LazyVim; the only nvim config is a 19-line sysinit.vim. CI PATCHes
this file into the Docker Hub description on every release, so this was a
false claim published to the world. Removed.
- agent-browser + Playwright + Chromium is the single largest addition in the
image (~625 MB) and had zero mentions in README, DOCKER_HUB or THIRD_PARTY --
it was documented only to agents, in the AGENTS.md managed block. Now
documented to humans, including the Chromium licence dimension.
- typst and socat appeared in README prose but not in the "What's inside"
inventory. Added.
OBSERVABILITY -- the three gaps v1.8.5 listed as still open
- build-manifest.json now records mempalace core, read from the live binary
(ground truth, not the build ARG). Placed as a sibling of pi_version rather
than inside components{}, because pi-devbox-version renders that map through
[0:12] and would truncate a version string.
- smoke asserts the pi-observational-memory clone actually CONTAINS the ce9fc98
auth fix, pinned to src/runtime.ts. Deliberately not a repo-wide grep: two of
the three markers also live under tests/, so the repo-wide form stays green
with the fix site reverted. That is the third false-green of this exact family
in this repo (canary phrase in both snapshots; reconciler fixture using a
non-owned name; now this) -- pin containment checks to the fix site.
- smoke asserts the feeder's pi@<device> agent default behaviourally. The
earlier audit concluded this needed a --print-config added upstream; it does
not. AGENT is assigned before arg parsing, so `bash -x mempalace-pi-session
--help` observes the real resolution with no toolkit change. Two-sided:
device set => pi@<device>, unset => must not be pi@*.
- pi-devbox-version now prints a palace: line with the same live-vs-baked drift
detection pi already had. This matters more than it looks: mempalace is the
one component that is both client (here) and server (synlig), so skew between
them is a real failure mode. Degrades quietly on pre-v1.8.6 images.
Deferred deliberately: a native arm64 act_runner on tor-ms22 (the current
runner is on synlig, x86_64, so every arm64 layer ships QEMU-emulated).
Analysis and caveats filed to the palace rather than actioned here.
|
||
|
|
26f223568d |
.env.example: document MEMPALACE_PALACE_PATH and why nothing exports it
The only MemPalace variable the template never mentioned, and the one that moves the feeders' stage as a side effect: the palace root resolves as $MEMPALACE_PALACE_PATH -> $MEMPAL_PALACE_PATH -> ~/.mempalace/config.json -> ~/.mempalace/palace, and the stage is derived from it (<palace-root>/pi-stage). The comment states that precedence, records why neither the image nor the entrypoint exports it (pinning the palace without carrying the stage along re-creates the split a shared root removed, v1.8.2), warns that a stage whose persistence differs from the palace makes a scoped `mempalace sync` prune conversation drawers whose dedup key is the staged path, and notes it is a path INSIDE the container unlike the host-side WORKSPACE_PATH/SSH_KEY_PATH above it. Found while auditing a live host whose .env sets it redundantly to the default value. |
||
|
|
01abda3456 |
v1.8.5: the skillset owns its skills, and a dangling link no longer kills boot
Publish Docker Image / base-decide (push) Successful in 18s
Publish Docker Image / build-base (push) Successful in 41m59s
Publish Docker Image / smoke (push) Successful in 7m31s
Publish Docker Image / smoke-studio (push) Successful in 20m56s
Publish Docker Image / build-variant (push) Successful in 16m11s
Publish Docker Image / promote-base-latest (push) Successful in 15s
Publish Docker Image / update-description (push) Successful in 14s
Publish Docker Image / build-variant-studio (push) Successful in 17m32s
Lint / hadolint (push) Successful in 8s
Publish Docker Image / resolve-versions (push) Successful in 15s
Lint / actionlint (push) Successful in 20s
No pin moved except mempalace-toolkit fd8b15f5 -> 0fe64c4 (feeder defaults --agent to pi@$MEMPALACE_PI_DEVICE, so hand-filed palace writes carry provenance; $USER remains the fallback when the var is unset). pi 0.84.2 and mempalace 3.7.1 are still upstream-latest, atelier v0.8.2 keeps the >=0.7.1 floor for pi >=0.84, and om master is still ce9fc982 -- nothing landed after the merge that fixed the eight-week silent-observation bug. Expect a full multi-arch base rebuild: entrypoint-user.sh, rootfs/** and the resolved toolkit SHA all feed the base hash. |
||
|
|
b5810654f6 |
skills: let the skillset own the skills it owns, and stop a dangling link from killing boot
Baked skill links won over the live skillset clone for all three vendored skills, so a pushed edit to skills/mempalace/SKILL.md was invisible in every container until the next image build -- measured on two hosts (live md5 129bcc4752 vs baked 5236024fef). Cause was ordering, not intent: the baked links are created early with a create-only-when-absent guard to close a smoke readiness race, and the skillset deploy runs last and treats them as foreign. The comment claimed the opposite of the behaviour. The fix is not "skillset always wins". Ownership is per-skill: pi-extensions is owned by its package repo and copied over the snapshot at build time, so the skillset's lagging duplicate must keep losing; pi-devbox-environment is authored here. Only mempalace is skillset-owned. devbox-skill-reconcile therefore runs after the deploy and repoints only the names in skills/skillset-owned.txt, replacing a link solely when it points into the baked tree, so a real directory or a link pointing elsewhere is never disturbed. Precedence is now user override -> live clone (owned names) -> baked snapshot, with the early links intact as the fallback so the readiness race stays closed. Reviewing that turned up a latent boot-abort in the pre-existing baked-link block: `[ ! -e "$link" ]` is TRUE for a dangling symlink, so once a link can point into /workspace/skillset, a vanished mount makes plain `ln -s` fail with "File exists" -- and under `set -euo pipefail` that aborts container start before `exec "$@"`. Reachable on `docker restart` or a host reboot, not on a recreate, since ~/.agents is not a volume on any host. Now `ln -sfn`, which heals the link back to the baked fallback. Smoke additions cover what let this ship: the stale-snapshot canary grepped a phrase present in BOTH the stale and fresh copies, so it passed throughout; it now pins the newest section. Link targets are asserted, not just `test -L`; the owned-list content is asserted both ways; and the reconciler's replace path -- which no CI container exercises, since none mounts a skillset -- is covered by fabricating one. A mutation test showed the obvious three assertions still pass with the "is this link ours?" guard deleted, so a discriminating case was added: an owned name whose link is a user override outside the baked tree. Also refreshes the mempalace snapshot to skillset 670f7f1 (without it the fix helps only hosts that mount skillset) and corrects README, which documented the old, wrong precedence in three places. Verified with 12 fixture cases plus 2 mutants: ownership respected against the real trees, user overrides preserved, relative/trailing-slash/CRLF/space/glob inputs handled, dangling link healed, read-only skills dir exits 0, idempotent. |
||
|
|
4f6f470518 |
changelog: file the vendored-skill shadowing bug for v1.8.5
~/.agents/skills is asymmetric: the three pi-devbox-specific skills resolve to the baked copies while all others resolve to the live skillset clone. Root cause is ordering in entrypoint-user.sh -- baked links are created early (line 65) with a create-only-when-absent guard, and the skillset deploy runs last (line 387) and leaves them alone as foreign links. The comment at line 61 states the intent as protecting the skillset skill from being clobbered, but the effect is the reverse. Cost measured on two hosts: a pushed edit to the mempalace skill (live md5 129bcc4752) was invisible to both containers, which kept loading the baked copy (md5 5236024fef). Editing those three skills appears to work and silently does nothing until a rebuild. Filed as a known issue with a proposed fix rather than fixed here: changing symlink precedence is image behaviour and wants its own review plus a smoke assertion, and the early-link ordering exists to close a readiness race that must not regress. |
||
|
|
fbc1f86612 |
docs: a negative result is usually your own filter (skill + AGENTS.md)
Three false negatives in one session, all self-inflicted, all convincing because the command "succeeded": a `| head -20` proved an SSH peer absent that sits at line 454 of a ~500-line config; `ssh mac 'docker ps'` proved the host had no Docker, when the non-interactive PATH simply lacks /usr/local/bin; and `grep 'ssh '` proved no ControlMaster was running, when those processes rename themselves to `ssh: <path> [mux]`. Same root cause each time, so it goes in the skill rather than in a commit message: a positive result carries its own evidence, absence has to be earned. The skill (rootfs/, symlinked into ~/.agents/skills) is BAKED, so this is an image change and is logged in CHANGELOG Unreleased accordingly. Its §3 also now records that a live ControlMaster socket makes later commands authenticate not at all -- after editing a peer's authorized_keys, "it still works" proves nothing; prove it with -o ControlPath=none, or the breakage waits for a future session that has no memory of the edit. AGENTS.md: corrected a stale CI claim while placing the pointer. It said a tag push produces two runs including lint; lint.yml has since been scoped to branches: ['**'], which excludes tag refs, and refs/tags/v1.8.4 duly produced run 571 (publish) and nothing else. Kept the head_sha + workflow path filter advice, which is cheap and guards against a future v*-triggered workflow. Added a short section on verifying this repo from inside a container, including that docker-compose.yml here is a TEMPLATE pinning :latest while a real host runs its own per-machine file -- recreating from the repo copy can silently move a host off :latest-studio. Placement note: AGENTS.md is only auto-read when the cwd is this repo, so the durable rule lives in the skill, which loads by description match in any pi-devbox session. |
||
|
|
2ebf00d6d4 |
v1.8.4: the om fix lands upstream, pi-atelier v0.8.2, todo edit
Lint / hadolint (push) Successful in 9s
Lint / actionlint (push) Successful in 22s
Publish Docker Image / resolve-versions (push) Successful in 19s
Publish Docker Image / base-decide (push) Successful in 11s
Publish Docker Image / build-base (push) Successful in 42m16s
Publish Docker Image / smoke-studio (push) Successful in 5m0s
Publish Docker Image / smoke (push) Successful in 19m0s
Publish Docker Image / build-variant-studio (push) Successful in 17m38s
Publish Docker Image / build-variant (push) Successful in 28m36s
Publish Docker Image / update-description (push) Successful in 8s
Publish Docker Image / promote-base-latest (push) Successful in 10s
Headline: pi-observational-memory 37986b6 -> ce9fc98. The ambient-credential gate fix (6f694e6 + 699ccc7) was merged upstream as PR #52 on 2026-08-22, closing issue #51, so this release picks it up through the ordinary PI_OBSMEM_REF=master path with nothing carried locally. Every image up to and including v1.8.3 silently recorded zero observations on a Bedrock host using ambient AWS credentials; from this one on, /opt is the fix and the settings.json packages[] workaround should be deleted (verify against /etc/pi-devbox/build-manifest.json first). npm still ships the broken 3.0.4, which does not matter here because the image clones the ref instead. Pin bump: PI_ATELIER_REF / PI_ATELIER_VERSION v0.8.1 -> v0.8.2, audited per the floor note above the ARG. The only version in between is 0.8.2 itself and both its entries are Workspace-Pulse-internal (inspection coalescing and serialization; fresh inspection guaranteed at Turn end, retired sessions can no longer publish stale results). Nothing touches pi's private TUI renderer, which is the coupling behind the 0.6.0/0.7.0-under-pi-0.84 startup hang, and pi is unchanged at 0.84.2 - so the bump stays outside that risk class. Also baked by this build, no pin needed: pi-extensions 98eb07b -> 2022887 (the todo edit action), pi-fork 4a09af4 -> f1ff808, pi-studio 0.9.44 -> v0.9.48, mempalace-toolkit b609cf5 -> fd8b15f (docs only - the pi-session false-success guard was already baked in v1.8.3, confirmed by ancestry), aws-cli 2.36.24 -> 2.36.29 and the other *_VERSION=latest tools. README: the pin table also said mempalace 3.6.0, stale since v1.8.3 bumped it to 3.7.1. Fixed in passing. Unchanged and verified current: pi 0.84.2 (npm latest, published 2026-08-14), MEMPALACE_VERSION 3.7.1 (PyPI latest), pi-toolkit 0e1369e. |
||
|
|
c3b6d36778 |
docs(CHANGELOG): Unreleased — the todo extension gains an edit action
The commit itself lives in pi-extensions (2022887), but /opt/pi-extensions is baked into this image, so "which todo behaviour does this image have" is an image question. PI_EXTENSIONS_REF=main means the next build picks it up with no pin to bump -- worth stating explicitly, since a reader who expects a version bump will otherwise go looking for one. Also recorded, because it confused a session today: pi-atelier's tool_result hook replaces todo output with "N/M done - see sidebar" whenever the sidebar panel is visible, so an agent sees the counter and not the item text. Upstream's list returns every item; the terseness is atelier's deliberate context saving, not a limitation of the tool. |
||
|
|
3a509077c2 |
v1.8.3: mempalace 3.7.1, refreshed skill snapshot, census on PATH
Lint / hadolint (push) Successful in 8s
Lint / actionlint (push) Successful in 23s
Publish Docker Image / resolve-versions (push) Successful in 13s
Publish Docker Image / base-decide (push) Successful in 11s
Publish Docker Image / build-base (push) Successful in 53m49s
Publish Docker Image / smoke (push) Successful in 7m22s
Publish Docker Image / smoke-studio (push) Successful in 18m6s
Publish Docker Image / build-variant (push) Successful in 19m6s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / build-variant-studio (push) Successful in 27m5s
mempalace 3.6.0 -> 3.7.1. Verified against the 3.7.1 source rather than its
changelog, because the risk lands on palaces users cannot reconstruct: legacy
drawers lack the new chunk_total marker and both decision sites trust them, so
no mass re-mine; NORMALIZE_VERSION is 2 in both; chromadb stays <2 so no
index-format migration; no auto-migration exists; logstream.sqlite3 is created
lazily. Downgrade remains possible (3.6.0 has zero references to chunk_total).
Two behaviour changes documented in the CHANGELOG: ALLOW_PEER_WRITER no longer
works on local/chroma palaces, and writer-lock setup failures fail closed.
Neither affects this image's MCP-server-plus-CLI-feeder pattern, which already
serialised on the same lock under 3.6.0 -- the upstream "process-lifetime
single-writer" entry describes tightened escape hatches, not a new lease.
The motivation is the shared central palace: 3.7.1 drops the stale chromadb
SharedSystemClient cache on reconnect (3.6.0 could let a stale in-memory HNSW
segment overwrite a peer's writes, "index count going backwards"), releases the
writer lease on SIGTERM/SIGHUP, and stops treating an interrupted mine as
complete. The fleet primary was upgraded to 3.7.1 and restarted before this tag,
because 3.7.1 refuses writes when the served library drifts and reconnect cannot
clear that. opencode-devbox still pins 3.6.0, so the lockstep is broken until it
cuts its own release.
Vendored mempalace skill snapshot refreshed to skillset 936fed8 (was 63f3bf5).
This closes a gap that had been invisible for two commits: ~/.agents/skills/
mempalace symlinks to the IMAGE-BAKED copy, entrypoint-user.sh creates that link
first, and the skillset deploy never clobbers an existing name -- so in a devbox
container the vendored snapshot always wins and editing skillset alone changes
nothing a container reads. Brings the multi-machine shared-palace section and
the hand-crafted-provenance guard.
pi-global-AGENTS.append.md already carried the three shared-palace damage rules
(
|
||
|
|
ffd54750b9 |
docs: CHANGELOG for v1.8.2 — the silent transcript-feed failure and its guard
Publish Docker Image / resolve-versions (push) Successful in 25s
Lint / actionlint (push) Successful in 30s
Publish Docker Image / base-decide (push) Successful in 8s
Lint / hadolint (push) Successful in 46s
Publish Docker Image / build-base (push) Successful in 41m26s
Publish Docker Image / smoke (push) Successful in 4m45s
Publish Docker Image / smoke-studio (push) Successful in 5m11s
Publish Docker Image / build-variant-studio (push) Successful in 17m44s
Publish Docker Image / build-variant (push) Successful in 23m41s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 10s
|
||
|
|
53b41cd76b |
smoke: assert the pi stage $HOME-relative, and add a smoke_only dispatch
Lint / actionlint (push) Successful in 16s
Lint / hadolint (push) Successful in 13s
Publish Docker Image / resolve-versions (push) Successful in 10s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Successful in 5m20s
Publish Docker Image / smoke (push) Successful in 14m40s
Publish Docker Image / build-variant-studio (push) Successful in 21m51s
Publish Docker Image / build-variant (push) Successful in 15m59s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 17s
v1.8.0 never shipped: smoke (67/68) and smoke-studio (70/71) each failed the
same single assertion, so build-variant and everything downstream skipped and
latest stayed on v1.7.0.
The assertion was wrong, not the product. It grepped for a literal
stage=/home/developer/.mempalace/pi-stage/, but run() invokes
docker run --rm --entrypoint="" "$IMAGE" sh -c "$cmd"
and neither Dockerfile sets USER or ENV HOME — the published base image config
has no HOME at all; it is normally set by entrypoint-user.sh, which
--entrypoint="" skips on purpose. So the assertion executed as root with
HOME=/root, mempalace-pi-session correctly resolved
stage=/root/.mempalace/pi-stage/... (the stage is $HOME-relative by design), and
the literal grep could never match under any circumstances.
The tell was one line below in the log: the sibling assertion "pi stage follows
MEMPALACE_PALACE_PATH" PASSED, because it sets the variable explicitly and never
consults HOME. Default fails + explicit passes = wrong HOME, not broken staging.
Now asserts the invariant actually intended — the stage sits beside the resolved
palace, sharing its lifetime — which is user-independent:
case "$stage" in "stage=$HOME/.mempalace/pi-stage/"*) exit 0 ;; *) exit 1 ;; esac
$HOME is expanded by the container's own shell, so it holds as root, as
developer, or under any future user. Verified all four cases against the real
bin/mempalace-pi-session by extracting the committed assertion bodies and
running them under sh -c: virgin HOME -> exit 0; HOME=/home/developer -> exit 0;
MEMPALACE_PI_STAGE pinned to a .cache path -> exit 1 (the regression this
assertion exists to catch still fails it); developer-identity companion -> 0.
Added that companion assertion, "pi stage is palace-adjacent for the developer
user", which covers the deployment-specific path properly by SUPPLYING
HOME=/home/developer rather than assuming it.
Why this took a release to surface: docker-publish.yml triggers on push tags v*
only. The assertion was added on a push to main (
|
||
|
|
29b62093f0 |
v1.8.0: bump pi 0.84.1 → 0.84.2 and pi-atelier v0.8.0 → v0.8.1, audited
Publish Docker Image / resolve-versions (push) Successful in 12s
Lint / actionlint (push) Successful in 23s
Publish Docker Image / base-decide (push) Successful in 8s
Lint / hadolint (push) Successful in 1m20s
Publish Docker Image / build-base (push) Successful in 41m34s
Publish Docker Image / smoke (push) Failing after 4m41s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / smoke-studio (push) Failing after 8m22s
Publish Docker Image / build-variant-studio (push) Has been skipped
pi 0.84.2 closes the Amazon Bedrock tool-argument poison pill that v1.6.4 recorded as "Not fixed upstream". pi-ai 0.84.2 adds a recursive sanitizeBedrockDocument() and applies it at exactly the site that entry named (dist/api/bedrock-converse-stream.js, line 692 -> 704; upstream PR #7882): - toolUse: { ..., input: c.arguments }, + toolUse: { ..., input: sanitizeBedrockDocument(c.arguments) }, It strips object members whose key is the empty string, recursing through arrays and nested objects. It runs at request-build time, so it covers the live turn and a resume alike: a session already bricked by an empty-key tool argument now replays instead of dying on a Bedrock ValidationException. pi-session-repair is therefore no longer the recovery path on this image -- it stays useful for older images and for inspection, since the fix sanitises what is sent, not what was recorded. Bumping PI_VERSION is the ONLY way to get that fix: pi publishes an npm-shrinkwrap.json, so pi 0.84.1 pins pi-ai to exactly 0.84.1 even though its package.json range (^0.84.1) would admit 0.84.2. Transitive upstream fixes never leak into this image. pi-atelier v0.8.1 is the matching companion -- both sides changed fullscreen input handling within three days. Its only code change (src/split-pane.ts) stops atelier writing its own 1002h/1006h pair around a sidebar resize under pi's fullscreen renderer, which had been tearing down the mouse reporting pi itself enabled and leaving the wheel dead. Audited against the surfaces the pin policies name: - session .jsonl: identical migrateV1ToV2/migrateV2ToV3 ladder - node engine floor: unchanged >=22.19.0 (image ships 22.23.2) - atelier's three private couplings all intact in pi-tui 0.84.2 -- class TuiAltScreen extends TuiBase (detected by constructor NAME, so a rename would fail silently), inputListeners still `new Set()` at the same line 103, own render(width) descriptor still present - pi's mouse sequences byte-identical between 0.84.1 and 0.84.2 - atelier metadata unchanged: engines >=22.19.0, peerDeps >=0.80.7, zero runtime deps, so the "no npm install step" note holds Not proven by execution: CI smoke does not drive the TUI, and 0.84.2 adds a focused fullscreen search overlay that also participates in input handling. The pairing is reasoned from the diffs. Worth an alt+a plus a sidebar resize and wheel scroll in fullscreen on first use. |
||
|
|
cbd7cf5c67 |
entrypoint: announce the "remote palace, no inbox" skip instead of vanishing
When MEMPALACE_REMOTE_URL is set but MEMPALACE_PI_SSH_TARGET is not, the feeder has nowhere to ship staged transcripts, so skipping is correct. The problem was that the branch was a bare `:` AND the skip happens before the subshell that writes ~/.pi/agent/mempalace-catchup.log -- so a container in that state contributed nothing to the palace and left no artifact at all, not even an empty log, to explain why. It is indistinguishable from a healthy run that had nothing to file, which is the worst property a memory system can have: the failure looks exactly like success. Surfaced while flipping the first client onto the shared palace, where this is the single most likely way to end up quietly memory-less -- the palace is the only thing that survives a container recreate. The notice goes to both the container start output (docker logs) and the log path anyone debugging looks at first. It names both variables, says what still works (MCP tools read/write the shared palace; only this container's own conversations go nowhere), and points at MEMPALACE_FEED=0 for anyone who meant it -- "HTTPS first, mining later" is a documented interim state, so the notice has to be silenceable without being ignorable. Guarded against becoming a startup failure. mkdir -p in a branch that previously touched no filesystem is a new risk: an unwritable ~/.pi (root-owned volume, a classic Docker accident) fails under set -e and would abort the entire entrypoint. It now degrades to stdout-only. Verified all five paths by executing the extracted block under `set -euo pipefail`: the trap prints and writes the log; unwritable ~/.pi still exits 0 and still prints; MEMPALACE_FEED=0 stays completely silent (no message, no file); and both normal-remote and local mode still background the feeder with no notice. Two smoke assertions guard against a regression to the silent no-op. They test the entrypoint as shipped in the image rather than behaviour, because this branch only runs at container start and a `docker run` one-shot cannot reach it. entrypoint-user.sh is COPY'd in Dockerfile.base, so this rides the base rebuild the Unreleased feeder work already needs. |
||
|
|
7c00dd6001 |
mempalace: drop the stage ENV pin, fix the shared-server compose
The feeder now defaults to <palace-root>/pi-stage upstream, so pinning
MEMPALACE_PI_STAGE into ~/.pi here is unnecessary -- and was actively wrong. It
created a second convention that could still diverge from the palace: keep the
devbox-palace volume, drop devbox-pi-config, and a scoped `mempalace sync`
prunes every conversation drawer, because dedup keys on the staged path. Both
the ENV and the entrypoint export are gone; a comment explains why adding one
back re-introduces the split it was meant to fix.
docker-compose.mempalace.yml was broken on mempalace 3.6.0 in both directions:
- `--host 0.0.0.0` with no token in the environment makes the server refuse
to start, crash-looping under `restart: unless-stopped`.
- Supply a token and the healthcheck's unauthenticated `tools/list` POST 401s,
marking a perfectly healthy server unhealthy forever.
Now the token is required via ${MEMPALACE_REMOTE_TOKEN:?...} so it fails fast at
`docker compose up` with a readable message, and the healthcheck probes the
deliberately token-free /healthz. The "no authentication of its own" security
note has been stale since 3.6.0 and is replaced with the actual posture
(bearer token + Host pin + Origin allowlist), including why browser-shaped auth
must not be put in front of it.
Dockerfile.base: mempalace-pi-session symlinked onto PATH, with a build-time
`--help` check so a broken feeder fails the image build rather than the first
session.
smoke-test: assert the stage resolves beside the palace (default, and following
$MEMPALACE_PALACE_PATH) instead of asserting the removed ENV pin. The two
behavioural guards -- a synthetic session that must be captured, an abandoned
one that must not be -- are unchanged.
.env.example: recommend `mempalace serve` on the docker0 gateway rather than
`mempalace-mcp --transport http --host 0.0.0.0`, with the two binds to avoid.
|
||
|
|
43cd6e22f2 |
v1.7.0: bundle pi-atelier at a pinned tag; pin pi to an audited 0.84.1
Publish Docker Image / resolve-versions (push) Successful in 9s
Lint / actionlint (push) Successful in 15s
Lint / hadolint (push) Successful in 13s
Publish Docker Image / base-decide (push) Successful in 8s
Publish Docker Image / build-base (push) Successful in 41m22s
Publish Docker Image / smoke-studio (push) Successful in 5m16s
Publish Docker Image / smoke (push) Successful in 7m31s
Publish Docker Image / build-variant-studio (push) Successful in 18m31s
Publish Docker Image / build-variant (push) Successful in 27m18s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / update-description (push) Successful in 12s
Two changes that belong together, because the first is what makes the second dangerous to get wrong. pi-atelier (TUI sidebar + status rail) is now vendored to /opt/pi-atelier at PI_ATELIER_REF=v0.8.0 and registered by entrypoint-user.sh — the pi-fork / pi-observational-memory / pi-studio pattern, deliberately NOT `pi install npm:pi-atelier`, which writes into ~/.pi/npm-global on the config volume where it shadows the image and pins nothing. Unlike its siblings it gets no `npm install`: atelier declares zero runtime deps (peerDeps only, satisfied by the baked pi) and has no build step, so pi loads its TypeScript straight from the checkout via package.json `pi.extensions`. pi is no longer resolved to npm `latest` at build time. The pin lives in Dockerfile.variant and CI reads it from there, so a local `docker build` and a CI release ship the same versions by construction. The pin is a CHECKPOINT, NOT A FREEZE: bumping stays a one-line change; what stops is *unreviewed* adoption of whatever shipped that morning, in the same build that then gets tagged and published. CI fails when a pin is not concrete or not actually published on npm, and warns — never adopts — when npm latest moves ahead, naming what to re-check. Why this pairing needed care: pi-atelier 0.6.0/0.7.0 wrap pi's PRIVATE TUI renderer, and under pi 0.84 that wrapper recurses — pi hangs at startup burning CPU with no error. Upstream fixed the recursion in 0.7.1 and restored the non-overlapping split in 0.7.2; 0.8.0 is additive on top. atelier's own peerDependencies still say >=0.80.7, which does not express that floor, so nothing in npm metadata could have warned us. The floor is therefore encoded as an executable rule — pi >= 0.84 => pi-atelier >= 0.7.1 — asserted in both smoke-test.sh (build time) and recreate-sanity-check.sh (after a real recreate), verified against a 4x4 version matrix. Existing volumes needed migration, not just vendoring: a hand-installed `npm:pi-atelier` entry is counted as already-registered by the entrypoint guard, so every existing volume would have kept its unpinned npm copy — and a 0.6.x copy next to pi 0.84 is exactly the startup hang. The entrypoint now drops that one exact string (settings.json.bak.atelier.<ts> backup, distinct prefix so it cannot clobber the template merge's backup in the same second) and lets the pinned /opt copy register. Tested against a real settings.json: only that entry removed, other packages and all keys intact, idempotent, and unparseable JSON leaves the file untouched. DEVBOX_ATELIER=0 opts out entirely — in the entrypoint rather than via `pi uninstall`, because this component's failure mode is "pi will not start", which cannot be repaired from inside pi. 0.84.1 was audited for this release, not merely adopted: theme/TUI changes are additive, the session format is unchanged (CURRENT_SESSION_VERSION = 3 in both 0.83.0 and 0.84.1 with an identical migrateV1ToV2/migrateV2ToV3 ladder, so existing transcripts are neither migrated nor at risk and pi-session-repair stays valid), and the Node engine floor is unmoved at >=22.19.0. CI resolves the atelier tag to its PEELED commit SHA — atelier uses annotated tags, so the unpeeled ref is a tag object, not a commit; pi-studio's lightweight tags never exposed that distinction. Also: docs for overriding the read-only ~/.ssh/config from the container — container-only keys in ~/.ssh-local, hardened authorized_keys, the fact that `from=` must allow the HOST's addresses because container egress is NAT'd through it, and the macOS-only-keyword trap (`UseKeychain` is fatal to Linux OpenSSH and takes out dssh/pi --ssh while the host keeps working). Corrects two claims in "Naming LAN peers": ssh-lan.conf is not ProxyJump-only, and first-time creation does need one restart because the Include is emitted only when the file already exists at start. |
||
|
|
572430237f |
Bump mempalace pin 3.5.0 → 3.6.0 (lockstep with opencode-devbox v2.9.0)
3.6.0 (2026-07-17, PyPI latest) is additive/reliability only: secure
`mempalace serve` remote mode, optional Milvus backend, atomic KG
supersede(), conversation chronology, mining exclusions, plus recovery and
locking fixes.
Reviewed for MCP tool-schema changes before bumping — that being the exact
regression class this pin exists to catch, after an unpinned install once
swept in the broken 3.3.x/3.4.0 diary_write schema. There are none, and
nothing touches diary_write, so the perl workaround removed in v1.2.2 stays
removed.
Two fixes matter for how this image uses mempalace: read-only mode now covers
checkpoint + delete_by_source in _MUTATING_TOOLS (#1930), and agent
attribution is preserved in mempalace_checkpoint (#2023/#2034) — the latter
because the diary protocol relies on per-agent attribution.
Also adds a CHANGELOG Unreleased block that backfills the per-variant image
description labels (
|
||
|
|
e86e5df327 |
release: v1.6.4 — fork tool actually loads, pi 0.83.0 audited clean
Publish Docker Image / resolve-versions (push) Successful in 5s
Lint / hadolint (push) Successful in 13s
Lint / actionlint (push) Successful in 15s
Publish Docker Image / base-decide (push) Successful in 7s
Publish Docker Image / build-base (push) Successful in 41m24s
Publish Docker Image / smoke-studio (push) Successful in 5m11s
Publish Docker Image / smoke (push) Successful in 12m23s
Publish Docker Image / build-variant-studio (push) Successful in 22m46s
Publish Docker Image / build-variant (push) Successful in 22m58s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 13s
Headline is the fork-guard fix (the `fork` tool had never loaded since v1.0.0 because the registration guard grepped the whole settings.json and matched the pi-fork *config block* the template merge itself plants). pi 0.82.1 -> 0.83.0 is a clean hop with no intermediates. 0.83.0 carries an upstream Breaking Change (bundled TypeBox 1.1.38 -> 1.3.7, deprecated APIs removed) that cannot reach us: pi-fork vendors @sinclair/typebox (a different package name), obsmem's use is type-only, studio and atelier don't use TypeBox. Verified from source rather than from the audit narrative: extension-facing dist/core/extensions/*.d.ts declarations diff clean between the two versions, all six CLI flags pi-fork passes to child processes are present, and SESSION_VERSION is 3 in both so transcript tooling is unaffected. No PI_VERSION pin needed. Recorded for the release notes: the Bedrock poison pill is NOT fixed in pi-ai 0.83.0 (unsanitised input: c.arguments moved 634 -> 644), so pi-session-repair stays the recovery path. |
||
|
|
fa04d2083d |
docs(rootfs): re-sync pi-extensions skill snapshot (fork boundary mechanism)
Vendored floor snapshot re-synced from pi-extensions 98eb07b, which documents the mechanism behind fork boundary violations (full parent-transcript inheritance via index.ts:47) plus the corrected claims about tool restriction and narrative invention. CI resolves PI_EXTENSIONS_REF from main HEAD, so a normal build ships the package-owned copy; this keeps the committed floor identical so scripts/smoke-test.sh's cmp assertion holds either way. |
||
|
|
8248688d58 |
fix(entrypoint,tests): register pi-fork — guard matched its own config block
The `pi install /opt/<pkg>` loop in entrypoint-user.sh guarded on a whole-file substring grep of ~/.pi/agent/settings.json. settings.example.json ships a top-level "pi-fork" CONFIG block (fork effort profiles, pi-toolkit adb6907, 2026-06-17), so `grep -q pi-fork settings.json` matched the config key itself and `pi install /opt/pi-fork` never ran — on fresh or preserved volumes. The `fork` tool has therefore been absent since v1.0.0. The non-destructive template merge runs earlier in the same startup than the install loop, so the mechanism that delivers new template keys to an old volume is what plants the string that defeats the guard. pi-observational- memory and pi-studio escaped only by luck: the template key is "observational-memory" (no pi- prefix) and there is no studio block. Guard now inspects the `packages` array via jq, with a grep fallback on the stored `.../opt/<name>"` path form, which a config key can never produce. Existing volumes self-heal on the next container start. Both test suites asserted the bug as green — smoke-test.sh:244 and recreate-sanity-check.sh:204 used the same whole-file grep, so "pi-fork registered (fork tool)" passed on every build and recreate while the tool was missing. Both now assert against packages[] with the entrypoint's predicate, labels say packages[], and the smoke readiness wait loop uses the array check plus `docker exec -u developer` + $HOME instead of a hard-coded /home/developer path. Evidence: zero `fork` tool calls across all 19 sessions on this volume; the v1.6.3 session that tuned pi-fork.deep to opus-5 was configuring a tool that never loaded. |
||
|
|
e274510fd1 |
docs(changelog): unreleased — settings template defaults to Opus 5
Records the pi-toolkit 926f738 template change (defaultModel + pi-fork deep tier -> eu.anthropic.claude-opus-5). No tag, no build: CI resolves the pi-toolkit SHA from main at build time, so whichever release builds next picks it up. |
||
|
|
9ef7a92dce |
docs(changelog): v1.6.3 — pi 0.81.1→0.82.1
Publish Docker Image / resolve-versions (push) Successful in 7s
Publish Docker Image / base-decide (push) Successful in 15s
Publish Docker Image / build-base (push) Has been skipped
Lint / hadolint (push) Successful in 45s
Lint / actionlint (push) Successful in 15s
Publish Docker Image / smoke (push) Successful in 4m23s
Publish Docker Image / smoke-studio (push) Successful in 7m52s
Publish Docker Image / build-variant (push) Successful in 16m27s
Publish Docker Image / promote-base-latest (push) Successful in 5s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / build-variant-studio (push) Successful in 21m48s
Pure pi version bump (variant-only rebuild). CI resolves pi@latest=0.82.1 at build. 0.82.0/0.82.1 audited: additive, no breaking changes to the extension execution API (pi-observational-memory) or pi-agent-core types (pi-fork). |
||
|
|
6dfbded9c8 |
release: v1.6.2 — lift smoke size threshold (3500→3800)
Publish Docker Image / resolve-versions (push) Successful in 12s
Lint / hadolint (push) Successful in 7s
Lint / actionlint (push) Successful in 20s
Publish Docker Image / base-decide (push) Successful in 11s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke (push) Successful in 4m42s
Publish Docker Image / smoke-studio (push) Successful in 8m24s
Publish Docker Image / build-variant-studio (push) Successful in 18m8s
Publish Docker Image / build-variant (push) Successful in 25m53s
Publish Docker Image / promote-base-latest (push) Successful in 6s
Publish Docker Image / update-description (push) Successful in 12s
Completes v1.6.1's studio publish. Run 512 shipped v1.6.1 non-studio (3411 MB) cleanly, but smoke-studio failed the size gate at 3574 MB vs the 3500 MB threshold. Threshold was set in v1.0.0 pre-agent-browser (baseline was 3.20 GB local arm64 + 300 MB margin); v1.6.0 baked in agent-browser + Chromium (+~291 MB net) but the threshold was never lifted. v1.6.0 never got to smoke because of the network fault, so nothing surfaced this until v1.6.1's smoke-studio. Bump SIZE_THRESHOLD_MB to 3800 (~225 MB margin above observed studio number, tight enough to still catch a genuine +GB regression). Refresh the comment above the constant with the current baseline + run 512 actuals so future readers know where the number came from. CI-only change; image bytes identical to v1.6.1 except for the manifest's release_tag/source_revision. Not base-affecting. |
||
|
|
d00eef2acb |
docs(changelog): v1.6.1 — pi 0.80.6→0.81.1 (skips 0.81.0)
Publish Docker Image / resolve-versions (push) Successful in 7s
Lint / actionlint (push) Successful in 55s
Lint / hadolint (push) Successful in 9s
Publish Docker Image / base-decide (push) Successful in 55s
Publish Docker Image / build-base (push) Successful in 58m42s
Publish Docker Image / smoke (push) Failing after 4m50s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / smoke-studio (push) Failing after 13m22s
Publish Docker Image / build-variant-studio (push) Has been skipped
v1.6.0 was tagged 2026-07-17 but never reached Docker Hub (variant publish blocked by a site-network SYN-drop fault, since fixed). Cut v1.6.1 to land v1.6.0's content (agent-browser + pi-devbox-version) alongside a first pi bump since v1.5.0. 0.81.0 is skipped deliberately: it removed the default stream fallback for extensions using the pre-0.81 pi-agent-core API, which pi-observational-memory relies on via agentLoop + stream.result(). 0.81.1 restored the fallback (earendil-works/pi#6915), so 0.81.1 — but not 0.81.0 — is a safe drop-in. pi-fork only imports types; unaffected. Audit of 0.80.7–0.81.1 vs the two baked extensions and the base image: no breaking changes affect pi-devbox. Node engine bumped to >=22.19.0 in 0.81.0 (nodesource 22.x currently 22.23.1, so no engine bump needed). Base-affecting via the npm install line, so base-<hash> rebuilds. |
||
|
|
649fc44c5b |
chore(release): v1.6.0
Lint / hadolint (push) Failing after 22s
Lint / actionlint (push) Successful in 26s
Publish Docker Image / resolve-versions (push) Successful in 9s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Failing after 25m26s
Publish Docker Image / smoke (push) Has been skipped
Publish Docker Image / smoke-studio (push) Has been skipped
Publish Docker Image / build-variant-studio (push) Has been skipped
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Roll the unreleased changes into v1.6.0 (minor — significant base addition: agent-browser + headless Chromium baked into every variant for browser automation/front-end verification). Also includes the pi-devbox-version command and the bundled pi-toolkit sonnet-5 template bump. No pi bump — CI resolves latest pi from npm at build time (verified: 0.80.6→0.80.10 has no breaking changes affecting pi-devbox). |
||
|
|
89a8dc7fab |
perf(base): trim agent-browser footprint ~960MB→~625MB before release
Size concern ahead of the v1.6.0 base rebuild. agent-browser drives the full chrome (verified headless: open/title/eval with the headless_shell removed), so Playwright's chromium_headless_shell-* build is dead weight — drop it (~334MB), and clean apt/npm caches in-layer. Net browser footprint ~625MB/arch. CI disk is otherwise fine: build-base's 'Reclaim runner disk' step frees ~20-30GB (hostedtoolcache/dotnet/android/jvm) + docker prune before buildx. hadolint clean. |
||
|
|
6625d66f3a |
docs(agents): point the global AGENTS.md managed block at agent-browser
Discoverability follow-up to baking agent-browser into the base: agents won't reach for a browser they don't know they have (the exact gap that made this capability easy to miss). Add a short pointer section to the pi-devbox managed block — capability, the preset AGENT_BROWSER_EXECUTABLE_PATH, and `agent-browser skills get core --full` for the command set. Pointer only; depth stays in the skill. rootfs change → folds into the same base-<hash> rebuild. CHANGELOG note updated. |
||
|
|
8caafc3f49 |
base: bake agent-browser + Playwright Chromium (headless browsing, all variants)
Gives the agent a real browser it can drive so front-end work involving live DOM/WebGL can be VERIFIED, not guessed. The agent-browser skill (from the skillset repo) was a no-op without the binary; it now works out of the box. - agent-browser CLI (standalone Rust, ships no browser) via npm, prefixed NPM_CONFIG_PREFIX=/usr so it survives the ~/.pi/npm-global volume. - Chromium fetched with 'playwright install --with-deps chromium' into PLAYWRIGHT_BROWSERS_PATH=/usr/local/share/ms-playwright — a system path that the /home/developer volume can't shadow (unlike agent-browser's own ~/.agent-browser/browsers default, which WOULD vanish on recreate). - Stable /usr/local/bin/agent-chrome symlink, exported as AGENT_BROWSER_EXECUTABLE_PATH, insulates the ENV from Playwright's per-version chromium-<rev> dir name. Verified end-to-end (this session): agent-browser drives the baked Chromium headless (open + title + screenshot + eval into a WebGL SPA); doctor launch test passes in ~0.5s. Debian trixie --with-deps resolution verified (exit 0; t64 lib renames handled). hadolint clean; check-base-hash OK (only *_VERSION args added). Cost ~960 MB (Chromium + headless shell). Base-affecting → rebuilds base-<hash> on next release. |
||
|
|
fb49828826 |
base: add pi-devbox-version command + startup banner
Wraps /etc/pi-devbox/build-manifest.json (already written at docker-build time in Dockerfile.variant) into a human-readable summary instead of requiring users to know the manifest path and pipe it through jq themselves. - rootfs/usr/local/bin/pi-devbox-version: human (default) / --json / --quiet output modes. Also flags live drift — compares the baked pi_version against the actually-running `pi --version` and warns on mismatch rather than trusting the manifest blindly (same ground-truth philosophy as the manifest generation itself). Exits 1 with a short stderr notice on images built before the manifest existed, instead of failing silently. - entrypoint-user.sh: calls it as the very first line. CMD is `bash -l` with tty:true in compose, so this banner lands directly above the first prompt on container start — no separate motd/bashrc hook needed (deliberately not wired into .bash_aliases, which would reprint on every docker exec -it). - Dockerfile.base: COPY + chmod, same pattern as dot-watch/studio-expose. - scripts/smoke-test.sh: 4 new checks (binary present+executable, human output has release tag, --json round-trips the manifest, --quiet is a single line). - README.md / AGENTS.md / CHANGELOG.md updated. |
||
|
|
02be95ac1f | docs(changelog): note bundled pi-toolkit sonnet-5 template bump (Unreleased) | ||
|
|
d68674d11e |
chore(release): v1.5.0
Publish Docker Image / resolve-versions (push) Successful in 12s
Publish Docker Image / base-decide (push) Successful in 9s
Lint / hadolint (push) Successful in 12s
Lint / actionlint (push) Successful in 13s
Publish Docker Image / build-base (push) Successful in 43m20s
Publish Docker Image / smoke (push) Successful in 4m20s
Publish Docker Image / smoke-studio (push) Successful in 7m2s
Publish Docker Image / build-variant (push) Successful in 16m18s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Publish Docker Image / build-variant-studio (push) Successful in 17m47s
Roll the 7 unreleased commits since v1.4.0 into v1.5.0 (minor — significant base additions: readable Neovim true-colour, broad terminal terminfo support). Also includes: typst PDF template font fix, .claude gitignore seed, pi-studio semver-tag pin + version label, and repo hygiene (LICENSE, THIRD_PARTY.md, .dockerignore, hadolint lint, IDEAS backlog). No pi bump — CI resolves latest pi from npm at build time as usual. |
||
|
|
8c27894cf2 |
base: support modern terminals (ncurses-term + xterm-ghostty alias)
The base only shipped ncurses-base (xterm-256color, tmux), so SSHing in from WezTerm/Alacritty/foot/Ghostty degraded to a dumb TERM fallback. Following the maintainer's ansible common role: - Dockerfile.base: install ncurses-term (terminfo for wezterm, alacritty, foot, st, base ghostty entry, and many more). - rootfs/.../terminfo-src/ghostty.terminfo: thin xterm-ghostty alias (use=ghostty) — Ghostty connects as TERM=xterm-ghostty, which no distro packages. Compiled into the system db with 'tic -x'; build asserts it resolved via infocmp. - kitty (xterm-kitty) already covered by kitty-terminfo; iTerm2 uses xterm-256color (ncurses-base). - smoke-test: assert ncurses-term emulators + xterm-ghostty alias resolve. Validated end-to-end in a throwaway container: ncurses-term brings the entries, the alias compiles and equals the ghostty capability set. hadolint clean, bash -n OK. Base-affecting, rebuilds base-<hash>. No tag. |
||
|
|
291ae5345e |
repo: add LICENSE, THIRD_PARTY.md, .dockerignore, hadolint lint, IDEAS backlog
Repo/CI hygiene batch (none base-affecting; image contents unchanged): - LICENSE: actual MIT file (repo previously declared MIT only in prose). - THIRD_PARTY.md: notes bundled software + licenses (pi/pi-fork/pi-obsmem/ pi-studio MIT, gosu Apache-2.0, Debian packages under their own terms). - .dockerignore: trims build context to what the Dockerfiles COPY (rootfs/ + entrypoint*.sh); keeps .git/docs/scripts/compose out. Verified it excludes none of the required COPY sources. - lint.yml: new hadolint job (pinned v2.14.0) lints both Dockerfiles; .hadolint.yaml grandfathers deliberate choices (DL3008/DL3016/DL4006/DL3003/ SC2086, mirroring the shellcheck excludes), fails on anything new at warning+. Verified hadolint exit 0 and the repo shell-guard passes with the new job. - IDEAS.md: parks deferred follow-ups (SHA-pin actions, trivy, buildx SBOM/ provenance, Makefile, renovate). - README/DOCKER_HUB License sections now link LICENSE + THIRD_PARTY.md. No tag. |
||
|
|
38d8832d34 |
ci: record pi-studio version as image label
Lint workflows / actionlint (push) Successful in 1m16s
Adds se.jordbo.pi-devbox.pi-studio-version (e.g. v0.9.36) alongside the existing SHA label, so 'docker inspect' shows the human-readable version. Plumbing: resolve-versions exposes studio_tag output -> both studio build steps pass --build-arg PI_STUDIO_VERSION -> Dockerfile.variant declares ARG PI_STUDIO_VERSION=none and emits the LABEL. Studio-variant only. Validated: yq parse OK, resolve run-script bash -n OK. No tag. |
||
|
|
32586f19e7 |
ci: pin pi-studio to newest semver tag, not main HEAD
Lint workflows / actionlint (push) Successful in 23s
Upstream omaclaren/pi-studio stopped publishing GitHub Releases at v0.5.55 but keeps tagging every version (v0.9.36 now) and pushing to main. Tracking main HEAD risked baking half-finished commits that land after a tag. resolve-versions now lists all tags via a single git ls-remote (the REST tags API paginates at 100 and the repo has >140 tags, so a single page can miss the newest), picks the highest X.Y.Z with sort -V (pre-releases excluded), and peels it to a commit SHA for PI_STUDIO_REF. SHA (not moving tag) keeps cache-busting + reproducibility; require_sha still enforced. Studio-variant only, not base-affecting. No change to the resolved commit today (v0.9.36 == current main HEAD). Validated: yq parse OK, bash -n OK, live git ls-remote -> v0.9.36 -> 2ef38ef. No tag. |
||
|
|
aaf1be0bcb |
base: readable Neovim colours out of the box (termguicolors + kitty-terminfo)
Lint workflows / actionlint (push) Successful in 22s
Vanilla Neovim fell back to a 256-colour palette over ssh/kitty and rendered strings/comments in a muddy, low-contrast colour. Fix, all base-affecting: - rootfs/etc/xdg/nvim/sysinit.vim: system vimrc enabling termguicolors. Loads for every user before any personal ~/.config/nvim; overridable per-user. - Dockerfile.base: install kitty-terminfo (TERM=xterm-kitty understood; lets Neovim auto-detect true colour) and set ENV COLORTERM=truecolor. - smoke-test.sh: assert xterm-kitty terminfo present and nvim tgc default on. - CHANGELOG (Unreleased/Added) + README editor note. No tag — lands on the next base-<hash> rebuild. |
||
|
|
92212fa447 |
base: seed global gitignore with .claude/settings.local.json
Lint workflows / actionlint (push) Successful in 23s
Claude Code's per-machine local settings can carry credentials and should never be committed. Add the pattern to the seeded ~/.gitignore_global so fresh containers match a host global that already ignores it. Existing containers are unaffected (seed copied only when absent). Base-affecting; rebuilds base-<hash>. |
||
|
|
3d46c6615e |
fix(base): default pandoc typst template font so PDF export works without -V mainfont
Lint workflows / actionlint (push) Successful in 21s
pandoc's bundled typst template defaults the document font to an empty tuple (font: ()), so a naked `pandoc --pdf-engine=typst` fails with "font fallback list must not be empty". Patch the template default to Libertinus Serif (typst's own bundled default) at build time so PDF export works out of the box. Document usage in README and note the fix in CHANGELOG (Unreleased). Base-affecting. |
||
|
|
fa6e9dc9d6 |
release: v1.4.0 — typst PDF engine + host SSH startup check
Publish Docker Image / resolve-versions (push) Successful in 6s
Publish Docker Image / base-decide (push) Successful in 9s
Lint workflows / actionlint (push) Successful in 1m9s
Publish Docker Image / build-base (push) Successful in 34m32s
Publish Docker Image / smoke-studio (push) Successful in 4m16s
Publish Docker Image / smoke (push) Successful in 10m52s
Publish Docker Image / build-variant-studio (push) Successful in 18m1s
Publish Docker Image / build-variant (push) Successful in 18m51s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / update-description (push) Successful in 18s
Finalize the Unreleased batch as v1.4.0 (minor — significant base additions). Base rebuilds (Dockerfile.base for typst/xz-utils; .bash_aliases for the SSH check). pi auto-resolves latest (0.80.3 -> 0.80.6); mempalace stays 3.5.0 (current). |
||
|
|
bd0627a557 |
docs: note host SSH startup check in CHANGELOG Unreleased
Lint workflows / actionlint (push) Successful in 1m18s
|
||
|
|
67da05b99b |
feat(base): ship typst as lightweight pandoc PDF engine
Lint workflows / actionlint (push) Successful in 15s
pandoc has been in the base since v1.0.0 but only as a front-end; PDF export (studio_export_pdf / pandoc -o out.pdf) failed with 'xelatex not found' because no back-end engine was installed. Ship typst (~30 MB static Rust binary, no LaTeX) as the default engine via `pandoc --pdf-engine=typst`, chosen over a ~600 MB TeX Live install. texlive-xetex remains the higher-fidelity install-on-demand fallback. - Dockerfile.base: install typst (latest GitHub-release idiom, pin via TYPST_VERSION); add xz-utils (typst ships .tar.xz); bump BASE_REBUILD_DATE. Lands in base-<hash>. - smoke-test.sh: verify typst + a real pandoc --pdf-engine=typst render. - README/AGENTS/CHANGELOG: typst now shipped (supersedes the planned :latest-studio-tex variant). No tag pushed — CI build intentionally deferred. |
||
|
|
6002c6299d |
release: v1.3.0 — shared/external MemPalace + nano/micro editors + CI lint
Publish Docker Image / resolve-versions (push) Successful in 5s
Publish Docker Image / base-decide (push) Successful in 8s
Lint workflows / actionlint (push) Successful in 19s
Publish Docker Image / build-base (push) Successful in 33m17s
Publish Docker Image / smoke (push) Successful in 3m55s
Publish Docker Image / smoke-studio (push) Successful in 6m50s
Publish Docker Image / build-variant (push) Successful in 16m12s
Publish Docker Image / promote-base-latest (push) Successful in 9s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / build-variant-studio (push) Successful in 20m50s
Promotes the Unreleased block to v1.3.0. Bundled contents: - feat: shared/external MemPalace — mempalace.ts bridge honours MEMPALACE_REMOTE_URL; adds docker-compose.mempalace.yml. - feat: nano + micro non-modal editors (Dockerfile.base → base rebuild). - ci: lint.yml (sh-vs-bash guard + actionlint/shellcheck), docker-publish.yml bash defaults, promote-base-latest shell fix. pi stays 0.80.3 (== npm latest). Base rebuilds (mempalace-toolkit ref advanced + Dockerfile.base nano/micro), so the new bridge + editors land in base-<hash>. |
||
|
|
d73bf2e9d3 |
feat: optional shared/external MemPalace via MEMPALACE_REMOTE_URL
Lint workflows / actionlint (push) Successful in 21s
Wire the shared-palace option (implemented in mempalace-toolkit's mempalace.ts) into the container: - .env.example: document MEMPALACE_REMOTE_URL / MEMPALACE_REMOTE_TOKEN (env_file-only, per this repo's convention). - docker-compose.mempalace.yml: optional shared server (mempalace-mcp --transport http), loopback-bound by default. - docker-compose.yml: local-vs-external note on the palace-volume comment. - README + CHANGELOG (Unreleased). |
||
|
|
3a59e15563 |
feat: ship nano + micro (non-modal editors) alongside nvim
Lint workflows / actionlint (push) Successful in 13s
The image shipped only nvim (EDITOR=nvim), a modal vi-style editor. Not everyone is comfortable with vi keybindings, so add both a classic and a modern non-modal option: - nano (apt): ~2.8 MB installed; deps (libc6, libncursesw6, libtinfo6) already present via nvim/less/htop/tmux, so no extra packages pulled in. - micro: ~12 MB single static Go binary from GitHub releases (same pattern as bat/eza/zoxide). Desktop-style keys (Ctrl+S/Ctrl+Q), mouse, syntax highlighting. ARG MICRO_VERSION pins; defaults to latest. Combined ~15 MB (<0.5% of the ~3.2 GB image). EDITOR stays nvim; both new editors are opt-in (export EDITOR=micro | nano). Uses the canonical micro-editor/micro URL because the old zyedidia/micro org rename makes /releases/latest redirect to another /latest, defeating the tag-parsing latest-resolution idiom. Base-image change, so it lands on the next base-<hash> rebuild. Updates README tool table + EDITOR note, CHANGELOG (Unreleased/Added), and smoke-test.sh (nano + micro presence checks). |
||
|
|
26384fe9f1 |
ci: eliminate the sh-vs-bash footgun class (defaults + lint guard)
Lint workflows / actionlint (push) Failing after 34s
Root cause of the recurring 'Illegal option -o pipefail' failures ( |
||
|
|
b33e9dc592 |
fix(ci): promote-base-latest re-tag step needs shell: bash (set -o pipefail)
|
||
|
|
3cdc2069db |
release: v1.2.4 — pi 0.80.2 → 0.80.3; global gitignore, env_file-only secrets, promote-base-latest CI fix
Publish Docker Image / build-variant (push) Successful in 16m16s
Publish Docker Image / promote-base-latest (push) Failing after 4s
Publish Docker Image / build-variant-studio (push) Successful in 17m52s
Publish Docker Image / update-description (push) Successful in 12s
Publish Docker Image / resolve-versions (push) Successful in 1m2s
Publish Docker Image / base-decide (push) Successful in 43s
Publish Docker Image / build-base (push) Successful in 41m46s
Publish Docker Image / smoke (push) Successful in 4m10s
Publish Docker Image / smoke-studio (push) Successful in 6m42s
|