- Containerized Pi hello-world proof (image mosaic-poc-agent:0.84.4, non-root) - Four immutable contract fixtures loaded into a generated system prompt - build/hello/verify/reset scripts with exact-match gating and reset safety - Documented Pi discovery (v0.84.4, -p mode, --system-prompt, container auth) - Append-only BUILD-LOG with corrections; deferred layers in LAYERS.md - Architecture plan: docs/plans/2026-09-02_atomic-mosaic-foundation.md
8.9 KiB
8.9 KiB
BUILD-LOG
Append-only build log for the Minimal Mosaic Stack container proof of concept. Each phase records the plan before it runs and the observed result after it runs. No credentials are recorded in this file.
Phase 1: Pi package discovery
Entry 1.1 — before
- Timestamp: 2026-02-02 (session start, local)
- Intended action: Locate the current package documentation for
@earendil-works/pi-coding-agent, determine the current version, the supported noninteractive command, the custom system prompt mechanism, and the documented container authentication method. - Reason: The brief forbids guessing CLI flags, authentication paths, or SDK methods; all runtime commands must be derived from the package documentation.
- Expected result: Documented answers for all five discovery questions, recorded below, with the Pi package version pinned in the project.
Entry 1.2 — after
- Timestamp: 2026-02-02
- Commands run:
- Read
README.mdof the locally installed@earendil-works/pi-coding-agentpackage - Read
docs/containerization.md, excerpts ofdocs/models.mdanddocs/providers.md grep '"version"' .../pi-coding-agent/package.jsonnpm view @earendil-works/pi-coding-agent version- Inspected host auth store structure (keys only, values never printed)
- Read
- Observed result:
- Package documentation: full docs ship inside the installed package (
README.mdplus adocs/directory includingdocs/containerization.md). - Current package version:
0.84.4— the locally installed version and the npm registry latest are identical. Selected version to pin:0.84.4. - Supported noninteractive command:
pi -p/pi --print— "Print response and exit". Documented in the CLI Reference. Print mode also merges piped stdin into the initial prompt (not used here). - Custom system prompt: documented CLI flag
--system-prompt <text>— "Replace default prompt (context files and skills still appended)". Because the generated contract prompt must fully control behavior, the run also passes the documented discovery-off switches (--no-context-files,--no-skills,--no-extensions,--no-prompt-templates,--no-themes) so nothing is appended from ambient files. (APPEND_SYSTEM.md/.pi/SYSTEM.mdfile conventions exist but the CLI flag is the documented noninteractive path.) - Documented container authentication:
docs/containerization.mddocuments the "Plain Docker" pattern: run the wholepiprocess in the container and pass provider API keys at runtime via environment variables (example:docker run -e ANTHROPIC_API_KEY ...). The documented alternative credential store isauth.jsonwritten by/loginunder the pi agent directory (~/.pi/agent/auth.json).
- Package documentation: full docs ship inside the installed package (
- Decision: supply the credential at runtime as a read-only mounted credential file (host
auth.json, which contains a static API-key entry for the built-inzaiprovider) mounted at the container pi agent directory, and additionally allow the documented environment-variable path (ZAI_API_KEY/ANTHROPIC_API_KEY) as an alternative. Provider and model are non-secret settings supplied via.env(PI_PROVIDER=zai,PI_MODEL=glm-5.3-flash). - Failure or correction: none. Host check confirmed no API-key environment variables are exported on the host, so the read-only auth.json mount is the working runtime credential path for this experiment.
Phase 2: Project scaffold
Entry 2.1 — before
- Timestamp: 2026-02-02
- Intended action: Create the contract fixtures (exact brief contents), the contract loader (
src/load-contracts.sh), the one-shot agent runner (src/run-agent.sh), the four required scripts (scripts/build.sh,hello.sh,verify.sh,reset.sh),Containerfile,compose.yaml, pinnedpackage.json+package-lock.json,.gitignore,README.md,LAYERS.md. - Reason: Implement exactly the file set the brief requires, with no extra machinery (no schemas, overlays, manifests, or policy loading).
- Expected result: A complete project whose only remaining unknown is whether the pinned image builds and the real model request returns
MOSAIC_HELLO_OK.
Entry 2.2 — after
- Timestamp: 2026-02-02
- Commands run: file creation;
npm install --package-lock-only --ignore-scriptsto generate the lockfile from the pinned dependency. - Observed result: All files created;
package-lock.jsonpins@earendil-works/[email protected](exact, no range). - Failure or correction: none.
Phase 3: Container image build
Entry 3.1 — before
- Timestamp: 2026-02-02
- Intended action: Run
scripts/build.sh(Docker Compose build) to produce imagemosaic-poc-agent:0.84.4fromnode:24-bookworm-slimwith the pinned Pi, the four contract fixtures at/opt/mosaic/contracts, and a non-root user (uid/gid 1000). - Reason: Phase 1 of the required proof path;
node:24-bookworm-slimis the maintained base image used in Pi's own documented containerization example. - Expected result:
docker compose buildexits 0 and the image contains the contracts, the runner scripts, and the pinnedpibinary, with no credentials baked in.
Entry 3.2 — after
- Timestamp: 2026-02-02
- Commands run:
scripts/build.sh;docker run --rm mosaic-poc-agent:0.84.4 --version;idvia--entrypoint; contract listing; credential file scan. - Observed result:
- Build exit 0; image tagged
mosaic-poc-agent:0.84.4. pi --versioninside the image reports0.84.4(and this run also executed the contract loader successfully, writing/var/lib/mosaic/system-prompt.md).- Container user is
uid=1000(node) gid=1000(node)— non-root. - All four contract files present at
/opt/mosaic/contractswith read-only permissions (0555). - Credential scan: no
auth.jsonor other auth files exist in the image;/home/node/.pi/agent/is empty in the image.
- Build exit 0; image tagged
- Failure or correction:
- First build failed: Docker Compose expects
Dockerfileby default; fixed by settingbuild.dockerfile: Containerfileincompose.yaml. - Second build failed:
useraddexit 4 (uid 1000 already exists) because the maintained node image ships anodeuser at uid/gid 1000. Fixed by reusing the built-innodeuser (same 1000:1000 host mapping) instead of creating a duplicatemosaicuser; container paths updated from/home/mosaic/...to/home/node/...inContainerfile,compose.yaml,README.md,.env.example.
- First build failed: Docker Compose expects
Phase 4: Runtime verification
Entry 4.1 — before
- Timestamp: 2026-02-02
- Intended action: Run
scripts/hello.sh(one-shot request: "Return your startup marker and nothing else."), thenscripts/verify.sh(exact-match gate againstMOSAIC_HELLO_OK), then the negative test (EXPECTED_MARKER=MOSAIC_NOT_OK scripts/verify.shmust exit nonzero), then thescripts/reset.shsafety tests and a final rerun after reset. - Reason: Phases 2–7 of the required proof path plus acceptance criteria 5–11.
- Expected result: hello prints only the marker; verify exits 0; negative test exits nonzero; reset refuses unsafe paths and succeeds on the real path; rerun after reset reproduces the success.
Entry 4.2 — after
- Timestamp: 2026-02-02
- Commands run:
scripts/hello.sh;scripts/verify.sh;EXPECTED_MARKER=MOSAIC_NOT_OK scripts/verify.sh;scripts/reset.sh(refusal tests: missing marker, symlink with canary file, then real reset, then missing dir);scripts/build.sh && scripts/verify.shafter reset;docker compose configmount inspection. - Observed result:
hello.sh: stdout exactlyMOSAIC_HELLO_OK— a real model request (providerzai, modelglm-5.3-flash, auth via the read-only mounted auth.json credential file). The request string contains no marker.verify.sh:PASS: response matches expected marker, exit 0.- Negative test:
FAIL: response does not match expected marker(expectedMOSAIC_NOT_OK, actualMOSAIC_HELLO_OK), exit 1. reset.shrefusal tests: missing marker → exit 1, nothing deleted; symlink (with canary file at the target) → exit 1, canary survived; real path with marker → removed, exit 0; missing dir → "nothing to remove", exit 0.- Rerun after reset: build + verify → PASS, exit 0 (criterion 11).
- Resolved compose mounts: only
/home/jwoltje/.mosaic-dev → /var/lib/mosaic(rw) and~/.pi/agent/auth.json → /home/node/.pi/agent/auth.json(read-only). No~/.mosaicor~/.config/mosaicmounts, no Docker socket.
- Failure or correction:
- First hello run: the contract loader's status line was printed on stdout, mixing runtime data into the model response stream and contaminating the exact-match capture. Fixed by sending the loader's status message to stderr (
src/load-contracts.sh), rebuilt the image, reran: stdout is exactly the model response.
- First hello run: the contract loader's status line was printed on stdout, mixing runtime data into the model response stream and contaminating the exact-match capture. Fixed by sending the loader's status message to stderr (
- Credential check: no credential material appears in this log, in hello/verify output, or in the image (image scan found no auth files).
Result
All 11 acceptance criteria demonstrated. The real model request passed.