Summary
When switching chapters in a book with a pending AI proposal still open, the proposal correctly expires — but the journal entry recording that expiry gets attributed to the chapter you just switched into, not the one the proposal actually belonged to.
Found while auditing the ClioDeck wiki against the actual code (pass 15, editor & export cluster, concurrency-angle sweep).
Current behavior (verified against code)
src/renderer/src/stores/editorStore.ts's loadFile() calls set({ filePath, ... }) (writing the new chapter's path into the store) as soon as the IPC editor.loadFile call resolves — this happens before the outgoing CodeMirrorEditor view unmounts.
src/renderer/src/components/Editor/CodeMirrorEditor.tsx's effect cleanup (lines ~453-486) runs on the next render, by which point filePath in the store already holds the new chapter's path — the cleanup function is aware of this exact hazard for its own debounced-content-sync guard (if (pendingSync && useEditorStore.getState().filePath === ownFilePath), explicitly comparing against a closed-over ownFilePath to avoid writing into the wrong file) — but view.destroy(), called unconditionally right after, triggers a proposal-expiry event that has no equivalent guard.
src/renderer/src/components/Editor/proposals-ipc.ts's recordAdjudication() reads useEditorStore.getState().filePath at call time (line 36) rather than being passed the chapter path the expiring proposal actually belongs to — so the expiry event is journaled under the new chapter's path.
accept/reject/modify adjudications are unaffected, since those fire synchronously while the outgoing chapter is still the current one in the store.
Impact
The AI usage journal / research history records an inaccurate chapter attribution for expired-proposal events specifically. Low severity (doesn't lose data, doesn't affect the manuscript itself), but makes the journal's per-chapter AI-activity picture wrong for this one event type.
Suggested fix
Capture the outgoing chapter's path in a closure (the same pattern the debounced-sync guard already uses via ownFilePath) and pass it explicitly to the expiry-triggering code path, rather than having recordAdjudication() read live store state for this specific event type.
Evidence trail
src/renderer/src/stores/editorStore.ts — loadFile(), filePath write ordering.
src/renderer/src/components/Editor/CodeMirrorEditor.tsx:453-486 — cleanup function, existing ownFilePath guard pattern (correctly applied to content-sync, not to proposal expiry).
src/renderer/src/components/Editor/proposals-ipc.ts:29-42 — recordAdjudication(), reads live filePath.
- Wiki pages corrected in the same pass:
1.16-The-Editor.md, 1.15-Books-and-Chapters.md.
Summary
When switching chapters in a book with a pending AI proposal still open, the proposal correctly expires — but the journal entry recording that expiry gets attributed to the chapter you just switched into, not the one the proposal actually belonged to.
Found while auditing the ClioDeck wiki against the actual code (pass 15, editor & export cluster, concurrency-angle sweep).
Current behavior (verified against code)
src/renderer/src/stores/editorStore.ts'sloadFile()callsset({ filePath, ... })(writing the new chapter's path into the store) as soon as the IPCeditor.loadFilecall resolves — this happens before the outgoingCodeMirrorEditorview unmounts.src/renderer/src/components/Editor/CodeMirrorEditor.tsx's effect cleanup (lines ~453-486) runs on the next render, by which pointfilePathin the store already holds the new chapter's path — the cleanup function is aware of this exact hazard for its own debounced-content-sync guard (if (pendingSync && useEditorStore.getState().filePath === ownFilePath), explicitly comparing against a closed-overownFilePathto avoid writing into the wrong file) — butview.destroy(), called unconditionally right after, triggers a proposal-expiry event that has no equivalent guard.src/renderer/src/components/Editor/proposals-ipc.ts'srecordAdjudication()readsuseEditorStore.getState().filePathat call time (line 36) rather than being passed the chapter path the expiring proposal actually belongs to — so the expiry event is journaled under the new chapter's path.accept/reject/modifyadjudications are unaffected, since those fire synchronously while the outgoing chapter is still the current one in the store.Impact
The AI usage journal / research history records an inaccurate chapter attribution for expired-proposal events specifically. Low severity (doesn't lose data, doesn't affect the manuscript itself), but makes the journal's per-chapter AI-activity picture wrong for this one event type.
Suggested fix
Capture the outgoing chapter's path in a closure (the same pattern the debounced-sync guard already uses via
ownFilePath) and pass it explicitly to the expiry-triggering code path, rather than havingrecordAdjudication()read live store state for this specific event type.Evidence trail
src/renderer/src/stores/editorStore.ts—loadFile(), filePath write ordering.src/renderer/src/components/Editor/CodeMirrorEditor.tsx:453-486— cleanup function, existingownFilePathguard pattern (correctly applied to content-sync, not to proposal expiry).src/renderer/src/components/Editor/proposals-ipc.ts:29-42—recordAdjudication(), reads livefilePath.1.16-The-Editor.md,1.15-Books-and-Chapters.md.