Skip to content

Book export can race with in-progress footnote renumbering, producing a partially-renumbered manuscript #30

Description

@inactinique

Summary

Book-wide footnote renumbering writes each changed chapter to disk one at a time, with no UI lock preventing a PDF/Word export from starting mid-loop. If a user triggers an export while renumbering is still in progress, the exporter (which reads chapter files straight from disk via assembleManuscript()) can read a manuscript with some chapters already renumbered and others not — a silently inconsistent, partially-renumbered export.

Found while auditing the ClioDeck wiki against the actual code (pass 14, editor & export cluster, timing/race-condition angle). Narrow/rare in practice — requires two near-simultaneous user actions on two different UI surfaces (the renumber toolbar button and the export modal) — but real and unambiguous in the code.

Current behavior (verified against code)

  • src/renderer/src/components/Editor/EditorPanel.tsx:150-221 (handleRenumberFootnotes, book branch) — loops over every changed chapter and writes it via sequential await calls (useEditorStore.getState().saveFile() for the currently-open chapter, window.electron.editor.saveFile(...) for every other one). No isRenumbering-style flag exists anywhere in the component or the surrounding store; the toolbar button (line 265-271) has no disabled prop tied to the operation's in-flight state.
  • The operation's own rollback-on-failure logic (lines 202-217) is real and correct — it restores every already-written chapter to its original content if a later write in the same loop fails — but this only protects against the renumber operation's own internal failures, not against an unrelated concurrent read (an export) that happens to run while the loop is still mid-way through.
  • WordExportModal.tsx and the PDF export path both call assembleManuscript() in the main process, which reads each chapter's file straight from disk (except for the currently-open chapter, which uses live editor content via a liveOverrides entry) — so any chapter mid-renumber-loop that has already been written gets the new numbering, and any chapter not yet reached still has the old numbering, in the same assembled document.

Impact

A book export triggered during an in-progress renumber can silently produce a manuscript with inconsistent footnote numbering across chapters — some already renumbered, some not — with no error or warning to the user.

Suggested fix

Add a simple in-flight flag (e.g. isRenumbering in the editor or manuscript store) that: (a) disables the renumber toolbar button while set, and (b) either disables the export entry points or has them wait for the renumber operation to finish before reading chapter files from disk.

Evidence trail

  • src/renderer/src/components/Editor/EditorPanel.tsx:150-221 — the renumber loop and its rollback logic.
  • src/renderer/src/components/Editor/EditorPanel.tsx:265-271 — the toolbar button, no disabled-state wiring.
  • word-export.ts / manuscript-assembler.ts — the export-side read path (assembleManuscript()), confirmed reading straight from disk for non-open chapters.

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