[feature] a finished recording proposes its own name and folder - #327
Merged
Conversation
Both doors into a finished recording name it badly: a live meeting is saved as "即時會議 · <date>" and an upload keeps its file name, so the library fills with rows nobody can tell apart. Filing is worse — the ingest wizard asks which folder to use BEFORE transcription, at the one moment when nobody yet knows what the meeting was about. So the answer is produced afterwards. A new pass reads the transcript and proposes a title plus 2-3 candidate folders, offered as a card above the report page's filing bar. It rides the cheap realtime lane (like meetingKind) and has NO upstream dependency — the transcript alone is its input, so it dispatches in the same tick as the findings pass rather than queueing behind the expensive one. That also makes it the one stage a realtime-only user ever gets. Two things worth knowing about the design: The suggestion is a stage but NOT a report artifact — the titlebar chip counts four artifacts and must keep counting four, so the split is carried in the types (StudyStageKey vs StudyArtifactKey) rather than by a filter someone has to remember to write. Acceptance state is DERIVED, never stored: the title row hides once the recording is already called that, a folder chip hides once it is already filed there. Renaming from the titlebar or filing from the bar below therefore retires the row too. What IS stored is `filingSuggested`, for the same reason `analyzed` exists — a null suggestion can't distinguish "never ran" from "ran, and the user dealt with it", and without the flag every reopen would re-spend the pass. useRefile now resolves when the move has landed. Both setEntryFolder and the study persists are read-modify-write over one meta.json; started concurrently, the later write reads a pre-move copy and silently drops the folder change — visible only after a reload, since the store already shows the new folder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ SonarQube Quality Gate passed — pathorsAI_parley0 open issues on this PR. |
- S3735: applyTitle is synchronous now, so the click handler can be passed to onClick directly instead of discarding a promise in expression position. The rejection is handled inside the callback either way. - S3776: the folder-name index moves into its own function, taking resolveFilingFolders back under the cognitive-complexity limit and giving the "models re-type names rather than copying them" rationale a place to sit next to the code it explains. - S4624: the folder list is built into a local before it is interpolated, rather than nesting a template literal inside one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
After a recording is transcribed, a card above the report page's filing bar proposes a better title for it plus 2–3 candidate folders, each one click to accept. At most one candidate may be a folder that does not exist yet.
Why
Both doors into a finished recording name it badly: a live meeting is saved as
即時會議 · <date>and an upload keeps its file name, so the library fills with rows nobody can tell apart. Filing is worse — the ingest wizard asks which folder to use before transcription, at the one moment when nobody yet knows what the meeting was about.IngestWizard.tsxalready carries a comment conceding that this is what produces "recordings needing after-the-fact filing". This is the after-the-fact answer.Design notes
It rides the cheap realtime lane and has no upstream dependency. The transcript alone is its input, so it dispatches in the same tick as the findings pass rather than queueing behind the expensive one — the suggestion should land while the user is still looking at the recording. It is also the one stage a realtime-only user ever gets.
A stage, but not an artifact. The titlebar chip counts four report artifacts and must keep counting four; a suggestion is a prompt to the user, not a section of the report. The split is carried in the types (
StudyStageKeyvsStudyArtifactKey) so the compiler enforces it, rather than a filter someone has to remember to write.Acceptance state is derived, never stored. The title row hides once the recording is already called that; a folder chip hides once it is already filed there. So renaming from the titlebar, or filing from the bar below, retires the row just as well as the card's own buttons — an
appliedflag would only be a second copy of that fact, free to drift.What is stored is
filingSuggested, for the same reasonanalyzedexists: a nullfilingSuggestioncannot distinguish "never ran" from "ran, and the user dealt with it", and without the flag every reopen would re-spend the pass.The model is not trusted with the product rules.
resolveFilingFoldersis pure and enforces them regardless of what comes back: at most one new folder, at most three chips, the registry's spelling wins over the model's, and a model claimingisNewabout a folder that already exists resolves to the existing one.A write race this also fixes
useRefilenow resolves when the move has landed.setEntryFolderand the study persists are both read-modify-write over onemeta.json; started concurrently, the later write reads a pre-move copy and silently drops the folder change. The store already shows the new folder, so it only surfaces on the next load.Storage shape
No DB migration. Two optional fields are added to
HistoryEntry(filingSuggestion,filingSuggested), which is persisted as opaque JSON inmeta.jsonand pushed to the cloud verbatim — older entries simply omit them and read as "never ran".How it was verified
bunx tsc --noEmitpassesbunx vitest runpasses — 27 files, 285 testsbun run tauri dev) and exercised the changeresolveFilingFolders, 5 new for the pipeline schedulingzh-TWandeninsrc/i18n/messages.ts(7 keys, parity checked)Screenshots
Pending — see the note above.