Skip to content

xtask: add record-demo subcommand (v0)#190

Open
whme wants to merge 2 commits into
mainfrom
demo-automation
Open

xtask: add record-demo subcommand (v0)#190
whme wants to merge 2 commits into
mainfrom
demo-automation

Conversation

@whme
Copy link
Copy Markdown
Owner

@whme whme commented May 9, 2026

Summary

The README's demo/csshw.gif is currently re-recorded by hand each
time it drifts from the product, requiring a developer to manually
configure their workstation (wallpaper, fake SSH hosts, keystroke
overlay, ScreenToGif). This PR turns that artifact into a
reproducible build output.

This is v0 of a four-stage plan: a typed Rust DSL + driver +
local env that produces target/demo/csshw.gif on the developer's
own desktop. Requires ffmpeg and gifski on PATH. v1 (Windows
Sandbox provider, vendored SHA-pinned binaries, Carnac overlay,
visual normalisation) is in a separate stacked PR. v2 (CI workflows

  • append-only demo-assets orphan branch) and v3 (Press(Chord)
    primitive + full canonical scene) are out of scope.

The PRs are stacked Gerrit-style: the v1 PR currently contains v0+v1
commits and targets main; once this PR merges, the v1 PR will be
rebased on main so only v1 changes remain.

v0 highlights

  • DemoSystem trait + RealSystem behind which all side effects
    (Windows input synthesis, fs, subprocess) sit so unit tests
    exercise the driver via mockall with zero real-system effects.
  • Closed Step enum + Script builder validating capture pairing
    and regex compilation at build time, so a typo in the script
    fails cargo check rather than a half-completed recording.
  • Driver interprets steps via the trait; the in-flight ffmpeg
    capture is cleaned up even when a step errors mid-script.
  • config_override writes target/demo/csshw-config.toml, a
    dispatcher.bat that strips an optional user@ prefix from
    csshw's USERNAME_AT_HOST substitution, and per-host
    enter.bat scripts with curated home directories.
  • env/local.rs copies csshw.exe into target/demo/ so
    csshw's startup set_current_dir(exe_dir) lands on our config.
  • terminate_csshw kills the daemon child and best-effort kills
    any CREATE_NEW_CONSOLE-detached client csshw.exe instances.

Test plan

  • cargo build -p xtask clean (no warnings)
  • cargo fmt clean
  • cargo lint clean
  • cargo test (all green)
  • cargo doc-tests clean
  • cargo xtask check-typography clean
  • Manual: cargo xtask record-demo --env local produces a
    playable target/demo/csshw.gif

The full plan lives at
C:/Users/whme/.claude/plans/tranquil-hopping-karp.md.

The README's demo/csshw.gif is currently re-recorded by hand each
time it drifts from the product, requiring a developer to manually
configure their workstation (wallpaper, fake SSH hosts, keystroke
overlay, ScreenToGif). This commit lays the foundation for "demo
as code" - a typed Rust DSL describing the demo plus an xtask
subcommand that runs it.

This is v0 of a four-stage plan: a local-only proof that produces
target/demo/csshw.gif on the developer's own desktop. v1 adds
Windows Sandbox isolation + Carnac overlay + visual normalisation;
v2 adds CI workflows + an append-only demo-assets orphan branch
with SHA-pinned filenames; v3 adds the chord/Press DSL primitive
plus the full canonical scene.

Architecture mirrors the existing xtask modules (e.g.
social_preview.rs):

- DemoSystem trait + RealSystem behind which all side effects
  (windows input synthesis, fs, subprocess) sit so unit tests
  exercise the driver via mockall with zero real-system effects.
- Closed Step enum + Script builder validating capture pairing
  and regex compilation at build time, so a typo in the script
  fails cargo check rather than a half-completed recording.
- Driver interprets steps via the trait; the in-flight ffmpeg
  capture is cleaned up even when a step errors mid-script.
- config_override writes target/demo/csshw-config.toml, a
  dispatcher.bat that strips an optional user@ prefix from
  csshw's USERNAME_AT_HOST substitution, and per-host enter.bat
  scripts with curated home directories.
- env/local.rs copies csshw.exe into target/demo/ so csshw's
  startup set_current_dir(exe_dir) lands on our config (csshw
  rebases its cwd at startup, so a plain cwd-based override
  does not work).
- terminate_csshw kills the daemon child and best-effort kills
  any CREATE_NEW_CONSOLE-detached client csshw.exe instances.

v0 requires ffmpeg and gifski on PATH; v1 will vendor them as
SHA-pinned binaries downloaded into target/demo/bin/. The full
plan lives at C:/Users/whme/.claude/plans/tranquil-hopping-karp.md.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@whme whme added the no-news-fragment-needed Indicates that this PR does not require a news fragment label May 9, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

🤖 News Fragment Check

Success! This PR meets the news fragment requirements.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

🤖 Diff Coverage Check

Success!

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

No lines with coverage information in this diff.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

🤖 Coverage Check
Total Coverage: 61.78%

Filename Stmts Miss Cover Missing
src\protocol\serialization.rs 30 0 100.00%
src\protocol\deserialization.rs 53 0 100.00%
src\utils\windows.rs 120 14 88.33% 293, 516-518, 1004-1005, 1027-1031, 1177-1182
src\utils\config.rs 80 0 100.00%
src\client\mod.rs 223 146 34.53% 169-251, 310, 314, 319-323, 341-506
src\lib.rs 113 19 83.19% 257-265, 325-327, 345-354
src\cli.rs 314 57 81.85% 100-276
src\daemon\workspace.rs 21 0 100.00%
src\daemon\mod.rs 709 583 17.77% 188-190, 216-816, 877-1033, 1064-1065, 1108, 1138, 1182-1183, 1189-1191, 1204-1208, 1240, 1249-1253, 1272-1533
xtask\src\coverage.rs 55 3 94.55% 185, 199, 213
xtask\src\typography.rs 91 1 98.90% 259
xtask\src\changelog.rs 29 0 100.00%
xtask\src\readme.rs 72 1 98.61% 123
xtask\src\release.rs 144 15 89.58% 27, 492-502, 505, 509, 615
xtask\src\inject_agent_token.rs 48 0 100.00%
xtask\src\social_preview.rs 101 3 97.03% 259, 262, 265
TOTAL 2203 842 61.78%

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new cargo xtask record-demo subcommand that records a reproducible “demo as code” GIF via a typed Rust DSL, a mockable driver (DemoSystem), and a v0 local environment implementation.

Changes:

  • Add record-demo CLI subcommand wiring in xtask, plus a new xtask::demo module with DSL, driver, script, and environment scaffolding.
  • Implement v0 local demo environment: generates a demo-only csshw-config.toml + fake-host filesystem, launches csshw, and orchestrates ffmpeg→gifski recording.
  • Add unit tests for DSL validation, driver semantics, config override output, and canonical script structure.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
xtask/src/main.rs Adds RecordDemo subcommand and dispatches into demo::record_demo.
xtask/src/demo/mod.rs Defines DemoSystem abstraction + RealSystem and the record_demo entrypoint.
xtask/src/demo/dsl.rs Adds typed Step enum + Script builder with compile-time-ish validation via build().
xtask/src/demo/driver.rs Implements step interpreter, capture lifecycle, and window wait/focus/type primitives.
xtask/src/demo/script.rs Defines canonical v0 demo script builder.
xtask/src/demo/config_override.rs Writes demo-only config, dispatcher, and fake host home trees.
xtask/src/demo/recorder.rs Implements ffmpeg capture + frame extraction + gifski encoding pipeline.
xtask/src/demo/windows_input.rs Wraps Win32 window enumeration + input injection with non-Windows stubs.
xtask/src/demo/env/mod.rs Introduces env module namespace (v0: local only).
xtask/src/demo/env/local.rs v0 local provider: prepares demo tree, copies csshw.exe, runs driver, cleans up.
xtask/src/tests/test_demo_dsl.rs Tests DSL ordering, defaults, and validation failures.
xtask/src/tests/test_demo_driver.rs Tests driver behavior with a mock DemoSystem.
xtask/src/tests/test_demo_config_override.rs Tests config/dispatcher/fakehost generation via mock writes.
xtask/src/tests/test_demo_script.rs Sanity-checks canonical v0 script build and expected step shape.
xtask/src/tests/test_demo_mod.rs Smoke-tests demo module public surface types.
xtask/Cargo.toml Adds regex dependency and Windows-only windows dependency/features for demo input APIs.
Cargo.lock Locks new dependencies (regex and transitive crates).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread xtask/src/demo/driver.rs Outdated
Comment thread xtask/src/demo/mod.rs
Comment thread xtask/src/demo/env/local.rs Outdated
@whme whme changed the title xtask: add record-demo subcommand (v0) xtask: record-demo (v0+v1: sandbox env, vendored bins, Carnac) May 9, 2026
@whme whme force-pushed the demo-automation branch from 5b72dc9 to 5909103 Compare May 9, 2026 13:16
@whme whme changed the title xtask: record-demo (v0+v1: sandbox env, vendored bins, Carnac) xtask: add record-demo subcommand (v0) May 9, 2026
Three follow-ups on the v0 record-demo subcommand surfaced during
review:

- driver: thread `out_gif` through to `StopCapture` so the produced
  GIF lives at the path the caller requested instead of always
  ending in `.gif`-derived-from-`.mkv`. Mark `state.capturing = false`
  before calling `stop_recording` (both in the step handler and the
  cleanup path) so a failing stop cannot be retried by the cleanup
  loop and double-consume the in-flight ffmpeg child.
- mod: bail in `record_demo` when `cfg!(target_os = \"windows\")` is
  false, with the same message the `windows_input` stub already uses.
  Previously non-Windows callers proceeded and failed later with
  misleading `csshw.exe`/`gdigrab` errors.
- env/local: route the `csshw.exe` existence check through a new
  `DemoSystem::file_exists` method instead of `Path::exists`, so
  `env::local::run` is fully mockable and consistent with the rest
  of the trait's side-effect surface.

GitHub: #190
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-news-fragment-needed Indicates that this PR does not require a news fragment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants