Offer AI-notes generation for transcripts that never got a summary - #10
Conversation
…ip's place, and an externally deleted summary.md stays deleted A note recorded while AI notes were disabled had no first-generation entry point: the tab strip only exists once summary.md exists or a run is in flight. A strip-shaped row now offers a sparkles button in the exact spot the regenerate button later occupies, gated on a new watcher-refreshed transcriptHasCues plus no summary and idle status. Codex-degraded shows the button disabled with the usual tooltip. Also fixes the resurrect on external delete: removing summary.md while a note was open wrote the editor's held text back on note switch. The watcher now detects the idle-state disappearance and tells the view to drop the held text; the fresh-regenerate recovery (delete with a run in flight) is preserved. Fixes #3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 559f800d1a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| isWorking: summary.status == .working, | ||
| regenerateHelp: regenerateHelp, | ||
| onRegenerate: { summary.regenerate() }) | ||
| } else if summary.canOfferFirstGeneration && recorder.status == .idle { |
There was a problem hiding this comment.
Suppress first generation while an import is running
When a media import is transcribing or fixing, recorder.status remains .idle while cues are progressively written, so this condition exposes the new generate button during the import. generateFirst() likewise has no ImportController.isImporting guard, allowing generation from a partial transcript; if the import finishes while that run is still working, the completion hook skips its corrective auto-generation because alreadyWorking is true, leaving incomplete AI notes. Gate both the offer and action on the import being idle.
Useful? React with 👍 / 👎.
| if summaryExisted && !summaryExists && status == .idle { | ||
| summaryFileRemovedExternally?() |
There was a problem hiding this comment.
Honor external deletes in the failed state
If an in-place generation fails while an existing summary remains loaded, status is .failed; deleting summary.md externally then bypasses this callback solely because the controller is not idle. The editor retains its nonempty text, and NoteView.onDisappear calls saveNow(), recreating the file the user deleted. Fresh-regeneration recovery needs special handling, but external deletes following ordinary failed updates should still clear the held document.
Useful? React with 👍 / 👎.
Fixes #3.
A note recorded while AI notes were disabled (or whose auto-generation was missed: Codex degraded at stop time, a failed run, a quit mid-generation, late-draining uploads) had no way to generate AI notes: the tab strip is the only home of the generation controls, and it only exists once
summary.mdexists or a run is in flight.What changed
transcriptHasCues, ashouldOfferFirstGenerationpredicate (AI notes enabled, transcript has cues, nosummary.md, idle, not recording), andgenerateFirst()with the same guards as retry.beginRun()already detects first-ness from disk, so the placeholder, strip appearance, and failure handling reuse the existing paths.summary.mdwith the note open resurrected the file on note switch, becauseonDisappearsaved the editor's held text back. The watcher now detects an idle-state disappearance and tells the view to drop the held text. The fresh-regenerate recovery (the app's own delete, with a run already in flight) is preserved and covered by a test.On the happy path (setting on, generation succeeds at stop) the button never appears; it is purely the catch-up entry point.
Testing
transcriptHasCuesinitial read, external-delete hook fires exactly once, fresh-regenerate delete doesn't fire it), TDD'd red-first.🤖 Generated with Claude Code
https://claude.ai/code/session_01Efd1FPsfuTrWATZhdajiRf