Commit Graph

26 Commits

Author SHA1 Message Date
joakimp 8248688d58 fix(entrypoint,tests): register pi-fork — guard matched its own config block
Lint / actionlint (push) Successful in 32s
Lint / hadolint (push) Successful in 1m25s
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.
2026-07-29 19:21:49 +02:00
joakimp 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.
2026-07-23 08:43:57 +02:00
joakimp 45b6239777 test(smoke): don't hard-code a 'v' prefix on release_tag
Lint / hadolint (push) Successful in 8s
Lint / actionlint (push) Successful in 21s
Publish Docker Image / resolve-versions (push) Successful in 48s
Publish Docker Image / base-decide (push) Successful in 28s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke (push) Successful in 4m44s
Publish Docker Image / smoke-studio (push) Failing after 5m4s
Publish Docker Image / build-variant-studio (push) Has been skipped
Publish Docker Image / build-variant (push) Successful in 29m5s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / promote-base-latest (push) Successful in 15s
The smoke workflow deliberately passes RELEASE_TAG=smoke to the variant
build so smoke images don't collide with real vX.Y.Z tags. The variant
bakes that into /etc/pi-devbox/build-manifest.json, and pi-devbox-version
prints 'pi-devbox smoke' — correct behaviour. But the smoke assertion
required substring 'pi-devbox v', which only holds for real releases.

Assertion never fired before because pi-devbox-version was added after
v1.5.0 (fb49828, 2026-07-15) and every CI attempt since was blocked
before smoke ran (v1.6.0 network flake, v1.6.1 first successful base
build hit this). CI run 509 (v1.6.1) surfaced it.

Fix: require the substring 'pi-devbox ' (space, no v). The two
neighbouring assertions on --json and --quiet already cover the value
of release_tag; this one just verifies the human line renders.
Everything else in 55/56 checks passed on run 509 including
pi 0.81.1 reported and base-229f04e5d021 pushed OK.
2026-07-23 07:58:22 +02:00
joakimp fb49828826 base: add pi-devbox-version command + startup banner
Lint / hadolint (push) Successful in 8s
Lint / actionlint (push) Successful in 1m11s
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.
2026-07-15 14:50:47 +02:00
pi 8c27894cf2 base: support modern terminals (ncurses-term + xterm-ghostty alias)
Lint / hadolint (push) Successful in 8s
Lint / actionlint (push) Successful in 15s
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.
2026-07-13 18:45:38 +02:00
pi 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.
2026-07-12 23:19:19 +02:00
pi 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.
2026-07-11 17:04:19 +02:00
pi 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).
2026-07-01 23:11:25 +02:00
pi cda488c565 base: yq follows latest (was pinned v4.53.3), gate on major v4
Match the repo's latest-following convention (tealdeer/uv/etc.) and keep the
container in sync with the Mac's brew yq. smoke-test now asserts mikefarah AND
major v4, so a surprise yq v5 fails CI instead of silently breaking
provision.sh. Pin still available via --build-arg YQ_VERSION=vX.Y.Z.
2026-06-25 16:33:27 +02:00
pi 9ab9a28458 base: install mikefarah yq (pinned v4.53.3), drop Debian python yq
Debian/Ubuntu `apt install yq` is kislyuk/yq (Python, v3.x), incompatible
with the mikefarah v4 syntax the cloud-init repo's provision.sh/deploy.sh
require. Replace the apt package with a pinned mikefarah Go binary, mirroring
the existing tealdeer ARG (latest-or-pin) pattern, multi-arch amd64/arm64.
smoke-test.sh now asserts `yq --version` reports mikefarah so CI catches a
regression. CHANGELOG: Unreleased entry.
2026-06-25 16:29:51 +02:00
Joakim Persson 7551947466 feat(skills): add mempalace proactive-load directive for containers
Baking the mempalace fallback skill fixed *availability*, but mempalace had
no proactive-load directive anywhere (pi-toolkit's global AGENTS.md only
points to pi-extensions), so a new container would still surface it only via
description-matching — the same under-utilisation the pi-extensions directive
was created to fix.

