T-C4B1-vault-writer-task-exports-sessions
State: planning/draft · Impact: high · Complexity: large
Make the export fire for every user, not only on the skip-journal focus branch and the tracked End, and do it off the reducer and IPC tasks. One owned writer serializes all vault writes, so the completion and journal events, which reach the runner from different tasks, cannot race.
| Location | Role today |
|---|---|
apps/pumice/src-tauri/src/engine/handle.rs | session_row builds a VaultWrite on the JSAC-off focus completion (run_deadline_fired) and on every tracked End (run_end, which reads the config itself); CompletionContext carries template and vault_path |
apps/pumice/src-tauri/src/runner/handlers/completion_side_effects.rs | renders and writes inline on the runner task during event dispatch |
apps/pumice/crates/pumice/src/timer/mod.rs | TimerEngine::ended_at is the End-instant rule (paused_at while paused) the end_time column must follow |
apps/pumice/src-tauri/src/runner/mod.rs | dispatch_event awaits handlers sequentially; apply has no lock across callers |
apps/pumice/src-tauri/src/api/v1/journal.rs | pure DB write plus JournalUpdated event |
apps/pumice/src-tauri/src/schema/timer_session.rs | has title; no end_time, no note_written_at |
apps/pumice/src-tauri/src/persistence/migrations/ | sea-orm migrations |
apps/pumice/src-tauri/tests/engine_deadline_and_display.rs | asserts CompletionSideEffects fires on natural completion |
apps/pumice/src-tauri/src/engine/handle.rs tests end_tracked_* | assert the tracked End’s VaultWrite shape and its JSAC interplay |
Proposed
Section titled “Proposed”A migration adds nullable end_time and note_written_at. vault_write
leaves CompletionSideEffects; template and vault_path leave
CompletionContext. A VaultNoteHandler maps SessionCompleted and
JournalUpdated to WriteRequests on a bounded channel. A writer task
spawned at startup coalesces per session id, applies the timing rules (defer
under journal-after-focus for focus and tracked intervals until journal submit
or 120 s, since End requests the journal for both; write immediately
otherwise; honour include_breaks and include_cancelled), reads the
committed row with Store::get_timer_session, writes through vault-note
inside spawn_blocking (a note plus daily line, or one daily entry, per
write_mode; in daily-list mode a missing daily note is created), stamps
note_written_at, and emits vault:note_written or vault:note_failed
through the app handle. A startup sweep writes any completed session with
note_written_at IS NULL older than the window. crates/pumice/src/vault.rs
and the tera dependency are deleted.
Approach
Section titled “Approach”- Migration and conversion for the two columns; populate
end_timefromended_at. - Remove the reducer-side vault plumbing from
run_deadline_firedandrun_end;session_rowgoes with it. Retarget theend_tracked_*and deadline tests at the writer. - Writer task, channel, debounce table, sweep.
- Handler registration in
lib.rs; event emission through the bridge. - Tempdir end-to-end tests: default-settings completion then journal; skip-journal completion; two events from two tasks; sweep after a missed debounce.
- Delete
vault.rsandtera.
| Area | Note |
|---|---|
apps/pumice/src-tauri/src/persistence/migrations/ | new migration |
apps/pumice/src-tauri/src/persistence/db/conversions/timer_session.rs | end_time, note_written_at |
apps/pumice/src-tauri/src/schema/timer_session.rs | two columns |
apps/pumice/src-tauri/src/engine/handle.rs | drop vault plumbing |
apps/pumice/src-tauri/src/runner/handlers/completion_side_effects.rs | banner only |
apps/pumice/src-tauri/src/runner/handlers/vault_note.rs | handler |
apps/pumice/src-tauri/src/vault_writer.rs | writer task |
apps/pumice/src-tauri/src/lib.rs | spawn and register |
apps/pumice/crates/pumice/src/vault.rs | superseded |
apps/pumice/crates/pumice/Cargo.toml | drop tera |
apps/pumice/src-tauri/tests/engine_deadline_and_display.rs | updated assertions |
apps/pumice/src-tauri/tests/vault_export.rs | end-to-end |
Acceptance criteria
Section titled “Acceptance criteria”- Under default settings, one note and one daily line exist after
journal submit; none exist before it. The same holds for a tracked
End, and its note carries
pumice_title. -
SessionCompletedandJournalUpdatedfired back-to-back from two tasks yield one note and one daily line. - No
std::fscall executes on the reducer or IPC task (asserted by a test that blocks the writer and confirms the IPC returns). - A write failure emits
vault:note_failedand leavesnote_written_atnull. - With the vault folder absent, writes fail as
vault-missing, and the startup sweep writes them once the folder is back. -
moon run pumice:codegen-drift,pumice:check,pumice:testgreen.
Out of scope
Section titled “Out of scope”- Backfilling past sessions (T-20QT-vault-backfill-past-sessions); this task only leaves the writer id-keyed and idempotent.
- Frontend ops and UI (T-985N-vault-ontogen-ops, T-TJCU-vault-settings-ungated-open-note).
- Notification action buttons (T-0MYM-completion-notification-open-note-action).