Skip to content

fix(cli): add snapshot --no-clean to stop wiping prior output#1891

Open
miguel-heygen wants to merge 1 commit into
mainfrom
fix/snapshot-keep-existing-flag
Open

fix(cli): add snapshot --no-clean to stop wiping prior output#1891
miguel-heygen wants to merge 1 commit into
mainfrom
fix/snapshot-keep-existing-flag

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Summary

Two independent reports of the same friction: snapshot unconditionally deletes existing .png/.jpg files in the output directory before capturing new ones, so running it twice with different --at sets clobbers the first run's output with no way to opt out — "each run wipes snapshots/, so two consecutive runs for different timestamp sets clobber each other."

(The same report also mentioned snapshot "silently rounds requested timestamps" and "appends an unrequested extra frame" — both turned out to already be intentional, documented, tested behavior: computeSnapshotTimes/tailFrameTime round to millisecond precision deliberately and always append a readable end-of-timeline frame to avoid a known blank-at-exact-duration artifact, with an existing --no-end flag to opt out. Only the directory-wipe had no equivalent opt-out.)

Fix

Extracted the wipe into cleanSnapshotDir(), matching this file's existing computeSnapshotTimes/tailFrameTime extraction pattern so it's unit-testable directly, and gated it behind a new --clean flag (default true, preserving current behavior) — following the exact same default-true/--no-x-to-opt-out convention already established by --end/--no-end in this same command.

Test plan

  • bunx vitest run packages/cli/src/commands/snapshot.test.ts — 12 tests pass (9 existing + 3 new)
  • New tests: cleanSnapshotDir deletes existing image files, only removes image files (leaves e.g. descriptions.md untouched), and doesn't throw on a nonexistent directory
  • bun run build — full monorepo typecheck passes

Every `snapshot` run unconditionally deleted existing .png/.jpg files in
the output directory before capturing new ones, so two consecutive runs
with different --at sets clobbered each other with no way to opt out.

Extracted the wipe into cleanSnapshotDir(), matching the existing
computeSnapshotTimes/tailFrameTime extraction pattern in this file so it's
unit-testable directly, and gated it behind a new --clean flag (default
true, preserving current behavior) following the same default-true/
--no-x-to-opt-out convention already established by --end/--no-end.
@miguel-heygen miguel-heygen marked this pull request as ready for review July 4, 2026 20:16

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed at 43de93a.

🟢 Approve — well-scoped --no-clean opt-out for snapshot

What's right:

  • Fix matches the report cleanly: two consecutive snapshot runs with different --at sets no longer clobber each other; existing default (--clean=true) preserves current wipe behavior for the single-run case.
  • Convention alignment with the neighboring --end / --no-end flag in the same command — default-true + --no-x-to-opt-out — keeps the CLI feel consistent. Extracted cleanSnapshotDir() mirrors the sibling computeSnapshotTimes / tailFrameTime extraction pattern for direct unit-testability.
  • Test coverage geometry is exactly right for the extraction:
    • Deletes images ✓
    • Preserves non-image files (descriptions.md) — critical, since that file is emitted alongside snapshots by the describe mode ✓
    • No-throw on missing dir ✓
  • Extension regex \.(png|jpg|jpeg)$ matches the pre-PR inline regex verbatim — no scope change (won't accidentally delete .webp / .gif etc., but also won't accidentally start deleting them either — parity with existing behavior).
  • New --clean boolean flag threading (command → captureSnapshots opts → cleanSnapshotDir call site) is complete: args.clean !== false gate at snapshot.ts:657 matches the same pattern used for args.end !== false two lines up.

Minor observations:

  • nit — the --no-clean docstring on the flag is good, but if two runs write different --at frames to the same dir, they'll interleave with different frame-NN-at-… prefixes and the contact-sheet.jpg gets regenerated per-run overwriting the last. Might be worth noting in the docstring that the contact sheet is per-run even with --no-clean. Not a blocker.
  • nitcleanSnapshotDir's catch-all try { … } catch { } (best-effort) silently swallows anything other than ENOENT (permission errors on a real dir, for instance). Preserves the pre-PR behavior (the inline block did the same), so no regression, but the JSDoc could mention "swallows all errors" for the next reader.

Reviewed by AI Employee Rames D Jusso — bot-authored PR posture: comment-only, stamps require James Russo's explicit go-ahead.

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 43de93a.

🟢 R1 verdict — LGTM, co-signing Rames

Independently verified: --clean default-true with --no-clean opt-out follows the same convention as sibling --end/--no-end; extracted cleanSnapshotDir is unit-testable with the same shape as sibling computeSnapshotTimes/tailFrameTime; args.clean !== false gate at the call site is symmetric with args.end !== false two lines up. Rames covered the convention alignment, extension-regex parity, test-geometry, and best-effort-swallow nuance — all reproduce independently.

Runtime-interop cross-PR notes (none blocking)

  • vs #1913: different directories (snapshots/ vs browsers/), different processes. No overlap.
  • vs #1881: both touch snapshot.ts. --no-clean and repeated---at detection are orthogonal — countRepeatedAtFlag inspects process.argv and warns; --clean is a citty-parsed boolean flag threaded through opts. Merge-order independent, no cross-PR line conflict.

Rames flagged two nits (contact-sheet regen even under --no-clean, silent error-swallow in cleanSnapshotDir's catch). Both are pre-PR behaviors preserved verbatim — I agree with his non-blocking classification.

Fine to merge from a runtime-interop perspective.


Review by Via (runtime-interop lens) — co-signing Rames

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants