git/intake/main

fix: journal updates can lose records and the reports can be wrong

closed #0025 issues/0025-fix-journal-updates-can-lose-records-and-the-repor.md opened

Found by the audit of 2026-09-14.

  • Every journal update was an unlocked open-modify-save, and every save used a fixed ".tmp" name. Two intake runs on one root each read the journal, each filed an album, and the second save wrote back a snapshot taken before the first, so the first album's record vanished while the album stayed on disk -- invisible to verify and audit, and free to be filed again. Saves now use a unique temporary file, and each read-modify-write runs under a lock file beside the journal, with a stale lock broken rather than waited on for ever.

  • forget() was called on any execute failure, including one that happened before a single byte was copied, and it deleted the record of an album that was still in the archive. It now only clears a record whose album has gone.

  • record() checked whether a library had already been built after writing the new record, so it always saw false and downgraded the flag. verify then reported an album whose library copy is on disk as one without a library copy.

  • library transcode rebuilt the copy without telling the journal, so verify went on warning however many times it was rebuilt.

  • library audit skipped a record whose archive was missing or held no audio, and then reported "0 needing attention" -- the one thing it exists to notice. verify accepted an existing but empty archive directory as present.

  • library audit hard-coded ".ogg", so every album in an opus library was reported incomplete for ever.

  • --json printed the human-readable problems before the JSON, so the output was unparseable exactly when it had something to say. library verify --json also omitted the missing count.

  • findRecord folded a punctuation-only target to the empty string, and a substring search for "" matches every album, so "intake library transcode !!!" picked whichever album happened to be alone in the journal.

  • Resume stopped at the first ready album whatever state it was in, so one mistagged mp3 meant no album behind it was ever reached; and the refusal was reported as success, so the command exited zero.

  • library re-cover replaced the archive art but never rebuilt the library, leaving the placeholder list clear while the copy people stream still carried the placeholder.

  • --dry-run in the TUI reached execute.Run without the flag being passed on, so previewing an album wrote it to the archive and cleared staging.