Skip to content

Offer AI-notes generation for transcripts that never got a summary - #10

Merged
predict-woo merged 1 commit into
mainfrom
fix/ai-notes-first-generation
Aug 26, 2026
Merged

Offer AI-notes generation for transcripts that never got a summary#10
predict-woo merged 1 commit into
mainfrom
fix/ai-notes-first-generation

Conversation

@predict-woo

Copy link
Copy Markdown
Owner

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.md exists or a run is in flight.

What changed

  • SummaryController gains a watcher-refreshed transcriptHasCues, a shouldOfferFirstGeneration predicate (AI notes enabled, transcript has cues, no summary.md, idle, not recording), and generateFirst() 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.
  • NoteView renders a strip-shaped row when the strip is absent and the offer holds: a single sparkles button in the exact spot the regenerate button occupies once the strip exists (a hidden tab label keeps the row at the strip's height, so the working-state handoff doesn't shift the editor). Disabled with the usual tooltip when Codex is degraded; hidden while recording.
  • Bug found while testing: externally deleting summary.md with the note open resurrected the file on note switch, because onDisappear saved 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

  • 4 new swift-testing cases (offer gating matrix, transcriptHasCues initial read, external-delete hook fires exactly once, fresh-regenerate delete doesn't fire it), TDD'd red-first.
  • Full SimbiKit suite: 313 tests pass.
  • Verified by hand in the running app.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Efd1FPsfuTrWATZhdajiRf

…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
@predict-woo
predict-woo merged commit 3283122 into main Aug 26, 2026
3 checks passed
@predict-woo
predict-woo deleted the fix/ai-notes-first-generation branch August 26, 2026 18:18

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +69 to +70
if summaryExisted && !summaryExists && status == .idle {
summaryFileRemovedExternally?()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI notes cannot be generated for transcripts recorded while AI notes were disabled

1 participant