Skip to content

Journal entries (research + AI usage) can be misattributed to the wrong project after a switch #40

Description

@inactinique

Summary

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.tsHistoryService, single mutable historyManager field, getHistoryManager().
  • src/main/ipc/handlers/project-handlers.ts:55,116await historyService.init(projectPath), the async transition window.
  • src/main/ipc/handlers/proposal-handlers.tsproposals:adjudication handler, reads live manager state.
  • Related: issue AI-proposal expiry journal entry misattributed to wrong chapter after switch #31 (the specific proposal-expiry-misattribution-across-chapters bug that can compound with this one across projects too).
  • Wiki page corrected in the same pass: 1.9-Journal-and-History.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions