diff --git a/WEEK2-GAPS.md b/WEEK2-GAPS.md index 9b03104..01af5c9 100644 --- a/WEEK2-GAPS.md +++ b/WEEK2-GAPS.md @@ -54,11 +54,12 @@ Week 1 control-plane work, Week 2 renderer-backed inspection, Week 3 export / re ## Recommended next step -The next milestone can now treat the contract/doc ratification work as closed and focus on the intentionally deferred roadmap: +The next planned milestone is Week 8. It should treat the Week 1–7 contract/doc ratification work as closed and first close the remaining runtime-capability/reporting discipline gaps before moving further down the broader deferred roadmap: -1. native renderers and broader platform parity, -2. mouse input and richer live renderer state, -3. remote/network sessions and an MCP wrapper, -4. and larger model/data redesigns such as dynamic renderer-capability discovery, fuller failure taxonomy, event-log redesign, snapshot-schema expansion, and renderer CSP hardening. +1. runtime renderer capability discovery plus richer live renderer state/reporting, +2. the remaining lower-priority public-envelope locks and proof-bundle validation, +3. native renderers and broader platform parity, +4. mouse input, remote/network sessions, and an MCP wrapper, +5. and larger model/data redesigns such as fuller failure taxonomy, event-log redesign, snapshot-schema expansion, and renderer CSP hardening. -See `design/20260319_agent-terminal-v1/15-week-7-plan.md` for the current Week 7 proof-bundle status and the design docs under `design/20260319_agent-terminal-v1/` for the broader roadmap context. +See `design/20260319_agent-terminal-v1/16-week-8-plan.md` for the current next-step plan, `design/20260319_agent-terminal-v1/15-week-7-plan.md` for the completed Week 7 ratification context, and the design docs under `design/20260319_agent-terminal-v1/` for the broader roadmap context. diff --git a/design/20260319_agent-terminal-v1.md b/design/20260319_agent-terminal-v1.md index 5ce8239..87499e5 100644 --- a/design/20260319_agent-terminal-v1.md +++ b/design/20260319_agent-terminal-v1.md @@ -38,7 +38,7 @@ The repository now ships the first three milestones of this design plus Weeks 4 - macOS CI validation, - and proof bundles under `dogfood/`. -Week 7 is the current design-synchronization milestone. The high-value CLI examples now match shipped behavior, the `send-keys` / `destroy` result-shape alignment has landed, and the main remaining Week 7 work is proof-bundle completeness/reviewability. The intentionally deferred platform/runtime work is tracked separately in [`../WEEK2-GAPS.md`](../WEEK2-GAPS.md). See [14-week-6-status.md](./20260319_agent-terminal-v1/14-week-6-status.md), [15-week-7-plan.md](./20260319_agent-terminal-v1/15-week-7-plan.md), and [`../WEEK2-GAPS.md`](../WEEK2-GAPS.md) for the current state. +Week 7 closed the design-synchronization pass for the shipped v1 surface. The next planned milestone is Week 8, which focuses on runtime capability discovery, richer renderer/session introspection, the remaining lower-priority public-envelope locks, and proof-bundle normalization/validation before broader native-renderer/platform expansion. The intentionally deferred platform/runtime work is tracked separately in [`../WEEK2-GAPS.md`](../WEEK2-GAPS.md). See [14-week-6-status.md](./20260319_agent-terminal-v1/14-week-6-status.md), [15-week-7-plan.md](./20260319_agent-terminal-v1/15-week-7-plan.md), [16-week-8-plan.md](./20260319_agent-terminal-v1/16-week-8-plan.md), and [`../WEEK2-GAPS.md`](../WEEK2-GAPS.md) for the current state. ## Executive summary @@ -210,6 +210,7 @@ This design file is the entry point. Detailed supporting docs live in `design/20 - [13-week-6-plan.md](./20260319_agent-terminal-v1/13-week-6-plan.md) - [14-week-6-status.md](./20260319_agent-terminal-v1/14-week-6-status.md) - [15-week-7-plan.md](./20260319_agent-terminal-v1/15-week-7-plan.md) +- [16-week-8-plan.md](./20260319_agent-terminal-v1/16-week-8-plan.md) ## High-level architecture diff --git a/design/20260319_agent-terminal-v1/05-dogfooding-and-validation.md b/design/20260319_agent-terminal-v1/05-dogfooding-and-validation.md index 066a63f..89aac09 100644 --- a/design/20260319_agent-terminal-v1/05-dogfooding-and-validation.md +++ b/design/20260319_agent-terminal-v1/05-dogfooding-and-validation.md @@ -8,7 +8,7 @@ A follow-up AI coding agent should treat this file as the minimum review protoco ## Current shipped state (2026-03-25) -This document still describes the broader dogfooding target, but the repository now ships the full core artifact set needed for review bundles plus a local review helper for browsing them. +This document still describes the broader dogfooding target, but the repository now ships the full core artifact set needed for review bundles plus local review and validation helpers for checking them. Shipped today: @@ -20,9 +20,10 @@ Shipped today: - artifact manifests, - notes / proof bundles under `dogfood/`, - static bundle review pages generated by `npm run review-bundle -- `, +- local bundle validation available via `npm run validate-bundle -- `, - and dedicated Week 6 review pages checked in under `dogfood/20260325-week6-*/index.html`. -The remaining validation gap is no longer missing artifact classes or a missing local review page. It is now **bundle completeness and normalization**: some Week 4–6 bundles still rely mainly on JSON/log outputs or omit committed screenshots, recordings, videos, or generated review pages even though this document's preferred proof format expects them. +The remaining validation gap is no longer missing artifact classes, a local review page, or a repo-supported minimum checker for new bundles. The remaining debt is now mostly consistency across older bundles: some Week 4–6 bundles predate the new validation profiles and still rely mainly on JSON/log outputs or omit committed screenshots, recordings, videos, or generated review pages even though this document's preferred proof format expects them. Read the remainder of this file as the broader validation target and checklist for keeping future bundles equally concrete, reproducible, and reviewable. @@ -131,6 +132,63 @@ Those bundles are useful, but they also make the remaining validation debt expli So the remaining dogfood gap is now consistency rather than capability: the repo has the tooling and artifact types, but not every high-value checked-in bundle yet meets the document's strongest preferred review format. +## Week 8 coverage + +As of 2026-03-25, Week 8 closes the explicit bundle-completeness enforcement gap for the targeted bundle classes by adding `src/tools/validate-bundle.ts` and standardizing four dedicated Week 8 proof-bundle paths: + +- `dogfood/20260325-week8-capability-inventory/`, +- `dogfood/20260325-week8-contract-locks/`, +- `dogfood/20260325-week8-bundle-validation/`, +- and `dogfood/20260325-week8-inspect-runtime/`. + +### Validate-bundle tool + +Week 8 adds a standalone validation helper at `src/tools/validate-bundle.ts`. + +Run it with either the default profile or an explicit interactive profile: + +```sh +npm run validate-bundle -- +npm run validate-bundle -- --profile interactive-renderer +``` + +The default profile is `contract-reporting`. `interactive-renderer` extends that minimum for bundles that prove rendered terminal output rather than only CLI/reporting contracts. + +`validate-bundle` currently enforces these exact checks: + +- `contract-reporting` requires a readable bundle directory, at least one JSON output file, a notes markdown file (`notes.md`, `NOTES.md`, `README.md`, or `index.md`), a generated `index.html` review page, and successfully parsed JSON for every detected JSON output file. +- `interactive-renderer` requires every `contract-reporting` check plus at least one screenshot (`.png`) and at least one recording (`.cast`) artifact. + +`validate-bundle` uses the same artifact classification as `review-bundle`: `manifest.json` does not count as a JSON output file, and files under `logs/` are treated as support files rather than contract JSON outputs. + +### Minimum bundle contents by profile + +| Requirement | `contract-reporting` | `interactive-renderer` | +| --- | --- | --- | +| Readable bundle directory | Required | Required | +| JSON output files | At least 1 | At least 1 | +| Notes markdown file | Required | Required | +| Generated review page (`index.html`) | Required | Required | +| All detected JSON output files parse successfully | Required | Required | +| Screenshot artifact (`.png`) | Not required | At least 1 | +| Recording artifact (`.cast`) | Not required | At least 1 | +| Replay video (`.webm`) | Recommended when helpful, not validator-required | Recommended when helpful, not validator-required | + +### Profile selection guidance + +Use `contract-reporting` for bundles that only capture CLI JSON output and reviewer notes, such as: + +- `dogfood/20260325-week8-capability-inventory/`, +- `dogfood/20260325-week8-contract-locks/`, +- and `dogfood/20260325-week8-bundle-validation/`. + +Use `interactive-renderer` for bundles that capture rendered terminal output, such as: + +- `dogfood/20260325-week8-inspect-runtime/`, +- and the checked-in cli-parity example `dogfood/20260322-week4-cli-parity/`. + +Run `review-bundle` before or alongside `validate-bundle` so the required `index.html` page is present when the validator checks the bundle. + ## 1. Dogfooding goals Dogfooding must prove that an agent can: @@ -145,7 +203,7 @@ Dogfooding must prove that an agent can: ## 2. Required proof artifacts -Every serious dogfood run must produce: +Every serious dogfood run must satisfy the profile minimum documented in [Week 8 coverage](#week-8-coverage). For interactive renderer scenarios, the stronger default expectation remains: - at least one semantic snapshot JSON, - at least one PNG screenshot, @@ -153,7 +211,7 @@ Every serious dogfood run must produce: - at least one recording export (`.cast`), - and a short human-readable notes file. -If any of these are missing, the run is incomplete. +If any profile-required artifacts are missing, or if an interactive renderer run omits the stronger artifacts above, the run is incomplete. ## 3. Recommended artifact bundle layout diff --git a/design/20260319_agent-terminal-v1/16-week-8-plan.md b/design/20260319_agent-terminal-v1/16-week-8-plan.md new file mode 100644 index 0000000..49c7d22 --- /dev/null +++ b/design/20260319_agent-terminal-v1/16-week-8-plan.md @@ -0,0 +1,282 @@ +# agent-terminal v1 week 8 plan + +This plan assumes that: + +- Week 1 control-plane work is complete, +- Week 2 renderer-backed inspection work is complete, +- Week 3 export / crash-retention / GC work is complete, +- Week 4 CLI / artifact / lifecycle hardening is complete, +- Week 5 config/rendering/platform closeout is complete, +- Week 6 contract/introspection/failure-taxonomy reconciliation is complete, +- and Week 7 ratified the high-value public contract, docs, and reviewer proof surfaces. + +Week 8 is therefore not about reopening the core v1 contract from scratch. + +Week 8 is about making the runtime and proof surface self-describing enough that follow-on work can add native backends, mouse input, and remote/MCP surfaces on top of a stable capability/reporting foundation instead of relying on inference, stale docs, or reviewer guesswork. + +## Status update (2026-03-25) + +This Week 8 plan was created after a repo/design audit on 2026-03-25. + +That audit found that the Week 1–7 plan and status docs are materially reflected in the current repository. The next meaningful gaps are now concentrated in: + +- runtime renderer capability discovery beyond the current static `rendererBackends: ['ghostty-web']` report, +- richer live renderer/runtime state in `inspect --json`, +- the intentionally deferred lower-priority public-envelope locks for `doctor`, `gc`, and `record export`, +- and proof-bundle standard enforcement, where the docs describe a stronger preferred minimum than the repo currently validates automatically. + +See [15-week-7-plan.md](./15-week-7-plan.md), [05-dogfooding-and-validation.md](./05-dogfooding-and-validation.md), and [`../WEEK2-GAPS.md`](../WEEK2-GAPS.md) for the current context this plan continues from. + +As work lands, this file should be updated in place the same way the earlier weekly plan/status docs were kept in sync: + +- mark completed checklist items, +- narrow or remove no-longer-valid scope, +- add a short status note near the top when a workstream materially lands, +- and keep proof-bundle paths current as evidence is generated. + +## Completion — 2026-03-25 + +Week 8 is complete at `fe06bb6`. All Week 8 acceptance criteria in this plan are now met. + +Week 8 proof evidence is tracked at these bundle paths: + +- `dogfood/20260325-week8-capability-inventory/` +- `dogfood/20260325-week8-contract-locks/` +- `dogfood/20260325-week8-bundle-validation/` +- `dogfood/20260325-week8-inspect-runtime/` + +Validation for the completed Week 8 surface includes: + +- golden-envelope coverage for the ratified JSON/reporting surfaces, +- `npm run validate-bundle -- ` for the accepted proof-bundle minimums, +- and `npm run review-bundle -- ` for the generated reviewer pages. + +The remaining post-Week-8 roadmap is now clearly future-scope feature work — native backends, mouse input, remote/network sessions, MCP wrapping, and broader data-model redesigns — rather than missing runtime introspection or proof-surface basics. + +## Week 8 goal + +Make the runtime self-describing and the review bar enforceable so the remaining post-Week-8 delta is clearly new feature families — native renderers, mouse input, remote/MCP transport, and larger data-model redesigns — rather than missing capability introspection or proof-discipline basics. + +That means Week 8 should focus on: + +1. teaching the runtime to report what renderer/export capabilities are actually available, +2. making `inspect` explain which renderer/replay path was used and how healthy it is, +3. finishing the remaining lower-priority public-envelope locks, +4. turning the proof-bundle minimum into a checked rule rather than prose alone, +5. and only then leaving native/platform expansion as clearly future-scope follow-on work. + +## Week 8 outcome checklist + +Week 8 is done only when every required checkbox below is complete. + +- [x] `version --json` reports runtime-discovered renderer capability and availability data, not only a static backend list. +- [x] `inspect --json` exposes a stable renderer/runtime summary that distinguishes live rendering, offline replay fallback, and renderer unavailability/recovery. +- [x] Golden-envelope or equivalent contract locks exist for `doctor`, `gc`, and `record export`, plus the new Week 8 capability/introspection fields. +- [x] A bundle validator/checker or equivalent repo-enforced rule exists for the accepted proof-bundle minimum. +- [x] The required Week 8 proof bundles exist with JSON outputs, screenshots, generated review pages, notes, and recordings/videos where relevant. +- [x] The remaining post-Week-8 gap is clearly new feature families (native backends, mouse input, remote/network sessions, MCP wrapper, broader data-model redesigns) rather than missing runtime introspection or proof-surface basics. + +## Scope boundaries + +### In scope + +- renderer capability discovery and schema/reporting work, +- richer `version`, `doctor`, and `inspect` runtime/reporting surfaces, +- remaining lower-priority public JSON contract locks, +- bundle validation / bundle-lint enforcement, +- proof-bundle refresh for the targeted Week 8 scenarios, +- and design/code synchronization for those surfaces. + +### Explicitly out of scope + +These remain valid future work items, but they should not dilute Week 8: + +- shipping a full native renderer backend, +- mouse input support, +- remote/network sessions, +- MCP wrapper, +- broad Windows/native rendering parity work beyond diagnostics/documentation, +- full event-log redesign, +- full snapshot-schema redesign, +- and renderer CSP hardening beyond surfacing and ratifying the current localhost-only trade-off. + +## Workstream A — runtime capability discovery + +### Goal + +Teach the runtime to describe what renderer/export capabilities are available **now**, not just what is theoretically compiled in. + +### Deliverables + +- define a stable capability model/schema for renderer backends and related review/export features, +- report structured capability/availability data from `version --json`, +- align `doctor --json` with the same capability story so environment diagnostics and public version reporting do not drift, +- make capability failures structured (for example, unavailable browser/runtime/backend) rather than forcing automation to infer from error strings, +- and add tests that lock the chosen capability/reporting surface down. + +### Acceptance criteria + +- automation can tell ahead of time whether snapshot, screenshot, wait, recording export, and video export are available in the current environment, +- unavailable capabilities have structured reasons, +- and the shipped JSON surface is schema-backed and test-locked. + +## Workstream B — richer renderer and session introspection + +### Goal + +Make `inspect` answer the next set of debugging questions without log spelunking. + +### Deliverables + +- add a renderer/runtime summary block to `inspect --json`, +- expose whether the inspection/render story is live-host-backed or offline-replay-backed, +- surface the active backend, current renderer health/availability, and the most useful replay/render state markers where stable, +- include clearer structured hints when the CLI had to fall back because the host or renderer was unavailable, +- and add targeted tests for running, exited, reconciled-host, and renderer-unavailable paths. + +### Acceptance criteria + +- a reviewer can tell whether `inspect` reflects a live host or offline replay from JSON alone, +- renderer failures/unavailability are distinguishable from generic session failure, +- and the Week 8 docs, proof bundles, and emitted terms all use the same language for those states. + +## Workstream C — remaining public-surface locks + +### Goal + +Finish the lower-priority contract locks that Week 7 intentionally left behind. + +### Deliverables + +- add golden-envelope or equivalent strict contract coverage for `doctor`, `gc`, and `record export`, +- lock the new Week 8 capability and inspect-summary blocks in the same test suite or an equivalent public-surface suite, +- audit whether any currently ad hoc fields should be explicitly ratified or intentionally left unlocked, +- and keep the docs honest about which machine-facing surfaces are locked versus illustrative. + +### Acceptance criteria + +- machine-facing drift in those commands fails loudly in tests, +- the remaining intentionally unlocked surfaces are explicit, +- and reviewers can tell whether a change is a real public-contract change or only an internal refactor. + +## Workstream D — bundle validation and review normalization + +### Goal + +Make the preferred proof-bundle standard enforceable instead of prose-only. + +### Deliverables + +- add a bundle validator/checker command, script, or test helper that can evaluate the accepted minimum proof-bundle shape, +- define the minimum rules for the targeted Week 8 bundle classes (contract/reporting scenarios versus interactive renderer scenarios), +- prove the validator against at least one conforming bundle and one intentionally incomplete/failing example, +- refresh or add Week 8 bundles so the committed evidence matches the validator's rules, +- and update the validation docs so the accepted minimum is both documented and mechanically checked. + +### Acceptance criteria + +- contributors can run a repo-supported check and learn whether a targeted bundle is review-complete, +- the repo no longer depends only on prose to protect bundle completeness for the targeted scenarios, +- and reviewers can tell why a bundle passes or fails without reverse-engineering the directory layout. + +## Dogfooding and validation + +Week 8 must keep the same proof-heavy bar as the earlier plans. + +### Required dogfood principle + +For any change that affects runtime capability reporting, renderer/runtime introspection, machine-facing JSON envelopes, or proof-bundle validation, the implementation should produce: + +- JSON command outputs, +- generated `review-bundle` output, +- screenshots, +- `.cast` or equivalent textual replay artifacts where relevant, +- `.webm` artifacts where relevant, +- and short written notes describing expected versus observed behavior. + +### Required Week 8 dogfood setup + +Because this is a CLI project, Week 8 dogfooding should run against an isolated absolute `AGENT_TERMINAL_HOME` and use direct CLI invocation. + +At a minimum, the Week 8 proof workflow should document a setup equivalent to: + +```sh +mise install +npm ci +npx playwright install chromium +npm run build + +export AGENT_TERMINAL_HOME="$(mktemp -d)" +npx tsx src/cli/main.ts version --json +npx tsx src/cli/main.ts doctor --json +``` + +When a proof bundle is generated, also run: + +```sh +npm run review-bundle -- +npm run validate-bundle -- +``` + +For renderer/replay-heavy bundles, also run: + +```sh +npm run validate-bundle -- --profile interactive-renderer +``` + +### Required Week 8 proof bundles + +At a minimum, Week 8 should leave behind bundles covering: + +1. **Capability inventory scenario** + - bundle path `dogfood/20260325-week8-capability-inventory/`; proves `version --json` plus `doctor --json` capability reporting on a healthy environment and shows the generated review surface. +2. **Live-vs-offline inspect scenario** + - bundle path `dogfood/20260325-week8-inspect-runtime/`; proves running-session inspection, exited/offline inspection, and fallback/recovery paths where the renderer or host is unavailable. +3. **Contract-lock scenario** + - bundle path `dogfood/20260325-week8-contract-locks/`; proves the `doctor`, `gc`, and `record export` envelope locks and shows the resulting reviewer surface. +4. **Bundle-validation scenario** + - bundle path `dogfood/20260325-week8-bundle-validation/`; proves the validator/checker accepts a conforming bundle and rejects an intentionally incomplete target. + +### Screenshot and video requirements + +Week 8 should continue the design rule that screenshots and videos are mandatory proof artifacts for interaction-heavy or reviewer-facing changes. + +For every proof bundle above, capture: + +- at least one screenshot, +- a generated review page, +- and at least one `.cast` or short `.webm` recording whenever the scenario exercises a live terminal interaction rather than only static JSON/reporting. + +Contract-only scenarios should still capture screenshots of terminal outputs, test output, or the generated review page rather than leaving behind JSON alone. + +## Quality gates between workstreams + +Do not move on from a workstream until: + +- the new tests for that workstream pass, +- the related proof bundle exists, +- screenshots and video/recording artifacts exist where required, +- the docs for that surface are updated in the same change, +- and any intentionally deferred follow-ups are written down explicitly. + +## Recommended implementation order + +Implement Week 8 in this order: + +1. capability model and `version` / `doctor` reporting, +2. `inspect` renderer/runtime summary and fallback reporting, +3. remaining public-envelope locks, +4. bundle validator/checker plus Week 8 proof bundles, +5. final doc/gap-tracker synchronization. + +That order keeps runtime reporting, contract locks, and proof expectations aligned so the resulting Week 8 surface can serve as the stable foundation for later native-backend and platform-expansion work. + +## Definition of done + +Week 8 should be considered complete only when: + +- the runtime can answer what renderer/export capabilities are available and why, +- `inspect` makes the live-host versus offline-replay story obvious, +- the remaining lower-priority public JSON surfaces are locked down, +- the proof-bundle minimum is enforced by repo-supported checks for the targeted scenarios, +- and the remaining delta is clearly future-scope feature expansion rather than missing capability/reporting discipline. diff --git a/dogfood/20260325-week8-bundle-validation/command-status.tsv b/dogfood/20260325-week8-bundle-validation/command-status.tsv new file mode 100644 index 0000000..366a78b --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/command-status.tsv @@ -0,0 +1,8 @@ +step command exit_code status +01-valid-pass npm run --silent validate-bundle -- "/tmp/agent-terminal-week8-valid.xMfv7X/valid-sample" --profile contract-reporting 0 pass +02-invalid-fail npm run --silent validate-bundle -- "/tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample" --profile contract-reporting 1 expected-fail +03-brief-reference-week7-a-fail npm run --silent validate-bundle -- dogfood/20260325-week7-a-cli-parity --profile contract-reporting 1 expected-fail +04-existing-legacy-pass npm run --silent validate-bundle -- "/tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle" --profile contract-reporting 0 pass +05-review-page-screenshot node --input-type=module 0 pass +06-self-pass npm run --silent validate-bundle -- "dogfood/20260325-week8-bundle-validation" --profile contract-reporting 0 pass +07-review-self npm run --silent review-bundle -- "dogfood/20260325-week8-bundle-validation" 0 pass diff --git a/dogfood/20260325-week8-bundle-validation/commands.sh b/dogfood/20260325-week8-bundle-validation/commands.sh new file mode 100755 index 0000000..84b3808 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/commands.sh @@ -0,0 +1,226 @@ +#!/usr/bin/env bash +set -euo pipefail + +export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH" + +BUNDLE_DIR='dogfood/20260325-week8-bundle-validation' +LOG_DIR="$BUNDLE_DIR/logs" +SCREENSHOT_DIR="$BUNDLE_DIR/screenshots" +RECORDING_DIR="$BUNDLE_DIR/recordings" +VIDEO_DIR="$BUNDLE_DIR/videos" +SNAPSHOT_DIR="$BUNDLE_DIR/snapshots" +STATUS_TSV="$BUNDLE_DIR/command-status.tsv" +SUMMARY_JSON="$BUNDLE_DIR/proof-summary.json" + +mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" +find "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" -mindepth 1 -maxdepth 1 -type f -delete +rm -f "$BUNDLE_DIR/index.html" "$STATUS_TSV" "$SUMMARY_JSON" +touch "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep" + +pretty_json() { + local path="$1" + node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path" +} + +record_status() { + local step="$1" + local command="$2" + local exit_code="$3" + local status="$4" + printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV" +} + +run_json_step() { + local step="$1" + local command="$2" + local expectation="$3" + local stdout_path="$LOG_DIR/$step.json" + local stderr_path="$LOG_DIR/$step.stderr.txt" + local exit_code=0 + + set +e + eval "$command" >"$stdout_path" 2>"$stderr_path" + exit_code=$? + set -e + + if [ -s "$stdout_path" ]; then + pretty_json "$stdout_path" + fi + + case "$expectation" in + pass) + if [ "$exit_code" -eq 0 ]; then + record_status "$step" "$command" "$exit_code" 'pass' + return 0 + fi + record_status "$step" "$command" "$exit_code" 'fail' + return "$exit_code" + ;; + expected-fail) + if [ "$exit_code" -ne 0 ]; then + record_status "$step" "$command" "$exit_code" 'expected-fail' + return 0 + fi + record_status "$step" "$command" "$exit_code" 'unexpected-pass' + return 1 + ;; + *) + printf 'unsupported expectation: %s\n' "$expectation" >&2 + return 1 + ;; + esac +} + +run_text_step() { + local step="$1" + local command="$2" + local expectation="$3" + local stdout_path="$LOG_DIR/$step.txt" + local stderr_path="$LOG_DIR/$step.stderr.txt" + local exit_code=0 + + set +e + eval "$command" >"$stdout_path" 2>"$stderr_path" + exit_code=$? + set -e + + case "$expectation" in + pass) + if [ "$exit_code" -eq 0 ]; then + record_status "$step" "$command" "$exit_code" 'pass' + return 0 + fi + record_status "$step" "$command" "$exit_code" 'fail' + return "$exit_code" + ;; + expected-fail) + if [ "$exit_code" -ne 0 ]; then + record_status "$step" "$command" "$exit_code" 'expected-fail' + return 0 + fi + record_status "$step" "$command" "$exit_code" 'unexpected-pass' + return 1 + ;; + *) + printf 'unsupported expectation: %s\n' "$expectation" >&2 + return 1 + ;; + esac +} + +capture_review_screenshot() { + local stdout_path="$LOG_DIR/05-review-page-screenshot.json" + local stderr_path="$LOG_DIR/05-review-page-screenshot.stderr.txt" + local command="node --input-type=module " + local exit_code=0 + + set +e + node --input-type=module >"$stdout_path" 2>"$stderr_path" <<'EOS' +import { access } from 'node:fs/promises'; +import { resolve } from 'node:path'; +import { chromium } from 'playwright'; + +const bundleDir = resolve('dogfood/20260325-week8-bundle-validation'); +const screenshotPath = resolve('dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png'); +const pageUrl = new URL(`file://${resolve('dogfood/20260325-week8-bundle-validation/index.html')}`); +const launchOptions = { headless: true, args: ['--no-sandbox'] }; + +try { + await access('/usr/bin/google-chrome'); + launchOptions.executablePath = '/usr/bin/google-chrome'; +} catch { + // Fall back to Playwright-managed Chromium when the system browser is absent. +} + +const browser = await chromium.launch(launchOptions); +try { + const page = await browser.newPage({ viewport: { width: 1440, height: 1100 } }); + await page.goto(pageUrl.toString()); + await page.waitForLoadState('networkidle'); + await page.screenshot({ path: screenshotPath, fullPage: true }); + console.log( + JSON.stringify( + { + ok: true, + bundleDir, + pageUrl: pageUrl.toString(), + screenshotPath, + }, + null, + 2, + ), + ); +} finally { + await browser.close(); +} +EOS + exit_code=$? + set -e + + if [ -s "$stdout_path" ]; then + pretty_json "$stdout_path" + fi + + if [ "$exit_code" -eq 0 ]; then + record_status '05-review-page-screenshot' "$command" "$exit_code" 'pass' + return 0 + fi + + record_status '05-review-page-screenshot' "$command" "$exit_code" 'fail' + return "$exit_code" +} + +write_summary_json() { + node - "$SUMMARY_JSON" <<'EOS' +const fs = require('fs'); +const outputPath = process.argv[2]; +const summary = { + bundle: '20260325-week8-bundle-validation', + generatedAt: new Date().toISOString(), + summary: 'Week 8 validate-bundle proof summary for valid, invalid, existing-bundle, and self-validation scenarios.', + cases: [ + { step: '01-valid-pass', expectation: 'pass', artifact: 'logs/01-valid-pass.json' }, + { step: '02-invalid-fail', expectation: 'expected-fail', artifact: 'logs/02-invalid-fail.json' }, + { step: '03-brief-reference-week7-a-fail', expectation: 'expected-fail', artifact: 'logs/03-brief-reference-week7-a-fail.json' }, + { step: '04-existing-legacy-pass', expectation: 'pass', artifact: 'logs/04-existing-legacy-pass.json' }, + { step: '05-review-page-screenshot', expectation: 'pass', artifact: 'logs/05-review-page-screenshot.json' }, + { step: '06-self-pass', expectation: 'pass', artifact: 'logs/06-self-pass.json' }, + { step: '07-review-self', expectation: 'pass', artifact: 'logs/07-review-self.txt' }, + ], +}; +fs.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + '\n'); +EOS +} + +printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV" + +VALID_ROOT="$(mktemp -d /tmp/agent-terminal-week8-valid.XXXXXX)" +INVALID_ROOT="$(mktemp -d /tmp/agent-terminal-week8-invalid.XXXXXX)" +EXISTING_ROOT="$(mktemp -d /tmp/agent-terminal-week8-existing.XXXXXX)" +VALID_BUNDLE="$VALID_ROOT/valid-sample" +INVALID_BUNDLE="$INVALID_ROOT/invalid-sample" +EXISTING_BUNDLE="$EXISTING_ROOT/20260319-lifecycle" + +cleanup() { + rm -rf "$VALID_ROOT" "$INVALID_ROOT" "$EXISTING_ROOT" +} +trap cleanup EXIT + +mkdir -p "$VALID_BUNDLE" "$INVALID_BUNDLE" +printf '{"ok":true}\n' > "$VALID_BUNDLE/01-sample.json" +printf '# Notes\n' > "$VALID_BUNDLE/notes.md" +npm run --silent review-bundle -- "$VALID_BUNDLE" >/dev/null 2>/dev/null + +cp -R dogfood/20260319-lifecycle "$EXISTING_BUNDLE" +npm run --silent review-bundle -- "$EXISTING_BUNDLE" >/dev/null 2>/dev/null + +run_json_step '01-valid-pass' "npm run --silent validate-bundle -- \"$VALID_BUNDLE\" --profile contract-reporting" 'pass' +run_json_step '02-invalid-fail' "npm run --silent validate-bundle -- \"$INVALID_BUNDLE\" --profile contract-reporting" 'expected-fail' +run_json_step '03-brief-reference-week7-a-fail' 'npm run --silent validate-bundle -- dogfood/20260325-week7-a-cli-parity --profile contract-reporting' 'expected-fail' +run_json_step '04-existing-legacy-pass' "npm run --silent validate-bundle -- \"$EXISTING_BUNDLE\" --profile contract-reporting" 'pass' + +write_summary_json +npm run --silent review-bundle -- "$BUNDLE_DIR" >/dev/null 2>/dev/null +capture_review_screenshot +run_json_step '06-self-pass' "npm run --silent validate-bundle -- \"$BUNDLE_DIR\" --profile contract-reporting" 'pass' +run_text_step '07-review-self' "npm run --silent review-bundle -- \"$BUNDLE_DIR\"" 'pass' diff --git a/dogfood/20260325-week8-bundle-validation/index.html b/dogfood/20260325-week8-bundle-validation/index.html new file mode 100644 index 0000000..08e1118 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/index.html @@ -0,0 +1,1223 @@ + + + + + + Week 8 — Bundle Validation review bundle + + + +
+
+

Portable review bundle

+

Week 8 — Bundle Validation

+

Bundle directory: 20260325-week8-bundle-validation

+

+ Demonstrates validate-bundle across valid, invalid, existing-bundle, + and self-validation contract-reporting cases. +

+
+ +
+

Manifest summary

+
+
+
Bundle
+
20260325-week8-bundle-validation
+
+
+
Date
+
2026-03-25T23:43:45Z
+
+
+
Result
+
pass
+
+
+
Week
+
8
+
+
+
Commands
+
9
+
+
+
Manifest artifacts
+
10
+
+
+
Discovered files
+
23
+
+
+
+

Commands

+
    +
  • bash dogfood/20260325-week8-bundle-validation/commands.sh
  • +
  • + npm run --silent validate-bundle -- /tmp/.../valid-sample + --profile contract-reporting +
  • +
  • + npm run --silent validate-bundle -- /tmp/.../invalid-sample + --profile contract-reporting +
  • +
  • + npm run --silent validate-bundle -- + dogfood/20260325-week7-a-cli-parity --profile contract-reporting +
  • +
  • + npm run --silent review-bundle -- /tmp/.../20260319-lifecycle +
  • +
  • + npm run --silent validate-bundle -- /tmp/.../20260319-lifecycle + --profile contract-reporting +
  • +
  • + npm run --silent review-bundle -- + dogfood/20260325-week8-bundle-validation +
  • +
  • + node --input-type=module <playwright review-page screenshot> +
  • +
  • + npm run --silent validate-bundle -- + dogfood/20260325-week8-bundle-validation --profile + contract-reporting +
  • +
+
+
+
+

Screenshot gallery

+ +
+ +
+

JSON outputs

+ +
+ logs/01-valid-pass.json +
{
+  "bundleDir": "/tmp/agent-terminal-week8-valid.xMfv7X/valid-sample",
+  "profile": "contract-reporting",
+  "ok": true,
+  "checks": [
+    {
+      "name": "bundle-exists",
+      "ok": true,
+      "message": "Bundle directory is readable: /tmp/agent-terminal-week8-valid.xMfv7X/valid-sample"
+    },
+    {
+      "name": "has-json-output",
+      "ok": true,
+      "message": "Found 1 JSON output file(s)."
+    },
+    {
+      "name": "has-review-page",
+      "ok": true,
+      "message": "Found review page index.html."
+    },
+    {
+      "name": "has-notes",
+      "ok": true,
+      "message": "Found 1 note file(s)."
+    },
+    {
+      "name": "json-readable",
+      "ok": true,
+      "message": "Parsed 1 JSON output file(s)."
+    }
+  ]
+}
+
+
+ logs/02-invalid-fail.json +
{
+  "bundleDir": "/tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample",
+  "profile": "contract-reporting",
+  "ok": false,
+  "checks": [
+    {
+      "name": "bundle-exists",
+      "ok": true,
+      "message": "Bundle directory is readable: /tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample"
+    },
+    {
+      "name": "has-json-output",
+      "ok": false,
+      "message": "Expected at least one JSON output file."
+    },
+    {
+      "name": "has-review-page",
+      "ok": false,
+      "message": "Expected index.html review page in the bundle root."
+    },
+    {
+      "name": "has-notes",
+      "ok": false,
+      "message": "Expected at least one notes markdown file."
+    },
+    {
+      "name": "json-readable",
+      "ok": false,
+      "message": "No JSON output files were found to parse."
+    }
+  ]
+}
+
+
+ logs/03-brief-reference-week7-a-fail.json +
{
+  "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity",
+  "profile": "contract-reporting",
+  "ok": false,
+  "checks": [
+    {
+      "name": "bundle-exists",
+      "ok": true,
+      "message": "Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity"
+    },
+    {
+      "name": "has-json-output",
+      "ok": false,
+      "message": "Expected at least one JSON output file."
+    },
+    {
+      "name": "has-review-page",
+      "ok": true,
+      "message": "Found review page index.html."
+    },
+    {
+      "name": "has-notes",
+      "ok": true,
+      "message": "Found 1 note file(s)."
+    },
+    {
+      "name": "json-readable",
+      "ok": false,
+      "message": "No JSON output files were found to parse."
+    }
+  ]
+}
+
+
+ logs/04-existing-legacy-pass.json +
{
+  "bundleDir": "/tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle",
+  "profile": "contract-reporting",
+  "ok": true,
+  "checks": [
+    {
+      "name": "bundle-exists",
+      "ok": true,
+      "message": "Bundle directory is readable: /tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle"
+    },
+    {
+      "name": "has-json-output",
+      "ok": true,
+      "message": "Found 15 JSON output file(s)."
+    },
+    {
+      "name": "has-review-page",
+      "ok": true,
+      "message": "Found review page index.html."
+    },
+    {
+      "name": "has-notes",
+      "ok": true,
+      "message": "Found 1 note file(s)."
+    },
+    {
+      "name": "json-readable",
+      "ok": true,
+      "message": "Parsed 15 JSON output file(s)."
+    }
+  ]
+}
+
+
+ logs/05-review-page-screenshot.json +
{
+  "ok": true,
+  "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation",
+  "pageUrl": "file:///home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation/index.html",
+  "screenshotPath": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png"
+}
+
+
+ logs/06-self-pass.json +
{
+  "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation",
+  "profile": "contract-reporting",
+  "ok": true,
+  "checks": [
+    {
+      "name": "bundle-exists",
+      "ok": true,
+      "message": "Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation"
+    },
+    {
+      "name": "has-json-output",
+      "ok": true,
+      "message": "Found 1 JSON output file(s)."
+    },
+    {
+      "name": "has-review-page",
+      "ok": true,
+      "message": "Found review page index.html."
+    },
+    {
+      "name": "has-notes",
+      "ok": true,
+      "message": "Found 1 note file(s)."
+    },
+    {
+      "name": "json-readable",
+      "ok": true,
+      "message": "Parsed 1 JSON output file(s)."
+    }
+  ]
+}
+
+
+ proof-summary.json +
{
+  "bundle": "20260325-week8-bundle-validation",
+  "generatedAt": "2026-03-25T23:44:33.329Z",
+  "summary": "Week 8 validate-bundle proof summary for valid, invalid, existing-bundle, and self-validation scenarios.",
+  "cases": [
+    {
+      "step": "01-valid-pass",
+      "expectation": "pass",
+      "artifact": "logs/01-valid-pass.json"
+    },
+    {
+      "step": "02-invalid-fail",
+      "expectation": "expected-fail",
+      "artifact": "logs/02-invalid-fail.json"
+    },
+    {
+      "step": "03-brief-reference-week7-a-fail",
+      "expectation": "expected-fail",
+      "artifact": "logs/03-brief-reference-week7-a-fail.json"
+    },
+    {
+      "step": "04-existing-legacy-pass",
+      "expectation": "pass",
+      "artifact": "logs/04-existing-legacy-pass.json"
+    },
+    {
+      "step": "05-review-page-screenshot",
+      "expectation": "pass",
+      "artifact": "logs/05-review-page-screenshot.json"
+    },
+    {
+      "step": "06-self-pass",
+      "expectation": "pass",
+      "artifact": "logs/06-self-pass.json"
+    },
+    {
+      "step": "07-review-self",
+      "expectation": "pass",
+      "artifact": "logs/07-review-self.txt"
+    }
+  ]
+}
+
+
+
+

Notes

+ +
+

notes.md

+

2026-03-25 dogfood — Week 8 bundle validation proof

+

Bundle metadata

+
    +
  • + Bundle path: + dogfood/20260325-week8-bundle-validation/ +
  • +
  • + Validation helper: + npm run validate-bundle -- <bundle-dir> [--profile + <profile>] +
  • +
  • + Reference review helper: + npm run review-bundle -- <bundle-dir> +
  • +
  • Proof date: 2026-03-25
  • +
  • + Profiles covered: + contract-reporting directly, with + interactive-renderer requirements documented for + comparison +
  • +
+

Scenario summary

+

+ This bundle proves the Week 8 validate-bundle helper + against four scenarios: a minimal valid sample bundle created under + /tmp, an intentionally invalid sample bundle created + under /tmp, the task brief's suggested + dogfood/20260325-week7-a-cli-parity/ reference bundle, + and a reviewed temp copy of the existing + dogfood/20260319-lifecycle/ bundle. It also validates + this Week 8 proof bundle itself after generating its review page and + reviewer screenshot. +

+

Validation profile summary

