CURRENT CONTROL-PLANE CHECKPOINT — 2026-10-04 — SEQUENCE CORRECTION
CURRENT_MAIN = 8f1400c4bb6a84be8da940584b86b00233df1d4b
PR_957 = MERGED / Slice A
POST_#957_RETENTION = NOT COMPLETE
PR_958 = OPEN / docs-only Slice-B gap matrix
PR_958_HEAD = 0eac3d4c10ddd854eee9027db038f55899b66caf
SEQUENCE_DRIFT = YES — #958 opened before #957 retention terminal
PR_958_PROGRAM_STATE = FROZEN
MERGE_#958 = NO
FURTHER_SLICE_B_IMPLEMENTATION = NO
NEXT = finish #957 retention first → then resume #958
PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO
HISTORICAL / SUPERSEDED — previous control-plane checkpoint
CURRENT_MAIN = 2001e63f3b7b1fbf2faa0270238f0de43ba84db1
PR_953 = MERGED / Successor A read-snapshot slice landed
PR_954 = OPEN / DRAFT / MERGEABLE
PR_954_HEAD = 93384b5a71f8bed4f2cde1428b762d2db0b13790
PR_954_BASE = 2001e63f3b7b1fbf2faa0270238f0de43ba84db1
PR_954_SCOPE = Successor B — Mutation / Root-Recovery / Lifecycle Closure
PR_954_SIZE = 16 files / 2 commits / +2121 -100
CODEANT = Quality / Coverage / SCR / SAST / SCA SUCCESS exact head
CODERABBIT = status SUCCESS, but Draft PR auto-review is disabled; do not call review clean
DEEPSOURCE = Python / Shell / Docker SUCCESS; review grade A; Rust status not inferred
VERCEL_PREVIEW = dpl_2JuoEypL2STM2hXvq9ESVa1Gmz9N BUILDING exact head
CI_CD = IN PROGRESS / not yet terminally proven here
CODEQL = not yet terminally proven here
REVIEWS = no submitted reviewer epoch yet
UNRESOLVED_REVIEW_THREADS = 0 at this checkpoint
PR_952 = OPEN / DRAFT / FROZEN PROVENANCE — DO NOT MERGE
GATE_4B = ACTIVE
4C / 4D / 4E = PENDING
PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO
Successor B is correctly based on the #953 resulting main and carries the semantic A+B union: #953 SessionBinding/rebind semantics plus the mutation/root-recovery/lifecycle mechanisms extracted from frozen #952. The prepared-root Step-F same-instance recovery R1–R5 proof remains this PR's material closure scope.
Do not close Gate 4B, #922, #445, or advance #359/4D until #954 converges on its exact final head, merges normally, and the exact resulting-main CI/CD + CodeQL + Vercel Production gate is terminal.
HISTORICAL / SUPERSEDED — predecessor update — 2026-10-03
PR #953 successor A is now on final-correction head 12cf7833822c453aeba1fb2156ba06301c6172c6. This still does not admit Gate 4D. The prepared-root coordinator-recovery Critical remains successor-B scope; #359 remains gated behind complete 4B and 4C.
LIVE PREDECESSOR NOTE — 2026-10-03
PR #953 is the live Gate 4B successor A (Read/Snapshot Admission Closure), head 590bdb868054648a82edad3a61680add5a876e07. This does not admit 4D. The prepared-root coordinator-recovery Critical remains successor-B scope; #359 remains gated behind complete 4B and 4C.
CURRENT EXECUTION HOLD — 2026-10-03 — 4D NOT PULLED FORWARD
OWNER = Gate 4D / WorldScript-Studio#359 / QNB-11
CURRENT_GATE_4_WORK = 4B superseding split
PR_952 = frozen at e34aae28bba31269a814a9a2778346e568c3577e
FIRST_SUCCESSOR = Read/Snapshot Admission Closure
SECOND_SUCCESSOR = Mutation/Root-Recovery/Lifecycle Closure
4C = after complete 4B
4D = after 4C
PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO
The newly validated prepared-root Step-F recovery gap does not pull this issue forward. That gap is ordinary-operation/root-publication coordinator recovery and belongs to Gate 4B successor B, using the existing authenticated recover_root primitive. This issue remains the later crash-resumable rotation/rekey owner and must not be used to import journal/rekey state-machine scope into the current 4B split.
The current resource-bounded coding-agent run stops after the first successor PR reaches terminal resulting-main state; it must not begin this issue.
CURRENT R-15 RECONCILIATION — 2026-10-02
#359 is no longer blocked on Gate 3. It remains open and sequenced to Gate 4 slice 4D, where it also closes the Gate-3-carried key-epoch crash window.
HISTORICAL / SUPERSEDED — CURRENT R-15 RECONCILIATION — 2026-10-01 — superseded 2026-10-02
OWNER = Gate 4 / #922
PREDECESSOR = Gate 3 terminal
REQUIRED_FIX = authenticated journal/checkpoints + crash resume/recovery + exact replay semantics
PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO
The historical Tauri migration defect below remains valid provenance, but the implementation destination is now renderer-neutral Gate 4. Do not add a second Tauri-only migration authority; Gate 4 owns the shared journal/state machine and cross-process transition semantics.
Summary
services/fs/fsEncryptionMigration.ts#migrateAllProtectedFsData (added to fix the disable/rotate/set data-stranding bugs — see the CHANGELOG entries for those fixes) re-keys every fs-backed protected file directly, one file at a time, with no persistent journal, checkpoint, or resumable cursor. If the desktop process is killed (crash, forced quit, power loss) partway through a passphrase rotation, the result can be a mixed-key filesystem state: some files already re-encrypted under the new key, others still under the old one — while the durable IDB sentinel (which determines which key gets derived on next unlock) has not yet been updated, since rotateIdbPassphrase() runs after the fs bridge completes.
Why this is safer than it sounds, but still a real gap
- The app's protected-read paths (
unprotectTextValue, getApiKey, etc.) already fail closed and preserve (never discard) a file that fails to decrypt under the current key — a mixed-key state does not crash the app or silently destroy data outright. A file re-keyed by the interrupted migration will simply read as "temporarily unreadable," the same as a locked session.
- There is currently no way for the user or the app to know which specific files were re-keyed before the crash, and no automated path back to a fully-consistent state — recovering requires manual intervention (or, in the worst case, re-entering the old passphrase and treating some files as permanently stuck).
- This is architecturally weaker than the IDB migration path, which already has a full journal/phase state machine (
services/storage/encryptionMigrationJournal.ts, encryptionMigrationOrchestrator.ts) with per-store checkpoints, a resumable cursor, and a recovery-required terminal state surfaced by EncryptionRecoveryModal.
Interim mitigation shipped alongside this issue
migrateAllProtectedFsData now writes a durable marker file (config/fs-migration-marker.json) before starting and deletes it only on successful completion. On next startup, FsCore.initialize() detects a stale marker and surfaces a status notification warning the user that a previous encryption migration did not finish and some files may be in a mixed key state — an honest "stuck state detected" signal rather than silence, but not automated recovery.
Real fix
Give the fs bridge the same journal/checkpoint architecture the IDB path already has: a durable per-file inventory + status (pending/done), a source/target key epoch, a resumable cursor, and a commit marker — so an interrupted rotation can resume exactly where it left off (or be cleanly rolled back) instead of requiring manual recovery. Ideally this should be a shared state machine coordinating both IDB and fs stores under one journal, since a rotation logically spans both.
Found via
Surfaced during the PR #356 (fix/desktop-project-data-encryption) review-correction loop, external assessment of the migration bridge's crash-safety properties (2026-08-13).
CURRENT CONTROL-PLANE CHECKPOINT — 2026-10-04 — SEQUENCE CORRECTION
HISTORICAL / SUPERSEDED — previous control-plane checkpoint
Successor B is correctly based on the #953 resulting main and carries the semantic A+B union: #953 SessionBinding/rebind semantics plus the mutation/root-recovery/lifecycle mechanisms extracted from frozen #952. The prepared-root Step-F same-instance recovery R1–R5 proof remains this PR's material closure scope.
Do not close Gate 4B, #922, #445, or advance #359/4D until #954 converges on its exact final head, merges normally, and the exact resulting-main CI/CD + CodeQL + Vercel Production gate is terminal.
HISTORICAL / SUPERSEDED — predecessor update — 2026-10-03
PR #953 successor A is now on final-correction head
12cf7833822c453aeba1fb2156ba06301c6172c6. This still does not admit Gate 4D. The prepared-root coordinator-recovery Critical remains successor-B scope; #359 remains gated behind complete 4B and 4C.LIVE PREDECESSOR NOTE — 2026-10-03
PR #953 is the live Gate 4B successor A (Read/Snapshot Admission Closure), head
590bdb868054648a82edad3a61680add5a876e07. This does not admit 4D. The prepared-root coordinator-recovery Critical remains successor-B scope; #359 remains gated behind complete 4B and 4C.CURRENT EXECUTION HOLD — 2026-10-03 — 4D NOT PULLED FORWARD
The newly validated prepared-root Step-F recovery gap does not pull this issue forward. That gap is ordinary-operation/root-publication coordinator recovery and belongs to Gate 4B successor B, using the existing authenticated
recover_rootprimitive. This issue remains the later crash-resumable rotation/rekey owner and must not be used to import journal/rekey state-machine scope into the current 4B split.The current resource-bounded coding-agent run stops after the first successor PR reaches terminal resulting-main state; it must not begin this issue.
CURRENT R-15 RECONCILIATION — 2026-10-02
#359 is no longer blocked on Gate 3. It remains open and sequenced to Gate 4 slice 4D, where it also closes the Gate-3-carried key-epoch crash window.
HISTORICAL / SUPERSEDED — CURRENT R-15 RECONCILIATION — 2026-10-01 — superseded 2026-10-02
The historical Tauri migration defect below remains valid provenance, but the implementation destination is now renderer-neutral Gate 4. Do not add a second Tauri-only migration authority; Gate 4 owns the shared journal/state machine and cross-process transition semantics.
Summary
services/fs/fsEncryptionMigration.ts#migrateAllProtectedFsData(added to fix the disable/rotate/set data-stranding bugs — see the CHANGELOG entries for those fixes) re-keys every fs-backed protected file directly, one file at a time, with no persistent journal, checkpoint, or resumable cursor. If the desktop process is killed (crash, forced quit, power loss) partway through a passphrase rotation, the result can be a mixed-key filesystem state: some files already re-encrypted under the new key, others still under the old one — while the durable IDB sentinel (which determines which key gets derived on next unlock) has not yet been updated, sincerotateIdbPassphrase()runs after the fs bridge completes.Why this is safer than it sounds, but still a real gap
unprotectTextValue,getApiKey, etc.) already fail closed and preserve (never discard) a file that fails to decrypt under the current key — a mixed-key state does not crash the app or silently destroy data outright. A file re-keyed by the interrupted migration will simply read as "temporarily unreadable," the same as a locked session.services/storage/encryptionMigrationJournal.ts,encryptionMigrationOrchestrator.ts) with per-store checkpoints, a resumable cursor, and arecovery-requiredterminal state surfaced byEncryptionRecoveryModal.Interim mitigation shipped alongside this issue
migrateAllProtectedFsDatanow writes a durable marker file (config/fs-migration-marker.json) before starting and deletes it only on successful completion. On next startup,FsCore.initialize()detects a stale marker and surfaces a status notification warning the user that a previous encryption migration did not finish and some files may be in a mixed key state — an honest "stuck state detected" signal rather than silence, but not automated recovery.Real fix
Give the fs bridge the same journal/checkpoint architecture the IDB path already has: a durable per-file inventory + status (pending/done), a source/target key epoch, a resumable cursor, and a commit marker — so an interrupted rotation can resume exactly where it left off (or be cleanly rolled back) instead of requiring manual recovery. Ideally this should be a shared state machine coordinating both IDB and fs stores under one journal, since a rotation logically spans both.
Found via
Surfaced during the PR #356 (
fix/desktop-project-data-encryption) review-correction loop, external assessment of the migration bridge's crash-safety properties (2026-08-13).