5.8 KiB
Independent recovery publication correction review
Source acceptance for integration and fresh rerun; no remaining repair-required finding in this bounded correction. This closes F1 at the source level from the immutable 92ff review. It does not establish test or native acceptance.
Reviewed 554cfbd9fe6fd64f0eb3012bcbb3efd7a95f7424, exact parent 92ff99eb54b75a67c6320cad93d11790413bbd8d. Verified clean .local/tu at that pin, the entire two-file diff (+166/-7), and both committed blob IDs/byte counts/SHA-256 values against the author's source manifest. No source edits, compiler, GUI, or test execution occurred.
Production repair
At crates/oa-desktop/src/app.rs:3395–3403, SourceRecovered captures whether the previous diagnostic publication matches the retained source key before filtering pending_save. It republishes only when that precondition was true. Thus a stale publication stays stale, absence stays absent, and current retained warnings acquire a new generation that rejects queued old actions.
The fix does not clear valid warnings as a shortcut, mint authority from span validity, or recompute diagnostics over unrelated disk bytes. Subsequent unapplied reload, applied draft retention, previous-Save-As target handling, and reload-error branches are unchanged. A later destination/session reset uses its existing freshness check, so this guard does not make stale warnings current indirectly through that reset. Filtering the retained vector under a stale generation is safe here: actions and annotations remain rejected by the mismatching source key. The new generation on a current publication also causes annotation/gesture identity to change when the displayed vector changes.
Test discrimination and limits
The two new exact declarations are:
app::text_workspace::lifecycle_tests::recovery_retains_current_warning_authority_with_drafts_and_reload_failureapp::text_workspace::lifecycle_tests::recovery_does_not_republish_stale_warnings_after_workspace_key_departure
Each exercises successful reload and an explicitly injected receipt.disk = Err(...). The fixture creates a private temporary source project and the real journal format, reads the actual pending-save target, dispatches normal SourceRecover, calls recover_snapshot, and delivers its matching receipt. Source inspection confirms that worker invokes recover_project and then load_project. App-returned asynchronous tasks are deliberately not polled. The error arm is a deterministic receipt-branch test after successful journal recovery, not evidence of an actual operating-system reload failure.
Both controls include newer applied-workspace drafts. The positive publishes the warning after that draft, so the warning is current; the negative publishes before Workspace::edit advances the key, preserving the old diagnostic vector while awaiting lint. This directly distinguishes the formerly unconditional republish: at 92ff the negative's publication-equality, missing-annotation and unavailable-action assertions fail. A blanket clear/revoke workaround fails the positive's retained warning, annotation and fresh primary/related navigation assertions.
The tests also require exact restored disk bytes, removed journal and pending-save notice, consumed recovery ownership, unchanged source key/cursor/drawer visit/raw bundle, exact accepted Project Arc, revision and Undo/Redo state. Captured old primary and related actions must remain inert before interaction/reveal changes; freshly captured current primary and related actions must navigate. The stale test's newly generated action check covers the primary location, while its captured-action loop covers both primary and related locations; both use the same publication admission. No test was removed or ignored.
This is a focused applied-workspace/key-departure test rather than an end-to-end Create File/Close File or OS failure test. No-publication and unapplied-reload branches receive no new dedicated test, but the simple Option guard handles absence and leaves the existing reload branch intact. These are scope limits, not additional repair requirements identified by this review.
Required gates
Run both new cases and the retained app::text_workspace::lifecycle_tests suite, diagnostic/publication/gesture and local-boundary tests on the exact corrected source. Retain the root recovery regressions: recovery_open_finds_first_save_journal_without_an_entry_file, recovery_after_first_save_failure_retains_target_then_retries_without_reentering_path, recovery_target_survives_edit_lint_apply_and_rejected_recovery_until_success, and recovery_of_previous_save_as_target_does_not_redirect_current_project.
The combined Text union still needs fresh actual listing, relevant focused/full/strict gates, core raw-include tests, vendor/notice/package input binding, and native interaction verification described in the original review. This correction adds exactly two authored desktop tests to the prior inventory; it does not convert lexical counts to executed outcomes. All tests remain UNCOMPILED/UNRUN in this review.
Evidence bindings
- Handoff:
text-recovery-publication-fix-handoff-20260913.md, SHA-256c19bd1573e1579df012ec4cff0a03939b8866f282f51b6dba2b8234d86f5f036. - Exact source manifest:
text-recovery-publication-fix-source-manifest-20260913.json, SHA-256b90d42d23601ab415afb5dde1406d66f6ae1c2af0a2076184a870bddb12e741b. - Exact diff:
text-recovery-publication-fix-20260913.patch, SHA-25635b5b224a50aa7e3e7e1b535388e5e7239669719c0fcba8fcacae5323e80442c. app.rsblob1e80ec40bb23c6c205fb79f5fb9300319a3996da, 252446 bytes, SHA-256d96da53d9d27a32af4a0efa587dc9eded3eb4e8be3abe4eb254c42b0f7d937f1.app/text_workspace/lifecycle_tests.rsblob53b62878f2a6f44a5faf2b9981d243b1dac86d8c, 23909 bytes, SHA-256077cb8592297ef04c506abf8a97773cee142078def0ea5428fb859356c7b7206.