uttrflow / uttrflow/uttrflow-swift
Early transcription ends a sentence at every piece seam, so a pause mid-sentence becomes a full stop the one-shot result doesn't have
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## What happens
When a dictation is long enough to be cut into pieces while the key is held, `PieceJoiner.seamed` (`Sources/UttrflowPipeline/PieceJoiner.swift:50-54`) ends every piece except the last with `endedAtSeam` (lines 57-64), which calls `WordShape.finished(text)` whenever the destination uses terminal stops. The doc comment says it plainly: a seam is "a pause the windowing cut at, which every stopping place reads as a sentence end."
A pause is not always a sentence end. On a real-voice test of early transcription, a sentence with a pause in the middle came back from the live path with a full stop at the seam, splitting it in two, while the one-shot transcription of the same audio kept it as one sentence. An invented example of the shape: "we moved the review. To Thursday because the room was taken" instead of "we moved the review to Thursday because the room was taken".
This behaviour was introduced deliberately by #362 (fixing #183, where seams lost their stops in chat destinations). That fix is right for a seam that really is a sentence end, and wrong for one that is not.
## Why it matters
The live path exists to make the wait shorter, and its output is supposed to match the one-shot result. A wrong full stop in the middle of a sentence is a visible error in the inserted text, it appears only on longer dictations, and it will not show up in any test that cuts at a real sentence boundary.
## How to reproduce or check
- In `Tests/UttrflowPipelineTests/PieceJoinerTests.swift`, `PieceJoiner.seamed(["we moved the review", "to Thursday because the room was taken"], under: .standard(for: ))` gives a first piece ending in a full stop.
- On a real microphone (see #55): dictate one long sentence with a clear pause in the middle, long enough to be cut, and compare with a one-shot transcription of the same recording (retry it from the Dictation page).
## Acceptance criteria
- A seam gets a sentence end only when there is evidence of one, for example the recogniser's own punctuation or capitalisation on either side of the cut, or the length of the pause the windowing cut at. Otherwise the pieces are joined without adding a stop, and the next piece is not capitalised.
- The chat and messaging cases from #183 still keep their stops where a sentence did end.
- One existing test asserts the opposite on purpose: "a seam the recogniser left unmarked is still a sentence end in a chat" in `Tests/UttrflowPipelineTests/DictationPipelineSeamTests.swift` (added by #362). Which signal decides a seam, and what happens to that case, should be agreed on this issue before the change, since a seam with no evidence either way has to fall on one side.
- Tests in `PieceJoinerTests.swift` and `DictationPipelineSeamTests.swift` cover: a mid-sentence seam (no stop added), a real sentence end at a seam (stop kept), and the #183 cases.
- If possible, a corpus case in the evaluation set compares a cut dictation with the one-shot result of the same audio.
Related: #55 (real-microphone verification of early transcription), #230 (whole-dictation policies evaluated per piece).
Contributor guide
Research direction
Start with PieceJoiner.seamed in Sources/UttrflowPipeline/PieceJoiner.swift and run the related tests in Tests/UttrflowPipelineTests/PieceJoinerTests.swift and DictationPipelineSeamTests.swift. Review the existing #362 assertion and agree which signal determines a sentence boundary before changing behavior. Done means mid-sentence seams avoid added stops, genuine sentence ends and the #183 chat cases remain correct, with an evaluation case added if possible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100