You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Both journals — the research journal (brain.db, via HistoryService) and the AI usage journal (journal.db, via usageJournalService) — can have an event misattributed to the wrong project if a project switch completes while a journal-writing IPC call is still in flight. This is the same "re-reads live singleton state after an await" pattern already found in issues #33 (Zotero), #34 (Tropy), #35 (Obsidian), and #39 (MCP), applied here to journal writes specifically.
Found while auditing the ClioDeck wiki against the actual code (pass 17, LLM & analysis cluster), as a targeted check of whether the two journals share this pattern.
Current behavior (verified against code)
src/main/services/history-service.ts's HistoryService holds a single this.historyManager, replaced whenever a new project loads. getHistoryManager() always returns the current value — there is no per-call capture of which project's manager should receive a given event.
src/main/ipc/handlers/project-handlers.ts:55,116 calls await historyService.init(projectPath) as part of loading a new project — a real async operation (closing the previous HistoryManager, opening a new SQLite connection for the new project).
src/main/ipc/handlers/proposal-handlers.ts's proposals:adjudication handler calls historyService.logProposalAdjudication(...) and usageJournalService.recordAdjudication(...) directly against whatever manager is current at the moment the IPC handler executes — not necessarily the project that was open when the renderer originally sent the event.
Because IPC messages are dispatched asynchronously and historyService.init() involves real I/O, there is a genuine window where a proposals:adjudication message for the outgoing project (e.g., the proposal-expiry event already known to misattribute across chapters — issue AI-proposal expiry journal entry misattributed to wrong chapter after switch #31) can be processed by the main process after a concurrent project:load has already swapped historyService's internal manager to the new project — landing the event in the wrong project's journal(s).
Impact
Low-to-moderate severity: no data corruption in the manuscript or bibliography, but the AI usage journal / research journal's per-project activity record can be inaccurate — an event that happened in Project A gets recorded under Project B. Same severity class as #35/#39 (audit-trail/logging misattribution, not data loss).
Suggested fix
Capture the target project's HistoryManager/usage-journal-store reference at the moment the renderer event was generated (or at least at the start of the IPC handler, before any concurrent project:load can complete), rather than reading the live singleton state when the handler happens to run. Alternatively, tag each journal-write IPC payload with the project path it was generated for, and have the handler route to the correct manager (or drop the event) if it no longer matches the current project.
Evidence trail
src/main/services/history-service.ts — HistoryService, single mutable historyManager field, getHistoryManager().
src/main/ipc/handlers/project-handlers.ts:55,116 — await historyService.init(projectPath), the async transition window.
src/main/ipc/handlers/proposal-handlers.ts — proposals:adjudication handler, reads live manager state.
Summary
Both journals — the research journal (
brain.db, viaHistoryService) and the AI usage journal (journal.db, viausageJournalService) — can have an event misattributed to the wrong project if a project switch completes while a journal-writing IPC call is still in flight. This is the same "re-reads live singleton state after an await" pattern already found in issues #33 (Zotero), #34 (Tropy), #35 (Obsidian), and #39 (MCP), applied here to journal writes specifically.Found while auditing the ClioDeck wiki against the actual code (pass 17, LLM & analysis cluster), as a targeted check of whether the two journals share this pattern.
Current behavior (verified against code)
src/main/services/history-service.ts'sHistoryServiceholds a singlethis.historyManager, replaced whenever a new project loads.getHistoryManager()always returns the current value — there is no per-call capture of which project's manager should receive a given event.src/main/ipc/handlers/project-handlers.ts:55,116callsawait historyService.init(projectPath)as part of loading a new project — a real async operation (closing the previousHistoryManager, opening a new SQLite connection for the new project).src/main/ipc/handlers/proposal-handlers.ts'sproposals:adjudicationhandler callshistoryService.logProposalAdjudication(...)andusageJournalService.recordAdjudication(...)directly against whatever manager is current at the moment the IPC handler executes — not necessarily the project that was open when the renderer originally sent the event.historyService.init()involves real I/O, there is a genuine window where aproposals:adjudicationmessage for the outgoing project (e.g., the proposal-expiry event already known to misattribute across chapters — issue AI-proposal expiry journal entry misattributed to wrong chapter after switch #31) can be processed by the main process after a concurrentproject:loadhas already swappedhistoryService's internal manager to the new project — landing the event in the wrong project's journal(s).Impact
Low-to-moderate severity: no data corruption in the manuscript or bibliography, but the AI usage journal / research journal's per-project activity record can be inaccurate — an event that happened in Project A gets recorded under Project B. Same severity class as #35/#39 (audit-trail/logging misattribution, not data loss).
Suggested fix
Capture the target project's
HistoryManager/usage-journal-store reference at the moment the renderer event was generated (or at least at the start of the IPC handler, before any concurrentproject:loadcan complete), rather than reading the live singleton state when the handler happens to run. Alternatively, tag each journal-write IPC payload with the project path it was generated for, and have the handler route to the correct manager (or drop the event) if it no longer matches the current project.Evidence trail
src/main/services/history-service.ts—HistoryService, single mutablehistoryManagerfield,getHistoryManager().src/main/ipc/handlers/project-handlers.ts:55,116—await historyService.init(projectPath), the async transition window.src/main/ipc/handlers/proposal-handlers.ts—proposals:adjudicationhandler, reads live manager state.1.9-Journal-and-History.md.