Skip to content

fix(recording): reopen NeedsRemux Studio projects and make recovery publication transactional - #4242

Open
richiemcilroy wants to merge 10 commits into
mainfrom
fix/studio-recovery-needsremux
Open

richiemcilroy wants to merge 10 commits into
mainfrom
fix/studio-recovery-needsremux

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

Fixes #4135.

Since 0.6.3, opening a Studio project whose recording-meta.json says NeedsRemux (the remux after stop never ran or never finished, typically because Cap was closed while finishing) no longer opens it the way 0.6.2 did. On Windows a failed attempt can also leave the project with its fragments moved into .recovery-<uuid>/, after which every open fails with "No video was saved" or "Failed to open video".

Root cause

Two separate defects stacked up:

  1. Open path. fix(recording): interrupted Studio recordings open again, and say what was kept when they cannot #3701 made every editor open of a NeedsRemux project run RecoveryManager::salvage first (apps/desktop-gpui/src/library.rs, recover_interrupted_for_editing_in). 0.6.2 opened those projects straight from their fragments. Salvage copies the whole recording, remuxes it and swaps it in before the editor appears, so any failure blocks a project that opened fine before, and success rewrites it on disk.
  2. Publication was not transactional. Publishing a recovery renames segments out, renames the rebuilt segments in, then replaces config and metadata (the commit point). On Windows a rename can be refused while antivirus or the indexer holds a just-written file. If a later rename and the rollback were refused (or the app was killed between renames), the next lock's reconcile classified "rebuilt segments installed, original metadata still in place" as acceptable and deleted the journal. That leaves metadata pointing at content/segments/segment-0/display/ (now empty or missing) while the real fragments sit in .recovery-<uuid>/original-segments. Nothing ever moved them back.
stateDiagram-v2
    [*] --> Original
    Original --> SegmentsOut: rename 1
    SegmentsOut --> Installed: rename 2
    Installed --> ConfigInstalled: rename 3
    ConfigInstalled --> Committed: rename 4 (metadata)
    SegmentsOut --> Original: reconcile (unchanged)
    Installed --> Original: reconcile (now rolls back, was kept)
    ConfigInstalled --> Original: reconcile (now rolls back, was kept)
Loading

