From 94a63aaf4222e2b0696b745abfcacd7baee57f73 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Thu, 30 Apr 2026 07:59:58 +0000 Subject: [PATCH 1/4] refactor: share screenshot capture and artifact persistence Live host screenshot RPCs and CLI offline replay screenshots now both flow through a single `captureScreenshotResult(...)` helper in `src/screenshot/capture.ts`, mirroring the snapshot capture refactor in #69. The shared seam owns: - temp-file allocation under the session artifacts directory - renderer screenshot invocation (with optional `showCursor` threaded through) - the union of the live and offline runtime invariants (sessionId, profileName, pngSizeBytes, requested vs returned artifactPath, sha256) - allowlisted public `ScreenshotResult` construction - Zod validation through `ScreenshotResultSchema` *before* any rename or manifest append side effect - the final `screenshot--.png` rename - artifact manifest entry creation with the same metadata fields as before - temp-file cleanup on failure Public CLI/RPC behavior, screenshot filenames, manifest entries, and `screenshot --json` output are unchanged (verified by the `dogfood/issue-64-share-screenshot-capture/` proof bundle: byte-identical PNGs and identical manifest metadata before and after, against parent commit b2d5068). Tracking #64. --- CHANGELOG.md | 1 + CONTEXT.md | 12 + .../README.md | 95 ++++ .../after/01-create-live.json | 12 + .../after/02-wait-live.json | 13 + .../after/03-screenshot-live-default.json | 20 + .../after/04-screenshot-live-show-cursor.json | 20 + .../after/10-create-offline.json | 12 + .../after/11-wait-offline.json | 9 + .../after/12-screenshot-offline-default.json | 20 + .../13-screenshot-offline-show-cursor.json | 20 + .../after/98-destroy-offline.json | 9 + .../after/99-destroy-live.json | 9 + .../after/agent-tty-home.txt | 1 + .../after/live-session-id.txt | 1 + .../after/manifest-live.json | 46 ++ .../after/manifest-offline.json | 46 ++ .../after/offline-session-id.txt | 1 + .../after/screenshot-live-default.png | Bin 0 -> 3790 bytes .../after/screenshot-live-show-cursor.png | Bin 0 -> 3790 bytes .../after/screenshot-offline-default.png | Bin 0 -> 3749 bytes .../after/screenshot-offline-show-cursor.png | Bin 0 -> 3749 bytes .../after/sha256-summary.txt | 8 + .../after/transcript.txt | 152 ++++++ .../before/01-create-live.json | 12 + .../before/02-wait-live.json | 13 + .../before/03-screenshot-live-default.json | 20 + .../04-screenshot-live-show-cursor.json | 20 + .../before/10-create-offline.json | 12 + .../before/11-wait-offline.json | 9 + .../before/12-screenshot-offline-default.json | 20 + .../13-screenshot-offline-show-cursor.json | 20 + .../before/98-destroy-offline.json | 9 + .../before/99-destroy-live.json | 9 + .../before/agent-tty-home.txt | 1 + .../before/live-session-id.txt | 1 + .../before/manifest-live.json | 46 ++ .../before/manifest-offline.json | 46 ++ .../before/offline-session-id.txt | 1 + .../before/screenshot-live-default.png | Bin 0 -> 3790 bytes .../before/screenshot-live-show-cursor.png | Bin 0 -> 3790 bytes .../before/screenshot-offline-default.png | Bin 0 -> 3749 bytes .../before/screenshot-offline-show-cursor.png | Bin 0 -> 3749 bytes .../before/sha256-summary.txt | 8 + .../before/transcript.txt | 152 ++++++ .../commands.sh | 126 +++++ src/cli/commands/screenshot.ts | 146 +----- src/host/hostMain.ts | 97 +--- src/screenshot/capture.ts | 146 ++++++ test/unit/screenshot/capture.test.ts | 468 ++++++++++++++++++ 50 files changed, 1682 insertions(+), 207 deletions(-) create mode 100644 dogfood/issue-64-share-screenshot-capture/README.md create mode 100644 dogfood/issue-64-share-screenshot-capture/after/01-create-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/02-wait-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/03-screenshot-live-default.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/04-screenshot-live-show-cursor.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/10-create-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/11-wait-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/12-screenshot-offline-default.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/13-screenshot-offline-show-cursor.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/98-destroy-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/99-destroy-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/agent-tty-home.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/after/live-session-id.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/after/manifest-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/manifest-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/after/offline-session-id.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/after/screenshot-live-default.png create mode 100644 dogfood/issue-64-share-screenshot-capture/after/screenshot-live-show-cursor.png create mode 100644 dogfood/issue-64-share-screenshot-capture/after/screenshot-offline-default.png create mode 100644 dogfood/issue-64-share-screenshot-capture/after/screenshot-offline-show-cursor.png create mode 100644 dogfood/issue-64-share-screenshot-capture/after/sha256-summary.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/after/transcript.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/before/01-create-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/02-wait-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/03-screenshot-live-default.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/04-screenshot-live-show-cursor.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/10-create-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/11-wait-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/12-screenshot-offline-default.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/13-screenshot-offline-show-cursor.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/98-destroy-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/99-destroy-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/agent-tty-home.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/before/live-session-id.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/before/manifest-live.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/manifest-offline.json create mode 100644 dogfood/issue-64-share-screenshot-capture/before/offline-session-id.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/before/screenshot-live-default.png create mode 100644 dogfood/issue-64-share-screenshot-capture/before/screenshot-live-show-cursor.png create mode 100644 dogfood/issue-64-share-screenshot-capture/before/screenshot-offline-default.png create mode 100644 dogfood/issue-64-share-screenshot-capture/before/screenshot-offline-show-cursor.png create mode 100644 dogfood/issue-64-share-screenshot-capture/before/sha256-summary.txt create mode 100644 dogfood/issue-64-share-screenshot-capture/before/transcript.txt create mode 100755 dogfood/issue-64-share-screenshot-capture/commands.sh create mode 100644 src/screenshot/capture.ts create mode 100644 test/unit/screenshot/capture.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c85e952..4a85cbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Session-status policy is now centralized in a single module that classifies every `SessionStatus` as active, terminal, commandable, live-host eligible, offline-replay eligible, collectable, and destroyed; lifecycle, inspect, gc, wait, host, and command-state checks share the same predicates. CLI JSON, protocol schemas, event-log behavior, and artifact formats are unchanged ([#67](https://github.com/coder/agent-tty/pull/67)). - Persisted event-log validation, JSONL parsing, and contiguous-sequence checks for live hydration, offline replay, and `record export` now flow through a shared codec at `src/storage/eventLogCodec.ts`. Validation errors use neutral event-log wording; missing-log policy remains caller-specific (offline replay treats missing logs as empty; `record export` still surfaces missing logs as an error) ([#68](https://github.com/coder/agent-tty/pull/68)). - Snapshot result construction and artifact persistence are now shared between live host RPCs and offline replay through `src/snapshot/capture.ts`. Snapshot artifact filenames, JSON formatting (two-space indent + trailing newline), manifest metadata, and `rendererBackend` reporting are unchanged ([#69](https://github.com/coder/agent-tty/pull/69)). +- Screenshot capture, validation, and artifact persistence are now shared between live host RPCs and offline replay through `src/screenshot/capture.ts`. The shared seam owns temporary-file allocation, renderer screenshot invocation, allowlisted public result construction, Zod validation before any rename or manifest append, manifest metadata creation, and temp-file cleanup on failure. Screenshot filenames, final PNG paths, manifest entry shape (`profileName`, `cols`, `rows`, `pngSizeBytes`, `cursorVisible`, `rendererBackend`, `pixelWidth`, `pixelHeight`, `sha256`, `renderProfileHash`), and `screenshot --json` output are unchanged (tracking [#64](https://github.com/coder/agent-tty/issues/64)). - Waited-run completion bookkeeping (sentinel scanning, postamble sanitization, waiter resolution, timeout, exit, `run_complete` appends) moved out of `hostMain` into a dedicated `RunCompletionCoordinator`. No public CLI, protocol, or event-log changes ([#70](https://github.com/coder/agent-tty/pull/70)). - Refreshed contributor and agent guidance in `AGENTS.md` with an outcome-first operating contract, validation guidance, and grouped project invariants ([#46](https://github.com/coder/agent-tty/pull/46)). Added in-repo agent skills under `.agents/skills/` (`diagnose`, `tdd`, `triage`, `improve-codebase-architecture`, `grill-with-docs`, `to-issues`, `to-prd`) plus matching links from `.claude/skills/` and `.mux/skills/` ([#65](https://github.com/coder/agent-tty/pull/65)). diff --git a/CONTEXT.md b/CONTEXT.md index 20870a7..0fcb703 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -69,6 +69,16 @@ A persisted JSON artifact containing exactly the **Snapshot Result** returned to The operation that derives a **Snapshot Result** from a **Semantic Snapshot** and records the matching **Snapshot Artifact**. _Avoid_: Renderer capture +**Screenshot Result**: +A public screenshot payload returned to a caller describing the rendered PNG of a **Session** at a captured event-log sequence. + +**Screenshot Artifact**: +A persisted PNG file plus its manifest entry recording the **Screenshot Result** for a **Session** at a captured event-log sequence. + +**Screenshot Capture**: +The operation that invokes the renderer to produce the PNG, builds the **Screenshot Result**, and records the matching **Screenshot Artifact**. +_Avoid_: Renderer capture + **Release Prep Workflow**: The maintainer-facing process for choosing the next release version and preparing release changes for review before they land on the default branch. @@ -90,6 +100,8 @@ The tag-triggered automation that validates, packages, and publishes a release a - An **Offline Replay Eligible Session** is reconstructed from its persisted **Event Log** and manifest. - A **Snapshot Result** is derived from exactly one **Semantic Snapshot**. - A **Snapshot Artifact** contains exactly the **Snapshot Result** emitted to the caller. +- A **Screenshot Capture** produces exactly one **Screenshot Result** and exactly one **Screenshot Artifact** for the same captured event-log sequence. +- A **Screenshot Artifact** is the persisted PNG plus its manifest entry that the **Screenshot Result** describes to the caller. - A **Command Target** is exactly one **Commandable Session** selected by an input or control command. - A **Commandable Session** can accept a **Waited Run**. - A **Render Wait** may include text, regex, cursor, or **Screen Stability** conditions. diff --git a/dogfood/issue-64-share-screenshot-capture/README.md b/dogfood/issue-64-share-screenshot-capture/README.md new file mode 100644 index 0000000..4ca3623 --- /dev/null +++ b/dogfood/issue-64-share-screenshot-capture/README.md @@ -0,0 +1,95 @@ +# Issue 64 — Share screenshot capture and artifact persistence + +This bundle compares screenshot output from the running session (live) and +exited session (offline replay) paths, before and after the refactor that +moves both paths through the shared `captureScreenshotResult(...)` helper in +`src/screenshot/capture.ts`. + +## How it was generated + +The same `commands.sh` was run twice: + +1. **before**: against parent commit `b2d5068` (`refactor: share render wait +matching (#76)`) in a temporary `git worktree`. This still has the + inline screenshot logic in `src/host/hostMain.ts` and a duplicate + implementation in `src/cli/commands/screenshot.ts`. +2. **after**: against this branch, where both paths call the new + `captureScreenshotResult(...)` helper. + +Each run uses an isolated absolute `AGENT_TTY_HOME` (recorded in +`