Add a session-start pointer to the pi-devbox managed AGENTS.md block
(pi-global-AGENTS.append.md): gated to pi-devbox containers and conditional on
the MemPalace MCP tools being present. Memory continuity matters most in a
frequently-recreated container — the palace is its only cross-recreate memory.

- pi-global-AGENTS.append.md: '## Session start: load the mempalace skill'.
- smoke-test: assert the pointer merges into the global AGENTS.md at build.
- docs: VENDORED.md, README, CHANGELOG [Unreleased].

Now both skills are complete in pi-devbox: directive + skill file.
pi-extensions = directive (pi-toolkit) + baked skill; mempalace = directive
(this block) + baked skill.
2026-06-23 15:54:13 +02:00
Joakim Persson a7d6a7d235 feat(skills): bake pi-extensions + mempalace fallback skills
The pi-toolkit global AGENTS.md tells every pi session to read
~/.agents/skills/pi-extensions/SKILL.md at start (the fork/recall
under-utilisation fix), but that skill lived only in the private skillset
repo — so the pointer dangled in any container started without skillset
mounted. Bake fallbacks so the pointer always resolves.

- pi-extensions (Option 1 + Option 2, layered):
  * Canonical skill promoted to the public pi-extensions package repo under
    skill/ (separate commit there); co-located with the code it documents.
  * rootfs/ carries a committed snapshot (the floor).
  * Dockerfile.variant copies /opt/pi-extensions/skill/ over the snapshot
    after the pinned clone, so a normal build ships the fresh package copy
    (recorded via PI_EXTENSIONS_REF) and an old-ref/mirror build still ships
    the snapshot. Helper evaluate-extension-usage.py travels with it.
- mempalace (Option 2 only): snapshot in rootfs/. Its consumer skill has no
  public package home (mempalace-toolkit ships a different skill,
  opencode-mempalace-bridge), so no build-time refresh.
- entrypoint links both (only-when-absent; mounted skillset still wins).
- smoke-test: build-time presence + package-match check + runtime symlink
  assertions; readiness gate now waits on the last-linked skill.
- docs: skills/VENDORED.md (provenance + refresh), README, AGENTS.md,
  CHANGELOG [Unreleased].

Note: shipped in the NEXT release; v1.2.0 (run 409) predates this.
2026-06-23 15:32:04 +02:00
Joakim Persson d619a6e2ec fix(entrypoint,smoke): link image-baked skills early to fix smoke race
Publish Docker Image / resolve-versions (push) Successful in 21s
Publish Docker Image / base-decide (push) Successful in 7s
Publish Docker Image / build-base (push) Successful in 33m43s
Publish Docker Image / smoke-studio (push) Successful in 4m5s
Publish Docker Image / smoke (push) Successful in 5m42s
Publish Docker Image / build-variant (push) Successful in 15m55s
Publish Docker Image / promote-base-latest (push) Successful in 7s
Publish Docker Image / build-variant-studio (push) Successful in 17m45s
Publish Docker Image / update-description (push) Successful in 56s
The runtime 'pi-devbox-environment skill linked' smoke assertion failed in
CI run 408 (gating build-variant). Root cause: the skill-linking block ran
AFTER the pi-toolkit/extensions deploy, but the smoke readiness gate only
waits on pi-deploy markers (keybindings.json, mempalace.ts) — which land
before the skill symlink — so the assertion sampled too early.

- entrypoint-user.sh: move the image-baked-skills symlink loop to run early
  (before the pi deploy block), so it completes before any readiness marker.
  Still before the skillset deploy, so foreign-link semantics are unchanged.
- smoke-test.sh: add the skill symlink to the readiness gate as well.

Build-time checks (baked skill, append snippet, merged AGENTS marker) all
passed in 408; only the timing of the runtime check was wrong.
2026-06-23 14:29:52 +02:00
Joakim Persson 2abfee141b feat: image-baked agent skills + pi-devbox-environment skill (v1.2.0)
Publish Docker Image / resolve-versions (push) Successful in 35s
Publish Docker Image / base-decide (push) Successful in 23s
Publish Docker Image / build-base (push) Successful in 41m32s
Publish Docker Image / smoke-studio (push) Failing after 4m5s
Publish Docker Image / build-variant-studio (push) Has been skipped
Publish Docker Image / smoke (push) Failing after 5m46s
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Ship skills inside the image (independent of any mounted skillset repo):
- rootfs/usr/local/share/pi-devbox/skills/<name>/ symlinked into
  ~/.agents/skills/ by entrypoint-user.sh (foreign-link, survives volume
  recreate, never clobbers a skillset/user skill of the same name).