What changed

  • recover_interrupted_for_editing takes an opens_as_is check (the editor's own preflight). A NeedsRemux project the editor can read opens untouched, exactly as in 0.6.2. Only one it cannot read goes through salvage. InProgress, Failed and active-recording handling is unchanged.
  • Reconcile treats the metadata replace as the commit point: an installed but uncommitted publication rolls back (config first, then segments) to the original layout. Every intermediate state is one reconcile already accepts, so a kill during rollback resumes on the next lock.
  • rename_recovery_path retries sharing and access refusals on Windows for up to 5 s, for publication, rollback and reconcile alike (same error codes RecordingMeta already retries).
  • A publication that rolled back cleanly removes its staging copy instead of leaving a full copy of the recording behind.
  • restore_unpublished_originals repairs projects older builds already stranded: when exactly one .recovery-* backup belongs to the current metadata (byte for byte, or differing only in a Failed status a later scan wrote) and the project is missing any of its files, the original metadata, config and segments move back. Whatever the project held moves to <workspace>/displaced-segments; nothing is deleted. It runs under the recovery lock and before deciding how to open a recording. Each step is idempotent, so a kill mid-restore finishes on the next open.

Callers checked

  • reconcile_recovery_publication runs on every RecoveryLock::acquire: library recovery scan (find_incomplete, find_incomplete_single), remux_if_needed and remux_stopped_with_preparing at stop, recover / salvage / finalize*, retirement, and the new restore_interrupted_recovery. acquire_for_preparing_preview does not reconcile and is unchanged.
  • rename_recovery_path is used by publish_recovery and by rename_recovery_path_with_source for every rename other than the relocatable source move.
  • recover_interrupted_for_editing has one caller, the ordinary editor open in app_windows.rs.
  • A committed publication (metadata replaced) is never rolled back or restored: its metadata no longer matches the backup's, which restoration_leaves_settled_and_repaired_projects_alone pins, including the backup kept for a preparing editor.

Tests

crates/recording/src/recovery/needs_remux_tests.rs builds projects with the real segmented encoder, laid out as every release since 0.6.0 writes them (the manifest writer's fields are unchanged since cap-v0.6.0):

  • NeedsRemux with 1, 999, 1000, 1356 and 5000 fragments: manifests validate, fragments stay in index order past segment_999, and the remux matches a reference remux of the untouched fragments. Paths contain spaces, non-ASCII and Copy (2), and are canonical, so Windows runs them as \\?\ paths.
  • Partial last fragment, missing last fragment, missing manifest, torn middle fragment (refused, named in the error, nothing changed), metadata missing.
  • Publication killed after each rename, rollback killed after each rename, publication failing at each rename, Windows refusing the rollback (finished by the next open).
  • Stranded by an older build: segments installed, config installed, segments missing, status marked Failed, empty display/ left over, a second attempt's backup, read-only media; restore killed at each step; settled, hand-repaired and foreign backups left alone.
  • Two opens at once, full disk while staging.
  • apps/desktop-gpui/src/library.rs: a NeedsRemux project the editor can read opens with every file unchanged; one it cannot read fails salvage with nothing changed and no copy left; a stranded project is restored before the editor checks it.

Existing expectations that kept a half-installed publication now expect the rollback.

Proof (Windows Server 2025, NVIDIA T4)

Projects recorded with the released 0.6.2 cap-cli and stopped the moment it wrote NeedsRemux: 29 fragments (59 s) and 1,418 fragments (51 min 39 s, past 999 like the report).

0.6.3 release This PR
1,418-fragment NeedsRemux still loading after 60 s while it copies and remuxes the whole recording opens in under 15 s, all 1,428 files byte-identical, no .recovery-*
0.6.3 fix
29-fragment NeedsRemux rewritten on open (Complete, originals parked in .recovery-*) opens, all 39 files byte-identical
Stranded layout (real 0.6.3 output installed, empty display/, originals in the backup) salvages the rebuilt copy again, originals stay stranded restored to the original layout (0 differences), opens; rebuilt output kept in displaced-segments
Same, status also marked Failed restored, status back to NeedsRemux, opens
stranded

A copy of the 1,418-fragment project that 0.6.3 was killed on mid-recovery also opens with this PR, media and metadata unchanged.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; both previous findings are fixed and no new actionable issues remain.

Summary

This PR lets readable NeedsRemux projects open without rewriting them and makes interrupted recovery return to the original files.

  • Readable NeedsRemux projects open without being rewritten.
  • Uncommitted remuxes return the project to its original files.
  • Older stranded projects get their original recordings back.

Reviews (2) · Last reviewed commit: "fix(recording): refuse to restore throug..." · Reviewed by Greptile

@vercel

vercel Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
cap-status Ignored Ignored Preview Nov 14, 2026 7:28am UTC

@richiemcilroy
richiemcilroy marked this pull request as ready for review November 14, 2026 07:16
@sandchest-com

sandchest-com Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Sandchest

Sandchest reviewed 6b9bf1c: nothing exploitable found in this change.

Sandchest reviews every change for vulnerabilities an attacker could use · 9 credits · Review details

@capy-ai capy-ai Bot 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.

Capy found 2 potential issues (1 high, 1 medium).

View 2 other findings in Capy.

Open in Capy

Comment thread crates/recording/src/recovery.rs Resolved
Comment thread crates/recording/src/recovery.rs Resolved
Comment thread crates/recording/src/recovery.rs Resolved
Comment thread crates/recording/src/recovery.rs Resolved

@capy-ai capy-ai Bot 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.

Capy found no issues.

Open in Capy

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

This branch was successfully deployed

1 active (outdated) deployment
Preview – cap-web — 3a0163f5 Deployed Nov 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Projects can't be opened anymore in studio since updating "This recording could not be opened No video was saved before this recording stopped"

1 participant