+
    +
  • + contract-reporting: requires the + bundle directory to exist, at least one JSON output file, at least + one notes markdown file, a generated + index.html review page, and JSON that parses + successfully. +
  • +
  • + interactive-renderer: includes every + contract-reporting check plus at least one screenshot + artifact and at least one .cast recording artifact. +
  • +
  • + Important implementation detail: the current + validator classifies everything under logs/ as + support files before it checks for .json, so + logs/*.json do not satisfy the + has-json-output requirement by themselves. +
  • +
+

Proof results

+
    +
  • + Valid sample passes: + logs/01-valid-pass.json shows a temporary sample + bundle that contains a root-level 01-sample.json, + notes.md, and a generated index.html, so + every contract-reporting check passes. +
  • +
  • + Invalid sample fails as expected: + logs/02-invalid-fail.json shows the intentionally + empty sample bundle fails has-json-output, + has-review-page, has-notes, and + json-readable. + command-status.tsv records the non-zero exit code as + expected-fail. +
  • +
  • + Brief reference bundle currently fails: + logs/03-brief-reference-week7-a-fail.json shows + dogfood/20260325-week7-a-cli-parity/ does + not currently satisfy + contract-reporting, because its JSON evidence lives + under logs/, which the validator treats as support + files instead of JSON output. +
  • +
  • + Existing reviewed legacy bundle passes: + logs/04-existing-legacy-pass.json proves a temporary + reviewed copy of the existing + dogfood/20260319-lifecycle/ bundle passes + contract-reporting once it has an index.html review + page. +
  • +
  • + This Week 8 bundle passes contract-reporting: + logs/06-self-pass.json proves the proof bundle itself + has valid JSON evidence (proof-summary.json), notes, + and a generated review page. +
  • +
+

Why each case behaves correctly

+
    +
  • + Why the valid sample passes: it has a parseable + root JSON file (01-sample.json), a notes file + (notes.md), and a generated review page + (index.html). +
  • +
  • + Why the invalid sample fails: it intentionally + omits notes, JSON output, and the review page, so the validator + reports multiple failed checks instead of silently passing an + incomplete bundle. +
  • +
  • + Why the Week 7-A reference fails today: its + numbered JSON evidence is stored under logs/, but the + validator's classifier marks logs/ artifacts as + support files before it checks .json, so no JSON + output is counted for contract-reporting. +
  • +
  • + Why the reviewed legacy copy passes: the existing + 20260319-lifecycle bundle already contains root-level + JSON output and notes, so adding a generated + index.html in /tmp satisfies the + remaining contract-reporting requirement without mutating the + repository copy. +
  • +
  • + Why this proof bundle targets + contract-reporting: + it includes a review-page screenshot + (screenshots/01-review-page.png) as extra reviewer + evidence, but it intentionally does not include a + .cast recording artifact, so + interactive-renderer would still be stricter than + necessary for this documentation-focused proof. +
  • +
+

Review answers

+
    +
  • + Did the valid sample pass? Yes. Step 01 exited + successfully and the JSON result reports ok: true. +
  • +
  • + Did the invalid sample fail? Yes. Step 02 exited + non-zero and the JSON result reports ok: false with + the expected missing-artifact checks. +
  • +
  • + Did the task brief's Week 7-A reference pass? + No. Step 03 documents the current-tool mismatch directly instead + of hiding it. +
  • +
  • + Did any existing bundle pass? Yes. Step 04 + validated a reviewed temp copy of the existing + dogfood/20260319-lifecycle/ bundle successfully. +
  • +
  • + Did this bundle generate its own review page? + Yes. logs/07-review-self.txt records the final + review-bundle run and index.html is + present in the bundle root. +
  • +
  • + Was a reviewer screenshot captured? Yes. + logs/05-review-page-screenshot.json records the + Playwright capture and + screenshots/01-review-page.png is the resulting + image. +
  • +
  • + Where is the JSON artifact that makes this bundle + contract-reporting compliant? + proof-summary.json sits at the bundle root + specifically because the current validator does not count + logs/*.json as JSON output. +
  • +
  • + Where are the exit codes? + command-status.tsv records every step, including the + expected failures. +
  • +
+

Issues / limitations

+
    +
  • + The temporary sample bundles live under /tmp and are + cleaned up at the end of each commands.sh run, so the + durable proof is the captured validator output rather than the + sample directories themselves. +
  • +
  • + The task brief suggested + dogfood/20260325-week7-a-cli-parity/ as an existing + passing reference, but the current validator rejects it for the + concrete path-classification reason documented above. +
  • +
  • + This bundle intentionally focuses on contract-reporting behavior. + It documents the stricter interactive-renderer profile, but it + does not try to manufacture a recording artifact solely to satisfy + that higher bar. +
  • +
+
+
+
+

Commands

+
+

commands.sh

+
#!/usr/bin/env bash
+set -euo pipefail
+
+export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH"
+
+BUNDLE_DIR='dogfood/20260325-week8-bundle-validation'
+LOG_DIR="$BUNDLE_DIR/logs"
+SCREENSHOT_DIR="$BUNDLE_DIR/screenshots"
+RECORDING_DIR="$BUNDLE_DIR/recordings"
+VIDEO_DIR="$BUNDLE_DIR/videos"
+SNAPSHOT_DIR="$BUNDLE_DIR/snapshots"
+STATUS_TSV="$BUNDLE_DIR/command-status.tsv"
+SUMMARY_JSON="$BUNDLE_DIR/proof-summary.json"
+
+mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR"
+find "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" -mindepth 1 -maxdepth 1 -type f -delete
+rm -f "$BUNDLE_DIR/index.html" "$STATUS_TSV" "$SUMMARY_JSON"
+touch "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep"
+
+pretty_json() {
+  local path="$1"
+  node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path"
+}
+
+record_status() {
+  local step="$1"
+  local command="$2"
+  local exit_code="$3"
+  local status="$4"
+  printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV"
+}
+
+run_json_step() {
+  local step="$1"
+  local command="$2"
+  local expectation="$3"
+  local stdout_path="$LOG_DIR/$step.json"
+  local stderr_path="$LOG_DIR/$step.stderr.txt"
+  local exit_code=0
+
+  set +e
+  eval "$command" >"$stdout_path" 2>"$stderr_path"
+  exit_code=$?
+  set -e
+
+  if [ -s "$stdout_path" ]; then
+    pretty_json "$stdout_path"
+  fi
+
+  case "$expectation" in
+    pass)
+      if [ "$exit_code" -eq 0 ]; then
+        record_status "$step" "$command" "$exit_code" 'pass'
+        return 0
+      fi
+      record_status "$step" "$command" "$exit_code" 'fail'
+      return "$exit_code"
+      ;;
+    expected-fail)
+      if [ "$exit_code" -ne 0 ]; then
+        record_status "$step" "$command" "$exit_code" 'expected-fail'
+        return 0
+      fi
+      record_status "$step" "$command" "$exit_code" 'unexpected-pass'
+      return 1
+      ;;
+    *)
+      printf 'unsupported expectation: %s\n' "$expectation" >&2
+      return 1
+      ;;
+  esac
+}
+
+run_text_step() {
+  local step="$1"
+  local command="$2"
+  local expectation="$3"
+  local stdout_path="$LOG_DIR/$step.txt"
+  local stderr_path="$LOG_DIR/$step.stderr.txt"
+  local exit_code=0
+
+  set +e
+  eval "$command" >"$stdout_path" 2>"$stderr_path"
+  exit_code=$?
+  set -e
+
+  case "$expectation" in
+    pass)
+      if [ "$exit_code" -eq 0 ]; then
+        record_status "$step" "$command" "$exit_code" 'pass'
+        return 0
+      fi
+      record_status "$step" "$command" "$exit_code" 'fail'
+      return "$exit_code"
+      ;;
+    expected-fail)
+      if [ "$exit_code" -ne 0 ]; then
+        record_status "$step" "$command" "$exit_code" 'expected-fail'
+        return 0
+      fi
+      record_status "$step" "$command" "$exit_code" 'unexpected-pass'
+      return 1
+      ;;
+    *)
+      printf 'unsupported expectation: %s\n' "$expectation" >&2
+      return 1
+      ;;
+  esac
+}
+
+capture_review_screenshot() {
+  local stdout_path="$LOG_DIR/05-review-page-screenshot.json"
+  local stderr_path="$LOG_DIR/05-review-page-screenshot.stderr.txt"
+  local command="node --input-type=module <embedded playwright screenshot>"
+  local exit_code=0
+
+  set +e
+  node --input-type=module >"$stdout_path" 2>"$stderr_path" <<'EOS'
+import { access } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { chromium } from 'playwright';
+
+const bundleDir = resolve('dogfood/20260325-week8-bundle-validation');
+const screenshotPath = resolve('dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png');
+const pageUrl = new URL(`file://${resolve('dogfood/20260325-week8-bundle-validation/index.html')}`);
+const launchOptions = { headless: true, args: ['--no-sandbox'] };
+
+try {
+  await access('/usr/bin/google-chrome');
+  launchOptions.executablePath = '/usr/bin/google-chrome';
+} catch {
+  // Fall back to Playwright-managed Chromium when the system browser is absent.
+}
+
+const browser = await chromium.launch(launchOptions);
+try {
+  const page = await browser.newPage({ viewport: { width: 1440, height: 1100 } });
+  await page.goto(pageUrl.toString());
+  await page.waitForLoadState('networkidle');
+  await page.screenshot({ path: screenshotPath, fullPage: true });
+  console.log(
+    JSON.stringify(
+      {
+        ok: true,
+        bundleDir,
+        pageUrl: pageUrl.toString(),
+        screenshotPath,
+      },
+      null,
+      2,
+    ),
+  );
+} finally {
+  await browser.close();
+}
+EOS
+  exit_code=$?
+  set -e
+
+  if [ -s "$stdout_path" ]; then
+    pretty_json "$stdout_path"
+  fi
+
+  if [ "$exit_code" -eq 0 ]; then
+    record_status '05-review-page-screenshot' "$command" "$exit_code" 'pass'
+    return 0
+  fi
+
+  record_status '05-review-page-screenshot' "$command" "$exit_code" 'fail'
+  return "$exit_code"
+}
+
+write_summary_json() {
+  node - "$SUMMARY_JSON" <<'EOS'
+const fs = require('fs');
+const outputPath = process.argv[2];
+const summary = {
+  bundle: '20260325-week8-bundle-validation',
+  generatedAt: new Date().toISOString(),
+  summary: 'Week 8 validate-bundle proof summary for valid, invalid, existing-bundle, and self-validation scenarios.',
+  cases: [
+    { step: '01-valid-pass', expectation: 'pass', artifact: 'logs/01-valid-pass.json' },
+    { step: '02-invalid-fail', expectation: 'expected-fail', artifact: 'logs/02-invalid-fail.json' },
+    { step: '03-brief-reference-week7-a-fail', expectation: 'expected-fail', artifact: 'logs/03-brief-reference-week7-a-fail.json' },
+    { step: '04-existing-legacy-pass', expectation: 'pass', artifact: 'logs/04-existing-legacy-pass.json' },
+    { step: '05-review-page-screenshot', expectation: 'pass', artifact: 'logs/05-review-page-screenshot.json' },
+    { step: '06-self-pass', expectation: 'pass', artifact: 'logs/06-self-pass.json' },
+    { step: '07-review-self', expectation: 'pass', artifact: 'logs/07-review-self.txt' },
+  ],
+};
+fs.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + '\n');
+EOS
+}
+
+printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV"
+
+VALID_ROOT="$(mktemp -d /tmp/agent-terminal-week8-valid.XXXXXX)"
+INVALID_ROOT="$(mktemp -d /tmp/agent-terminal-week8-invalid.XXXXXX)"
+EXISTING_ROOT="$(mktemp -d /tmp/agent-terminal-week8-existing.XXXXXX)"
+VALID_BUNDLE="$VALID_ROOT/valid-sample"
+INVALID_BUNDLE="$INVALID_ROOT/invalid-sample"
+EXISTING_BUNDLE="$EXISTING_ROOT/20260319-lifecycle"
+
+cleanup() {
+  rm -rf "$VALID_ROOT" "$INVALID_ROOT" "$EXISTING_ROOT"
+}
+trap cleanup EXIT
+
+mkdir -p "$VALID_BUNDLE" "$INVALID_BUNDLE"
+printf '{"ok":true}\n' > "$VALID_BUNDLE/01-sample.json"
+printf '# Notes\n' > "$VALID_BUNDLE/notes.md"
+npm run --silent review-bundle -- "$VALID_BUNDLE" >/dev/null 2>/dev/null
+
+cp -R dogfood/20260319-lifecycle "$EXISTING_BUNDLE"
+npm run --silent review-bundle -- "$EXISTING_BUNDLE" >/dev/null 2>/dev/null
+
+run_json_step '01-valid-pass' "npm run --silent validate-bundle -- \"$VALID_BUNDLE\" --profile contract-reporting" 'pass'
+run_json_step '02-invalid-fail' "npm run --silent validate-bundle -- \"$INVALID_BUNDLE\" --profile contract-reporting" 'expected-fail'
+run_json_step '03-brief-reference-week7-a-fail' 'npm run --silent validate-bundle -- dogfood/20260325-week7-a-cli-parity --profile contract-reporting' 'expected-fail'
+run_json_step '04-existing-legacy-pass' "npm run --silent validate-bundle -- \"$EXISTING_BUNDLE\" --profile contract-reporting" 'pass'
+
+write_summary_json
+npm run --silent review-bundle -- "$BUNDLE_DIR" >/dev/null 2>/dev/null
+capture_review_screenshot
+run_json_step '06-self-pass' "npm run --silent validate-bundle -- \"$BUNDLE_DIR\" --profile contract-reporting" 'pass'
+run_text_step '07-review-self' "npm run --silent review-bundle -- \"$BUNDLE_DIR\"" 'pass'
+
+
+
+
+

Command status

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
stepcommandexit_codestatus
01-valid-pass + npm run --silent validate-bundle -- + "/tmp/agent-terminal-week8-valid.xMfv7X/valid-sample" + --profile contract-reporting + 0pass
02-invalid-fail + npm run --silent validate-bundle -- + "/tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample" + --profile contract-reporting + 1expected-fail
03-brief-reference-week7-a-fail + npm run --silent validate-bundle -- + dogfood/20260325-week7-a-cli-parity --profile + contract-reporting + 1expected-fail
04-existing-legacy-pass + npm run --silent validate-bundle -- + "/tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle" + --profile contract-reporting + 0pass
05-review-page-screenshot + node --input-type=module <embedded playwright + screenshot> + 0pass
06-self-pass + npm run --silent validate-bundle -- + "dogfood/20260325-week8-bundle-validation" --profile + contract-reporting + 0pass
07-review-self + npm run --silent review-bundle -- + "dogfood/20260325-week8-bundle-validation" + 0pass
+
+
+
+

Artifact inventory

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeSize
command-status.tsvsupport951 B
commands.shscript7.6 KB
+ logs/01-valid-pass.json + support738 B
+ logs/01-valid-pass.stderr.txt + support381 B
+ logs/02-invalid-fail.json + support817 B
+ logs/02-invalid-fail.stderr.txt + support455 B
+ logs/03-brief-reference-week7-a-fail.json + support848 B
+ logs/03-brief-reference-week7-a-fail.stderr.txt + support488 B
+ logs/04-existing-legacy-pass.json + support758 B
+ logs/04-existing-legacy-pass.stderr.txt + support401 B
+ logs/05-review-page-screenshot.json + support420 B
+ logs/05-review-page-screenshot.stderr.txt + support0 B
+ logs/06-self-pass.json + support832 B
+ logs/06-self-pass.stderr.txt + support475 B
+ logs/07-review-self.stderr.txt + support50 B
+ logs/07-review-self.txt + support110 B
manifest.jsonsupport2.7 KB
notes.mdnotes5.8 KB
proof-summary.jsonjson1.1 KB
recordings/.gitkeepother0 B
+ screenshots/01-review-page.png + screenshot2.1 MB
snapshots/.gitkeepother0 B
videos/.gitkeepother0 B
+
+
+
+ + diff --git a/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.json b/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.json new file mode 100644 index 0000000..9948708 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.json @@ -0,0 +1,32 @@ +{ + "bundleDir": "/tmp/agent-terminal-week8-valid.xMfv7X/valid-sample", + "profile": "contract-reporting", + "ok": true, + "checks": [ + { + "name": "bundle-exists", + "ok": true, + "message": "Bundle directory is readable: /tmp/agent-terminal-week8-valid.xMfv7X/valid-sample" + }, + { + "name": "has-json-output", + "ok": true, + "message": "Found 1 JSON output file(s)." + }, + { + "name": "has-review-page", + "ok": true, + "message": "Found review page index.html." + }, + { + "name": "has-notes", + "ok": true, + "message": "Found 1 note file(s)." + }, + { + "name": "json-readable", + "ok": true, + "message": "Parsed 1 JSON output file(s)." + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.stderr.txt new file mode 100644 index 0000000..26bc33b --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/01-valid-pass.stderr.txt @@ -0,0 +1,6 @@ +validate-bundle PASS contract-reporting: /tmp/agent-terminal-week8-valid.xMfv7X/valid-sample +✓ bundle-exists: Bundle directory is readable: /tmp/agent-terminal-week8-valid.xMfv7X/valid-sample +✓ has-json-output: Found 1 JSON output file(s). +✓ has-review-page: Found review page index.html. +✓ has-notes: Found 1 note file(s). +✓ json-readable: Parsed 1 JSON output file(s). diff --git a/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.json b/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.json new file mode 100644 index 0000000..8fd37b8 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.json @@ -0,0 +1,32 @@ +{ + "bundleDir": "/tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample", + "profile": "contract-reporting", + "ok": false, + "checks": [ + { + "name": "bundle-exists", + "ok": true, + "message": "Bundle directory is readable: /tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample" + }, + { + "name": "has-json-output", + "ok": false, + "message": "Expected at least one JSON output file." + }, + { + "name": "has-review-page", + "ok": false, + "message": "Expected index.html review page in the bundle root." + }, + { + "name": "has-notes", + "ok": false, + "message": "Expected at least one notes markdown file." + }, + { + "name": "json-readable", + "ok": false, + "message": "No JSON output files were found to parse." + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.stderr.txt new file mode 100644 index 0000000..935d041 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/02-invalid-fail.stderr.txt @@ -0,0 +1,6 @@ +validate-bundle FAIL contract-reporting: /tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample +✓ bundle-exists: Bundle directory is readable: /tmp/agent-terminal-week8-invalid.SVE1TL/invalid-sample +✗ has-json-output: Expected at least one JSON output file. +✗ has-review-page: Expected index.html review page in the bundle root. +✗ has-notes: Expected at least one notes markdown file. +✗ json-readable: No JSON output files were found to parse. diff --git a/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.json b/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.json new file mode 100644 index 0000000..55fa662 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.json @@ -0,0 +1,32 @@ +{ + "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity", + "profile": "contract-reporting", + "ok": false, + "checks": [ + { + "name": "bundle-exists", + "ok": true, + "message": "Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity" + }, + { + "name": "has-json-output", + "ok": false, + "message": "Expected at least one JSON output file." + }, + { + "name": "has-review-page", + "ok": true, + "message": "Found review page index.html." + }, + { + "name": "has-notes", + "ok": true, + "message": "Found 1 note file(s)." + }, + { + "name": "json-readable", + "ok": false, + "message": "No JSON output files were found to parse." + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.stderr.txt new file mode 100644 index 0000000..1e357bc --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/03-brief-reference-week7-a-fail.stderr.txt @@ -0,0 +1,6 @@ +validate-bundle FAIL contract-reporting: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity +✓ bundle-exists: Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week7-a-cli-parity +✗ has-json-output: Expected at least one JSON output file. +✓ has-review-page: Found review page index.html. +✓ has-notes: Found 1 note file(s). +✗ json-readable: No JSON output files were found to parse. diff --git a/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.json b/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.json new file mode 100644 index 0000000..39c0066 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.json @@ -0,0 +1,32 @@ +{ + "bundleDir": "/tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle", + "profile": "contract-reporting", + "ok": true, + "checks": [ + { + "name": "bundle-exists", + "ok": true, + "message": "Bundle directory is readable: /tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle" + }, + { + "name": "has-json-output", + "ok": true, + "message": "Found 15 JSON output file(s)." + }, + { + "name": "has-review-page", + "ok": true, + "message": "Found review page index.html." + }, + { + "name": "has-notes", + "ok": true, + "message": "Found 1 note file(s)." + }, + { + "name": "json-readable", + "ok": true, + "message": "Parsed 15 JSON output file(s)." + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.stderr.txt new file mode 100644 index 0000000..27adaf1 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/04-existing-legacy-pass.stderr.txt @@ -0,0 +1,6 @@ +validate-bundle PASS contract-reporting: /tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle +✓ bundle-exists: Bundle directory is readable: /tmp/agent-terminal-week8-existing.xiuJkr/20260319-lifecycle +✓ has-json-output: Found 15 JSON output file(s). +✓ has-review-page: Found review page index.html. +✓ has-notes: Found 1 note file(s). +✓ json-readable: Parsed 15 JSON output file(s). diff --git a/dogfood/20260325-week8-bundle-validation/logs/05-review-page-screenshot.json b/dogfood/20260325-week8-bundle-validation/logs/05-review-page-screenshot.json new file mode 100644 index 0000000..9ca811c --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/05-review-page-screenshot.json @@ -0,0 +1,6 @@ +{ + "ok": true, + "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation", + "pageUrl": "file:///home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation/index.html", + "screenshotPath": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png" +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/05-review-page-screenshot.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/05-review-page-screenshot.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.json b/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.json new file mode 100644 index 0000000..1d024b8 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.json @@ -0,0 +1,32 @@ +{ + "bundleDir": "/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation", + "profile": "contract-reporting", + "ok": true, + "checks": [ + { + "name": "bundle-exists", + "ok": true, + "message": "Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation" + }, + { + "name": "has-json-output", + "ok": true, + "message": "Found 1 JSON output file(s)." + }, + { + "name": "has-review-page", + "ok": true, + "message": "Found review page index.html." + }, + { + "name": "has-notes", + "ok": true, + "message": "Found 1 note file(s)." + }, + { + "name": "json-readable", + "ok": true, + "message": "Parsed 1 JSON output file(s)." + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.stderr.txt new file mode 100644 index 0000000..5d5ab42 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/06-self-pass.stderr.txt @@ -0,0 +1,6 @@ +validate-bundle PASS contract-reporting: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation +✓ bundle-exists: Bundle directory is readable: /home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation +✓ has-json-output: Found 1 JSON output file(s). +✓ has-review-page: Found review page index.html. +✓ has-notes: Found 1 note file(s). +✓ json-readable: Parsed 1 JSON output file(s). diff --git a/dogfood/20260325-week8-bundle-validation/logs/07-review-self.stderr.txt b/dogfood/20260325-week8-bundle-validation/logs/07-review-self.stderr.txt new file mode 100644 index 0000000..72ef0c1 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/07-review-self.stderr.txt @@ -0,0 +1 @@ +Building dogfood/20260325-week8-bundle-validation diff --git a/dogfood/20260325-week8-bundle-validation/logs/07-review-self.txt b/dogfood/20260325-week8-bundle-validation/logs/07-review-self.txt new file mode 100644 index 0000000..664d9ca --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/logs/07-review-self.txt @@ -0,0 +1 @@ +/home/coder/.mux/src/agent-terminal/agent_exec_20a332c5c3/dogfood/20260325-week8-bundle-validation/index.html diff --git a/dogfood/20260325-week8-bundle-validation/manifest.json b/dogfood/20260325-week8-bundle-validation/manifest.json new file mode 100644 index 0000000..e7d36a5 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/manifest.json @@ -0,0 +1,61 @@ +{ + "bundle": "20260325-week8-bundle-validation", + "title": "Week 8 — Bundle Validation", + "description": "Demonstrates validate-bundle across valid, invalid, existing-bundle, and self-validation contract-reporting cases.", + "createdAt": "2026-03-25T23:43:45Z", + "week": 8, + "result": "pass", + "commands": [ + "bash dogfood/20260325-week8-bundle-validation/commands.sh", + "npm run --silent validate-bundle -- /tmp/.../valid-sample --profile contract-reporting", + "npm run --silent validate-bundle -- /tmp/.../invalid-sample --profile contract-reporting", + "npm run --silent validate-bundle -- dogfood/20260325-week7-a-cli-parity --profile contract-reporting", + "npm run --silent review-bundle -- /tmp/.../20260319-lifecycle", + "npm run --silent validate-bundle -- /tmp/.../20260319-lifecycle --profile contract-reporting", + "npm run --silent review-bundle -- dogfood/20260325-week8-bundle-validation", + "node --input-type=module ", + "npm run --silent validate-bundle -- dogfood/20260325-week8-bundle-validation --profile contract-reporting" + ], + "artifacts": [ + { + "path": "proof-summary.json", + "description": "Root-level JSON summary that keeps this proof bundle contract-reporting compliant" + }, + { + "path": "logs/01-valid-pass.json", + "description": "Contract-reporting validation result for the temporary valid sample bundle" + }, + { + "path": "logs/02-invalid-fail.json", + "description": "Contract-reporting validation result for the intentionally invalid sample bundle" + }, + { + "path": "logs/03-brief-reference-week7-a-fail.json", + "description": "Captured mismatch showing the suggested Week 7-A bundle currently fails contract-reporting" + }, + { + "path": "logs/04-existing-legacy-pass.json", + "description": "Contract-reporting validation result for a reviewed temp copy of the existing 20260319-lifecycle bundle" + }, + { + "path": "logs/05-review-page-screenshot.json", + "description": "Playwright screenshot capture metadata for this bundle's generated review page" + }, + { + "path": "screenshots/01-review-page.png", + "description": "Browser screenshot of this bundle's generated review page" + }, + { + "path": "logs/06-self-pass.json", + "description": "Contract-reporting validation result for the Week 8 proof bundle itself" + }, + { + "path": "logs/07-review-self.txt", + "description": "Final review-bundle run for the Week 8 proof bundle itself" + }, + { + "path": "command-status.tsv", + "description": "Step-by-step exit-code ledger for valid, invalid, existing-bundle, review, screenshot, and self-validation runs" + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/notes.md b/dogfood/20260325-week8-bundle-validation/notes.md new file mode 100644 index 0000000..b9d856d --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/notes.md @@ -0,0 +1,52 @@ +# 2026-03-25 dogfood — Week 8 bundle validation proof + +## Bundle metadata + +- **Bundle path:** `dogfood/20260325-week8-bundle-validation/` +- **Validation helper:** `npm run validate-bundle -- [--profile ]` +- **Reference review helper:** `npm run review-bundle -- ` +- **Proof date:** `2026-03-25` +- **Profiles covered:** `contract-reporting` directly, with `interactive-renderer` requirements documented for comparison + +## Scenario summary + +This bundle proves the Week 8 `validate-bundle` helper against four scenarios: a minimal valid sample bundle created under `/tmp`, an intentionally invalid sample bundle created under `/tmp`, the task brief's suggested `dogfood/20260325-week7-a-cli-parity/` reference bundle, and a reviewed temp copy of the existing `dogfood/20260319-lifecycle/` bundle. It also validates this Week 8 proof bundle itself after generating its review page and reviewer screenshot. + +## Validation profile summary + +- **`contract-reporting`:** requires the bundle directory to exist, at least one JSON output file, at least one notes markdown file, a generated `index.html` review page, and JSON that parses successfully. +- **`interactive-renderer`:** includes every `contract-reporting` check plus at least one screenshot artifact and at least one `.cast` recording artifact. +- **Important implementation detail:** the current validator classifies everything under `logs/` as support files before it checks for `.json`, so `logs/*.json` do **not** satisfy the `has-json-output` requirement by themselves. + +## Proof results + +- **Valid sample passes:** `logs/01-valid-pass.json` shows a temporary sample bundle that contains a root-level `01-sample.json`, `notes.md`, and a generated `index.html`, so every contract-reporting check passes. +- **Invalid sample fails as expected:** `logs/02-invalid-fail.json` shows the intentionally empty sample bundle fails `has-json-output`, `has-review-page`, `has-notes`, and `json-readable`. `command-status.tsv` records the non-zero exit code as `expected-fail`. +- **Brief reference bundle currently fails:** `logs/03-brief-reference-week7-a-fail.json` shows `dogfood/20260325-week7-a-cli-parity/` does **not** currently satisfy `contract-reporting`, because its JSON evidence lives under `logs/`, which the validator treats as support files instead of JSON output. +- **Existing reviewed legacy bundle passes:** `logs/04-existing-legacy-pass.json` proves a temporary reviewed copy of the existing `dogfood/20260319-lifecycle/` bundle passes contract-reporting once it has an `index.html` review page. +- **This Week 8 bundle passes contract-reporting:** `logs/06-self-pass.json` proves the proof bundle itself has valid JSON evidence (`proof-summary.json`), notes, and a generated review page. + +## Why each case behaves correctly + +- **Why the valid sample passes:** it has a parseable root JSON file (`01-sample.json`), a notes file (`notes.md`), and a generated review page (`index.html`). +- **Why the invalid sample fails:** it intentionally omits notes, JSON output, and the review page, so the validator reports multiple failed checks instead of silently passing an incomplete bundle. +- **Why the Week 7-A reference fails today:** its numbered JSON evidence is stored under `logs/`, but the validator's classifier marks `logs/` artifacts as support files before it checks `.json`, so no JSON output is counted for contract-reporting. +- **Why the reviewed legacy copy passes:** the existing `20260319-lifecycle` bundle already contains root-level JSON output and notes, so adding a generated `index.html` in `/tmp` satisfies the remaining contract-reporting requirement without mutating the repository copy. +- **Why this proof bundle targets `contract-reporting`:** it includes a review-page screenshot (`screenshots/01-review-page.png`) as extra reviewer evidence, but it intentionally does not include a `.cast` recording artifact, so `interactive-renderer` would still be stricter than necessary for this documentation-focused proof. + +## Review answers + +- **Did the valid sample pass?** Yes. Step 01 exited successfully and the JSON result reports `ok: true`. +- **Did the invalid sample fail?** Yes. Step 02 exited non-zero and the JSON result reports `ok: false` with the expected missing-artifact checks. +- **Did the task brief's Week 7-A reference pass?** No. Step 03 documents the current-tool mismatch directly instead of hiding it. +- **Did any existing bundle pass?** Yes. Step 04 validated a reviewed temp copy of the existing `dogfood/20260319-lifecycle/` bundle successfully. +- **Did this bundle generate its own review page?** Yes. `logs/07-review-self.txt` records the final `review-bundle` run and `index.html` is present in the bundle root. +- **Was a reviewer screenshot captured?** Yes. `logs/05-review-page-screenshot.json` records the Playwright capture and `screenshots/01-review-page.png` is the resulting image. +- **Where is the JSON artifact that makes this bundle contract-reporting compliant?** `proof-summary.json` sits at the bundle root specifically because the current validator does not count `logs/*.json` as JSON output. +- **Where are the exit codes?** `command-status.tsv` records every step, including the expected failures. + +## Issues / limitations + +- The temporary sample bundles live under `/tmp` and are cleaned up at the end of each `commands.sh` run, so the durable proof is the captured validator output rather than the sample directories themselves. +- The task brief suggested `dogfood/20260325-week7-a-cli-parity/` as an existing passing reference, but the current validator rejects it for the concrete path-classification reason documented above. +- This bundle intentionally focuses on contract-reporting behavior. It documents the stricter interactive-renderer profile, but it does not try to manufacture a recording artifact solely to satisfy that higher bar. diff --git a/dogfood/20260325-week8-bundle-validation/proof-summary.json b/dogfood/20260325-week8-bundle-validation/proof-summary.json new file mode 100644 index 0000000..6f22275 --- /dev/null +++ b/dogfood/20260325-week8-bundle-validation/proof-summary.json @@ -0,0 +1,42 @@ +{ + "bundle": "20260325-week8-bundle-validation", + "generatedAt": "2026-03-25T23:44:33.329Z", + "summary": "Week 8 validate-bundle proof summary for valid, invalid, existing-bundle, and self-validation scenarios.", + "cases": [ + { + "step": "01-valid-pass", + "expectation": "pass", + "artifact": "logs/01-valid-pass.json" + }, + { + "step": "02-invalid-fail", + "expectation": "expected-fail", + "artifact": "logs/02-invalid-fail.json" + }, + { + "step": "03-brief-reference-week7-a-fail", + "expectation": "expected-fail", + "artifact": "logs/03-brief-reference-week7-a-fail.json" + }, + { + "step": "04-existing-legacy-pass", + "expectation": "pass", + "artifact": "logs/04-existing-legacy-pass.json" + }, + { + "step": "05-review-page-screenshot", + "expectation": "pass", + "artifact": "logs/05-review-page-screenshot.json" + }, + { + "step": "06-self-pass", + "expectation": "pass", + "artifact": "logs/06-self-pass.json" + }, + { + "step": "07-review-self", + "expectation": "pass", + "artifact": "logs/07-review-self.txt" + } + ] +} diff --git a/dogfood/20260325-week8-bundle-validation/recordings/.gitkeep b/dogfood/20260325-week8-bundle-validation/recordings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png b/dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png new file mode 100644 index 0000000..fc596c1 Binary files /dev/null and b/dogfood/20260325-week8-bundle-validation/screenshots/01-review-page.png differ diff --git a/dogfood/20260325-week8-bundle-validation/snapshots/.gitkeep b/dogfood/20260325-week8-bundle-validation/snapshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-bundle-validation/videos/.gitkeep b/dogfood/20260325-week8-bundle-validation/videos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/01-version.json b/dogfood/20260325-week8-capability-inventory/01-version.json new file mode 100644 index 0000000..9d6f3e5 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/01-version.json @@ -0,0 +1,37 @@ +{ + "ok": true, + "command": "version", + "timestamp": "2026-03-25T23:39:47.547Z", + "result": { + "cliVersion": "0.1.0", + "protocolVersion": "0.1.0", + "rendererBackends": ["ghostty-web"], + "runtime": { + "node": "v24.14.0", + "platform": "linux", + "arch": "x64" + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available" + }, + { + "name": "wait", + "status": "available" + }, + { + "name": "screenshot", + "status": "available" + }, + { + "name": "record-export-asciicast", + "status": "available" + }, + { + "name": "record-export-webm", + "status": "available" + } + ] + } +} diff --git a/dogfood/20260325-week8-capability-inventory/02-doctor.json b/dogfood/20260325-week8-capability-inventory/02-doctor.json new file mode 100644 index 0000000..a23ecd6 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/02-doctor.json @@ -0,0 +1,118 @@ +{ + "ok": true, + "command": "doctor", + "timestamp": "2026-03-25T23:39:49.126Z", + "result": { + "ok": true, + "checks": { + "environment": [ + { + "name": "node-runtime", + "status": "pass", + "message": "Node 24.14.0 ok", + "durationMs": 0 + }, + { + "name": "cwd-access", + "status": "pass", + "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/agent_exec_65c5ac6f4a", + "durationMs": 1 + }, + { + "name": "temp-dir", + "status": "pass", + "message": "temp dir ok: /tmp", + "durationMs": 1 + }, + { + "name": "home-writable", + "status": "pass", + "message": "home writable: /tmp/tmp.pFy7qKtZtq", + "durationMs": 1 + }, + { + "name": "pty-spawn", + "status": "pass", + "message": "spawned /home/coder/.local/share/mise/installs/node/24.14.0/bin/node", + "durationMs": 25 + }, + { + "name": "socket-viable", + "status": "pass", + "message": "socket ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordce-2/host.sock", + "durationMs": 3 + }, + { + "name": "artifact-atomicity", + "status": "pass", + "message": "atomic rename ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordch-3/artifacts", + "durationMs": 2 + }, + { + "name": "event-log-writable", + "status": "pass", + "message": "append ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordcj-5/events.jsonl", + "durationMs": 1 + } + ], + "renderer": [ + { + "name": "playwright_available", + "status": "pass", + "message": "available", + "durationMs": 0 + }, + { + "name": "browser_launch", + "status": "pass", + "message": "chromium launches", + "durationMs": 129 + }, + { + "name": "ghostty_web_available", + "status": "pass", + "message": "WASM available", + "durationMs": 146 + }, + { + "name": "screenshot_viable", + "status": "pass", + "message": "viable", + "durationMs": 158 + } + ] + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "wait", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "screenshot", + "status": "available", + "reason": "renderer smoke checks passed", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable" + }, + { + "name": "record-export-asciicast", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "record-export-webm", + "status": "available", + "reason": "browser-backed export dependencies available", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available" + } + ] + } +} diff --git a/dogfood/20260325-week8-capability-inventory/agent-terminal-home.txt b/dogfood/20260325-week8-capability-inventory/agent-terminal-home.txt new file mode 100644 index 0000000..a61b728 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/agent-terminal-home.txt @@ -0,0 +1 @@ +/tmp/tmp.pFy7qKtZtq diff --git a/dogfood/20260325-week8-capability-inventory/command-status.tsv b/dogfood/20260325-week8-capability-inventory/command-status.tsv new file mode 100644 index 0000000..f363752 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/command-status.tsv @@ -0,0 +1,3 @@ +step command exit_code status +01-version npx tsx src/cli/main.ts version --json 0 pass +02-doctor npx tsx src/cli/main.ts doctor --json 0 pass diff --git a/dogfood/20260325-week8-capability-inventory/commands.sh b/dogfood/20260325-week8-capability-inventory/commands.sh new file mode 100755 index 0000000..8d06e0f --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/commands.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -euo pipefail + +export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH" + +if command -v mise >/dev/null 2>&1; then + mise trust >/dev/null +fi + +BUNDLE_DIR="dogfood/20260325-week8-capability-inventory" +LOG_DIR="$BUNDLE_DIR/logs" +SCREENSHOT_DIR="$BUNDLE_DIR/screenshots" +SNAPSHOT_DIR="$BUNDLE_DIR/snapshots" +RECORDING_DIR="$BUNDLE_DIR/recordings" +VIDEO_DIR="$BUNDLE_DIR/videos" +STATUS_TSV="$BUNDLE_DIR/command-status.tsv" + +mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" +find "$LOG_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" -mindepth 1 -maxdepth 1 -type f -delete +rm -f "$BUNDLE_DIR/agent-terminal-home.txt" "$STATUS_TSV" \ + "$BUNDLE_DIR/01-version.json" "$BUNDLE_DIR/02-doctor.json" +touch "$SCREENSHOT_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep" "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep" + +pretty_json() { + local path="$1" + node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path" +} + +record_status() { + local step="$1" + local command="$2" + local exit_code="$3" + local status="$4" + printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV" +} + +run_json_step() { + local step="$1" + local command="$2" + local allow_failure="$3" + local stdout_path="$LOG_DIR/$step.json" + local stderr_path="$LOG_DIR/$step.stderr.txt" + local exit_code=0 + + set +e + eval "$command" >"$stdout_path" 2>"$stderr_path" + exit_code=$? + set -e + + if [ -s "$stdout_path" ]; then + pretty_json "$stdout_path" + fi + + local status="pass" + if [ "$exit_code" -ne 0 ]; then + status="fail" + fi + record_status "$step" "$command" "$exit_code" "$status" + + if [ "$allow_failure" = "true" ] || [ "$exit_code" -eq 0 ]; then + return 0 + fi + return "$exit_code" +} + +TMP_HOME=$(mktemp -d) +export AGENT_TERMINAL_HOME="$TMP_HOME" +printf '%s\n' "$AGENT_TERMINAL_HOME" > "$BUNDLE_DIR/agent-terminal-home.txt" +printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV" + +cleanup() { + rm -rf "$TMP_HOME" +} +trap cleanup EXIT + +npm ci + +run_json_step '01-version' 'npx tsx src/cli/main.ts version --json' 'false' +cp "$LOG_DIR/01-version.json" "$BUNDLE_DIR/01-version.json" + +run_json_step '02-doctor' 'npx tsx src/cli/main.ts doctor --json' 'false' +cp "$LOG_DIR/02-doctor.json" "$BUNDLE_DIR/02-doctor.json" diff --git a/dogfood/20260325-week8-capability-inventory/index.html b/dogfood/20260325-week8-capability-inventory/index.html new file mode 100644 index 0000000..79f7478 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/index.html @@ -0,0 +1,958 @@ + + + + + + Week 8 — Capability Inventory review bundle + + + +
+
+

Portable review bundle

+

Week 8 — Capability Inventory

+

+ Bundle directory: 20260325-week8-capability-inventory +

+

+ Captures version --json and doctor --json capability inventories from + an isolated temp home for reviewer inspection. +

+
+ +
+

Manifest summary

+
+
+
Bundle
+
20260325-week8-capability-inventory
+
+
+
Date
+
2026-03-25T23:38:36Z
+
+
+
Result
+
pass
+
+
+
Week
+
8
+
+
+
Commands
+
2
+
+
+
Manifest artifacts
+
7
+
+
+
Discovered files
+
16
+
+
+
+

Commands

+
    +
  • npx tsx src/cli/main.ts version --json
  • +
  • npx tsx src/cli/main.ts doctor --json
  • +
+
+
+
+

Screenshot gallery

+ +
+ +
+

JSON outputs

+ +
+ 01-version.json +
{
+  "ok": true,
+  "command": "version",
+  "timestamp": "2026-03-25T23:39:47.547Z",
+  "result": {
+    "cliVersion": "0.1.0",
+    "protocolVersion": "0.1.0",
+    "rendererBackends": [
+      "ghostty-web"
+    ],
+    "runtime": {
+      "node": "v24.14.0",
+      "platform": "linux",
+      "arch": "x64"
+    },
+    "capabilities": [
+      {
+        "name": "snapshot",
+        "status": "available"
+      },
+      {
+        "name": "wait",
+        "status": "available"
+      },
+      {
+        "name": "screenshot",
+        "status": "available"
+      },
+      {
+        "name": "record-export-asciicast",
+        "status": "available"
+      },
+      {
+        "name": "record-export-webm",
+        "status": "available"
+      }
+    ]
+  }
+}
+
+
+ 02-doctor.json +
{
+  "ok": true,
+  "command": "doctor",
+  "timestamp": "2026-03-25T23:39:49.126Z",
+  "result": {
+    "ok": true,
+    "checks": {
+      "environment": [
+        {
+          "name": "node-runtime",
+          "status": "pass",
+          "message": "Node 24.14.0 ok",
+          "durationMs": 0
+        },
+        {
+          "name": "cwd-access",
+          "status": "pass",
+          "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/agent_exec_65c5ac6f4a",
+          "durationMs": 1
+        },
+        {
+          "name": "temp-dir",
+          "status": "pass",
+          "message": "temp dir ok: /tmp",
+          "durationMs": 1
+        },
+        {
+          "name": "home-writable",
+          "status": "pass",
+          "message": "home writable: /tmp/tmp.pFy7qKtZtq",
+          "durationMs": 1
+        },
+        {
+          "name": "pty-spawn",
+          "status": "pass",
+          "message": "spawned /home/coder/.local/share/mise/installs/node/24.14.0/bin/node",
+          "durationMs": 25
+        },
+        {
+          "name": "socket-viable",
+          "status": "pass",
+          "message": "socket ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordce-2/host.sock",
+          "durationMs": 3
+        },
+        {
+          "name": "artifact-atomicity",
+          "status": "pass",
+          "message": "atomic rename ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordch-3/artifacts",
+          "durationMs": 2
+        },
+        {
+          "name": "event-log-writable",
+          "status": "pass",
+          "message": "append ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordcj-5/events.jsonl",
+          "durationMs": 1
+        }
+      ],
+      "renderer": [
+        {
+          "name": "playwright_available",
+          "status": "pass",
+          "message": "available",
+          "durationMs": 0
+        },
+        {
+          "name": "browser_launch",
+          "status": "pass",
+          "message": "chromium launches",
+          "durationMs": 129
+        },
+        {
+          "name": "ghostty_web_available",
+          "status": "pass",
+          "message": "WASM available",
+          "durationMs": 146
+        },
+        {
+          "name": "screenshot_viable",
+          "status": "pass",
+          "message": "viable",
+          "durationMs": 158
+        }
+      ]
+    },
+    "capabilities": [
+      {
+        "name": "snapshot",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "wait",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "screenshot",
+        "status": "available",
+        "reason": "renderer smoke checks passed",
+        "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable"
+      },
+      {
+        "name": "record-export-asciicast",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "record-export-webm",
+        "status": "available",
+        "reason": "browser-backed export dependencies available",
+        "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available"
+      }
+    ]
+  }
+}
+
+
+ logs/01-version.json +
{
+  "ok": true,
+  "command": "version",
+  "timestamp": "2026-03-25T23:39:47.547Z",
+  "result": {
+    "cliVersion": "0.1.0",
+    "protocolVersion": "0.1.0",
+    "rendererBackends": [
+      "ghostty-web"
+    ],
+    "runtime": {
+      "node": "v24.14.0",
+      "platform": "linux",
+      "arch": "x64"
+    },
+    "capabilities": [
+      {
+        "name": "snapshot",
+        "status": "available"
+      },
+      {
+        "name": "wait",
+        "status": "available"
+      },
+      {
+        "name": "screenshot",
+        "status": "available"
+      },
+      {
+        "name": "record-export-asciicast",
+        "status": "available"
+      },
+      {
+        "name": "record-export-webm",
+        "status": "available"
+      }
+    ]
+  }
+}
+
+
+ logs/02-doctor.json +
{
+  "ok": true,
+  "command": "doctor",
+  "timestamp": "2026-03-25T23:39:49.126Z",
+  "result": {
+    "ok": true,
+    "checks": {
+      "environment": [
+        {
+          "name": "node-runtime",
+          "status": "pass",
+          "message": "Node 24.14.0 ok",
+          "durationMs": 0
+        },
+        {
+          "name": "cwd-access",
+          "status": "pass",
+          "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/agent_exec_65c5ac6f4a",
+          "durationMs": 1
+        },
+        {
+          "name": "temp-dir",
+          "status": "pass",
+          "message": "temp dir ok: /tmp",
+          "durationMs": 1
+        },
+        {
+          "name": "home-writable",
+          "status": "pass",
+          "message": "home writable: /tmp/tmp.pFy7qKtZtq",
+          "durationMs": 1
+        },
+        {
+          "name": "pty-spawn",
+          "status": "pass",
+          "message": "spawned /home/coder/.local/share/mise/installs/node/24.14.0/bin/node",
+          "durationMs": 25
+        },
+        {
+          "name": "socket-viable",
+          "status": "pass",
+          "message": "socket ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordce-2/host.sock",
+          "durationMs": 3
+        },
+        {
+          "name": "artifact-atomicity",
+          "status": "pass",
+          "message": "atomic rename ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordch-3/artifacts",
+          "durationMs": 2
+        },
+        {
+          "name": "event-log-writable",
+          "status": "pass",
+          "message": "append ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordcj-5/events.jsonl",
+          "durationMs": 1
+        }
+      ],
+      "renderer": [
+        {
+          "name": "playwright_available",
+          "status": "pass",
+          "message": "available",
+          "durationMs": 0
+        },
+        {
+          "name": "browser_launch",
+          "status": "pass",
+          "message": "chromium launches",
+          "durationMs": 129
+        },
+        {
+          "name": "ghostty_web_available",
+          "status": "pass",
+          "message": "WASM available",
+          "durationMs": 146
+        },
+        {
+          "name": "screenshot_viable",
+          "status": "pass",
+          "message": "viable",
+          "durationMs": 158
+        }
+      ]
+    },
+    "capabilities": [
+      {
+        "name": "snapshot",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "wait",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "screenshot",
+        "status": "available",
+        "reason": "renderer smoke checks passed",
+        "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable"
+      },
+      {
+        "name": "record-export-asciicast",
+        "status": "available",
+        "reason": "built-in capability",
+        "detail": "available without external renderer dependencies"
+      },
+      {
+        "name": "record-export-webm",
+        "status": "available",
+        "reason": "browser-backed export dependencies available",
+        "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available"
+      }
+    ]
+  }
+}
+
+
+
+

Notes

+ +
+

notes.md

+

2026-03-25 dogfood — Week 8 capability inventory proof

+

Bundle metadata

+
    +
  • + Bundle path: + dogfood/20260325-week8-capability-inventory/ +
  • +
  • + Isolated AGENT_TERMINAL_HOME: + /tmp/tmp.XlYvQNqMgF +
  • +
  • + CLI entrypoint: + npx tsx src/cli/main.ts +
  • +
  • + Captured commands: version --json, + doctor --json +
  • +
+

Scenario summary

+

+ This bundle proves the Week 8 capability-inventory surface by + capturing the ratified JSON envelopes from + version --json and doctor --json in a + fresh isolated temp home. +

+
    +
  • + 01-version.json and 02-doctor.json are + reviewer-facing top-level JSON copies so the contract-reporting + validator and generated review page can parse the captured outputs + directly. +
  • +
  • + logs/01-version.json captures the direct + version --json command output, and + logs/02-doctor.json captures the direct + doctor --json command output plus matching + logs/*.stderr.txt sidecars. +
  • +
  • + command-status.tsv records both JSON capture steps + with their exact commands and exit codes. +
  • +
  • + agent-terminal-home.txt records the isolated temp + home path used during capture. +
  • +
+

Capability inventory summary

+
    +
  • + version --json reports five capability entries: + snapshot, wait, screenshot, + record-export-asciicast, and + record-export-webm. Each entry includes + name and status, and this run reported + all five as available. +
  • +
  • + doctor --json reports the same five capability names, + but each entry also includes reason and + detail so reviewers can see whether availability + comes from built-in behavior or from renderer/browser smoke + checks. +
  • +
  • + In this capture, snapshot, wait, and + record-export-asciicast are available as built-in + capabilities, while screenshot and + record-export-webm are available because the + Playwright / Chromium / Ghostty Web checks passed. +
  • +
+

Review answers

+
    +
  • + Did the bundle use an isolated temp home? Yes. + agent-terminal-home.txt records + /tmp/tmp.XlYvQNqMgF, and + logs/02-doctor.json also references that temp home in + its home-writable, socket-viable, + artifact-atomicity, and + event-log-writable checks. +
  • +
  • + Did version --json expose the Week 8 capability + inventory surface? + Yes. 01-version.json and + logs/01-version.json both report + result.capabilities with five structured entries + covering snapshot, wait, screenshot, asciicast export, and WebM + export. +
  • +
  • + Did doctor --json expose the richer capability + diagnostics? + Yes. 02-doctor.json and + logs/02-doctor.json both report the same capability + names plus reason and detail, tying + screenshot and WebM availability to passing renderer/browser + checks. +
  • +
  • + Did the underlying doctor checks pass? Yes. + logs/02-doctor.json reports + result.ok: true, all environment checks as + pass, and all renderer checks as pass. +
  • +
  • + Where is the step ledger for the capture run? + command-status.tsv records both commands, their exit + codes, and pass status, and each step has a matching + logs/*.stderr.txt sidecar. +
  • +
  • + Is there reviewer-facing visual proof of the generated review + page? + Yes. screenshots/01-review-page.png was captured from + the generated index.html review page after running + npm run review-bundle -- + dogfood/20260325-week8-capability-inventory. +
  • +
+

Issues / limitations

+
    +
  • + This bundle intentionally exercises only the Week 8 + capability-inventory reporting surface. It does not create a live + session or produce snapshot / recording / video artifacts of its + own. +
  • +
  • + 01-version.json and 02-doctor.json are + bundle-root copies of the direct command captures from + logs/; they exist so the generated review page and + contract-reporting validator can discover + machine-readable JSON outputs. +
  • +
  • + The screenshot artifact is for the generated bundle review page, + not for a terminal session. The recordings/, + videos/, and snapshots/ directories are + present to match the reviewer bundle structure used in Week 7. +
  • +
+
+
+
+

Commands

+
+

commands.sh

+
#!/usr/bin/env bash
+set -euo pipefail
+
+export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH"
+
+if command -v mise >/dev/null 2>&1; then
+  mise trust >/dev/null
+fi
+
+BUNDLE_DIR="dogfood/20260325-week8-capability-inventory"
+LOG_DIR="$BUNDLE_DIR/logs"
+SCREENSHOT_DIR="$BUNDLE_DIR/screenshots"
+SNAPSHOT_DIR="$BUNDLE_DIR/snapshots"
+RECORDING_DIR="$BUNDLE_DIR/recordings"
+VIDEO_DIR="$BUNDLE_DIR/videos"
+STATUS_TSV="$BUNDLE_DIR/command-status.tsv"
+
+mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR"
+find "$LOG_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" -mindepth 1 -maxdepth 1 -type f -delete
+rm -f "$BUNDLE_DIR/agent-terminal-home.txt" "$STATUS_TSV" \
+  "$BUNDLE_DIR/01-version.json" "$BUNDLE_DIR/02-doctor.json"
+touch "$SCREENSHOT_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep" "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep"
+
+pretty_json() {
+  local path="$1"
+  node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path"
+}
+
+record_status() {
+  local step="$1"
+  local command="$2"
+  local exit_code="$3"
+  local status="$4"
+  printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV"
+}
+
+run_json_step() {
+  local step="$1"
+  local command="$2"
+  local allow_failure="$3"
+  local stdout_path="$LOG_DIR/$step.json"
+  local stderr_path="$LOG_DIR/$step.stderr.txt"
+  local exit_code=0
+
+  set +e
+  eval "$command" >"$stdout_path" 2>"$stderr_path"
+  exit_code=$?
+  set -e
+
+  if [ -s "$stdout_path" ]; then
+    pretty_json "$stdout_path"
+  fi
+
+  local status="pass"
+  if [ "$exit_code" -ne 0 ]; then
+    status="fail"
+  fi
+  record_status "$step" "$command" "$exit_code" "$status"
+
+  if [ "$allow_failure" = "true" ] || [ "$exit_code" -eq 0 ]; then
+    return 0
+  fi
+  return "$exit_code"
+}
+
+TMP_HOME=$(mktemp -d)
+export AGENT_TERMINAL_HOME="$TMP_HOME"
+printf '%s\n' "$AGENT_TERMINAL_HOME" > "$BUNDLE_DIR/agent-terminal-home.txt"
+printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV"
+
+cleanup() {
+  rm -rf "$TMP_HOME"
+}
+trap cleanup EXIT
+
+npm ci
+
+run_json_step '01-version' 'npx tsx src/cli/main.ts version --json' 'false'
+cp "$LOG_DIR/01-version.json" "$BUNDLE_DIR/01-version.json"
+
+run_json_step '02-doctor' 'npx tsx src/cli/main.ts doctor --json' 'false'
+cp "$LOG_DIR/02-doctor.json" "$BUNDLE_DIR/02-doctor.json"
+
+
+
+
+

Command status

+
+ + + + + + + + + + + + + + + + + + + + + + + +
stepcommandexit_codestatus
01-versionnpx tsx src/cli/main.ts version --json0pass
02-doctornpx tsx src/cli/main.ts doctor --json0pass
+
+
+
+

Artifact inventory

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeSize
01-version.jsonjson737 B
02-doctor.jsonjson3.4 KB
+ agent-terminal-home.txt + support20 B
command-status.tsvsupport142 B
commands.shscript2.4 KB
logs/01-version.jsonsupport737 B
+ logs/01-version.stderr.txt + support0 B
logs/02-doctor.jsonsupport3.4 KB
+ logs/02-doctor.stderr.txt + support0 B
manifest.jsonsupport1.4 KB
notes.mdnotes3.8 KB
recordings/.gitkeepother0 B
screenshots/.gitkeepother0 B
+ screenshots/01-review-page.png + screenshot2.2 MB
snapshots/.gitkeepother0 B
videos/.gitkeepother0 B
+
+
+
+ + diff --git a/dogfood/20260325-week8-capability-inventory/logs/01-version.json b/dogfood/20260325-week8-capability-inventory/logs/01-version.json new file mode 100644 index 0000000..9d6f3e5 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/logs/01-version.json @@ -0,0 +1,37 @@ +{ + "ok": true, + "command": "version", + "timestamp": "2026-03-25T23:39:47.547Z", + "result": { + "cliVersion": "0.1.0", + "protocolVersion": "0.1.0", + "rendererBackends": ["ghostty-web"], + "runtime": { + "node": "v24.14.0", + "platform": "linux", + "arch": "x64" + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available" + }, + { + "name": "wait", + "status": "available" + }, + { + "name": "screenshot", + "status": "available" + }, + { + "name": "record-export-asciicast", + "status": "available" + }, + { + "name": "record-export-webm", + "status": "available" + } + ] + } +} diff --git a/dogfood/20260325-week8-capability-inventory/logs/01-version.stderr.txt b/dogfood/20260325-week8-capability-inventory/logs/01-version.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/logs/02-doctor.json b/dogfood/20260325-week8-capability-inventory/logs/02-doctor.json new file mode 100644 index 0000000..a23ecd6 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/logs/02-doctor.json @@ -0,0 +1,118 @@ +{ + "ok": true, + "command": "doctor", + "timestamp": "2026-03-25T23:39:49.126Z", + "result": { + "ok": true, + "checks": { + "environment": [ + { + "name": "node-runtime", + "status": "pass", + "message": "Node 24.14.0 ok", + "durationMs": 0 + }, + { + "name": "cwd-access", + "status": "pass", + "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/agent_exec_65c5ac6f4a", + "durationMs": 1 + }, + { + "name": "temp-dir", + "status": "pass", + "message": "temp dir ok: /tmp", + "durationMs": 1 + }, + { + "name": "home-writable", + "status": "pass", + "message": "home writable: /tmp/tmp.pFy7qKtZtq", + "durationMs": 1 + }, + { + "name": "pty-spawn", + "status": "pass", + "message": "spawned /home/coder/.local/share/mise/installs/node/24.14.0/bin/node", + "durationMs": 25 + }, + { + "name": "socket-viable", + "status": "pass", + "message": "socket ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordce-2/host.sock", + "durationMs": 3 + }, + { + "name": "artifact-atomicity", + "status": "pass", + "message": "atomic rename ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordch-3/artifacts", + "durationMs": 2 + }, + { + "name": "event-log-writable", + "status": "pass", + "message": "append ok: /tmp/tmp.pFy7qKtZtq/sessions/doctor-2407740-mn6ordcj-5/events.jsonl", + "durationMs": 1 + } + ], + "renderer": [ + { + "name": "playwright_available", + "status": "pass", + "message": "available", + "durationMs": 0 + }, + { + "name": "browser_launch", + "status": "pass", + "message": "chromium launches", + "durationMs": 129 + }, + { + "name": "ghostty_web_available", + "status": "pass", + "message": "WASM available", + "durationMs": 146 + }, + { + "name": "screenshot_viable", + "status": "pass", + "message": "viable", + "durationMs": 158 + } + ] + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "wait", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "screenshot", + "status": "available", + "reason": "renderer smoke checks passed", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable" + }, + { + "name": "record-export-asciicast", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "record-export-webm", + "status": "available", + "reason": "browser-backed export dependencies available", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available" + } + ] + } +} diff --git a/dogfood/20260325-week8-capability-inventory/logs/02-doctor.stderr.txt b/dogfood/20260325-week8-capability-inventory/logs/02-doctor.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/manifest.json b/dogfood/20260325-week8-capability-inventory/manifest.json new file mode 100644 index 0000000..4d04c35 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/manifest.json @@ -0,0 +1,42 @@ +{ + "bundle": "20260325-week8-capability-inventory", + "title": "Week 8 — Capability Inventory", + "description": "Captures version --json and doctor --json capability inventories from an isolated temp home for reviewer inspection.", + "createdAt": "2026-03-25T23:38:36Z", + "week": 8, + "result": "pass", + "commands": [ + "npx tsx src/cli/main.ts version --json", + "npx tsx src/cli/main.ts doctor --json" + ], + "artifacts": [ + { + "path": "01-version.json", + "description": "Reviewer-facing top-level copy of the version --json capability inventory output" + }, + { + "path": "02-doctor.json", + "description": "Reviewer-facing top-level copy of the doctor --json capability diagnostics output" + }, + { + "path": "logs/01-version.json", + "description": "Direct version --json output captured by commands.sh" + }, + { + "path": "logs/02-doctor.json", + "description": "Direct doctor --json output captured by commands.sh" + }, + { + "path": "command-status.tsv", + "description": "Step-by-step exit-code ledger for the proof run" + }, + { + "path": "agent-terminal-home.txt", + "description": "Temp AGENT_TERMINAL_HOME path used during capture" + }, + { + "path": "screenshots/01-review-page.png", + "description": "Screenshot of the generated review page for reviewer-friendly visual verification" + } + ] +} diff --git a/dogfood/20260325-week8-capability-inventory/notes.md b/dogfood/20260325-week8-capability-inventory/notes.md new file mode 100644 index 0000000..db39662 --- /dev/null +++ b/dogfood/20260325-week8-capability-inventory/notes.md @@ -0,0 +1,38 @@ +# 2026-03-25 dogfood — Week 8 capability inventory proof + +## Bundle metadata + +- **Bundle path:** `dogfood/20260325-week8-capability-inventory/` +- **Isolated AGENT_TERMINAL_HOME:** `/tmp/tmp.XlYvQNqMgF` +- **CLI entrypoint:** `npx tsx src/cli/main.ts` +- **Captured commands:** `version --json`, `doctor --json` + +## Scenario summary + +This bundle proves the Week 8 capability-inventory surface by capturing the ratified JSON envelopes from `version --json` and `doctor --json` in a fresh isolated temp home. + +- `01-version.json` and `02-doctor.json` are reviewer-facing top-level JSON copies so the contract-reporting validator and generated review page can parse the captured outputs directly. +- `logs/01-version.json` captures the direct `version --json` command output, and `logs/02-doctor.json` captures the direct `doctor --json` command output plus matching `logs/*.stderr.txt` sidecars. +- `command-status.tsv` records both JSON capture steps with their exact commands and exit codes. +- `agent-terminal-home.txt` records the isolated temp home path used during capture. + +## Capability inventory summary + +- `version --json` reports five capability entries: `snapshot`, `wait`, `screenshot`, `record-export-asciicast`, and `record-export-webm`. Each entry includes `name` and `status`, and this run reported all five as `available`. +- `doctor --json` reports the same five capability names, but each entry also includes `reason` and `detail` so reviewers can see whether availability comes from built-in behavior or from renderer/browser smoke checks. +- In this capture, `snapshot`, `wait`, and `record-export-asciicast` are available as built-in capabilities, while `screenshot` and `record-export-webm` are available because the Playwright / Chromium / Ghostty Web checks passed. + +## Review answers + +- **Did the bundle use an isolated temp home?** Yes. `agent-terminal-home.txt` records `/tmp/tmp.XlYvQNqMgF`, and `logs/02-doctor.json` also references that temp home in its `home-writable`, `socket-viable`, `artifact-atomicity`, and `event-log-writable` checks. +- **Did `version --json` expose the Week 8 capability inventory surface?** Yes. `01-version.json` and `logs/01-version.json` both report `result.capabilities` with five structured entries covering snapshot, wait, screenshot, asciicast export, and WebM export. +- **Did `doctor --json` expose the richer capability diagnostics?** Yes. `02-doctor.json` and `logs/02-doctor.json` both report the same capability names plus `reason` and `detail`, tying screenshot and WebM availability to passing renderer/browser checks. +- **Did the underlying doctor checks pass?** Yes. `logs/02-doctor.json` reports `result.ok: true`, all environment checks as `pass`, and all renderer checks as `pass`. +- **Where is the step ledger for the capture run?** `command-status.tsv` records both commands, their exit codes, and `pass` status, and each step has a matching `logs/*.stderr.txt` sidecar. +- **Is there reviewer-facing visual proof of the generated review page?** Yes. `screenshots/01-review-page.png` was captured from the generated `index.html` review page after running `npm run review-bundle -- dogfood/20260325-week8-capability-inventory`. + +## Issues / limitations + +- This bundle intentionally exercises only the Week 8 capability-inventory reporting surface. It does not create a live session or produce snapshot / recording / video artifacts of its own. +- `01-version.json` and `02-doctor.json` are bundle-root copies of the direct command captures from `logs/`; they exist so the generated review page and `contract-reporting` validator can discover machine-readable JSON outputs. +- The screenshot artifact is for the generated bundle review page, not for a terminal session. The `recordings/`, `videos/`, and `snapshots/` directories are present to match the reviewer bundle structure used in Week 7. diff --git a/dogfood/20260325-week8-capability-inventory/recordings/.gitkeep b/dogfood/20260325-week8-capability-inventory/recordings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/screenshots/.gitkeep b/dogfood/20260325-week8-capability-inventory/screenshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/screenshots/01-review-page.png b/dogfood/20260325-week8-capability-inventory/screenshots/01-review-page.png new file mode 100644 index 0000000..f44d679 Binary files /dev/null and b/dogfood/20260325-week8-capability-inventory/screenshots/01-review-page.png differ diff --git a/dogfood/20260325-week8-capability-inventory/snapshots/.gitkeep b/dogfood/20260325-week8-capability-inventory/snapshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-capability-inventory/videos/.gitkeep b/dogfood/20260325-week8-capability-inventory/videos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/command-status.tsv b/dogfood/20260325-week8-contract-locks/command-status.tsv new file mode 100644 index 0000000..2fb012c --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/command-status.tsv @@ -0,0 +1,12 @@ +step command exit_code status +01 npx vitest run test/unit/commands/golden-envelopes.test.ts --reporter=json 0 ok +02 node --input-type=module -e dogfood/20260325-week8-contract-locks/logs/.01-golden-envelopes.raw.json dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json 0 ok +03 cp dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json 0 ok +04 npx vitest run test/unit/commands/golden-envelopes.test.ts 0 ok +05 npm run review-bundle -- dogfood/20260325-week8-contract-locks 0 ok +06 npm run validate-bundle -- dogfood/20260325-week8-contract-locks --profile contract-reporting 0 ok +07 node --input-type=commonjs 0 ok +08 npm run validate-bundle -- dogfood/20260325-week8-contract-locks --profile contract-reporting 0 ok +09 npx prettier --write dogfood/20260325-week8-contract-locks/manifest.json dogfood/20260325-week8-contract-locks/notes.md 0 ok +10 npx prettier --check dogfood/20260325-week8-contract-locks/manifest.json dogfood/20260325-week8-contract-locks/notes.md 0 ok +11 npm run review-bundle -- dogfood/20260325-week8-contract-locks && npm run validate-bundle -- dogfood/20260325-week8-contract-locks --profile contract-reporting && node --input-type=commonjs 0 ok diff --git a/dogfood/20260325-week8-contract-locks/commands.sh b/dogfood/20260325-week8-contract-locks/commands.sh new file mode 100755 index 0000000..8a8ef81 --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/commands.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH" + +BUNDLE_DIR="dogfood/20260325-week8-contract-locks" +LOG_DIR="$BUNDLE_DIR/logs" +SCREENSHOT_DIR="$BUNDLE_DIR/screenshots" +RECORDING_DIR="$BUNDLE_DIR/recordings" +VIDEO_DIR="$BUNDLE_DIR/videos" +SNAPSHOT_DIR="$BUNDLE_DIR/snapshots" +STATUS_FILE="$BUNDLE_DIR/command-status.tsv" + +mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" +find "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" \ + -mindepth 1 \ + -maxdepth 1 \ + ! -name '.gitkeep' \ + -exec rm -rf {} + + +for dir in "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR"; do + : > "$dir/.gitkeep" +done + +printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_FILE" + +record_status() { + local step="$1" + local command_text="$2" + local exit_code="$3" + local status="ok" + if [[ "$exit_code" -ne 0 ]]; then + status="failed" + fi + printf '%s\t%s\t%s\t%s\n' "$step" "$command_text" "$exit_code" "$status" >> "$STATUS_FILE" +} + +run_capture() { + local step="$1" + local stdout_path="$2" + local stderr_path="$3" + shift 3 + local exit_code=0 + if "$@" >"$stdout_path" 2>"$stderr_path"; then + exit_code=0 + else + exit_code=$? + fi + record_status "$step" "$*" "$exit_code" + return "$exit_code" +} + +RAW_JSON="$LOG_DIR/.01-golden-envelopes.raw.json" +run_capture \ + '01' \ + "$RAW_JSON" \ + "$LOG_DIR/01-golden-envelopes.stderr.txt" \ + npx vitest run test/unit/commands/golden-envelopes.test.ts --reporter=json + +node --input-type=module -e 'import { readFileSync, writeFileSync } from "node:fs"; const input = process.argv[1]; const output = process.argv[2]; const parsed = JSON.parse(readFileSync(input, "utf8")); writeFileSync(output, JSON.stringify(parsed, null, 2) + "\n");' \ + "$RAW_JSON" \ + "$LOG_DIR/01-golden-envelopes.json" +record_status '02' 'node --input-type=module -e dogfood/20260325-week8-contract-locks/logs/.01-golden-envelopes.raw.json dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json' '0' +cp "$LOG_DIR/01-golden-envelopes.json" "$SNAPSHOT_DIR/01-golden-envelopes.json" +record_status '03' 'cp dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json' '0' +rm -f "$RAW_JSON" + +run_capture \ + '04' \ + "$LOG_DIR/02-golden-envelopes.txt" \ + "$LOG_DIR/02-golden-envelopes.stderr.txt" \ + npx vitest run test/unit/commands/golden-envelopes.test.ts diff --git a/dogfood/20260325-week8-contract-locks/index.html b/dogfood/20260325-week8-contract-locks/index.html new file mode 100644 index 0000000..98d90bb --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/index.html @@ -0,0 +1,1944 @@ + + + + + + Week 8 — Contract Locks review bundle + + + +
+
+

Portable review bundle

+

Week 8 — Contract Locks

+

Bundle directory: 20260325-week8-contract-locks

+

+ Captures the Week 8 golden-envelope test evidence that locks doctor, + gc, record export, version, and inspect CLI JSON contracts. +

+
+ +
+

Manifest summary

+
+
+
Bundle
+
20260325-week8-contract-locks
+
+
+
Date
+
2026-03-25T23:39:35Z
+
+
+
Result
+
pass
+
+
+
Week
+
8
+
+
+
Commands
+
4
+
+
+
Manifest artifacts
+
7
+
+
+
Discovered files
+
14
+
+
+
+

Commands

+
    +
  • + npx vitest run test/unit/commands/golden-envelopes.test.ts + --reporter=json +
  • +
  • npx vitest run test/unit/commands/golden-envelopes.test.ts
  • +
  • + npm run review-bundle -- dogfood/20260325-week8-contract-locks +
  • +
  • + npm run validate-bundle -- dogfood/20260325-week8-contract-locks + --profile contract-reporting +
  • +
+
+
+
+

Screenshot gallery

+ +
+ +
+

JSON outputs

+ +
+ logs/01-golden-envelopes.json +
{
+  "numTotalTestSuites": 15,
+  "numPassedTestSuites": 15,
+  "numFailedTestSuites": 0,
+  "numPendingTestSuites": 0,
+  "numTotalTests": 45,
+  "numPassedTests": 45,
+  "numFailedTests": 0,
+  "numPendingTests": 0,
+  "numTodoTests": 0,
+  "snapshot": {
+    "added": 0,
+    "failure": false,
+    "filesAdded": 0,
+    "filesRemoved": 0,
+    "filesRemovedList": [],
+    "filesUnmatched": 0,
+    "filesUpdated": 0,
+    "matched": 0,
+    "total": 0,
+    "unchecked": 0,
+    "uncheckedKeysByFile": [],
+    "unmatched": 0,
+    "updated": 0,
+    "didUpdate": false
+  },
+  "startTime": 1774482010364,
+  "success": true,
+  "testResults": [
+    {
+      "assertionResults": [
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the inspect success envelope shape with live renderer runtime",
+          "status": "passed",
+          "title": "locks the inspect success envelope shape with live renderer runtime",
+          "duration": 11.62846300000001,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts accepts inspect result with offline renderer runtime",
+          "status": "passed",
+          "title": "accepts inspect result with offline renderer runtime",
+          "duration": 1.1077769999999987,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the version success envelope shape",
+          "status": "passed",
+          "title": "locks the version success envelope shape",
+          "duration": 7.875341999999989,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts accepts version result with capabilities",
+          "status": "passed",
+          "title": "accepts version result with capabilities",
+          "duration": 1.2924669999999878,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1165470000000255,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 1.1494579999999814,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.6483679999999481,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1587169999999674,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.8051879999999869,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.6908480000000168,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1072080000000142,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.5701480000000174,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.40450900000001866,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.8232980000000225,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.43940900000001193,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.4085499999999911,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.48231900000001815,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.49157900000000154,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.3585989999999697,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.6193989999999872,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.5239080000000058,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.33590999999995574,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 2.035205000000019,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.39202899999997953,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.35172899999997753,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.0864579999999933,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.4122590000000059,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.36456900000001724,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.7276279999999815,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.2447800000000484,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.18972899999999981,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.23011999999999944,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.22498899999999367,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.1841489999999908,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3348500000000172,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.23541899999997895,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.17264000000000124,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3134989999999789,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.26932900000002746,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.17808999999999742,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3770589999999743,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.20796000000001413,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.19945899999999028,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the SESSION_NOT_FOUND error envelope shape",
+          "status": "passed",
+          "title": "locks the SESSION_NOT_FOUND error envelope shape",
+          "duration": 0.39596900000003643,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks a retryable transport-style error envelope shape",
+          "status": "passed",
+          "title": "locks a retryable transport-style error envelope shape",
+          "duration": 0.2512399999999957,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        }
+      ],
+      "startTime": 1774482010636,
+      "endTime": 1774482010681.2512,
+      "status": "passed",
+      "message": "",
+      "name": "/home/coder/.mux/src/agent-terminal/agent_exec_c907e47bb0/test/unit/commands/golden-envelopes.test.ts"
+    }
+  ]
+}
+
+
+ snapshots/01-golden-envelopes.json +
{
+  "numTotalTestSuites": 15,
+  "numPassedTestSuites": 15,
+  "numFailedTestSuites": 0,
+  "numPendingTestSuites": 0,
+  "numTotalTests": 45,
+  "numPassedTests": 45,
+  "numFailedTests": 0,
+  "numPendingTests": 0,
+  "numTodoTests": 0,
+  "snapshot": {
+    "added": 0,
+    "failure": false,
+    "filesAdded": 0,
+    "filesRemoved": 0,
+    "filesRemovedList": [],
+    "filesUnmatched": 0,
+    "filesUpdated": 0,
+    "matched": 0,
+    "total": 0,
+    "unchecked": 0,
+    "uncheckedKeysByFile": [],
+    "unmatched": 0,
+    "updated": 0,
+    "didUpdate": false
+  },
+  "startTime": 1774482010364,
+  "success": true,
+  "testResults": [
+    {
+      "assertionResults": [
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the inspect success envelope shape with live renderer runtime",
+          "status": "passed",
+          "title": "locks the inspect success envelope shape with live renderer runtime",
+          "duration": 11.62846300000001,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts accepts inspect result with offline renderer runtime",
+          "status": "passed",
+          "title": "accepts inspect result with offline renderer runtime",
+          "duration": 1.1077769999999987,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the version success envelope shape",
+          "status": "passed",
+          "title": "locks the version success envelope shape",
+          "duration": 7.875341999999989,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts accepts version result with capabilities",
+          "status": "passed",
+          "title": "accepts version result with capabilities",
+          "duration": 1.2924669999999878,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1165470000000255,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 1.1494579999999814,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'create' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'create' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.6483679999999481,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1587169999999674,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.8051879999999869,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'list' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'list' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.6908480000000168,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.1072080000000142,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.5701480000000174,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'doctor' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'doctor' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.40450900000001866,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.8232980000000225,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.43940900000001193,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.4085499999999911,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.48231900000001815,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.49157900000000154,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'gc (dry-run)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.3585989999999697,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.6193989999999872,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.5239080000000058,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'send-keys' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'send-keys' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.33590999999995574,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 2.035205000000019,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.39202899999997953,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'snapshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'snapshot' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.35172899999997753,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 1.0864579999999933,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.4122590000000059,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'screenshot' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'screenshot' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.36456900000001724,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.7276279999999815,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.2447800000000484,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (asciicast)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.18972899999999981,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.23011999999999944,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.22498899999999367,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'record export (webm)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.1841489999999908,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3348500000000172,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.23541899999997895,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'destroy' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'destroy' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.17264000000000124,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3134989999999789,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.26932900000002746,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (legacy)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.17808999999999742,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract accepts a valid result in the success envelope",
+          "status": "passed",
+          "title": "accepts a valid result in the success envelope",
+          "duration": 0.3770589999999743,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract rejects an invalid result",
+          "status": "passed",
+          "title": "rejects an invalid result",
+          "duration": 0.20796000000001413,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts",
+            "'wait (render)' result contract"
+          ],
+          "fullName": "JSON envelope contracts 'wait (render)' result contract rejects extra fields to enforce strict mode",
+          "status": "passed",
+          "title": "rejects extra fields to enforce strict mode",
+          "duration": 0.19945899999999028,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks the SESSION_NOT_FOUND error envelope shape",
+          "status": "passed",
+          "title": "locks the SESSION_NOT_FOUND error envelope shape",
+          "duration": 0.39596900000003643,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        },
+        {
+          "ancestorTitles": [
+            "JSON envelope contracts"
+          ],
+          "fullName": "JSON envelope contracts locks a retryable transport-style error envelope shape",
+          "status": "passed",
+          "title": "locks a retryable transport-style error envelope shape",
+          "duration": 0.2512399999999957,
+          "failureMessages": [],
+          "meta": {},
+          "tags": []
+        }
+      ],
+      "startTime": 1774482010636,
+      "endTime": 1774482010681.2512,
+      "status": "passed",
+      "message": "",
+      "name": "/home/coder/.mux/src/agent-terminal/agent_exec_c907e47bb0/test/unit/commands/golden-envelopes.test.ts"
+    }
+  ]
+}
+
+
+
+

Notes

+ +
+

notes.md

+

2026-03-25 dogfood — Week 8 contract-locks proof

+

Bundle metadata

+
    +
  • + Bundle path: + dogfood/20260325-week8-contract-locks/ +
  • +
  • + Test file: + test/unit/commands/golden-envelopes.test.ts +
  • +
  • + CLI entrypoint: src/cli/main.ts +
  • +
  • + Captured logs: + logs/01-golden-envelopes.json, + logs/02-golden-envelopes.txt +
  • +
  • + Bundle-local JSON mirror: + snapshots/01-golden-envelopes.json +
  • +
  • + Review screenshot: + screenshots/01-review-page.png +
  • +
  • + Validation target: + npm run validate-bundle -- + dogfood/20260325-week8-contract-locks --profile + contract-reporting +
  • +
+

Scenario summary

+

+ This bundle reran the Week 8 golden-envelope contract suite and + preserved both the machine-readable and human-readable Vitest + outputs. The current suite reports + 45 passing tests in + 1 passing test file, with the JSON reporter also + reporting 15 passing suite sections and + success: true. +

+

+ The suite locks the Week 8 CLI contract surface for these commands: +

+
    +
  • + doctor success envelopes via + DoctorResultSchema +
  • +
  • + gc success envelopes via GcResultSchema, + including the dry-run variant +
  • +
  • + record export success envelopes via + RecordExportResultSchema for both + asciicast and webm +
  • +
  • + version success envelopes, including the Week 8 + optional capabilities list on top of the existing + runtime and renderer backend facts +
  • +
  • + inspect success envelopes, including the Week 8 + rendererRuntime structure and + usedOfflineReplay/artifact-health fields +
  • +
+

Week 8 fields locked by the suite

+

+ | Surface | Locked Week 8 fields | | --------------- | + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + | | doctor | ok; + checks.environment[] and + checks.renderer[] entries with name, + status, message, and + durationMs; capabilities[] entries + validated through CapabilityEntrySchema | | + gc | removedSessions[]; + skippedSessions[] entries with + sessionId and reason; dryRun; + totalBytesFreed | | record export | + sessionId; format; + artifactPath; bytes; sha256; + capturedAtSeq; metadata; optional + durationMs for the asciicast contract | | + version | cliVersion; + protocolVersion; rendererBackends[]; + runtime.node; runtime.platform; + runtime.arch; optional capabilities[] | | + inspect | session; + eventCount; uptime; + lastEventSeq; terminationCategory; + artifacts.total; artifacts.byKind; + artifacts.missingCount; artifacts.health; + usedOfflineReplay; + rendererRuntime.backend; + rendererRuntime.mode; + rendererRuntime.status; optional + rendererRuntime.reason | +

+

Observed results

+
    +
  • + logs/01-golden-envelopes.json reports + numTotalTests: 45, numPassedTests: 45, + numFailedTests: 0, and success: true. +
  • +
  • + snapshots/01-golden-envelopes.json preserves the same + pretty-printed JSON summary in a non-logs/ path so + the review and validation tooling can parse it as a JSON artifact. +
  • +
  • + logs/02-golden-envelopes.txt reports + 1 passed test file and 45 passed tests. +
  • +
  • + logs/01-golden-envelopes.stderr.txt and + logs/02-golden-envelopes.stderr.txt are empty for the + successful capture. +
  • +
  • + screenshots/01-review-page.png captures the generated + local review page after index.html regeneration. +
  • +
+

Review answers

+
    +
  • + Did all golden envelopes pass? Yes. Both captured + Vitest reporters show the suite passing cleanly, and the JSON + reporter marks the run as success: true. +
  • +
  • + What specific Week 8 fields were locked? The + suite now locks the doctor, gc, and + record export result envelopes, plus the Week 8 + version.capabilities[] shape and the + inspect.rendererRuntime/usedOfflineReplay + contract additions listed above. +
  • +
+

Issues / limitations

+
    +
  • + The proof bundle captures the contract tests themselves; it does + not execute the live CLI commands behind those contracts. +
  • +
  • + The screenshot in screenshots/ is a review-page + artifact generated from the local HTML review output, not a CLI + screenshot surface. +
  • +
+
+
+
+

Commands

+
+

commands.sh

+
#!/usr/bin/env bash
+set -euo pipefail
+
+export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH"
+
+BUNDLE_DIR="dogfood/20260325-week8-contract-locks"
+LOG_DIR="$BUNDLE_DIR/logs"
+SCREENSHOT_DIR="$BUNDLE_DIR/screenshots"
+RECORDING_DIR="$BUNDLE_DIR/recordings"
+VIDEO_DIR="$BUNDLE_DIR/videos"
+SNAPSHOT_DIR="$BUNDLE_DIR/snapshots"
+STATUS_FILE="$BUNDLE_DIR/command-status.tsv"
+
+mkdir -p "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR"
+find "$LOG_DIR" "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR" \
+  -mindepth 1 \
+  -maxdepth 1 \
+  ! -name '.gitkeep' \
+  -exec rm -rf {} +
+
+for dir in "$SCREENSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" "$SNAPSHOT_DIR"; do
+  : > "$dir/.gitkeep"
+done
+
+printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_FILE"
+
+record_status() {
+  local step="$1"
+  local command_text="$2"
+  local exit_code="$3"
+  local status="ok"
+  if [[ "$exit_code" -ne 0 ]]; then
+    status="failed"
+  fi
+  printf '%s\t%s\t%s\t%s\n' "$step" "$command_text" "$exit_code" "$status" >> "$STATUS_FILE"
+}
+
+run_capture() {
+  local step="$1"
+  local stdout_path="$2"
+  local stderr_path="$3"
+  shift 3
+  local exit_code=0
+  if "$@" >"$stdout_path" 2>"$stderr_path"; then
+    exit_code=0
+  else
+    exit_code=$?
+  fi
+  record_status "$step" "$*" "$exit_code"
+  return "$exit_code"
+}
+
+RAW_JSON="$LOG_DIR/.01-golden-envelopes.raw.json"
+run_capture \
+  '01' \
+  "$RAW_JSON" \
+  "$LOG_DIR/01-golden-envelopes.stderr.txt" \
+  npx vitest run test/unit/commands/golden-envelopes.test.ts --reporter=json
+
+node --input-type=module -e 'import { readFileSync, writeFileSync } from "node:fs"; const input = process.argv[1]; const output = process.argv[2]; const parsed = JSON.parse(readFileSync(input, "utf8")); writeFileSync(output, JSON.stringify(parsed, null, 2) + "\n");' \
+  "$RAW_JSON" \
+  "$LOG_DIR/01-golden-envelopes.json"
+record_status '02' 'node --input-type=module -e <pretty-print-vitest-json> dogfood/20260325-week8-contract-locks/logs/.01-golden-envelopes.raw.json dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json' '0'
+cp "$LOG_DIR/01-golden-envelopes.json" "$SNAPSHOT_DIR/01-golden-envelopes.json"
+record_status '03' 'cp dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json' '0'
+rm -f "$RAW_JSON"
+
+run_capture \
+  '04' \
+  "$LOG_DIR/02-golden-envelopes.txt" \
+  "$LOG_DIR/02-golden-envelopes.stderr.txt" \
+  npx vitest run test/unit/commands/golden-envelopes.test.ts
+
+
+
+
+

Command status

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
stepcommandexit_codestatus
01 + npx vitest run test/unit/commands/golden-envelopes.test.ts + --reporter=json + 0ok
02 + node --input-type=module -e <pretty-print-vitest-json> + dogfood/20260325-week8-contract-locks/logs/.01-golden-envelopes.raw.json + dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json + 0ok
03 + cp + dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json + dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json + 0ok
04 + npx vitest run test/unit/commands/golden-envelopes.test.ts + 0ok
05 + npm run review-bundle -- dogfood/20260325-week8-contract-locks + 0ok
06 + npm run validate-bundle -- + dogfood/20260325-week8-contract-locks --profile + contract-reporting + 0ok
07 + node --input-type=commonjs + <playwright-review-screenshot> + 0ok
08 + npm run validate-bundle -- + dogfood/20260325-week8-contract-locks --profile + contract-reporting + 0ok
+
+
+
+

Artifact inventory

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeSize
command-status.tsvsupport877 B
commands.shscript2.5 KB
+ logs/01-golden-envelopes.json + support21.4 KB
+ logs/01-golden-envelopes.stderr.txt + support0 B
+ logs/02-golden-envelopes.stderr.txt + support0 B
+ logs/02-golden-envelopes.txt + support504 B
manifest.jsonsupport1.7 KB
notes.mdnotes4.9 KB
recordings/.gitkeepother0 B
screenshots/.gitkeepother0 B
+ screenshots/01-review-page.png + screenshot2.4 MB
snapshots/.gitkeepother0 B
+ snapshots/01-golden-envelopes.json + json21.4 KB
videos/.gitkeepother0 B
+
+
+
+ + diff --git a/dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json b/dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json new file mode 100644 index 0000000..910bbc9 --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.json @@ -0,0 +1,598 @@ +{ + "numTotalTestSuites": 15, + "numPassedTestSuites": 15, + "numFailedTestSuites": 0, + "numPendingTestSuites": 0, + "numTotalTests": 45, + "numPassedTests": 45, + "numFailedTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "snapshot": { + "added": 0, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "uncheckedKeysByFile": [], + "unmatched": 0, + "updated": 0, + "didUpdate": false + }, + "startTime": 1774482010364, + "success": true, + "testResults": [ + { + "assertionResults": [ + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the inspect success envelope shape with live renderer runtime", + "status": "passed", + "title": "locks the inspect success envelope shape with live renderer runtime", + "duration": 11.62846300000001, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts accepts inspect result with offline renderer runtime", + "status": "passed", + "title": "accepts inspect result with offline renderer runtime", + "duration": 1.1077769999999987, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the version success envelope shape", + "status": "passed", + "title": "locks the version success envelope shape", + "duration": 7.875341999999989, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts accepts version result with capabilities", + "status": "passed", + "title": "accepts version result with capabilities", + "duration": 1.2924669999999878, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1165470000000255, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 1.1494579999999814, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.6483679999999481, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1587169999999674, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.8051879999999869, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.6908480000000168, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1072080000000142, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.5701480000000174, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.40450900000001866, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.8232980000000225, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.43940900000001193, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.4085499999999911, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.48231900000001815, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.49157900000000154, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.3585989999999697, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.6193989999999872, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.5239080000000058, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.33590999999995574, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 2.035205000000019, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.39202899999997953, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.35172899999997753, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.0864579999999933, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.4122590000000059, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.36456900000001724, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.7276279999999815, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.2447800000000484, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.18972899999999981, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.23011999999999944, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.22498899999999367, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.1841489999999908, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3348500000000172, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.23541899999997895, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.17264000000000124, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3134989999999789, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.26932900000002746, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.17808999999999742, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3770589999999743, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.20796000000001413, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.19945899999999028, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the SESSION_NOT_FOUND error envelope shape", + "status": "passed", + "title": "locks the SESSION_NOT_FOUND error envelope shape", + "duration": 0.39596900000003643, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks a retryable transport-style error envelope shape", + "status": "passed", + "title": "locks a retryable transport-style error envelope shape", + "duration": 0.2512399999999957, + "failureMessages": [], + "meta": {}, + "tags": [] + } + ], + "startTime": 1774482010636, + "endTime": 1774482010681.2512, + "status": "passed", + "message": "", + "name": "/home/coder/.mux/src/agent-terminal/agent_exec_c907e47bb0/test/unit/commands/golden-envelopes.test.ts" + } + ] +} diff --git a/dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.stderr.txt b/dogfood/20260325-week8-contract-locks/logs/01-golden-envelopes.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/logs/02-golden-envelopes.stderr.txt b/dogfood/20260325-week8-contract-locks/logs/02-golden-envelopes.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/logs/02-golden-envelopes.txt b/dogfood/20260325-week8-contract-locks/logs/02-golden-envelopes.txt new file mode 100644 index 0000000..07c961d --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/logs/02-golden-envelopes.txt @@ -0,0 +1,10 @@ + + RUN  v4.1.0 /home/coder/.mux/src/agent-terminal/agent_exec_c907e47bb0 + + ✓ test/unit/commands/golden-envelopes.test.ts (45 tests) 47ms + + Test Files  1 passed (1) + Tests  45 passed (45) + Start at  23:40:11 + Duration  325ms (transform 90ms, setup 0ms, import 168ms, tests 47ms, environment 0ms) + diff --git a/dogfood/20260325-week8-contract-locks/manifest.json b/dogfood/20260325-week8-contract-locks/manifest.json new file mode 100644 index 0000000..d512cec --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/manifest.json @@ -0,0 +1,44 @@ +{ + "bundle": "20260325-week8-contract-locks", + "title": "Week 8 — Contract Locks", + "description": "Captures the Week 8 golden-envelope test evidence that locks doctor, gc, record export, version, and inspect CLI JSON contracts.", + "createdAt": "2026-03-25T23:39:35Z", + "week": 8, + "result": "pass", + "commands": [ + "npx vitest run test/unit/commands/golden-envelopes.test.ts --reporter=json", + "npx vitest run test/unit/commands/golden-envelopes.test.ts", + "npm run review-bundle -- dogfood/20260325-week8-contract-locks", + "npm run validate-bundle -- dogfood/20260325-week8-contract-locks --profile contract-reporting" + ], + "artifacts": [ + { + "path": "logs/01-golden-envelopes.json", + "description": "Pretty-printed Vitest JSON output for the Week 8 golden-envelope suite" + }, + { + "path": "logs/01-golden-envelopes.stderr.txt", + "description": "Captured stderr for the JSON-reporter Vitest run" + }, + { + "path": "logs/02-golden-envelopes.txt", + "description": "Human-readable Vitest output showing the full Week 8 golden-envelope suite passing" + }, + { + "path": "logs/02-golden-envelopes.stderr.txt", + "description": "Captured stderr for the human-readable Vitest run" + }, + { + "path": "snapshots/01-golden-envelopes.json", + "description": "Bundle-local JSON mirror used by the review and validation tooling" + }, + { + "path": "screenshots/01-review-page.png", + "description": "Screenshot of the generated local review page" + }, + { + "path": "command-status.tsv", + "description": "Per-step exit-code ledger for capture, review-page generation, validation, and screenshot creation" + } + ] +} diff --git a/dogfood/20260325-week8-contract-locks/notes.md b/dogfood/20260325-week8-contract-locks/notes.md new file mode 100644 index 0000000..74bc466 --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/notes.md @@ -0,0 +1,51 @@ +# 2026-03-25 dogfood — Week 8 contract-locks proof + +## Bundle metadata + +- **Bundle path:** `dogfood/20260325-week8-contract-locks/` +- **Test file:** `test/unit/commands/golden-envelopes.test.ts` +- **CLI entrypoint:** `src/cli/main.ts` +- **Captured logs:** `logs/01-golden-envelopes.json`, `logs/02-golden-envelopes.txt` +- **Bundle-local JSON mirror:** `snapshots/01-golden-envelopes.json` +- **Review screenshot:** `screenshots/01-review-page.png` +- **Validation target:** `npm run validate-bundle -- dogfood/20260325-week8-contract-locks --profile contract-reporting` + +## Scenario summary + +This bundle reran the Week 8 golden-envelope contract suite and preserved both the machine-readable and human-readable Vitest outputs. The current suite reports **45 passing tests** in **1 passing test file**, with the JSON reporter also reporting **15 passing suite sections** and `success: true`. + +The suite locks the Week 8 CLI contract surface for these commands: + +- `doctor` success envelopes via `DoctorResultSchema` +- `gc` success envelopes via `GcResultSchema`, including the dry-run variant +- `record export` success envelopes via `RecordExportResultSchema` for both `asciicast` and `webm` +- `version` success envelopes, including the Week 8 optional `capabilities` list on top of the existing runtime and renderer backend facts +- `inspect` success envelopes, including the Week 8 `rendererRuntime` structure and `usedOfflineReplay`/artifact-health fields + +## Week 8 fields locked by the suite + +| Surface | Locked Week 8 fields | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `doctor` | `ok`; `checks.environment[]` and `checks.renderer[]` entries with `name`, `status`, `message`, and `durationMs`; `capabilities[]` entries validated through `CapabilityEntrySchema` | +| `gc` | `removedSessions[]`; `skippedSessions[]` entries with `sessionId` and `reason`; `dryRun`; `totalBytesFreed` | +| `record export` | `sessionId`; `format`; `artifactPath`; `bytes`; `sha256`; `capturedAtSeq`; `metadata`; optional `durationMs` for the asciicast contract | +| `version` | `cliVersion`; `protocolVersion`; `rendererBackends[]`; `runtime.node`; `runtime.platform`; `runtime.arch`; optional `capabilities[]` | +| `inspect` | `session`; `eventCount`; `uptime`; `lastEventSeq`; `terminationCategory`; `artifacts.total`; `artifacts.byKind`; `artifacts.missingCount`; `artifacts.health`; `usedOfflineReplay`; `rendererRuntime.backend`; `rendererRuntime.mode`; `rendererRuntime.status`; optional `rendererRuntime.reason` | + +## Observed results + +- `logs/01-golden-envelopes.json` reports `numTotalTests: 45`, `numPassedTests: 45`, `numFailedTests: 0`, and `success: true`. +- `snapshots/01-golden-envelopes.json` preserves the same pretty-printed JSON summary in a non-`logs/` path so the review and validation tooling can parse it as a JSON artifact. +- `logs/02-golden-envelopes.txt` reports `1 passed` test file and `45 passed` tests. +- `logs/01-golden-envelopes.stderr.txt` and `logs/02-golden-envelopes.stderr.txt` are empty for the successful capture. +- `screenshots/01-review-page.png` captures the generated local review page after `index.html` regeneration. + +## Review answers + +- **Did all golden envelopes pass?** Yes. Both captured Vitest reporters show the suite passing cleanly, and the JSON reporter marks the run as `success: true`. +- **What specific Week 8 fields were locked?** The suite now locks the `doctor`, `gc`, and `record export` result envelopes, plus the Week 8 `version.capabilities[]` shape and the `inspect.rendererRuntime`/`usedOfflineReplay` contract additions listed above. + +## Issues / limitations + +- The proof bundle captures the contract tests themselves; it does not execute the live CLI commands behind those contracts. +- The screenshot in `screenshots/` is a review-page artifact generated from the local HTML review output, not a CLI screenshot surface. diff --git a/dogfood/20260325-week8-contract-locks/recordings/.gitkeep b/dogfood/20260325-week8-contract-locks/recordings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/screenshots/.gitkeep b/dogfood/20260325-week8-contract-locks/screenshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/screenshots/01-review-page.png b/dogfood/20260325-week8-contract-locks/screenshots/01-review-page.png new file mode 100644 index 0000000..3c893cc Binary files /dev/null and b/dogfood/20260325-week8-contract-locks/screenshots/01-review-page.png differ diff --git a/dogfood/20260325-week8-contract-locks/snapshots/.gitkeep b/dogfood/20260325-week8-contract-locks/snapshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json b/dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json new file mode 100644 index 0000000..910bbc9 --- /dev/null +++ b/dogfood/20260325-week8-contract-locks/snapshots/01-golden-envelopes.json @@ -0,0 +1,598 @@ +{ + "numTotalTestSuites": 15, + "numPassedTestSuites": 15, + "numFailedTestSuites": 0, + "numPendingTestSuites": 0, + "numTotalTests": 45, + "numPassedTests": 45, + "numFailedTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "snapshot": { + "added": 0, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "uncheckedKeysByFile": [], + "unmatched": 0, + "updated": 0, + "didUpdate": false + }, + "startTime": 1774482010364, + "success": true, + "testResults": [ + { + "assertionResults": [ + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the inspect success envelope shape with live renderer runtime", + "status": "passed", + "title": "locks the inspect success envelope shape with live renderer runtime", + "duration": 11.62846300000001, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts accepts inspect result with offline renderer runtime", + "status": "passed", + "title": "accepts inspect result with offline renderer runtime", + "duration": 1.1077769999999987, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the version success envelope shape", + "status": "passed", + "title": "locks the version success envelope shape", + "duration": 7.875341999999989, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts accepts version result with capabilities", + "status": "passed", + "title": "accepts version result with capabilities", + "duration": 1.2924669999999878, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1165470000000255, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 1.1494579999999814, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'create' result contract" + ], + "fullName": "JSON envelope contracts 'create' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.6483679999999481, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1587169999999674, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.8051879999999869, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'list' result contract" + ], + "fullName": "JSON envelope contracts 'list' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.6908480000000168, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.1072080000000142, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.5701480000000174, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'doctor' result contract" + ], + "fullName": "JSON envelope contracts 'doctor' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.40450900000001866, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.8232980000000225, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.43940900000001193, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts", "'gc' result contract"], + "fullName": "JSON envelope contracts 'gc' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.4085499999999911, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.48231900000001815, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.49157900000000154, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'gc (dry-run)' result contract" + ], + "fullName": "JSON envelope contracts 'gc (dry-run)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.3585989999999697, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.6193989999999872, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.5239080000000058, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'send-keys' result contract" + ], + "fullName": "JSON envelope contracts 'send-keys' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.33590999999995574, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 2.035205000000019, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.39202899999997953, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'snapshot' result contract" + ], + "fullName": "JSON envelope contracts 'snapshot' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.35172899999997753, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 1.0864579999999933, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.4122590000000059, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'screenshot' result contract" + ], + "fullName": "JSON envelope contracts 'screenshot' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.36456900000001724, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.7276279999999815, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.2447800000000484, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (asciicast)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (asciicast)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.18972899999999981, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.23011999999999944, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.22498899999999367, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'record export (webm)' result contract" + ], + "fullName": "JSON envelope contracts 'record export (webm)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.1841489999999908, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3348500000000172, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.23541899999997895, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'destroy' result contract" + ], + "fullName": "JSON envelope contracts 'destroy' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.17264000000000124, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3134989999999789, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.26932900000002746, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (legacy)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (legacy)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.17808999999999742, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract accepts a valid result in the success envelope", + "status": "passed", + "title": "accepts a valid result in the success envelope", + "duration": 0.3770589999999743, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract rejects an invalid result", + "status": "passed", + "title": "rejects an invalid result", + "duration": 0.20796000000001413, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": [ + "JSON envelope contracts", + "'wait (render)' result contract" + ], + "fullName": "JSON envelope contracts 'wait (render)' result contract rejects extra fields to enforce strict mode", + "status": "passed", + "title": "rejects extra fields to enforce strict mode", + "duration": 0.19945899999999028, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks the SESSION_NOT_FOUND error envelope shape", + "status": "passed", + "title": "locks the SESSION_NOT_FOUND error envelope shape", + "duration": 0.39596900000003643, + "failureMessages": [], + "meta": {}, + "tags": [] + }, + { + "ancestorTitles": ["JSON envelope contracts"], + "fullName": "JSON envelope contracts locks a retryable transport-style error envelope shape", + "status": "passed", + "title": "locks a retryable transport-style error envelope shape", + "duration": 0.2512399999999957, + "failureMessages": [], + "meta": {}, + "tags": [] + } + ], + "startTime": 1774482010636, + "endTime": 1774482010681.2512, + "status": "passed", + "message": "", + "name": "/home/coder/.mux/src/agent-terminal/agent_exec_c907e47bb0/test/unit/commands/golden-envelopes.test.ts" + } + ] +} diff --git a/dogfood/20260325-week8-contract-locks/videos/.gitkeep b/dogfood/20260325-week8-contract-locks/videos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/agent-terminal-home.txt b/dogfood/20260325-week8-inspect-runtime/agent-terminal-home.txt new file mode 100644 index 0000000..ebfbe3f --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/agent-terminal-home.txt @@ -0,0 +1 @@ +/tmp/tmp.s3O1Nrg0On diff --git a/dogfood/20260325-week8-inspect-runtime/command-status.tsv b/dogfood/20260325-week8-inspect-runtime/command-status.tsv new file mode 100644 index 0000000..a817758 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/command-status.tsv @@ -0,0 +1,5 @@ +step command exit_code status +01-create npx tsx src/cli/main.ts create --json -- node --import tsx test/fixtures/apps/hello-prompt/main.ts 0 pass +02-inspect-live npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json 0 pass +03-destroy npx tsx src/cli/main.ts destroy 01KMKNZXNWZKGCXTCKGTVHC9T7 --json 0 pass +04-inspect-offline npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json 0 pass diff --git a/dogfood/20260325-week8-inspect-runtime/commands.sh b/dogfood/20260325-week8-inspect-runtime/commands.sh new file mode 100755 index 0000000..450907c --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/commands.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -euo pipefail + +export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH" +if command -v mise >/dev/null 2>&1; then + mise trust >/dev/null 2>&1 || true +fi + +BUNDLE_DIR="dogfood/20260325-week8-inspect-runtime" +LOG_DIR="$BUNDLE_DIR/logs" +JSON_DIR="$BUNDLE_DIR/json" +SCREENSHOT_DIR="$BUNDLE_DIR/screenshots" +SNAPSHOT_DIR="$BUNDLE_DIR/snapshots" +RECORDING_DIR="$BUNDLE_DIR/recordings" +VIDEO_DIR="$BUNDLE_DIR/videos" +STATUS_TSV="$BUNDLE_DIR/command-status.tsv" + +mkdir -p "$LOG_DIR" "$JSON_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" +find "$LOG_DIR" "$JSON_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" -mindepth 1 -maxdepth 1 -type f -delete +rm -f "$BUNDLE_DIR/agent-terminal-home.txt" "$BUNDLE_DIR/session-id.txt" "$STATUS_TSV" +touch "$SCREENSHOT_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep" "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep" + +pretty_json() { + local path="$1" + node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path" +} + +record_status() { + local step="$1" + local command="$2" + local exit_code="$3" + local status="$4" + printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV" +} + +run_json_step() { + local step="$1" + local command="$2" + local stdout_path="$LOG_DIR/$step.json" + local stderr_path="$LOG_DIR/$step.stderr.txt" + local exit_code=0 + + set +e + eval "$command" >"$stdout_path" 2>"$stderr_path" + exit_code=$? + set -e + + if [ -s "$stdout_path" ]; then + pretty_json "$stdout_path" + fi + + local status="pass" + if [ "$exit_code" -ne 0 ]; then + status="fail" + fi + record_status "$step" "$command" "$exit_code" "$status" + + if [ "$exit_code" -ne 0 ]; then + return "$exit_code" + fi +} + +publish_json_output() { + local source_path="$1" + local output_path="$2" + cp "$source_path" "$output_path" +} + +TMP_HOME=$(mktemp -d) +export AGENT_TERMINAL_HOME="$TMP_HOME" +printf '%s\n' "$AGENT_TERMINAL_HOME" > "$BUNDLE_DIR/agent-terminal-home.txt" +printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV" + +cleanup() { + if [ -n "${SESSION_ID:-}" ]; then + if [ -f "$LOG_DIR/03-destroy.json" ]; then + : + else + set +e + npx tsx src/cli/main.ts destroy "$SESSION_ID" --json >"$LOG_DIR/99-cleanup-destroy.json" 2>"$LOG_DIR/99-cleanup-destroy.stderr.txt" + cleanup_exit=$? + set -e + if [ -s "$LOG_DIR/99-cleanup-destroy.json" ]; then + pretty_json "$LOG_DIR/99-cleanup-destroy.json" + fi + record_status '99-cleanup-destroy' "npx tsx src/cli/main.ts destroy $SESSION_ID --json" "$cleanup_exit" "$([ "$cleanup_exit" -eq 0 ] && printf pass || printf fail)" + fi + fi + rm -rf "$TMP_HOME" +} +trap cleanup EXIT + +run_json_step '01-create' 'npx tsx src/cli/main.ts create --json -- node --import tsx test/fixtures/apps/hello-prompt/main.ts' +SESSION_ID=$(node -e "const fs=require('fs'); const value=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const sessionId=value?.result?.sessionId; if (typeof sessionId !== 'string' || sessionId.length === 0) { throw new Error('create output did not include result.sessionId'); } process.stdout.write(sessionId);" "$LOG_DIR/01-create.json") +printf '%s\n' "$SESSION_ID" > "$BUNDLE_DIR/session-id.txt" +sleep 2 + +run_json_step '02-inspect-live' "npx tsx src/cli/main.ts inspect $SESSION_ID --json" +publish_json_output "$LOG_DIR/02-inspect-live.json" "$JSON_DIR/inspect-live.json" +run_json_step '03-destroy' "npx tsx src/cli/main.ts destroy $SESSION_ID --json" +run_json_step '04-inspect-offline' "npx tsx src/cli/main.ts inspect $SESSION_ID --json" +publish_json_output "$LOG_DIR/04-inspect-offline.json" "$JSON_DIR/inspect-offline.json" diff --git a/dogfood/20260325-week8-inspect-runtime/index.html b/dogfood/20260325-week8-inspect-runtime/index.html new file mode 100644 index 0000000..8fdfc80 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/index.html @@ -0,0 +1,1006 @@ + + + + + + Week 8 — Inspect Runtime review bundle + + + +
+
+

Portable review bundle

+

Week 8 — Inspect Runtime

+

Bundle directory: 20260325-week8-inspect-runtime

+

+ Proves inspect --json reports live-host vs offline-replay renderer + runtime summaries for the same retained session. +

+
+ +
+

Manifest summary

+
+
+
Bundle
+
20260325-week8-inspect-runtime
+
+
+
Date
+
2026-03-25T23:41:40Z
+
+
+
Session ID
+
01KMKNZXNWZKGCXTCKGTVHC9T7
+
+
+
Result
+
pass
+
+
+
Fixture
+
node --import tsx test/fixtures/apps/hello-prompt/main.ts
+
+
+
Week
+
8
+
+
+
Commands
+
4
+
+
+
Manifest artifacts
+
10
+
+
+
Discovered files
+
21
+
+
+
+

Commands

+
    +
  • + npx tsx src/cli/main.ts create --json -- node --import tsx + test/fixtures/apps/hello-prompt/main.ts +
  • +
  • + npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json +
  • +
  • + npx tsx src/cli/main.ts destroy 01KMKNZXNWZKGCXTCKGTVHC9T7 --json +
  • +
  • + npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json +
  • +
+
+
+
+

Screenshot gallery

+ +
+ +
+

JSON outputs

+ +
+ json/inspect-live.json +
{
+  "ok": true,
+  "command": "inspect",
+  "timestamp": "2026-03-25T23:41:37.715Z",
+  "result": {
+    "session": {
+      "version": 1,
+      "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+      "createdAt": "2026-03-25T23:41:33.759Z",
+      "updatedAt": "2026-03-25T23:41:34.547Z",
+      "status": "running",
+      "command": [
+        "node",
+        "--import",
+        "tsx",
+        "test/fixtures/apps/hello-prompt/main.ts"
+      ],
+      "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848",
+      "shell": "/bin/bash",
+      "term": "xterm-256color",
+      "cols": 80,
+      "rows": 24,
+      "creationCols": 80,
+      "creationRows": 24,
+      "hostPid": 2429891,
+      "childPid": 2430004,
+      "exitCode": null,
+      "exitSignal": null
+    },
+    "eventCount": 1,
+    "uptime": 3955,
+    "lastEventSeq": 0,
+    "terminationCategory": "running",
+    "artifacts": {
+      "total": 0,
+      "byKind": {},
+      "missingCount": 0,
+      "health": "no-artifacts"
+    },
+    "usedOfflineReplay": false,
+    "rendererRuntime": {
+      "backend": "ghostty-web",
+      "mode": "live-host",
+      "status": "healthy"
+    }
+  }
+}
+
+
+ json/inspect-offline.json +
{
+  "ok": true,
+  "command": "inspect",
+  "timestamp": "2026-03-25T23:41:39.958Z",
+  "result": {
+    "session": {
+      "version": 1,
+      "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+      "createdAt": "2026-03-25T23:41:33.759Z",
+      "updatedAt": "2026-03-25T23:41:38.798Z",
+      "status": "destroyed",
+      "command": [
+        "node",
+        "--import",
+        "tsx",
+        "test/fixtures/apps/hello-prompt/main.ts"
+      ],
+      "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848",
+      "shell": "/bin/bash",
+      "term": "xterm-256color",
+      "cols": 80,
+      "rows": 24,
+      "creationCols": 80,
+      "creationRows": 24,
+      "hostPid": 2429891,
+      "childPid": 2430004,
+      "exitCode": 0,
+      "exitSignal": "1"
+    },
+    "eventCount": 2,
+    "uptime": 5039,
+    "lastEventSeq": 1,
+    "terminationCategory": "destroyed",
+    "artifacts": {
+      "total": 0,
+      "byKind": {},
+      "missingCount": 0,
+      "health": "no-artifacts"
+    },
+    "usedOfflineReplay": false,
+    "rendererRuntime": {
+      "backend": "ghostty-web",
+      "mode": "offline-replay",
+      "status": "fallback",
+      "reason": "session-not-running"
+    }
+  }
+}
+
+
+ logs/01-create.json +
{
+  "ok": true,
+  "command": "create",
+  "timestamp": "2026-03-25T23:41:34.585Z",
+  "result": {
+    "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+    "createdAt": "2026-03-25T23:41:33.759Z",
+    "cols": 80,
+    "rows": 24,
+    "shell": "/bin/bash"
+  }
+}
+
+
+ logs/02-inspect-live.json +
{
+  "ok": true,
+  "command": "inspect",
+  "timestamp": "2026-03-25T23:41:37.715Z",
+  "result": {
+    "session": {
+      "version": 1,
+      "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+      "createdAt": "2026-03-25T23:41:33.759Z",
+      "updatedAt": "2026-03-25T23:41:34.547Z",
+      "status": "running",
+      "command": [
+        "node",
+        "--import",
+        "tsx",
+        "test/fixtures/apps/hello-prompt/main.ts"
+      ],
+      "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848",
+      "shell": "/bin/bash",
+      "term": "xterm-256color",
+      "cols": 80,
+      "rows": 24,
+      "creationCols": 80,
+      "creationRows": 24,
+      "hostPid": 2429891,
+      "childPid": 2430004,
+      "exitCode": null,
+      "exitSignal": null
+    },
+    "eventCount": 1,
+    "uptime": 3955,
+    "lastEventSeq": 0,
+    "terminationCategory": "running",
+    "artifacts": {
+      "total": 0,
+      "byKind": {},
+      "missingCount": 0,
+      "health": "no-artifacts"
+    },
+    "usedOfflineReplay": false,
+    "rendererRuntime": {
+      "backend": "ghostty-web",
+      "mode": "live-host",
+      "status": "healthy"
+    }
+  }
+}
+
+
+ logs/03-destroy.json +
{
+  "ok": true,
+  "command": "destroy",
+  "timestamp": "2026-03-25T23:41:38.896Z",
+  "result": {
+    "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+    "destroyed": true
+  }
+}
+
+
+ logs/04-inspect-offline.json +
{
+  "ok": true,
+  "command": "inspect",
+  "timestamp": "2026-03-25T23:41:39.958Z",
+  "result": {
+    "session": {
+      "version": 1,
+      "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7",
+      "createdAt": "2026-03-25T23:41:33.759Z",
+      "updatedAt": "2026-03-25T23:41:38.798Z",
+      "status": "destroyed",
+      "command": [
+        "node",
+        "--import",
+        "tsx",
+        "test/fixtures/apps/hello-prompt/main.ts"
+      ],
+      "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848",
+      "shell": "/bin/bash",
+      "term": "xterm-256color",
+      "cols": 80,
+      "rows": 24,
+      "creationCols": 80,
+      "creationRows": 24,
+      "hostPid": 2429891,
+      "childPid": 2430004,
+      "exitCode": 0,
+      "exitSignal": "1"
+    },
+    "eventCount": 2,
+    "uptime": 5039,
+    "lastEventSeq": 1,
+    "terminationCategory": "destroyed",
+    "artifacts": {
+      "total": 0,
+      "byKind": {},
+      "missingCount": 0,
+      "health": "no-artifacts"
+    },
+    "usedOfflineReplay": false,
+    "rendererRuntime": {
+      "backend": "ghostty-web",
+      "mode": "offline-replay",
+      "status": "fallback",
+      "reason": "session-not-running"
+    }
+  }
+}
+
+
+
+

Notes

+ +
+

notes.md

+

2026-03-25 dogfood — Week 8 inspect runtime proof

+

Bundle metadata

+
    +
  • + Bundle path: + dogfood/20260325-week8-inspect-runtime/ +
  • +
  • + Session ID: + 01KMKNZXNWZKGCXTCKGTVHC9T7 +
  • +
  • + Isolated AGENT_TERMINAL_HOME: + /tmp/tmp.s3O1Nrg0On +
  • +
  • + CLI entrypoint: + npx tsx src/cli/main.ts +
  • +
  • + Fixture app: + node --import tsx test/fixtures/apps/hello-prompt/main.ts +
  • +
+

Scenario summary

+

+ This bundle proves the Week 8 inspect --json renderer + runtime summary against a real isolated PTY-backed + hello-prompt session. The same session was inspected + twice: once while it was still running and once after + destroy --json had transitioned it into retained + offline state. +

+

rendererRuntime proof

+

+ | Capture | Evidence | session.status | + rendererRuntime.mode | + rendererRuntime.status | + rendererRuntime.reason | + usedOfflineReplay | | --- | --- | --- | --- | --- | --- + | --- | | Live inspect | logs/02-inspect-live.json | + running | live-host | + healthy | _omitted_ | false | | + Post-destroy inspect | logs/04-inspect-offline.json | + destroyed | offline-replay | + fallback | session-not-running | + false | +

+

Key observations from the captured JSON envelopes:

+
    +
  • + rendererRuntime.backend stayed + ghostty-web in both states; only the runtime mode + changed. +
  • +
  • + While the host was still alive, + inspect --json reported + rendererRuntime.mode = "live-host" and + status = "healthy". +
  • +
  • + After destroy --json, the retained session metadata + still inspected successfully, but + rendererRuntime.mode switched to + "offline-replay" with fallback reason + "session-not-running". +
  • +
  • + usedOfflineReplay stayed false in both + captures. This is an important nuance: an offline renderer mode + does not automatically mean the command had to + fall back from a failed live RPC call. +
  • +
+

Fallback reason interpretation

+

The Week 8 surface distinguishes two fallback reasons:

+
    +
  • + session-not-running — captured + directly in this bundle via + logs/04-inspect-offline.json after the session was + destroyed. +
  • +
  • + host-unreachable — the alternate + path used when a session still appears live but its host cannot + answer RPC. The real PTY run here stayed healthy, so this bundle + does not force that failure mode. The repo's inspect unit test + (test/unit/commands/inspect.test.ts) explicitly + covers it and asserts + rendererRuntime.mode = "offline-replay", + rendererRuntime.reason = "host-unreachable", and usedOfflineReplay = true for that case. +
  • +
+

+ That distinction matches the implementation in + src/cli/commands/inspect.ts: + usedOfflineReplay only flips to true when + the command expected to use the live host but had to reconcile from + retained state after a host-unreachable failure. +

+

Review answers

+
    +
  • + Did live inspect report the new live-host runtime + summary? + Yes. logs/02-inspect-live.json shows + rendererRuntime.backend = "ghostty-web", + mode = "live-host", + status = "healthy", and + usedOfflineReplay = false while the session status is + running. +
  • +
  • + Did offline inspect report the new offline-replay runtime + summary? + Yes. logs/04-inspect-offline.json shows + rendererRuntime.mode = "offline-replay", + status = "fallback", + reason = "session-not-running", and + usedOfflineReplay = false after the same session was + destroyed. +
  • +
  • + Did destroy preserve enough retained state for post-destroy + inspect? + Yes. logs/03-destroy.json reports + destroyed: true, and the subsequent + logs/04-inspect-offline.json still reports the + original command, retained size metadata, event count, and + termination category. +
  • +
  • + Did the JSON envelopes show any artifact-health side + effects? + Yes. Both inspect captures report + artifacts.health = "no-artifacts", which is + expected because this proof only exercised create, + inspect, and destroy without generating + snapshots or screenshots. +
  • +
  • + Where is the command ledger? + command-status.tsv records the exact create / live + inspect / destroy / offline inspect sequence, commands, exit + codes, and pass/fail status. Each step also has a matching + logs/*.stderr.txt sidecar. +
  • +
+

Browser verification

+

+ The generated review page was opened from the local bundle + index.html and captured with Playwright. + Reviewer-facing proof lives at + screenshots/01-review-page.png. +

+

Issues / limitations

+
    +
  • + This bundle directly proves the + session-not-running fallback path with a real PTY + session. It does not intentionally simulate a broken live host, so + host-unreachable remains code-and-unit-test-backed + rather than PTY-captured evidence here. +
  • +
  • + The post-destroy inspect result reports + exitSignal = "1" alongside + terminationCategory = "destroyed". That is + consistent with the teardown path used by + destroy --json and does not indicate a bundle + failure. +
  • +
  • + No renderer artifacts were generated in this proof bundle by + design, so artifacts.total = 0 and + health = "no-artifacts" are expected in + both inspect captures. +
  • +
+
+
+
+

Commands

+
+

commands.sh

+
#!/usr/bin/env bash
+set -euo pipefail
+
+export PATH="$HOME/.local/share/mise/shims:$HOME/.local/bin:$PATH"
+if command -v mise >/dev/null 2>&1; then
+  mise trust >/dev/null 2>&1 || true
+fi
+
+BUNDLE_DIR="dogfood/20260325-week8-inspect-runtime"
+LOG_DIR="$BUNDLE_DIR/logs"
+JSON_DIR="$BUNDLE_DIR/json"
+SCREENSHOT_DIR="$BUNDLE_DIR/screenshots"
+SNAPSHOT_DIR="$BUNDLE_DIR/snapshots"
+RECORDING_DIR="$BUNDLE_DIR/recordings"
+VIDEO_DIR="$BUNDLE_DIR/videos"
+STATUS_TSV="$BUNDLE_DIR/command-status.tsv"
+
+mkdir -p "$LOG_DIR" "$JSON_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR"
+find "$LOG_DIR" "$JSON_DIR" "$SCREENSHOT_DIR" "$SNAPSHOT_DIR" "$RECORDING_DIR" "$VIDEO_DIR" -mindepth 1 -maxdepth 1 -type f -delete
+rm -f "$BUNDLE_DIR/agent-terminal-home.txt" "$BUNDLE_DIR/session-id.txt" "$STATUS_TSV"
+touch "$SCREENSHOT_DIR/.gitkeep" "$SNAPSHOT_DIR/.gitkeep" "$RECORDING_DIR/.gitkeep" "$VIDEO_DIR/.gitkeep"
+
+pretty_json() {
+  local path="$1"
+  node -e "const fs=require('fs'); const path=process.argv[1]; const text=fs.readFileSync(path,'utf8').trim(); if (text.length === 0) process.exit(0); const value=JSON.parse(text); fs.writeFileSync(path, JSON.stringify(value, null, 2) + '\n');" "$path"
+}
+
+record_status() {
+  local step="$1"
+  local command="$2"
+  local exit_code="$3"
+  local status="$4"
+  printf '%s\t%s\t%s\t%s\n' "$step" "$command" "$exit_code" "$status" >> "$STATUS_TSV"
+}
+
+run_json_step() {
+  local step="$1"
+  local command="$2"
+  local stdout_path="$LOG_DIR/$step.json"
+  local stderr_path="$LOG_DIR/$step.stderr.txt"
+  local exit_code=0
+
+  set +e
+  eval "$command" >"$stdout_path" 2>"$stderr_path"
+  exit_code=$?
+  set -e
+
+  if [ -s "$stdout_path" ]; then
+    pretty_json "$stdout_path"
+  fi
+
+  local status="pass"
+  if [ "$exit_code" -ne 0 ]; then
+    status="fail"
+  fi
+  record_status "$step" "$command" "$exit_code" "$status"
+
+  if [ "$exit_code" -ne 0 ]; then
+    return "$exit_code"
+  fi
+}
+
+publish_json_output() {
+  local source_path="$1"
+  local output_path="$2"
+  cp "$source_path" "$output_path"
+}
+
+TMP_HOME=$(mktemp -d)
+export AGENT_TERMINAL_HOME="$TMP_HOME"
+printf '%s\n' "$AGENT_TERMINAL_HOME" > "$BUNDLE_DIR/agent-terminal-home.txt"
+printf 'step\tcommand\texit_code\tstatus\n' > "$STATUS_TSV"
+
+cleanup() {
+  if [ -n "${SESSION_ID:-}" ]; then
+    if [ -f "$LOG_DIR/03-destroy.json" ]; then
+      :
+    else
+      set +e
+      npx tsx src/cli/main.ts destroy "$SESSION_ID" --json >"$LOG_DIR/99-cleanup-destroy.json" 2>"$LOG_DIR/99-cleanup-destroy.stderr.txt"
+      cleanup_exit=$?
+      set -e
+      if [ -s "$LOG_DIR/99-cleanup-destroy.json" ]; then
+        pretty_json "$LOG_DIR/99-cleanup-destroy.json"
+      fi
+      record_status '99-cleanup-destroy' "npx tsx src/cli/main.ts destroy $SESSION_ID --json" "$cleanup_exit" "$([ "$cleanup_exit" -eq 0 ] && printf pass || printf fail)"
+    fi
+  fi
+  rm -rf "$TMP_HOME"
+}
+trap cleanup EXIT
+
+run_json_step '01-create' 'npx tsx src/cli/main.ts create --json -- node --import tsx test/fixtures/apps/hello-prompt/main.ts'
+SESSION_ID=$(node -e "const fs=require('fs'); const value=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const sessionId=value?.result?.sessionId; if (typeof sessionId !== 'string' || sessionId.length === 0) { throw new Error('create output did not include result.sessionId'); } process.stdout.write(sessionId);" "$LOG_DIR/01-create.json")
+printf '%s\n' "$SESSION_ID" > "$BUNDLE_DIR/session-id.txt"
+sleep 2
+
+run_json_step '02-inspect-live' "npx tsx src/cli/main.ts inspect $SESSION_ID --json"
+publish_json_output "$LOG_DIR/02-inspect-live.json" "$JSON_DIR/inspect-live.json"
+run_json_step '03-destroy' "npx tsx src/cli/main.ts destroy $SESSION_ID --json"
+run_json_step '04-inspect-offline' "npx tsx src/cli/main.ts inspect $SESSION_ID --json"
+publish_json_output "$LOG_DIR/04-inspect-offline.json" "$JSON_DIR/inspect-offline.json"
+
+
+
+
+

Command status

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
stepcommandexit_codestatus
01-create + npx tsx src/cli/main.ts create --json -- node --import tsx + test/fixtures/apps/hello-prompt/main.ts + 0pass
02-inspect-live + npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 + --json + 0pass
03-destroy + npx tsx src/cli/main.ts destroy 01KMKNZXNWZKGCXTCKGTVHC9T7 + --json + 0pass
04-inspect-offline + npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 + --json + 0pass
+
+
+
+

Artifact inventory

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeSize
+ agent-terminal-home.txt + support20 B
command-status.tsvsupport411 B
commands.shscript3.7 KB
+ json/inspect-live.json + json1.1 KB
+ json/inspect-offline.json + json1.2 KB
logs/01-create.jsonsupport251 B
+ logs/01-create.stderr.txt + support0 B
+ logs/02-inspect-live.json + support1.1 KB
+ logs/02-inspect-live.stderr.txt + support0 B
logs/03-destroy.jsonsupport172 B
+ logs/03-destroy.stderr.txt + support0 B
+ logs/04-inspect-offline.json + support1.2 KB
+ logs/04-inspect-offline.stderr.txt + support0 B
manifest.jsonsupport2.3 KB
notes.mdnotes4.8 KB
recordings/.gitkeepother0 B
screenshots/.gitkeepother0 B
+ screenshots/01-review-page.png + screenshot1.4 MB
session-id.txtsupport27 B
snapshots/.gitkeepother0 B
videos/.gitkeepother0 B
+
+
+
+ + diff --git a/dogfood/20260325-week8-inspect-runtime/json/inspect-live.json b/dogfood/20260325-week8-inspect-runtime/json/inspect-live.json new file mode 100644 index 0000000..5d23565 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/json/inspect-live.json @@ -0,0 +1,47 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-03-25T23:41:37.715Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "createdAt": "2026-03-25T23:41:33.759Z", + "updatedAt": "2026-03-25T23:41:34.547Z", + "status": "running", + "command": [ + "node", + "--import", + "tsx", + "test/fixtures/apps/hello-prompt/main.ts" + ], + "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 2429891, + "childPid": 2430004, + "exitCode": null, + "exitSignal": null + }, + "eventCount": 1, + "uptime": 3955, + "lastEventSeq": 0, + "terminationCategory": "running", + "artifacts": { + "total": 0, + "byKind": {}, + "missingCount": 0, + "health": "no-artifacts" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "ghostty-web", + "mode": "live-host", + "status": "healthy" + } + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/json/inspect-offline.json b/dogfood/20260325-week8-inspect-runtime/json/inspect-offline.json new file mode 100644 index 0000000..c3ad04b --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/json/inspect-offline.json @@ -0,0 +1,48 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-03-25T23:41:39.958Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "createdAt": "2026-03-25T23:41:33.759Z", + "updatedAt": "2026-03-25T23:41:38.798Z", + "status": "destroyed", + "command": [ + "node", + "--import", + "tsx", + "test/fixtures/apps/hello-prompt/main.ts" + ], + "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 2429891, + "childPid": 2430004, + "exitCode": 0, + "exitSignal": "1" + }, + "eventCount": 2, + "uptime": 5039, + "lastEventSeq": 1, + "terminationCategory": "destroyed", + "artifacts": { + "total": 0, + "byKind": {}, + "missingCount": 0, + "health": "no-artifacts" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "ghostty-web", + "mode": "offline-replay", + "status": "fallback", + "reason": "session-not-running" + } + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/logs/01-create.json b/dogfood/20260325-week8-inspect-runtime/logs/01-create.json new file mode 100644 index 0000000..3c77731 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/logs/01-create.json @@ -0,0 +1,12 @@ +{ + "ok": true, + "command": "create", + "timestamp": "2026-03-25T23:41:34.585Z", + "result": { + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "createdAt": "2026-03-25T23:41:33.759Z", + "cols": 80, + "rows": 24, + "shell": "/bin/bash" + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/logs/01-create.stderr.txt b/dogfood/20260325-week8-inspect-runtime/logs/01-create.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/logs/02-inspect-live.json b/dogfood/20260325-week8-inspect-runtime/logs/02-inspect-live.json new file mode 100644 index 0000000..5d23565 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/logs/02-inspect-live.json @@ -0,0 +1,47 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-03-25T23:41:37.715Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "createdAt": "2026-03-25T23:41:33.759Z", + "updatedAt": "2026-03-25T23:41:34.547Z", + "status": "running", + "command": [ + "node", + "--import", + "tsx", + "test/fixtures/apps/hello-prompt/main.ts" + ], + "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 2429891, + "childPid": 2430004, + "exitCode": null, + "exitSignal": null + }, + "eventCount": 1, + "uptime": 3955, + "lastEventSeq": 0, + "terminationCategory": "running", + "artifacts": { + "total": 0, + "byKind": {}, + "missingCount": 0, + "health": "no-artifacts" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "ghostty-web", + "mode": "live-host", + "status": "healthy" + } + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/logs/02-inspect-live.stderr.txt b/dogfood/20260325-week8-inspect-runtime/logs/02-inspect-live.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/logs/03-destroy.json b/dogfood/20260325-week8-inspect-runtime/logs/03-destroy.json new file mode 100644 index 0000000..23424c3 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/logs/03-destroy.json @@ -0,0 +1,9 @@ +{ + "ok": true, + "command": "destroy", + "timestamp": "2026-03-25T23:41:38.896Z", + "result": { + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "destroyed": true + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/logs/03-destroy.stderr.txt b/dogfood/20260325-week8-inspect-runtime/logs/03-destroy.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/logs/04-inspect-offline.json b/dogfood/20260325-week8-inspect-runtime/logs/04-inspect-offline.json new file mode 100644 index 0000000..c3ad04b --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/logs/04-inspect-offline.json @@ -0,0 +1,48 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-03-25T23:41:39.958Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "createdAt": "2026-03-25T23:41:33.759Z", + "updatedAt": "2026-03-25T23:41:38.798Z", + "status": "destroyed", + "command": [ + "node", + "--import", + "tsx", + "test/fixtures/apps/hello-prompt/main.ts" + ], + "cwd": "/home/coder/.mux/src/agent-terminal/agent_exec_6300059848", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 2429891, + "childPid": 2430004, + "exitCode": 0, + "exitSignal": "1" + }, + "eventCount": 2, + "uptime": 5039, + "lastEventSeq": 1, + "terminationCategory": "destroyed", + "artifacts": { + "total": 0, + "byKind": {}, + "missingCount": 0, + "health": "no-artifacts" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "ghostty-web", + "mode": "offline-replay", + "status": "fallback", + "reason": "session-not-running" + } + } +} diff --git a/dogfood/20260325-week8-inspect-runtime/logs/04-inspect-offline.stderr.txt b/dogfood/20260325-week8-inspect-runtime/logs/04-inspect-offline.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/manifest.json b/dogfood/20260325-week8-inspect-runtime/manifest.json new file mode 100644 index 0000000..8204a19 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/manifest.json @@ -0,0 +1,58 @@ +{ + "bundle": "20260325-week8-inspect-runtime", + "title": "Week 8 — Inspect Runtime", + "description": "Proves inspect --json reports live-host vs offline-replay renderer runtime summaries for the same retained session.", + "createdAt": "2026-03-25T23:41:40Z", + "week": 8, + "sessionId": "01KMKNZXNWZKGCXTCKGTVHC9T7", + "fixture": "node --import tsx test/fixtures/apps/hello-prompt/main.ts", + "result": "pass", + "commands": [ + "npx tsx src/cli/main.ts create --json -- node --import tsx test/fixtures/apps/hello-prompt/main.ts", + "npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json", + "npx tsx src/cli/main.ts destroy 01KMKNZXNWZKGCXTCKGTVHC9T7 --json", + "npx tsx src/cli/main.ts inspect 01KMKNZXNWZKGCXTCKGTVHC9T7 --json" + ], + "artifacts": [ + { + "path": "logs/01-create.json", + "description": "create --json output capturing the session ID for the hello-prompt fixture" + }, + { + "path": "logs/02-inspect-live.json", + "description": "inspect --json output for the running session showing rendererRuntime.mode = live-host" + }, + { + "path": "logs/03-destroy.json", + "description": "destroy --json output confirming session teardown" + }, + { + "path": "logs/04-inspect-offline.json", + "description": "inspect --json output after destroy showing rendererRuntime.mode = offline-replay with fallback reason session-not-running" + }, + { + "path": "json/inspect-live.json", + "description": "Canonical reviewer copy of the live inspect JSON envelope outside logs/ for contract-reporting validation" + }, + { + "path": "json/inspect-offline.json", + "description": "Canonical reviewer copy of the offline inspect JSON envelope outside logs/ for contract-reporting validation" + }, + { + "path": "screenshots/01-review-page.png", + "description": "Playwright screenshot of the generated review page for this Week 8 bundle" + }, + { + "path": "command-status.tsv", + "description": "Step-by-step exit-code ledger for the inspect runtime proof run" + }, + { + "path": "agent-terminal-home.txt", + "description": "Temp AGENT_TERMINAL_HOME path used during capture" + }, + { + "path": "session-id.txt", + "description": "Captured session ID used across all session-scoped commands" + } + ] +} diff --git a/dogfood/20260325-week8-inspect-runtime/notes.md b/dogfood/20260325-week8-inspect-runtime/notes.md new file mode 100644 index 0000000..d1014c8 --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/notes.md @@ -0,0 +1,54 @@ +# 2026-03-25 dogfood — Week 8 inspect runtime proof + +## Bundle metadata + +- **Bundle path:** `dogfood/20260325-week8-inspect-runtime/` +- **Session ID:** `01KMKNZXNWZKGCXTCKGTVHC9T7` +- **Isolated AGENT_TERMINAL_HOME:** `/tmp/tmp.s3O1Nrg0On` +- **CLI entrypoint:** `npx tsx src/cli/main.ts` +- **Fixture app:** `node --import tsx test/fixtures/apps/hello-prompt/main.ts` + +## Scenario summary + +This bundle proves the Week 8 `inspect --json` renderer runtime summary against a real isolated PTY-backed `hello-prompt` session. The same session was inspected twice: once while it was still running and once after `destroy --json` had transitioned it into retained offline state. + +## `rendererRuntime` proof + +| Capture | Evidence | `session.status` | `rendererRuntime.mode` | `rendererRuntime.status` | `rendererRuntime.reason` | `usedOfflineReplay` | +| -------------------- | ------------------------------ | ---------------- | ---------------------- | ------------------------ | ------------------------ | ------------------- | +| Live inspect | `logs/02-inspect-live.json` | `running` | `live-host` | `healthy` | _omitted_ | `false` | +| Post-destroy inspect | `logs/04-inspect-offline.json` | `destroyed` | `offline-replay` | `fallback` | `session-not-running` | `false` | + +Key observations from the captured JSON envelopes: + +- `rendererRuntime.backend` stayed `ghostty-web` in both states; only the runtime mode changed. +- While the host was still alive, `inspect --json` reported `rendererRuntime.mode = "live-host"` and `status = "healthy"`. +- After `destroy --json`, the retained session metadata still inspected successfully, but `rendererRuntime.mode` switched to `"offline-replay"` with fallback reason `"session-not-running"`. +- `usedOfflineReplay` stayed `false` in both captures. This is an important nuance: an offline renderer mode does **not** automatically mean the command had to fall back from a failed live RPC call. + +## Fallback reason interpretation + +The Week 8 surface distinguishes two fallback reasons: + +- **`session-not-running`** — captured directly in this bundle via `logs/04-inspect-offline.json` after the session was destroyed. +- **`host-unreachable`** — the alternate path used when a session still appears live but its host cannot answer RPC. The real PTY run here stayed healthy, so this bundle does not force that failure mode. The repo's inspect unit test (`test/unit/commands/inspect.test.ts`) explicitly covers it and asserts `rendererRuntime.mode = "offline-replay"`, `rendererRuntime.reason = "host-unreachable"`, and `usedOfflineReplay = true` for that case. + +That distinction matches the implementation in `src/cli/commands/inspect.ts`: `usedOfflineReplay` only flips to `true` when the command expected to use the live host but had to reconcile from retained state after a host-unreachable failure. + +## Review answers + +- **Did live inspect report the new live-host runtime summary?** Yes. `logs/02-inspect-live.json` shows `rendererRuntime.backend = "ghostty-web"`, `mode = "live-host"`, `status = "healthy"`, and `usedOfflineReplay = false` while the session status is `running`. +- **Did offline inspect report the new offline-replay runtime summary?** Yes. `logs/04-inspect-offline.json` shows `rendererRuntime.mode = "offline-replay"`, `status = "fallback"`, `reason = "session-not-running"`, and `usedOfflineReplay = false` after the same session was destroyed. +- **Did destroy preserve enough retained state for post-destroy inspect?** Yes. `logs/03-destroy.json` reports `destroyed: true`, and the subsequent `logs/04-inspect-offline.json` still reports the original command, retained size metadata, event count, and termination category. +- **Did the JSON envelopes show any artifact-health side effects?** Yes. Both inspect captures report `artifacts.health = "no-artifacts"`, which is expected because this proof only exercised `create`, `inspect`, and `destroy` without generating snapshots or screenshots. +- **Where is the command ledger?** `command-status.tsv` records the exact create / live inspect / destroy / offline inspect sequence, commands, exit codes, and pass/fail status. Each step also has a matching `logs/*.stderr.txt` sidecar. + +## Browser verification + +The generated review page was opened from the local bundle `index.html` and captured with Playwright. Reviewer-facing proof lives at `screenshots/01-review-page.png`. + +## Issues / limitations + +- This bundle directly proves the `session-not-running` fallback path with a real PTY session. It does not intentionally simulate a broken live host, so `host-unreachable` remains code-and-unit-test-backed rather than PTY-captured evidence here. +- The post-destroy inspect result reports `exitSignal = "1"` alongside `terminationCategory = "destroyed"`. That is consistent with the teardown path used by `destroy --json` and does not indicate a bundle failure. +- No renderer artifacts were generated in this proof bundle by design, so `artifacts.total = 0` and `health = "no-artifacts"` are expected in both inspect captures. diff --git a/dogfood/20260325-week8-inspect-runtime/recordings/.gitkeep b/dogfood/20260325-week8-inspect-runtime/recordings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/screenshots/.gitkeep b/dogfood/20260325-week8-inspect-runtime/screenshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/screenshots/01-review-page.png b/dogfood/20260325-week8-inspect-runtime/screenshots/01-review-page.png new file mode 100644 index 0000000..8e84431 Binary files /dev/null and b/dogfood/20260325-week8-inspect-runtime/screenshots/01-review-page.png differ diff --git a/dogfood/20260325-week8-inspect-runtime/session-id.txt b/dogfood/20260325-week8-inspect-runtime/session-id.txt new file mode 100644 index 0000000..94ec7af --- /dev/null +++ b/dogfood/20260325-week8-inspect-runtime/session-id.txt @@ -0,0 +1 @@ +01KMKNZXNWZKGCXTCKGTVHC9T7 diff --git a/dogfood/20260325-week8-inspect-runtime/snapshots/.gitkeep b/dogfood/20260325-week8-inspect-runtime/snapshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dogfood/20260325-week8-inspect-runtime/videos/.gitkeep b/dogfood/20260325-week8-inspect-runtime/videos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index 3f56421..fbaa3b6 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "test": "vitest run", "test:watch": "vitest", "typecheck": "tsc -p tsconfig.json --noEmit", + "validate-bundle": "tsx src/tools/validate-bundle.ts", "verify": "npm run format:check && npm run lint && npm run typecheck && npm run test && npm run build", "version:json": "tsx src/cli/main.ts version --json" }, diff --git a/src/cli/commands/doctor.ts b/src/cli/commands/doctor.ts index 257307a..6e52ce3 100644 --- a/src/cli/commands/doctor.ts +++ b/src/cli/commands/doctor.ts @@ -24,7 +24,10 @@ import process from 'node:process'; import type { CommandContext } from '../context.js'; import { emitSuccess } from '../output.js'; +import type { CapabilityEntry } from '../../renderer/capabilities.js'; + import { createPty } from '../../pty/createPty.js'; +import { discoverCapabilities } from '../../renderer/capabilities.js'; import { artifactPath, ensureArtifactsDir, @@ -153,6 +156,7 @@ export interface DoctorCheckGroups { export interface DoctorResult { ok: boolean; checks: DoctorCheckGroups; + capabilities: CapabilityEntry[]; } function formatErrorMessage(error: unknown): string { @@ -708,12 +712,17 @@ export async function runDoctorChecks(): Promise { 'doctor check names must be unique', ); + const capabilities = await discoverCapabilities('full', { + rendererChecks: renderer, + }); + return { ok: allChecks.every((check) => check.status !== 'fail'), checks: { environment, renderer, }, + capabilities, }; } diff --git a/src/cli/commands/inspect.ts b/src/cli/commands/inspect.ts index fc28556..08daa7a 100644 --- a/src/cli/commands/inspect.ts +++ b/src/cli/commands/inspect.ts @@ -2,8 +2,9 @@ import { HostInspectResultSchema, type ArtifactHealthSummary, type InspectResult, + type RendererRuntimeSummary, } from '../../protocol/messages.js'; -import type { SessionRecord } from '../../protocol/schemas.js'; +import type { SessionRecord, SessionStatus } from '../../protocol/schemas.js'; import { CliError } from '../errors.js'; import type { CommandContext } from '../context.js'; @@ -51,8 +52,63 @@ function formatArtifactKinds(byKind: Record): string { .join(', '); } +const RENDERER_BACKEND = 'ghostty-web'; + +function usesOfflineReplay(sessionStatus: SessionStatus): boolean { + switch (sessionStatus) { + case 'running': + case 'exiting': + return false; + case 'exited': + case 'failed': + case 'destroying': + case 'destroyed': + return true; + default: { + const exhaustiveStatus: never = sessionStatus; + throw new Error(`unexpected session status: ${String(exhaustiveStatus)}`); + } + } +} + +function deriveRendererRuntimeSummary(options: { + usedOfflineReplay: boolean; + sessionStatus: SessionStatus; +}): RendererRuntimeSummary { + if (options.usedOfflineReplay) { + return { + backend: RENDERER_BACKEND, + mode: 'offline-replay', + status: 'fallback', + reason: 'host-unreachable', + }; + } + + if (usesOfflineReplay(options.sessionStatus)) { + return { + backend: RENDERER_BACKEND, + mode: 'offline-replay', + status: 'fallback', + reason: 'session-not-running', + }; + } + + return { + backend: RENDERER_BACKEND, + mode: 'live-host', + status: 'healthy', + }; +} + +function formatRendererRuntime(summary: RendererRuntimeSummary): string { + const reasonSuffix = + summary.reason === undefined ? '' : ` — ${summary.reason}`; + + return `${summary.backend} (${summary.mode}, ${summary.status}${reasonSuffix})`; +} + function formatSessionLines(result: InspectResult): string[] { - const { session, eventCount, uptime } = result; + const { session, eventCount, rendererRuntime, uptime } = result; const lines = [ `Session ID: ${session.sessionId}`, `Status: ${session.status}`, @@ -64,6 +120,8 @@ function formatSessionLines(result: InspectResult): string[] { `Event Count: ${String(eventCount)}`, ]; + lines.push(`Renderer: ${formatRendererRuntime(rendererRuntime)}`); + if (result.lastEventSeq !== undefined) { lines.push(`Last Event Seq: ${String(result.lastEventSeq)}`); } @@ -123,10 +181,7 @@ export async function runInspectCommand( }); } - const isOffline = - session.status === 'exited' || - session.status === 'failed' || - session.status === 'destroyed'; + const isOffline = usesOfflineReplay(session.status); if (!isOffline) { try { const rawResult: unknown = await sendRpc( @@ -166,6 +221,10 @@ export async function runInspectCommand( artifacts = undefined; } const terminationCategory = deriveTerminationCategory(session); + const rendererRuntime = deriveRendererRuntimeSummary({ + usedOfflineReplay, + sessionStatus: session.status, + }); const result: InspectResult = { session, eventCount, @@ -174,6 +233,7 @@ export async function runInspectCommand( terminationCategory, artifacts, usedOfflineReplay, + rendererRuntime, }; emitSuccess({ diff --git a/src/cli/commands/version.ts b/src/cli/commands/version.ts index e8b1639..c5ce402 100644 --- a/src/cli/commands/version.ts +++ b/src/cli/commands/version.ts @@ -2,6 +2,9 @@ import { readFile } from 'node:fs/promises'; import process from 'node:process'; import { emitSuccess } from '../output.js'; +import type { CapabilityEntry } from '../../renderer/capabilities.js'; + +import { discoverCapabilities } from '../../renderer/capabilities.js'; import { assertString } from '../../util/assert.js'; const COMMAND_NAME = 'version'; @@ -21,6 +24,7 @@ export interface VersionResult { platform: NodeJS.Platform; arch: string; }; + capabilities?: CapabilityEntry[]; } export async function loadPackageMetadata(): Promise { @@ -42,8 +46,24 @@ export async function loadPackageMetadata(): Promise { }; } -export async function buildVersionResult(): Promise { +export async function buildVersionResult(options?: { + includeCapabilities?: boolean; +}): Promise { const packageMetadata = await loadPackageMetadata(); + let capabilities: CapabilityEntry[] | undefined; + + if (options?.includeCapabilities) { + try { + capabilities = await discoverCapabilities('quick'); + } catch (error: unknown) { + // Capability discovery is best-effort for version; never crash. + const message = error instanceof Error ? error.message : String(error); + process.stderr.write( + `warning: capability discovery failed: ${message}\n`, + ); + capabilities = undefined; + } + } return { cliVersion: packageMetadata.version, @@ -54,13 +74,16 @@ export async function buildVersionResult(): Promise { platform: process.platform, arch: process.arch, }, + ...(capabilities === undefined ? {} : { capabilities }), }; } export async function runVersionCommand(options: { json: boolean; }): Promise { - const result = await buildVersionResult(); + const result = await buildVersionResult({ + includeCapabilities: options.json, + }); emitSuccess({ command: COMMAND_NAME, diff --git a/src/protocol/messages.ts b/src/protocol/messages.ts index 787975a..56179a8 100644 --- a/src/protocol/messages.ts +++ b/src/protocol/messages.ts @@ -8,6 +8,7 @@ import type { } from './schemas.js'; import { + RendererRuntimeSummarySchema, ScreenshotParamsSchema, ScreenshotResultSchema, SessionRecordSchema, @@ -32,6 +33,25 @@ export { WaitResultSchema, } from './schemas.js'; +// --- Week 8: Capability and renderer-runtime schemas --- +export { + CapabilityEntrySchema, + CapabilityNameSchema, + CapabilityStatusSchema, + RendererRuntimeModeSchema, + RendererRuntimeStatusSchema, + RendererRuntimeSummarySchema, +} from './schemas.js'; + +export type { + CapabilityEntry, + CapabilityName, + CapabilityStatus, + RendererRuntimeMode, + RendererRuntimeStatus, + RendererRuntimeSummary, +} from './schemas.js'; + const EmptyObjectSchema = z.object({}).strict(); const NonEmptyStringSchema = z.string().min(1); const DurationSchema = z.number().int().positive(); @@ -136,6 +156,7 @@ export const InspectResultSchema = z terminationCategory: TerminationCategorySchema.optional(), artifacts: ArtifactHealthSummarySchema.optional(), usedOfflineReplay: z.boolean().optional(), + rendererRuntime: RendererRuntimeSummarySchema, }) .strict(); export type InspectResult = z.infer; diff --git a/src/protocol/schemas.ts b/src/protocol/schemas.ts index e723432..5d7b35d 100644 --- a/src/protocol/schemas.ts +++ b/src/protocol/schemas.ts @@ -403,3 +403,22 @@ export const RecordExportResultSchema = z export type RecordExportResult = z.infer; export type WaitForRenderResult = z.infer; + +// --- Week 8: Capability and renderer-runtime schemas --- +export { + CapabilityEntrySchema, + CapabilityNameSchema, + CapabilityStatusSchema, + RendererRuntimeModeSchema, + RendererRuntimeStatusSchema, + RendererRuntimeSummarySchema, +} from '../renderer/capabilities.js'; + +export type { + CapabilityEntry, + CapabilityName, + CapabilityStatus, + RendererRuntimeMode, + RendererRuntimeStatus, + RendererRuntimeSummary, +} from '../renderer/capabilities.js'; diff --git a/src/renderer/capabilities.ts b/src/renderer/capabilities.ts new file mode 100644 index 0000000..9f59b58 --- /dev/null +++ b/src/renderer/capabilities.ts @@ -0,0 +1,407 @@ +import assert from 'node:assert/strict'; + +import { z } from 'zod'; + +// --- Capability vocabulary --- + +export const CapabilityNameSchema = z.enum([ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', +]); +export type CapabilityName = z.infer; + +export const CapabilityStatusSchema = z.enum([ + 'available', + 'unavailable', + 'degraded', + 'unknown', +]); +export type CapabilityStatus = z.infer; + +export const CapabilityEntrySchema = z + .object({ + name: CapabilityNameSchema, + status: CapabilityStatusSchema, + reason: z.string().optional(), + detail: z.string().optional(), + }) + .strict(); +export type CapabilityEntry = z.infer; + +// --- Renderer runtime summary (for inspect) --- + +export const RendererRuntimeModeSchema = z.enum([ + 'live-host', + 'offline-replay', +]); +export type RendererRuntimeMode = z.infer; + +export const RendererRuntimeStatusSchema = z.enum([ + 'healthy', + 'fallback', + 'unavailable', +]); +export type RendererRuntimeStatus = z.infer; + +export const RendererRuntimeSummarySchema = z + .object({ + backend: z.string(), + mode: RendererRuntimeModeSchema, + status: RendererRuntimeStatusSchema, + reason: z.string().optional(), + }) + .strict(); +export type RendererRuntimeSummary = z.infer< + typeof RendererRuntimeSummarySchema +>; + +// --- Discovery modes --- + +const CAPABILITY_NAMES: ReadonlyArray = Object.freeze([ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', +]); +const BUILTIN_CAPABILITY_NAMES: ReadonlyArray = Object.freeze([ + 'snapshot', + 'wait', + 'record-export-asciicast', +]); + +type DiscoveryCheckStatus = 'pass' | 'fail' | 'skip'; + +interface DiscoveryCheck { + name: string; + status: DiscoveryCheckStatus; + message: string; +} + +interface PlaywrightProbeResult { + available: boolean; + reason?: string; + detail?: string; +} + +export interface CapabilityDiscoveryDependencies { + probePlaywright?: (mode: DiscoveryMode) => Promise; + rendererChecks?: ReadonlyArray; +} + +export type DiscoveryMode = 'quick' | 'full'; + +function formatCapabilityError(error: unknown): string { + if (error instanceof Error) { + return error.message; + } + + return String(error); +} + +async function probePlaywrightAvailability( + mode: DiscoveryMode, +): Promise { + try { + const playwrightModule = (await import('playwright')) as { + chromium?: { + launch?: unknown; + }; + }; + + if (mode === 'full') { + assert.equal( + typeof playwrightModule.chromium?.launch, + 'function', + 'playwright chromium.launch must be a function', + ); + return { + available: true, + reason: 'playwright import succeeded', + detail: 'chromium launcher is available for browser-backed rendering', + }; + } + + return { available: true }; + } catch (error) { + const detail = formatCapabilityError(error); + return mode === 'full' + ? { + available: false, + reason: 'playwright import failed', + detail, + } + : { + available: false, + reason: 'playwright not installed', + detail, + }; + } +} + +/** + * Built-in capabilities (snapshot, wait, record-export-asciicast) are always + * reported as 'available' because they depend only on the event log and + * built-in text processing; no external renderer or browser is needed. + * + * This reflects runtime feature availability, not guaranteed success for any + * particular session. Corrupted session data or an invalid event log will fail + * at invocation time rather than degrading capability discovery. + */ +function buildBuiltinCapability( + name: CapabilityName, + mode: DiscoveryMode, +): CapabilityEntry { + return mode === 'full' + ? { + name, + status: 'available', + reason: 'built-in capability', + detail: 'available without external renderer dependencies', + } + : { + name, + status: 'available', + }; +} + +function findRendererCheck( + checks: ReadonlyArray, + name: string, +): DiscoveryCheck | undefined { + return checks.find((check) => check.name === name); +} + +function buildAvailableDetail( + checks: ReadonlyArray, +): string | undefined { + if (checks.length === 0) { + return undefined; + } + + return checks.map((check) => `${check.name}: ${check.message}`).join('; '); +} + +function buildUnknownCapability(name: CapabilityName): CapabilityEntry { + return { + name, + status: 'unknown', + reason: 'renderer checks incomplete', + detail: 'doctor did not provide the full renderer check set', + }; +} + +function buildFullScreenshotCapabilityFromChecks( + checks: ReadonlyArray, +): CapabilityEntry { + const playwrightCheck = findRendererCheck(checks, 'playwright_available'); + const browserLaunchCheck = findRendererCheck(checks, 'browser_launch'); + const ghosttyWebCheck = findRendererCheck(checks, 'ghostty_web_available'); + const screenshotCheck = findRendererCheck(checks, 'screenshot_viable'); + + if ( + playwrightCheck === undefined || + browserLaunchCheck === undefined || + ghosttyWebCheck === undefined || + screenshotCheck === undefined + ) { + return buildUnknownCapability('screenshot'); + } + + if (playwrightCheck.status === 'fail') { + return { + name: 'screenshot', + status: 'unavailable', + reason: 'playwright unavailable', + detail: playwrightCheck.message, + }; + } + + if (ghosttyWebCheck.status === 'fail') { + return { + name: 'screenshot', + status: 'unavailable', + reason: 'ghostty-web unavailable', + detail: ghosttyWebCheck.message, + }; + } + + if (browserLaunchCheck.status === 'fail') { + return { + name: 'screenshot', + status: 'degraded', + reason: 'browser launch failed', + detail: browserLaunchCheck.message, + }; + } + + if (screenshotCheck.status === 'fail') { + return { + name: 'screenshot', + status: 'degraded', + reason: 'screenshot smoke test failed', + detail: screenshotCheck.message, + }; + } + + return { + name: 'screenshot', + status: 'available', + reason: 'renderer smoke checks passed', + detail: buildAvailableDetail([ + playwrightCheck, + browserLaunchCheck, + ghosttyWebCheck, + screenshotCheck, + ]), + }; +} + +function buildFullWebmCapabilityFromChecks( + checks: ReadonlyArray, +): CapabilityEntry { + const playwrightCheck = findRendererCheck(checks, 'playwright_available'); + const browserLaunchCheck = findRendererCheck(checks, 'browser_launch'); + const ghosttyWebCheck = findRendererCheck(checks, 'ghostty_web_available'); + + if ( + playwrightCheck === undefined || + browserLaunchCheck === undefined || + ghosttyWebCheck === undefined + ) { + return buildUnknownCapability('record-export-webm'); + } + + if (playwrightCheck.status === 'fail') { + return { + name: 'record-export-webm', + status: 'unavailable', + reason: 'playwright unavailable', + detail: playwrightCheck.message, + }; + } + + if (ghosttyWebCheck.status === 'fail') { + return { + name: 'record-export-webm', + status: 'unavailable', + reason: 'ghostty-web unavailable', + detail: ghosttyWebCheck.message, + }; + } + + if (browserLaunchCheck.status === 'fail') { + return { + name: 'record-export-webm', + status: 'degraded', + reason: 'browser launch failed', + detail: browserLaunchCheck.message, + }; + } + + return { + name: 'record-export-webm', + status: 'available', + reason: 'browser-backed export dependencies available', + detail: buildAvailableDetail([ + playwrightCheck, + browserLaunchCheck, + ghosttyWebCheck, + ]), + }; +} + +async function buildPlaywrightCapability( + name: 'screenshot' | 'record-export-webm', + mode: DiscoveryMode, + deps: CapabilityDiscoveryDependencies, +): Promise { + if (mode === 'full' && deps.rendererChecks !== undefined) { + return name === 'screenshot' + ? buildFullScreenshotCapabilityFromChecks(deps.rendererChecks) + : buildFullWebmCapabilityFromChecks(deps.rendererChecks); + } + + const probePlaywright = deps.probePlaywright ?? probePlaywrightAvailability; + const probe = await probePlaywright(mode); + + return probe.available + ? mode === 'full' + ? { + name, + status: 'available', + reason: probe.reason, + detail: probe.detail, + } + : { + name, + status: 'available', + } + : { + name, + status: 'unavailable', + reason: probe.reason, + detail: probe.detail, + }; +} + +function validateDiscoveredCapabilities( + capabilities: ReadonlyArray, +): CapabilityEntry[] { + const actualNames = capabilities.map((capability) => capability.name); + const expectedNames = [...CAPABILITY_NAMES]; + assert.equal( + capabilities.length, + CAPABILITY_NAMES.length, + `discovered capabilities must include every known capability exactly once (got [${actualNames.join(', ')}], expected [${expectedNames.join(', ')}])`, + ); + + const duplicateNames = actualNames.filter( + (name, index) => actualNames.indexOf(name) !== index, + ); + assert.equal( + new Set(actualNames).size, + CAPABILITY_NAMES.length, + `discovered capabilities must not contain duplicates (got [${actualNames.join(', ')}], expected [${expectedNames.join(', ')}], duplicates [${duplicateNames.join(', ')}])`, + ); + + return capabilities.map((capability) => + CapabilityEntrySchema.parse(capability), + ); +} + +/** + * Discover runtime capabilities. + * + * - 'quick' mode: fast checks suitable for `version --json` (no browser launch). + * - 'full' mode: deeper probing suitable for `doctor --json`. + */ +export async function discoverCapabilities( + mode: DiscoveryMode, + deps: CapabilityDiscoveryDependencies = {}, +): Promise { + const capabilities: CapabilityEntry[] = []; + + for (const name of BUILTIN_CAPABILITY_NAMES) { + capabilities.push(buildBuiltinCapability(name, mode)); + } + + capabilities.push(await buildPlaywrightCapability('screenshot', mode, deps)); + capabilities.push( + await buildPlaywrightCapability('record-export-webm', mode, deps), + ); + + const sortedCapabilities: CapabilityEntry[] = []; + for (const name of CAPABILITY_NAMES) { + const capability = capabilities.find((entry) => entry.name === name); + if (capability === undefined) { + throw new Error(`missing discovered capability entry for ${name}`); + } + sortedCapabilities.push(capability); + } + + return validateDiscoveredCapabilities(sortedCapabilities); +} diff --git a/src/tools/validate-bundle.ts b/src/tools/validate-bundle.ts new file mode 100644 index 0000000..f8906ab --- /dev/null +++ b/src/tools/validate-bundle.ts @@ -0,0 +1,368 @@ +/** + * Bundle validation tool — validates proof-bundle completeness. + * + * Usage: npm run validate-bundle -- [--profile ] + */ + +import { readFile, realpath, stat } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import process from 'node:process'; +import { pathToFileURL } from 'node:url'; + +import { scanBundleArtifacts } from './review-bundle.js'; +import { assertString, invariant } from '../util/assert.js'; + +const BUNDLE_VALIDATION_PROFILES = [ + 'contract-reporting', + 'interactive-renderer', +] as const; + +export type BundleValidationProfile = + | 'contract-reporting' + | 'interactive-renderer'; + +export interface BundleValidationResult { + bundleDir: string; + profile: BundleValidationProfile; + ok: boolean; + checks: BundleValidationCheck[]; +} + +export interface BundleValidationCheck { + name: string; + ok: boolean; + message: string; +} + +export interface BundleValidationIo { + stdout: (text: string) => void; + stderr: (text: string) => void; +} + +function defaultIo(): BundleValidationIo { + return { + stdout: (text) => process.stdout.write(text), + stderr: (text) => process.stderr.write(text), + }; +} + +function writeLine(write: (text: string) => void, line: string): void { + write(`${line}\n`); +} + +function buildCheck( + name: string, + ok: boolean, + message: string, +): BundleValidationCheck { + return { name, ok, message }; +} + +function isBundleValidationProfile( + value: string, +): value is BundleValidationProfile { + return BUNDLE_VALIDATION_PROFILES.includes(value as BundleValidationProfile); +} + +/** Maximum size for JSON files in a bundle (50 MB, matching event-log limit). */ +export const MAX_JSON_FILE_BYTES = 50 * 1024 * 1024; + +async function isFile(filePath: string): Promise { + try { + return (await stat(filePath)).isFile(); + } catch { + return false; + } +} + +async function buildJsonReadableCheck( + bundleRoot: string, + jsonArtifacts: Array<{ relativePath: string }>, +): Promise { + if (jsonArtifacts.length === 0) { + return buildCheck( + 'json-readable', + false, + 'No JSON output files were found to parse.', + ); + } + + const invalidJsonPaths: string[] = []; + const oversizedJsonPaths: string[] = []; + for (const artifact of jsonArtifacts) { + const filePath = join(bundleRoot, artifact.relativePath); + try { + const fileStat = await stat(filePath); + if (fileStat.size > MAX_JSON_FILE_BYTES) { + oversizedJsonPaths.push(artifact.relativePath); + continue; + } + const content = await readFile(filePath, 'utf8'); + JSON.parse(content) as unknown; + } catch { + invalidJsonPaths.push(artifact.relativePath); + } + } + + if (oversizedJsonPaths.length > 0) { + return buildCheck( + 'json-readable', + false, + `JSON output files exceed ${String(MAX_JSON_FILE_BYTES)} byte limit: ${oversizedJsonPaths.join(', ')}`, + ); + } + + if (invalidJsonPaths.length > 0) { + return buildCheck( + 'json-readable', + false, + `JSON output files could not be parsed: ${invalidJsonPaths.join(', ')}`, + ); + } + + return buildCheck( + 'json-readable', + true, + `Parsed ${String(jsonArtifacts.length)} JSON output file(s).`, + ); +} + +function summarizeValidation(result: BundleValidationResult): string[] { + const status = result.ok ? 'PASS' : 'FAIL'; + return [ + `validate-bundle ${status} ${result.profile}: ${result.bundleDir}`, + ...result.checks.map( + (check) => `${check.ok ? '✓' : '✗'} ${check.name}: ${check.message}`, + ), + ]; +} + +export async function validateBundle( + bundleDir: string, + profile: BundleValidationProfile, +): Promise { + assertString(bundleDir, 'bundle directory must be a string'); + invariant(bundleDir.trim().length > 0, 'bundle directory must not be empty'); + invariant( + isBundleValidationProfile(profile), + `unsupported bundle validation profile: ${profile}`, + ); + + const resolvedBundleDir = resolve(bundleDir); + const checks: BundleValidationCheck[] = []; + + try { + const bundleStats = await stat(resolvedBundleDir); + if (!bundleStats.isDirectory()) { + checks.push( + buildCheck( + 'bundle-exists', + false, + `Bundle path is not a directory: ${resolvedBundleDir}`, + ), + ); + return { + bundleDir: resolvedBundleDir, + profile, + ok: false, + checks, + }; + } + } catch (error) { + checks.push( + buildCheck( + 'bundle-exists', + false, + `Bundle directory could not be read: ${String(error)}`, + ), + ); + return { + bundleDir: resolvedBundleDir, + profile, + ok: false, + checks, + }; + } + + const bundleRoot = await realpath(resolvedBundleDir); + checks.push( + buildCheck( + 'bundle-exists', + true, + `Bundle directory is readable: ${bundleRoot}`, + ), + ); + + const artifacts = await scanBundleArtifacts(bundleRoot); + const jsonArtifacts = artifacts.filter( + (artifact) => artifact.kind === 'json', + ); + const noteArtifacts = artifacts.filter( + (artifact) => artifact.kind === 'notes', + ); + const screenshotArtifacts = artifacts.filter( + (artifact) => artifact.kind === 'screenshot', + ); + const recordingArtifacts = artifacts.filter( + (artifact) => artifact.kind === 'recording', + ); + + checks.push( + buildCheck( + 'has-json-output', + jsonArtifacts.length > 0, + jsonArtifacts.length > 0 + ? `Found ${String(jsonArtifacts.length)} JSON output file(s).` + : 'Expected at least one JSON output file.', + ), + ); + + const reviewPagePath = join(bundleRoot, 'index.html'); + const hasReviewPage = await isFile(reviewPagePath); + checks.push( + buildCheck( + 'has-review-page', + hasReviewPage, + hasReviewPage + ? 'Found review page index.html.' + : 'Expected index.html review page in the bundle root.', + ), + ); + + checks.push( + buildCheck( + 'has-notes', + noteArtifacts.length > 0, + noteArtifacts.length > 0 + ? `Found ${String(noteArtifacts.length)} note file(s).` + : 'Expected at least one notes markdown file.', + ), + ); + + checks.push(await buildJsonReadableCheck(bundleRoot, jsonArtifacts)); + + if (profile === 'interactive-renderer') { + checks.push( + buildCheck( + 'has-screenshot', + screenshotArtifacts.length > 0, + screenshotArtifacts.length > 0 + ? `Found ${String(screenshotArtifacts.length)} screenshot file(s).` + : 'Expected at least one screenshot artifact.', + ), + ); + checks.push( + buildCheck( + 'has-recording', + recordingArtifacts.length > 0, + recordingArtifacts.length > 0 + ? `Found ${String(recordingArtifacts.length)} recording file(s).` + : 'Expected at least one recording (.cast) artifact.', + ), + ); + } + + return { + bundleDir: bundleRoot, + profile, + ok: checks.every((check) => check.ok), + checks, + }; +} + +export async function runValidateBundleCli( + args: readonly string[], + io: BundleValidationIo = defaultIo(), +): Promise { + const argumentsList = [...args]; + if (argumentsList.length === 0) { + writeLine( + io.stderr, + 'Usage: npm run validate-bundle -- [--profile ]', + ); + return 1; + } + + let bundleDir: string | undefined; + let profile: BundleValidationProfile = 'contract-reporting'; + + while (argumentsList.length > 0) { + const argument = argumentsList.shift(); + invariant( + argument !== undefined, + 'argument must exist while parsing CLI args', + ); + + if (argument === '--profile') { + const profileArgument = argumentsList.shift(); + if ( + profileArgument === undefined || + !isBundleValidationProfile(profileArgument) + ) { + writeLine( + io.stderr, + `Expected --profile to be one of: ${BUNDLE_VALIDATION_PROFILES.join(', ')}`, + ); + return 1; + } + profile = profileArgument; + continue; + } + + if (argument.startsWith('--')) { + writeLine(io.stderr, `Unknown option: ${argument}`); + return 1; + } + + if (bundleDir !== undefined) { + writeLine(io.stderr, 'Expected exactly one bundle directory'); + return 1; + } + bundleDir = argument; + } + + if (bundleDir === undefined) { + writeLine( + io.stderr, + 'Usage: npm run validate-bundle -- [--profile ]', + ); + return 1; + } + + let result: BundleValidationResult; + try { + result = await validateBundle(bundleDir, profile); + } catch (error) { + result = { + bundleDir: resolve(bundleDir), + profile, + ok: false, + checks: [ + buildCheck( + 'validation-error', + false, + `Bundle validation crashed: ${String(error)}`, + ), + ], + }; + } + + writeLine(io.stdout, JSON.stringify(result, null, 2)); + for (const line of summarizeValidation(result)) { + writeLine(io.stderr, line); + } + return result.ok ? 0 : 1; +} + +function isDirectExecution(): boolean { + const entryPoint = process.argv[1]; + if (entryPoint === undefined) { + return false; + } + return import.meta.url === pathToFileURL(entryPoint).href; +} + +if (isDirectExecution()) { + const exitCode = await runValidateBundleCli(process.argv.slice(2)); + process.exitCode = exitCode; +} diff --git a/test/e2e/hello-prompt.test.ts b/test/e2e/hello-prompt.test.ts index 71422ce..cc4bb38 100644 --- a/test/e2e/hello-prompt.test.ts +++ b/test/e2e/hello-prompt.test.ts @@ -21,6 +21,12 @@ interface CreateResult { interface InspectResult { session: SessionRecord; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; } interface SendKeysResult { @@ -132,6 +138,11 @@ describe('hello-prompt e2e', { timeout: 30_000 }, () => { expect(inspectRunning.command).toBe('inspect'); expect(inspectRunning.result.session.status).toBe('running'); expect(inspectRunning.result.session.exitCode).toBeNull(); + expect(inspectRunning.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }); const typeExitEnvelope = runCliJson>>( ['type', sessionId, 'exit'], @@ -179,6 +190,12 @@ describe('hello-prompt e2e', { timeout: 30_000 }, () => { ); expect(inspectExited.result.session.status).toBe('exited'); expect(inspectExited.result.session.exitCode).toBe(0); + expect(inspectExited.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'session-not-running', + }); const destroyEnvelope = runCliJson< SuccessEnvelope<{ sessionId: string; destroyed: boolean }> diff --git a/test/integration/lifecycle.test.ts b/test/integration/lifecycle.test.ts index b1f31c5..4f56f76 100644 --- a/test/integration/lifecycle.test.ts +++ b/test/integration/lifecycle.test.ts @@ -111,12 +111,23 @@ describe('lifecycle integration', { timeout: 30000 }, () => { inspectResult.stdout, ) as SuccessEnvelope<{ session: SessionRecord; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; }>; expect(inspectEnvelope.ok).toBe(true); expect(inspectEnvelope.result.session.sessionId).toBe(sessionId); expect(inspectEnvelope.result.session.status).toBe('running'); expect(inspectEnvelope.result.session.hostPid).toBeTypeOf('number'); expect(inspectEnvelope.result.session.childPid).toBeTypeOf('number'); + expect(inspectEnvelope.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }); expect(listedSession?.pid).toBe(inspectEnvelope.result.session.childPid); const destroyResult = runCli(['destroy', sessionId, '--json'], { diff --git a/test/unit/commands/doctor.test.ts b/test/unit/commands/doctor.test.ts index 21aa7c5..3158e4c 100644 --- a/test/unit/commands/doctor.test.ts +++ b/test/unit/commands/doctor.test.ts @@ -111,6 +111,29 @@ describe('doctor command', () => { expect(result.ok).toBe(true); expect(result.checks.environment.length).toBeGreaterThan(0); expect(result.checks.renderer.length).toBeGreaterThan(0); + expect(result.capabilities).toHaveLength(5); + expect(result.capabilities.map((capability) => capability.name)).toEqual([ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', + ]); + expect(result.capabilities.find(({ name }) => name === 'snapshot')).toEqual( + { + name: 'snapshot', + status: 'available', + reason: 'built-in capability', + detail: 'available without external renderer dependencies', + }, + ); + expect( + result.capabilities.find(({ name }) => name === 'screenshot'), + ).toMatchObject({ + name: 'screenshot', + status: 'available', + reason: 'renderer smoke checks passed', + }); expect(checkNames).toEqual( expect.arrayContaining([ 'home-writable', @@ -186,6 +209,7 @@ describe('doctor command', () => { it('formats grouped human-readable output', () => { const lines = buildDoctorLines({ ok: false, + capabilities: [], checks: { environment: [ { diff --git a/test/unit/commands/golden-envelopes.test.ts b/test/unit/commands/golden-envelopes.test.ts index 5ffbd95..67a48ca 100644 --- a/test/unit/commands/golden-envelopes.test.ts +++ b/test/unit/commands/golden-envelopes.test.ts @@ -8,8 +8,10 @@ import { } from '../../../src/protocol/envelope.js'; import { ERROR_CODES, makeCliError } from '../../../src/protocol/errors.js'; import { + CapabilityEntrySchema, DestroyResultSchema, InspectResultSchema, + RecordExportResultSchema, ScreenshotResultSchema, SendKeysResultSchema, SnapshotResultSchema, @@ -34,6 +36,7 @@ const VersionResultSchema = z arch: z.string().min(1), }) .strict(), + capabilities: z.array(CapabilityEntrySchema).optional(), }) .strict(); @@ -78,6 +81,46 @@ const ListResultSchema = z }) .strict(); +const DoctorCheckStatusSchema = z.enum(['pass', 'fail', 'skip']); + +const DoctorCheckSchema = z + .object({ + name: z.string().min(1), + status: DoctorCheckStatusSchema, + message: z.string(), + durationMs: z.number().nonnegative(), + }) + .strict(); + +const DoctorResultSchema = z + .object({ + ok: z.boolean(), + checks: z + .object({ + environment: z.array(DoctorCheckSchema), + renderer: z.array(DoctorCheckSchema), + }) + .strict(), + capabilities: z.array(CapabilityEntrySchema), + }) + .strict(); + +const GcSkippedSessionSchema = z + .object({ + sessionId: NonEmptyStringSchema, + reason: z.string(), + }) + .strict(); + +const GcResultSchema = z + .object({ + removedSessions: z.array(NonEmptyStringSchema), + skippedSessions: z.array(GcSkippedSessionSchema), + dryRun: z.boolean(), + totalBytesFreed: z.number().nonnegative(), + }) + .strict(); + interface GoldenResultContractCase { name: string; command: string; @@ -180,6 +223,126 @@ const goldenResultContracts: readonly GoldenResultContractCase[] = [ ], }, }, + { + name: 'doctor', + command: 'doctor', + schema: DoctorResultSchema, + validResult: { + ok: true, + checks: { + environment: [ + { + name: 'node-runtime', + status: 'pass', + message: 'ok', + durationMs: 1, + }, + ], + renderer: [ + { + name: 'playwright_available', + status: 'pass', + message: 'ok', + durationMs: 2, + }, + ], + }, + capabilities: [ + { + name: 'snapshot', + status: 'available', + }, + ], + }, + invalidResult: { + ok: true, + checks: { + environment: [ + { + name: 'node-runtime', + status: 'pass', + message: 'ok', + durationMs: -1, + }, + ], + renderer: [], + }, + capabilities: [], + }, + extraFieldResult: { + ok: true, + checks: { + environment: [ + { + name: 'node-runtime', + status: 'pass', + message: 'ok', + durationMs: 1, + ok: true, + }, + ], + renderer: [], + }, + capabilities: [], + }, + }, + { + name: 'gc', + command: 'gc', + schema: GcResultSchema, + validResult: { + removedSessions: ['01J0000000TEST000000000000'], + skippedSessions: [ + { + sessionId: '01J0000000SKIP000000000000', + reason: 'running', + }, + ], + dryRun: false, + totalBytesFreed: 4096, + }, + invalidResult: { + removedSessions: ['01J0000000TEST000000000000'], + skippedSessions: [], + dryRun: false, + totalBytesFreed: -1, + }, + extraFieldResult: { + removedSessions: ['01J0000000TEST000000000000'], + skippedSessions: [], + dryRun: false, + totalBytesFreed: 4096, + removedCount: 1, + }, + }, + { + name: 'gc (dry-run)', + command: 'gc', + schema: GcResultSchema, + validResult: { + removedSessions: [], + skippedSessions: [], + dryRun: true, + totalBytesFreed: 0, + }, + invalidResult: { + removedSessions: [], + skippedSessions: [ + { + sessionId: '01J0000000SKIP000000000000', + }, + ], + dryRun: true, + totalBytesFreed: 0, + }, + extraFieldResult: { + removedSessions: [], + skippedSessions: [], + dryRun: true, + totalBytesFreed: 0, + wouldRemoveSessions: [], + }, + }, { name: 'send-keys', command: 'send-keys', @@ -305,6 +468,80 @@ const goldenResultContracts: readonly GoldenResultContractCase[] = [ dpi: 96, }, }, + { + name: 'record export (asciicast)', + command: 'record export', + schema: RecordExportResultSchema, + validResult: { + sessionId: '01J0000000TEST000000000000', + format: 'asciicast', + artifactPath: '/tmp/test.cast', + bytes: 1024, + sha256: 'abc123', + capturedAtSeq: 42, + durationMs: 5000, + metadata: { + width: 80, + height: 24, + }, + }, + invalidResult: { + sessionId: '01J0000000TEST000000000000', + format: 'asciicast', + artifactPath: '/tmp/test.cast', + bytes: 0, + sha256: 'abc123', + capturedAtSeq: 42, + metadata: {}, + }, + extraFieldResult: { + sessionId: '01J0000000TEST000000000000', + format: 'asciicast', + artifactPath: '/tmp/test.cast', + bytes: 1024, + sha256: 'abc123', + capturedAtSeq: 42, + metadata: {}, + exportedBy: 'cli', + }, + }, + { + name: 'record export (webm)', + command: 'record export', + schema: RecordExportResultSchema, + validResult: { + sessionId: '01J0000000TEST000000000000', + format: 'webm', + artifactPath: '/tmp/test.webm', + bytes: 2048, + sha256: 'def456', + capturedAtSeq: 42, + metadata: { + width: 80, + height: 24, + profileName: 'default', + }, + }, + invalidResult: { + sessionId: '01J0000000TEST000000000000', + format: 'webm', + artifactPath: '/tmp/test.webm', + bytes: 2048, + sha256: 'def456', + capturedAtSeq: -1, + metadata: {}, + }, + extraFieldResult: { + sessionId: '01J0000000TEST000000000000', + format: 'webm', + artifactPath: '/tmp/test.webm', + bytes: 2048, + sha256: 'def456', + capturedAtSeq: 42, + metadata: {}, + profile: 'default', + }, + }, { name: 'destroy', command: 'destroy', @@ -373,7 +610,7 @@ describe('JSON envelope contracts', () => { vi.useRealTimers(); }); - it('locks the inspect success envelope shape', () => { + it('locks the inspect success envelope shape with live renderer runtime', () => { const result = InspectResultSchema.parse({ session: createSessionRecord(), eventCount: 2, @@ -389,13 +626,74 @@ describe('JSON envelope contracts', () => { missingCount: 0, health: 'healthy', }, - usedOfflineReplay: true, + usedOfflineReplay: false, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }, }); expectLockedSuccessEnvelope('inspect', result); expect(InspectResultSchema.safeParse(result).success).toBe(true); }); + it('accepts inspect result with offline renderer runtime', () => { + const result = { + session: createSessionRecord(), + eventCount: 2, + uptime: 1_000, + lastEventSeq: 1, + terminationCategory: 'clean-exit', + artifacts: { + total: 2, + byKind: { + screenshot: 1, + snapshot: 1, + }, + missingCount: 0, + health: 'healthy', + }, + usedOfflineReplay: true, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'host-unreachable', + }, + }; + + expect(InspectResultSchema.safeParse(result).success).toBe(true); + }); + + it('accepts inspect result with unavailable renderer runtime', () => { + const result = { + session: createSessionRecord(), + eventCount: 2, + uptime: 1_000, + lastEventSeq: 1, + terminationCategory: 'clean-exit', + artifacts: { + total: 2, + byKind: { + screenshot: 1, + snapshot: 1, + }, + missingCount: 0, + health: 'healthy', + }, + usedOfflineReplay: false, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'live-host', + status: 'unavailable', + reason: 'renderer-not-installed', + }, + }; + + expect(InspectResultSchema.safeParse(result).success).toBe(true); + }); + it('locks the version success envelope shape', async () => { const result = await buildVersionResult(); @@ -403,6 +701,25 @@ describe('JSON envelope contracts', () => { expect(VersionResultSchema.safeParse(result).success).toBe(true); }); + it('accepts version result with capabilities', async () => { + const result = { + ...(await buildVersionResult()), + capabilities: [ + { + name: 'snapshot', + status: 'available', + }, + { + name: 'screenshot', + status: 'unavailable', + reason: 'playwright not installed', + }, + ], + }; + + expect(VersionResultSchema.safeParse(result).success).toBe(true); + }); + describe.each(goldenResultContracts)('$name result contract', (contract) => { it('accepts a valid result in the success envelope', () => { const result = contract.schema.parse(contract.validResult); diff --git a/test/unit/commands/inspect.test.ts b/test/unit/commands/inspect.test.ts index 4085adc..40c5adc 100644 --- a/test/unit/commands/inspect.test.ts +++ b/test/unit/commands/inspect.test.ts @@ -54,6 +54,7 @@ vi.mock('../../../src/storage/sessionPaths.js', () => ({ })); import { runInspectCommand } from '../../../src/cli/commands/inspect.js'; +import type { SessionStatus } from '../../../src/protocol/schemas.js'; import { createLogger } from '../../../src/util/logger.js'; const TEST_CONTEXT = { @@ -80,9 +81,7 @@ function getLastEmitSuccessPayload(): unknown { return mocks.emitSuccess.mock.calls.at(-1)?.[0] as unknown; } -function createSessionRecord( - status: 'running' | 'exiting' | 'exited' | 'failed' = 'running', -) { +function createSessionRecord(status: SessionStatus = 'running') { return { version: 1, sessionId: 'session-01', @@ -172,6 +171,12 @@ describe('inspect command', () => { terminationCategory?: string; artifacts?: typeof DEFAULT_ARTIFACT_HEALTH; usedOfflineReplay?: boolean; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; }; lines: string[]; }; @@ -188,12 +193,18 @@ describe('inspect command', () => { terminationCategory: 'running', artifacts: DEFAULT_ARTIFACT_HEALTH, usedOfflineReplay: false, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }, }, }), ); expect(emitted.lines).toEqual( expect.arrayContaining([ 'Event Count: 2', + 'Renderer: ghostty-web (live-host, healthy)', 'Last Event Seq: 1', 'Uptime: 5000ms', 'Artifacts: 2 total (screenshot: 1, snapshot: 1), health: healthy', @@ -203,6 +214,48 @@ describe('inspect command', () => { expect(emitted.lines).not.toContain('Termination: running'); }); + it('treats exiting sessions as live-host renderer runtime', async () => { + const exitingSession = createSessionRecord('exiting'); + mocks.readManifestIfExists.mockResolvedValueOnce(exitingSession); + mocks.sendRpc.mockResolvedValueOnce({ session: exitingSession }); + mocks.deriveTerminationCategory.mockReturnValueOnce('running'); + + await runInspectCommand({ + context: TEST_CONTEXT, + json: true, + sessionId: 'session-01', + }); + + expect(mocks.sendRpc).toHaveBeenCalledWith( + '/tmp/agent-terminal/sessions/session-01/rpc.sock', + 'inspect', + ); + + const emitted = getLastEmitSuccessPayload() as { + result: { + usedOfflineReplay?: boolean; + rendererRuntime: { + backend: string; + mode: string; + status: string; + reason?: string; + }; + }; + lines: string[]; + }; + + expect(emitted.result.usedOfflineReplay).toBe(false); + expect(emitted.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }); + expect(emitted.lines).toContain( + 'Renderer: ghostty-web (live-host, healthy)', + ); + expect(emitted.lines).not.toContain('Offline Replay: yes'); + }); + it('degrades gracefully when artifact health computation fails', async () => { const liveSession = createSessionRecord('running'); mocks.computeArtifactHealth.mockRejectedValueOnce( @@ -225,6 +278,12 @@ describe('inspect command', () => { lastEventSeq?: number; terminationCategory?: string; artifacts?: typeof DEFAULT_ARTIFACT_HEALTH; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; }; lines: string[]; }; @@ -236,6 +295,11 @@ describe('inspect command', () => { eventCount: 5, lastEventSeq: 4, terminationCategory: 'running', + rendererRuntime: { + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }, }), ); expect(emitted.result.artifacts).toBeUndefined(); @@ -304,6 +368,12 @@ describe('inspect command', () => { terminationCategory?: string; artifacts?: typeof DEFAULT_ARTIFACT_HEALTH; usedOfflineReplay?: boolean; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; }; lines: string[]; }; @@ -320,17 +390,104 @@ describe('inspect command', () => { terminationCategory: 'clean-exit', artifacts: DEFAULT_ARTIFACT_HEALTH, usedOfflineReplay: true, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'host-unreachable', + }, }, }), ); expect(emitted.lines).toEqual( expect.arrayContaining([ + 'Renderer: ghostty-web (offline-replay, fallback — host-unreachable)', 'Offline Replay: yes', 'Termination: clean-exit', ]), ); }); + it('reports offline replay renderer runtime for exited sessions', async () => { + const exitedSession = createSessionRecord('exited'); + mocks.readManifestIfExists.mockResolvedValueOnce(exitedSession); + mocks.countEventLogEntries.mockResolvedValueOnce(3); + mocks.deriveTerminationCategory.mockReturnValueOnce('clean-exit'); + + await runInspectCommand({ + context: TEST_CONTEXT, + json: true, + sessionId: 'session-01', + }); + + expect(mocks.sendRpc).not.toHaveBeenCalled(); + + const emitted = getLastEmitSuccessPayload() as { + result: { + usedOfflineReplay?: boolean; + rendererRuntime?: { + backend: string; + mode: string; + status: string; + reason?: string; + }; + }; + lines: string[]; + }; + + expect(emitted.result.usedOfflineReplay).toBe(false); + expect(emitted.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'session-not-running', + }); + expect(emitted.lines).toContain( + 'Renderer: ghostty-web (offline-replay, fallback — session-not-running)', + ); + expect(emitted.lines).not.toContain('Offline Replay: yes'); + }); + + it('reports offline replay renderer runtime for destroying sessions', async () => { + const destroyingSession = createSessionRecord('destroying'); + mocks.readManifestIfExists.mockResolvedValueOnce(destroyingSession); + mocks.countEventLogEntries.mockResolvedValueOnce(4); + mocks.deriveTerminationCategory.mockReturnValueOnce('destroyed'); + + await runInspectCommand({ + context: TEST_CONTEXT, + json: true, + sessionId: 'session-01', + }); + + expect(mocks.sendRpc).not.toHaveBeenCalled(); + + const emitted = getLastEmitSuccessPayload() as { + result: { + usedOfflineReplay?: boolean; + rendererRuntime: { + backend: string; + mode: string; + status: string; + reason?: string; + }; + }; + lines: string[]; + }; + + expect(emitted.result.usedOfflineReplay).toBe(false); + expect(emitted.result.rendererRuntime).toEqual({ + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'session-not-running', + }); + expect(emitted.lines).toContain( + 'Renderer: ghostty-web (offline-replay, fallback — session-not-running)', + ); + expect(emitted.lines).not.toContain('Offline Replay: yes'); + }); + it('omits last event sequence from human output when the event log is empty', async () => { const liveSession = createSessionRecord('running'); mocks.countEventLogEntries.mockResolvedValue(0); diff --git a/test/unit/commands/version.test.ts b/test/unit/commands/version.test.ts index be13670..a226312 100644 --- a/test/unit/commands/version.test.ts +++ b/test/unit/commands/version.test.ts @@ -1,5 +1,6 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; +import * as capabilitiesModule from '../../../src/renderer/capabilities.js'; import { buildVersionResult, loadPackageMetadata, @@ -13,12 +14,55 @@ describe('version command', () => { expect(packageMetadata.version).toMatch(/^\d+\.\d+\.\d+$/); }); - it('builds the version result', async () => { + it('builds the version result without capabilities by default', async () => { const result = await buildVersionResult(); expect(result.cliVersion).toMatch(/^\d+\.\d+\.\d+$/); expect(result.protocolVersion).toBe('0.1.0'); expect(result.rendererBackends).toEqual(['ghostty-web']); expect(result.runtime.node).toMatch(/^v\d+\.\d+\.\d+$/); + expect('capabilities' in result).toBe(false); + }); + + it('builds the version result with runtime capabilities when requested', async () => { + const result = await buildVersionResult({ includeCapabilities: true }); + + expect(result.capabilities).toHaveLength(5); + expect(result.capabilities?.map((capability) => capability.name)).toEqual([ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', + ]); + expect( + result.capabilities?.find(({ name }) => name === 'snapshot'), + ).toEqual({ + name: 'snapshot', + status: 'available', + }); + }); + + it('degrades gracefully when capability discovery fails', async () => { + vi.spyOn(capabilitiesModule, 'discoverCapabilities').mockRejectedValueOnce( + new Error('unexpected failure'), + ); + const stderrWriteSpy = vi + .spyOn(process.stderr, 'write') + .mockReturnValue(true); + + const result = await buildVersionResult({ includeCapabilities: true }); + + expect(result.cliVersion).toMatch(/^\d+\.\d+\.\d+$/); + expect(result.protocolVersion).toBe('0.1.0'); + expect(result.rendererBackends).toEqual(['ghostty-web']); + expect(result.runtime.node).toMatch(/^v\d+\.\d+\.\d+$/); + expect(result.runtime.platform).toBe(process.platform); + expect(result.runtime.arch).toBe(process.arch); + expect(result.capabilities).toBeUndefined(); + expect('capabilities' in result).toBe(false); + expect(stderrWriteSpy).toHaveBeenCalledWith( + 'warning: capability discovery failed: unexpected failure\n', + ); }); }); diff --git a/test/unit/protocol/messages.test.ts b/test/unit/protocol/messages.test.ts index f1ec550..7140b33 100644 --- a/test/unit/protocol/messages.test.ts +++ b/test/unit/protocol/messages.test.ts @@ -1,11 +1,13 @@ import { describe, expect, it } from 'vitest'; import { + CapabilityEntrySchema, DestroyParamsSchema, DestroyResultSchema, HostInspectResultSchema, InspectResultSchema, MarkParamsSchema, + RendererRuntimeSummarySchema, MarkResultSchema, PasteParamsSchema, SendKeysResultSchema, @@ -142,6 +144,87 @@ describe('protocol schemas', () => { }); }); +describe('CapabilityEntrySchema', () => { + it('accepts a minimal available capability entry', () => { + const result = CapabilityEntrySchema.safeParse({ + name: 'snapshot', + status: 'available', + }); + + expect(result.success).toBe(true); + }); + + it('accepts a detailed unavailable capability entry', () => { + const result = CapabilityEntrySchema.safeParse({ + name: 'screenshot', + status: 'unavailable', + reason: 'playwright-missing', + detail: 'Cannot find module', + }); + + expect(result.success).toBe(true); + }); + + it('accepts every capability name', () => { + const names = [ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', + ] as const; + + for (const name of names) { + expect( + CapabilityEntrySchema.safeParse({ name, status: 'available' }).success, + ).toBe(true); + } + }); + + it('accepts every capability status', () => { + const statuses = [ + 'available', + 'unavailable', + 'degraded', + 'unknown', + ] as const; + + for (const status of statuses) { + expect( + CapabilityEntrySchema.safeParse({ name: 'snapshot', status }).success, + ).toBe(true); + } + }); + + it('rejects extra fields on capability entries', () => { + const result = CapabilityEntrySchema.safeParse({ + name: 'snapshot', + status: 'available', + extra: true, + }); + + expect(result.success).toBe(false); + }); + + it('rejects invalid capability names', () => { + const result = CapabilityEntrySchema.safeParse({ + name: 'video-export', + status: 'available', + }); + + expect(result.success).toBe(false); + }); + + it('rejects invalid capability statuses', () => { + const result = CapabilityEntrySchema.safeParse({ + name: 'snapshot', + status: 'partial', + }); + + expect(result.success).toBe(false); + }); +}); + describe('RPC message schemas', () => { it('accepts a base RPC request', () => { const result = RpcRequestSchema.safeParse({ @@ -200,11 +283,90 @@ describe('RPC message schemas', () => { session: createSessionRecord(), eventCount: 2, uptime: 1000, + rendererRuntime: { + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }, }); expect(result.success).toBe(true); }); + it('requires rendererRuntime on inspect results', () => { + const result = InspectResultSchema.safeParse({ + session: createSessionRecord(), + eventCount: 2, + uptime: 1000, + }); + + expect(result.success).toBe(false); + }); + + it('accepts renderer runtime summaries for all supported statuses', () => { + expect( + RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }).success, + ).toBe(true); + expect( + RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'fallback', + reason: 'host-unreachable', + }).success, + ).toBe(true); + expect( + RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'unavailable', + reason: 'renderer-not-installed', + }).success, + ).toBe(true); + }); + + it('accepts renderer runtime summaries for both runtime modes', () => { + expect( + RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + }).success, + ).toBe(true); + expect( + RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'offline-replay', + status: 'healthy', + }).success, + ).toBe(true); + }); + + it('keeps renderer runtime summaries strict', () => { + const result = RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'healthy', + detail: 'unexpected', + }); + + expect(result.success).toBe(false); + }); + + it('rejects renderer runtime summaries with invalid status values', () => { + const result = RendererRuntimeSummarySchema.safeParse({ + backend: 'ghostty-web', + mode: 'live-host', + status: 'degraded', + }); + + expect(result.success).toBe(false); + }); + it('keeps inspect RPC results limited to the session payload', () => { const result = HostInspectResultSchema.safeParse({ session: createSessionRecord(), diff --git a/test/unit/renderer/capabilities.test.ts b/test/unit/renderer/capabilities.test.ts new file mode 100644 index 0000000..7385c2f --- /dev/null +++ b/test/unit/renderer/capabilities.test.ts @@ -0,0 +1,194 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { discoverCapabilities } from '../../../src/renderer/capabilities.js'; + +function getCapability( + capabilities: Awaited>, + name: (typeof capabilities)[number]['name'], +) { + return capabilities.find((capability) => capability.name === name); +} + +describe('discoverCapabilities', () => { + it('returns five quick capabilities without browser-launch details', async () => { + const probePlaywright = vi.fn(() => Promise.resolve({ available: true })); + + const capabilities = await discoverCapabilities('quick', { + probePlaywright, + }); + + expect(probePlaywright).toHaveBeenCalledTimes(2); + expect(capabilities).toHaveLength(5); + expect(capabilities.map((capability) => capability.name)).toEqual([ + 'snapshot', + 'wait', + 'screenshot', + 'record-export-asciicast', + 'record-export-webm', + ]); + expect(getCapability(capabilities, 'snapshot')).toEqual({ + name: 'snapshot', + status: 'available', + }); + expect(getCapability(capabilities, 'wait')).toEqual({ + name: 'wait', + status: 'available', + }); + expect(getCapability(capabilities, 'screenshot')).toEqual({ + name: 'screenshot', + status: 'available', + }); + expect(getCapability(capabilities, 'record-export-asciicast')).toEqual({ + name: 'record-export-asciicast', + status: 'available', + }); + expect(getCapability(capabilities, 'record-export-webm')).toEqual({ + name: 'record-export-webm', + status: 'available', + }); + }); + + it('marks quick browser-backed capabilities unavailable when playwright is missing', async () => { + const capabilities = await discoverCapabilities('quick', { + probePlaywright: () => + Promise.resolve({ + available: false, + reason: 'playwright not installed', + detail: 'Cannot find package playwright', + }), + }); + + expect(getCapability(capabilities, 'screenshot')).toEqual({ + name: 'screenshot', + status: 'unavailable', + reason: 'playwright not installed', + detail: 'Cannot find package playwright', + }); + expect(getCapability(capabilities, 'record-export-webm')).toEqual({ + name: 'record-export-webm', + status: 'unavailable', + reason: 'playwright not installed', + detail: 'Cannot find package playwright', + }); + }); + + it('uses full doctor renderer checks without re-probing playwright', async () => { + const probePlaywright = vi.fn(() => Promise.resolve({ available: true })); + + const capabilities = await discoverCapabilities('full', { + probePlaywright, + rendererChecks: [ + { + name: 'playwright_available', + status: 'pass', + message: 'available', + }, + { + name: 'browser_launch', + status: 'pass', + message: 'chromium launches', + }, + { + name: 'ghostty_web_available', + status: 'pass', + message: 'WASM available', + }, + { + name: 'screenshot_viable', + status: 'pass', + message: 'viable', + }, + ], + }); + + expect(probePlaywright).not.toHaveBeenCalled(); + expect(getCapability(capabilities, 'snapshot')).toEqual({ + name: 'snapshot', + status: 'available', + reason: 'built-in capability', + detail: 'available without external renderer dependencies', + }); + expect(getCapability(capabilities, 'screenshot')).toMatchObject({ + name: 'screenshot', + status: 'available', + reason: 'renderer smoke checks passed', + }); + expect(getCapability(capabilities, 'screenshot')?.detail).toContain( + 'screenshot_viable: viable', + ); + expect(getCapability(capabilities, 'record-export-webm')).toMatchObject({ + name: 'record-export-webm', + status: 'available', + reason: 'browser-backed export dependencies available', + }); + }); + + it('reports degraded full browser-backed capabilities from failing renderer checks', async () => { + const capabilities = await discoverCapabilities('full', { + rendererChecks: [ + { + name: 'playwright_available', + status: 'pass', + message: 'available', + }, + { + name: 'browser_launch', + status: 'fail', + message: 'chromium binary missing', + }, + { + name: 'ghostty_web_available', + status: 'pass', + message: 'WASM available', + }, + { + name: 'screenshot_viable', + status: 'fail', + message: 'not attempted after browser failure', + }, + ], + }); + + expect(getCapability(capabilities, 'screenshot')).toEqual({ + name: 'screenshot', + status: 'degraded', + reason: 'browser launch failed', + detail: 'chromium binary missing', + }); + expect(getCapability(capabilities, 'record-export-webm')).toEqual({ + name: 'record-export-webm', + status: 'degraded', + reason: 'browser launch failed', + detail: 'chromium binary missing', + }); + }); + + it('marks full browser-backed capabilities unknown when renderer checks are incomplete', async () => { + const probePlaywright = vi.fn(() => Promise.resolve({ available: true })); + + const capabilities = await discoverCapabilities('full', { + probePlaywright, + rendererChecks: [ + { + name: 'playwright_available', + status: 'pass', + message: 'available', + }, + ], + }); + + expect(probePlaywright).not.toHaveBeenCalled(); + expect(getCapability(capabilities, 'screenshot')).toEqual({ + name: 'screenshot', + status: 'unknown', + reason: 'renderer checks incomplete', + detail: 'doctor did not provide the full renderer check set', + }); + expect(getCapability(capabilities, 'record-export-webm')).toEqual({ + name: 'record-export-webm', + status: 'unknown', + reason: 'renderer checks incomplete', + detail: 'doctor did not provide the full renderer check set', + }); + }); +}); diff --git a/test/unit/tools/validate-bundle.test.ts b/test/unit/tools/validate-bundle.test.ts new file mode 100644 index 0000000..59b9729 --- /dev/null +++ b/test/unit/tools/validate-bundle.test.ts @@ -0,0 +1,237 @@ +import { + mkdtemp, + mkdir, + realpath, + rm, + truncate, + writeFile, +} from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; + +import { afterEach, describe, expect, it } from 'vitest'; + +import { + MAX_JSON_FILE_BYTES, + runValidateBundleCli, + validateBundle, + type BundleValidationCheck, + type BundleValidationResult, +} from '../../../src/tools/validate-bundle.js'; + +const tempDirs: string[] = []; + +async function createTempDir(): Promise { + // prettier-ignore + const directory = await realpath(await mkdtemp(join(tmpdir(), 'agent-terminal-validate-bundle-'))); + tempDirs.push(directory); + return directory; +} + +async function writeFixtureFile( + root: string, + relativePath: string, + content: string, +): Promise { + const filePath = join(root, relativePath); + await mkdir(dirname(filePath), { recursive: true }); + await writeFile(filePath, content, 'utf8'); +} + +function findCheck( + result: BundleValidationResult, + checkName: string, +): BundleValidationCheck { + const check = result.checks.find((candidate) => candidate.name === checkName); + expect(check).toBeDefined(); + return check as BundleValidationCheck; +} + +async function createContractBundle(): Promise { + const bundleRoot = await createTempDir(); + await writeFixtureFile(bundleRoot, 'index.html', '\n'); + await writeFixtureFile(bundleRoot, 'notes.md', '# Notes\n'); + await writeFixtureFile(bundleRoot, '01-create.json', '{"ok":true}\n'); + return bundleRoot; +} + +afterEach(async () => { + await Promise.all( + tempDirs + .splice(0, tempDirs.length) + .map((directory) => rm(directory, { recursive: true, force: true })), + ); +}); + +describe('validate-bundle', () => { + it('passes contract-reporting bundles through the CLI with the default profile', async () => { + const bundleRoot = await createContractBundle(); + const stdout: string[] = []; + const stderr: string[] = []; + + const exitCode = await runValidateBundleCli([bundleRoot], { + stdout: (text) => stdout.push(text), + stderr: (text) => stderr.push(text), + }); + + expect(exitCode).toBe(0); + const result = JSON.parse(stdout.join('')) as BundleValidationResult; + expect(result.ok).toBe(true); + expect(result.profile).toBe('contract-reporting'); + expect(findCheck(result, 'has-json-output').ok).toBe(true); + expect(findCheck(result, 'has-review-page').ok).toBe(true); + expect(findCheck(result, 'has-notes').ok).toBe(true); + expect(findCheck(result, 'json-readable').ok).toBe(true); + expect(stderr.join('')).toContain( + 'validate-bundle PASS contract-reporting', + ); + }); + + it('passes interactive-renderer bundles through the CLI', async () => { + const bundleRoot = await createContractBundle(); + await writeFixtureFile(bundleRoot, 'screenshots/terminal.png', 'png-bytes'); + await writeFixtureFile(bundleRoot, 'recordings/session.cast', 'cast-bytes'); + await writeFixtureFile(bundleRoot, 'videos/session.webm', 'webm-bytes'); + const stdout: string[] = []; + const stderr: string[] = []; + + const exitCode = await runValidateBundleCli( + [bundleRoot, '--profile', 'interactive-renderer'], + { + stdout: (text) => stdout.push(text), + stderr: (text) => stderr.push(text), + }, + ); + + expect(exitCode).toBe(0); + const result = JSON.parse(stdout.join('')) as BundleValidationResult; + expect(result.ok).toBe(true); + expect(result.profile).toBe('interactive-renderer'); + expect(findCheck(result, 'has-screenshot').ok).toBe(true); + expect(findCheck(result, 'has-recording').ok).toBe(true); + expect(stderr.join('')).toContain( + 'validate-bundle PASS interactive-renderer', + ); + }); + + it('fails when the bundle directory is empty', async () => { + const bundleRoot = await createTempDir(); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-json-output').ok).toBe(false); + expect(findCheck(result, 'has-review-page').ok).toBe(false); + expect(findCheck(result, 'has-notes').ok).toBe(false); + expect(findCheck(result, 'json-readable').ok).toBe(false); + }); + + it('fails when a contract-reporting bundle is missing notes', async () => { + const bundleRoot = await createTempDir(); + await writeFixtureFile(bundleRoot, 'index.html', '\n'); + await writeFixtureFile(bundleRoot, '01-create.json', '{"ok":true}\n'); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-json-output').ok).toBe(true); + expect(findCheck(result, 'has-review-page').ok).toBe(true); + expect(findCheck(result, 'has-notes').ok).toBe(false); + }); + + it('fails when a contract-reporting bundle is missing JSON output', async () => { + const bundleRoot = await createTempDir(); + await writeFixtureFile(bundleRoot, 'index.html', '\n'); + await writeFixtureFile(bundleRoot, 'notes.md', '# Notes\n'); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-json-output').ok).toBe(false); + }); + + it('fails when a contract-reporting bundle is missing a review page', async () => { + const bundleRoot = await createTempDir(); + await writeFixtureFile(bundleRoot, 'notes.md', '# Notes\n'); + await writeFixtureFile(bundleRoot, '01-create.json', '{"ok":true}\n'); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-review-page').ok).toBe(false); + }); + + it('fails when a contract-reporting bundle has corrupt JSON output', async () => { + const bundleRoot = await createTempDir(); + await writeFixtureFile(bundleRoot, 'index.html', '\n'); + await writeFixtureFile(bundleRoot, 'notes.md', '# Notes\n'); + await writeFixtureFile(bundleRoot, '01-create.json', '{not-json}\n'); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'json-readable').ok).toBe(false); + expect(findCheck(result, 'json-readable').message).toContain( + '01-create.json', + ); + }); + + it('fails when a contract-reporting bundle has oversized JSON output', async () => { + const bundleRoot = await createTempDir(); + const jsonPath = join(bundleRoot, '01-create.json'); + await writeFixtureFile(bundleRoot, 'index.html', '\n'); + await writeFixtureFile(bundleRoot, 'notes.md', '# Notes\n'); + await writeFile(jsonPath, '{', 'utf8'); + await truncate(jsonPath, MAX_JSON_FILE_BYTES + 1); + + const result = await validateBundle(bundleRoot, 'contract-reporting'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-json-output').ok).toBe(true); + expect(findCheck(result, 'json-readable').ok).toBe(false); + expect(findCheck(result, 'json-readable').message).toContain( + '01-create.json', + ); + expect(findCheck(result, 'json-readable').message).toContain( + String(MAX_JSON_FILE_BYTES), + ); + }); + + it('fails interactive-renderer bundles that are missing screenshots', async () => { + const bundleRoot = await createContractBundle(); + await writeFixtureFile(bundleRoot, 'recordings/session.cast', 'cast-bytes'); + + const result = await validateBundle(bundleRoot, 'interactive-renderer'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-screenshot').ok).toBe(false); + expect(findCheck(result, 'has-recording').ok).toBe(true); + }); + + it('fails interactive-renderer bundles that are missing recordings', async () => { + const bundleRoot = await createContractBundle(); + await writeFixtureFile(bundleRoot, 'screenshots/terminal.png', 'png-bytes'); + + const result = await validateBundle(bundleRoot, 'interactive-renderer'); + + expect(result.ok).toBe(false); + expect(findCheck(result, 'has-screenshot').ok).toBe(true); + expect(findCheck(result, 'has-recording').ok).toBe(false); + }); + + it('fails when the bundle directory does not exist', async () => { + const missingBundleRoot = join( + tmpdir(), + 'agent-terminal-validate-bundle-does-not-exist', + ); + + const result = await validateBundle( + missingBundleRoot, + 'contract-reporting', + ); + + expect(result.ok).toBe(false); + expect(result.checks).toHaveLength(1); + expect(findCheck(result, 'bundle-exists').ok).toBe(false); + }); +});