- New pi-devbox-environment skill: persistence model, host/LAN SSH
  reachability, split-DNS mechanisms, interactive-vs-tool-shell alias
  gotcha, tmux 0-index, uv-first Python, pi-studio reachability. Agnostic
  to host OS / hostnames / domains / nameservers (discovered at runtime).
- Dockerfile.variant appends pi-global-AGENTS.append.md onto pi-toolkit's
  pi-global-AGENTS.md (single global slot) so the skill is loaded
  proactively; gated on /usr/local/lib/pi-devbox/. Idempotent.
- smoke-test: baked-skill + append-snippet + merged-marker presence and a
  runtime symlink assertion.
- docs: README 'Agent skills' section, AGENTS.md layout, DOCKER_HUB.md;
  moved studio-tex roadmap to v1.3.0.

pi 0.79.7 -> 0.79.10 (auto-resolved from npm latest at build).
2026-06-23 12:49:13 +02:00
pi 9eff3f3c48 release: v1.1.6 — build provenance + reproducibility hardening; pi 0.79.7 → 0.79.8
Publish Docker Image / resolve-versions (push) Failing after 52s
Publish Docker Image / base-decide (push) Has been skipped
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Has been skipped
Publish Docker Image / smoke (push) Has been skipped
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / build-variant-studio (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Adds OCI labels + /etc/pi-devbox/build-manifest.json so a published tag is
self-describing and reconstructable after CI logs rotate (manifest is
written from the actual checked-out HEAD of each /opt clone + live
pi --version, not just the intended build-args).

Hardens the build plumbing:
- scripts/check-base-hash.sh guards the base-rebuild invariant: every
  floating ARG *_REF in Dockerfile.base must be folded into the base_tag
  hash, else a ref-only change silently fails to rebuild the base
  (v1.1.2-class staleness footgun). Runs in base-decide and locally.
- resolve-versions now fails loud instead of falling back to a floating
  main/master on a transient API failure — validates each ref is a 40-hex
  SHA (and pi a real semver) and aborts the release otherwise.
- The three gitea companions (pi-toolkit, pi-extensions, mempalace-toolkit)
  gained overridable *_REPO build-args (defaulting to the canonical gitea
  origin) so a relocated/forked build can repoint them without editing the
  Dockerfiles — matching the existing PI_FORK_REPO/PI_OBSMEM_REPO pattern.

README documents the forked/relocated build-arg trick and how to read the
labels + manifest. smoke-test asserts the manifest + labels. pi bumps
0.79.7 → 0.79.8 (auto-resolved at build).
2026-06-19 18:23:11 +02:00
pi 7d8ee4cea1 feat(studio): bundle studio-expose bridge + socat (opt-in STUDIO_EXPOSE)
pi-studio binds the container's 127.0.0.1, which a published Docker port
can't reach. Add a robust, portable bridge rather than a doc-only one-liner:

- Dockerfile.base: add socat (~1 MB, generally useful TCP relay).
- rootfs/usr/local/bin/studio-expose: socat TCP relay listening on the
  container's egress IPv4 (not 0.0.0.0 — that would EADDRINUSE against
  Studio's loopback listener) forwarding to 127.0.0.1:PORT on the SAME
  port, so Studio's printed token URL works verbatim. Robust egress-IP
  detection (hostname -I, loopback-filtered; ip route get fallback),
  --help, port validation, foreground.
- entrypoint-user.sh: opt-in STUDIO_EXPOSE=1 auto-starts the bridge in the
  background (studio variant only). Default OFF — Studio stays loopback-only
  (its secure default) unless explicitly opted in.
- README: 'Using pi-studio' now documents host-networking (A) and the
  studio-expose/STUDIO_EXPOSE bridge (B) with a security note; ssh -L for
  remote, mosh caveat retained.
- smoke-test: assert socat + studio-expose present (base-level).
- CHANGELOG/AGENTS updated.

No tag — stopping for review.
2026-06-10 23:33:44 +02:00
pi a78e59fb5b feat(studio): add :latest-studio variant (PR-3)
Bundle pi-studio (omaclaren/pi-studio) as a new -studio image variant:
browser prompt editor, KaTeX/Mermaid preview, tmux-backed literate REPLs,
/studio command + studio_* agent tools.

- Dockerfile.variant: INSTALL_STUDIO + PI_STUDIO_REPO/REF args; vendor
  pi-studio to /opt/pi-studio (no build step — prebuilt client in git;
  npm install --omit=dev for 3 prod deps). STUDIO_PORT=8765 advisory.
- entrypoint-user.sh: register /opt/pi-studio via the existing pi install
  local-path loop (auto-skips in non-studio variant).
- smoke-test.sh: auto-detected studio assertions (clone + prebuilt client
  + pi install registration).
- CI: resolve PI_STUDIO_REF to a SHA; independent smoke-studio +
  build-variant-studio jobs that gate ONLY the -studio tags, so a studio
  failure never blocks the core :latest release.
- README: 'Using pi-studio' section documenting the container access
  reality — pi-studio hard-binds 127.0.0.1 (index.ts .listen(port,
  '127.0.0.1'), no --host flag), so -p publish alone can't reach it.
  Documents host-networking and loopback-bridge paths, the remote ssh -L
  forward, and the mosh caveat (no port forwarding; run parallel ssh -L).
- CHANGELOG/AGENTS/DOCKER_HUB updated. Will tag as v1.1.0 (minor).

No tag created — stopping for review.
2026-06-10 23:15:29 +02:00
pi c1154f1fa6 v1.0.0: decouple from opencode-devbox
Publish Docker Image / resolve-versions (push) Successful in 5s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 45m47s
Publish Docker Image / smoke (push) Successful in 8m18s
Publish Docker Image / build-variant (push) Successful in 22m41s
Publish Docker Image / update-description (push) Failing after 9s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Self-contained build chain — own Dockerfile.base + Dockerfile.variant
+ entrypoint scripts + rootfs + CI pipeline. Previously v0.79.0 and
earlier were thin re-brands of opencode-devbox's pi-only variant
(joakimp/pi-devbox:base-pi-only built by opencode-devbox CI).

Architectural changes:
- Replace 5-line Dockerfile shim with full base+variant pair.
- Adapt CI workflow from opencode-devbox/docker-publish-split.yml,
  simplified to a single variant. Includes content-addressed base hash,
  PI_VERSION concrete-resolution to defeat registry-buildcache footgun,
  crane-based base-latest promotion, and the c6f9d11 smoke-test gate.
- pi-devbox releases no longer require rebuilding opencode-devbox first.

Base image additions:
- pandoc, graphviz, imagemagick, yq — broadly useful, ~260 MB total.
- tldr (tealdeer) — Rust port replaces Node tldr global, saves 135 MB.
- /etc/tmux.conf with base-index 0 + pane-base-index 0 — required for
  the planned :latest-studio variant; pi-studio hard-codes :0.0 target.

Smoke test:
- New checks for pandoc, graphviz, imagemagick, yq, tldr, tmux config,
  /tmp/sshcm directory.
- Image-size measurement now sums docker history layers (the prior
  inspect --format='{{.Size}}' returned only the variant-unique layer
  with the new base/variant split, understating by 2+ GB).
- Threshold 2850 → 3500 MB to absorb base additions + arch margin.

Image size:
- Local arm64 build: 3.20 GB. ~390 MB up from prior pi-only equivalent.
- Will tighten threshold once amd64 actuals settle in CI.

Pre-1.0 history preserved at tag pre-v1.0.0-decouple-backup.

Future work:
- v1.1.0: :latest-studio variant (adds pi-studio).
- v1.2.0: :latest-studio-tex variant (adds texlive-xetex for PDF).
- opencode-devbox v2.0.0 will retire INSTALL_PI / pi-only paths.
2026-06-10 01:14:07 +02:00
pi 36afd3c716 Release v0.79.0 — pi 0.78.1 -> 0.79.0
Publish Docker Image / smoke (push) Successful in 2m56s
Publish Docker Image / publish (push) Successful in 10m38s
Publish Docker Image / update-description (push) Successful in 6s
First build on pi 0.79.0. Built FROM the republished base-pi-only from
opencode-devbox v1.16.2 (carries pi 0.79.0). Bump smoke size threshold
2750 -> 2850 MB in lockstep with opencode-devbox's pi-only variant.

Promote CHANGELOG Unreleased -> v0.79.0.
2026-06-08 22:00:24 +02:00
pi 1d1283f942 refactor: FROM opencode-devbox:latest-pi-only (lean, no opencode)
Re-point the re-brand at the new pi-only variant instead of with-pi, so
pi-devbox stays a lean pi-focused image (no opencode) while the pi install
logic still lives in one place upstream. This keeps pi-devbox meaningfully
distinct from opencode-devbox:latest-with-pi.

- Dockerfile: BASE_IMAGE default -> joakimp/opencode-devbox:latest-pi-only.
- smoke-test.sh: size threshold 2900 -> 2750 MB (pi-only = with-pi minus
  opencode's ~145 MB binary).
- Docs (README/AGENTS/DOCKER_HUB/CHANGELOG/docker-compose): drop the
  'also contains opencode' notes; describe pi-only basis and the distinction
  from with-pi.

Publish ordering unchanged: release opencode-devbox first so latest-pi-only
carries the target pi version, then tag here (smoke asserts pi --version).
2026-06-03 16:14:05 +02:00
pi c139be326f refactor: re-brand the opencode-devbox with-pi variant (single source of truth)
pi-devbox no longer installs pi itself. The Dockerfile is now a thin
FROM joakimp/opencode-devbox:latest-with-pi (overridable via BASE_IMAGE),
inheriting pi + pi-toolkit + pi-extensions + pi-fork (fork) +
pi-observational-memory (recall) + the LAN-access helper + all base tooling
from the single source of truth. Eliminates the install-logic duplication
that drifted against opencode-devbox/Dockerfile.variant (decision #3).

Consequences (documented in CHANGELOG/AGENTS):
- The image now ALSO contains opencode (with-pi has INSTALL_OPENCODE=true).
  A leaner pi-only image would need a dedicated pi-only variant upstream.
- Publish ordering: release opencode-devbox first so latest-with-pi carries
  the target pi version, THEN tag this repo. The smoke test asserts
  pi --version matches the tag (EXPECTED_PI_VERSION) and fails loudly if the
  base is stale — turning the version coupling into an enforced ordering guard.

CI: drop PI_VERSION build-arg (Dockerfile installs nothing); keep tag->version
resolution to feed the smoke base-freshness guard. Smoke adds fork/recall
clone + node_modules + settings.json registration checks; size threshold
2200 -> 2900 MB (now tracks with-pi). Docs updated across README, AGENTS,
DOCKER_HUB, .env.example, docker-compose.
2026-06-03 15:51:41 +02:00
joakimp 34cae2a1d2 Cut v0.75.5b — fix cache-hit silent same-bytes regression
Publish Docker Image / smoke (push) Successful in 2m18s
Publish Docker Image / publish (push) Successful in 12m59s
Publish Docker Image / update-description (push) Successful in 11s
ALL FOUR releases v0.74.0 -> v0.75.5 had been shipping the same image
bytes due to a Docker layer-cache hit on the bare 'npm install -g
@earendil-works/pi-coding-agent' command (when PI_VERSION=latest).
The command string is identical across builds, so the layer-hash is
identical, so registry buildcache (cache-from/cache-to) silently
reuses the layer from whatever pi version was current when the cache
was first populated.

Verification: docker manifest inspect joakimp/pi-devbox:vX.Y.Z showed
identical SHA256 digests on both linux/amd64 and linux/arm64 for
v0.74.0, v0.75.3, v0.75.4, v0.75.5. Users on :latest were getting
whatever pi version was baked into the v0.74.0 build.

DISCOVERED 2026-05-23 by user trying to update pi-devbox on MBP-M1
and seeing pi 0.74.0 reported despite pulling v0.75.5.

CHANGES

.gitea/workflows/docker-publish.yml — both smoke and publish jobs
get a new 'Resolve PI_VERSION from tag' step that strips the leading
'v' and any trailing letter suffix from github.ref_name. Result is
passed as a build-arg to docker/build-push-action so the npm install
layer's hash includes the concrete version, forcing cache miss when
pi bumps.

scripts/smoke-test.sh — new run_expect helper that asserts pi
--version contains the EXPECTED_PI_VERSION env var. Smoke job sets
this from the resolve step output. Would have caught this regression
on v0.75.3.

Dockerfile — comment block above ARG PI_VERSION=latest documenting
the cache-hit footgun. The 'if latest' branch in the install RUN is
preserved for local dev convenience but never fires in CI now.

AGENTS.md — new convention bullet explaining the cache-hit class of
bug and noting the latent same-bug in opencode-devbox's with-pi
variants (currently masked by OPENCODE_VERSION bumps; will manifest
when cutting a vN.N.Nb-style opencode-version-unchanged release that
only bumps pi).

CHANGELOG.md — full entry under v0.75.5b describing the recovery,
the silent-failure mechanism, and the verification steps.

NO IMAGE-CONTENT CHANGES vs v0.75.5 INTENT. This build produces the
actual pi 0.75.5 image content that v0.75.5 was supposed to ship.

NEXT FOLLOWUP (parked, not in this commit)

opencode-devbox should get the same workflow change for its
build-variant-with-pi and build-variant-omos-with-pi jobs. Currently
masked because every release also bumps OPENCODE_VERSION which
invalidates the cache, but that masking would fail on a pi-only bump
release.
2026-05-23 22:10:08 +02:00
joakimp 8ab0f9c760 smoke-test: docker exec -u developer for runtime deployment checks
Publish Docker Image / smoke (push) Successful in 5m22s
Publish Docker Image / publish (push) Failing after 10m40s
Publish Docker Image / update-description (push) Has been skipped
Without -u, docker exec runs as root and $HOME expands to /root, so
the test looks for ~/.pi/agent/keybindings.json under /root instead
of /home/developer. install.sh actually deploys correctly — the test
was just probing the wrong home directory.

Match opencode-devbox/scripts/smoke-test.sh's pattern.
2026-05-15 01:01:54 +02:00
joakimp aa3f4b7b90 smoke-test: keep baked entrypoint when probing runtime deployment
Publish Docker Image / smoke (push) Failing after 1m17s
Publish Docker Image / publish (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
The previous `docker run -d --entrypoint="" ... sleep 60` bypassed the
entrypoint chain entirely, so entrypoint-user.sh never ran, so
pi-toolkit/install.sh never deployed keybindings + extensions to
~/.pi/agent/. Result: 4/14 smoke checks always failed.

Match opencode-devbox's pattern: `docker run -d --rm "$IMAGE" tail -f
/dev/null` keeps the entrypoint chain intact and overrides only CMD.
2026-05-15 00:52:14 +02:00
joakimp 9302b3ebb9 smoke-test: replace ((VAR++)) with VAR=$((VAR+1)) to avoid set -e exit
Publish Docker Image / smoke (push) Failing after 4m31s
Publish Docker Image / publish (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
((VAR++)) returns the OLD value, so when PASS=0 the first  check
caused exit code 1, killing the script under set -e. Use VAR=$((VAR+1))
which always returns 0. Same pattern as opencode-devbox's smoke-test.
2026-05-15 00:40:28 +02:00
joakimp 7b634605b7 Initial commit: pi-devbox v0.74.0
Publish Docker Image / smoke (push) Failing after 27s
Publish Docker Image / publish (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
pi coding-agent container built on opencode-devbox:base-latest.
Includes Dockerfile, docker-compose, CI workflow, smoke-test,
README, CHANGELOG, AGENTS.md.
2026-05-14 19:57:17 +02:00