uttrflow / uttrflow/uttrflow-swift

A spoken mark after an abbreviation replaces its full stop: "5 p.m. question mark" becomes "5 p.m?"

Open Beginner friendly
#718 0 comments 0 reactions 0 assignees View on GitHub
area:dictation bug good first issue P2
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## What happens

`WordShape.marked(_:with:)` (`Sources/UttrflowCore/Cleaning/WordShape.swift:73-79`) replaces a clause mark already on the end of a word with the new one:

```swift
if let last = text.last, ",.;:!?".contains(last), ",.;:!?".contains(mark) {
return String(text.dropLast()) + mark
}
```

For a word whose full stop is part of an abbreviation ("p.m.", "a.m.", "etc.", "e.g."), that stop is not a sentence mark, but it is dropped all the same. Both `SpokenPunctuationPass` (`Sources/UttrflowAI/Passes/SpokenPunctuationPass.swift:149`) and the mark-carrying on a removed word (`Draft.carryMarks`, `Sources/UttrflowCore/Cleaning/Draft.swift:215-227`) go through it. `FirstWordPass.endsSentence` already recognises this shape (a stop inside the word's core, `Sources/UttrflowAI/Passes/FirstWordPass.swift:70-74`), so the two disagree about what "p.m." is.

Measured through `CleaningPipeline.standard` in a throwaway test:

| Dictation | Expected | Actual |
|---|---|---|
| `Is it 5 p.m. question mark` | `Is it 5 p.m.?` | `Is it 5 p.m?` |
| `We left at 5 p.m. comma then ate.` | `We left at 5 p.m., then ate.` | `We left at 5 p.m, then ate.` |
| `Bring apples, pears, etc. exclamation mark` | `Bring apples, pears, etc.!` | `Bring apples, pears, etc!` |
| `Meet at 5 p.m. exclamation mark` | `Meet at 5 p.m.!` | `Meet at 5 p.m!` |

A full stop after an abbreviation is still merged correctly (`It ends at 5 p.m.` stays as it is).

## Why it matters

Times and "etc." are among the most common things dictated before a question mark or comma. The output is visibly misspelled ("p.m?") and has to be fixed by hand.

## How to reproduce

In `Tests/UttrflowCoreTests/WordShapeTests.swift`:

```swift
#expect(WordShape.marked("p.m.", with: "?") == "p.m.?")
#expect(WordShape.marked("p.m.", with: ",") == "p.m.,")
#expect(WordShape.marked("p.m.", with: ".") == "p.m.")
#expect(WordShape.marked("done.", with: "?") == "done?")
```

## Acceptance criteria

- A trailing full stop that belongs to a dotted abbreviation (a stop inside the word as well, as `FirstWordPass.endsSentence` reads it) is kept when a comma, question, exclamation, colon or semicolon mark is added; a second full stop is still not added.
- An ordinary word's clause mark is still replaced as today ("done." + "?" → "done?").
- The rows above hold through `CleaningPipeline.standard` in `Tests/UttrflowAITests/Passes/SpokenPunctuationPassTests.swift`.

## Where to start

- `Sources/UttrflowCore/Cleaning/WordShape.swift:72-84`.
- Tests: `Tests/UttrflowCoreTests/WordShapeTests.swift`, `Tests/UttrflowAITests/Passes/SpokenPunctuationPassTests.swift`.
- Run `make verify` (export `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer` first) and read CONTRIBUTING.md.

**Size:** S, about an hour.

Contributor guide

Open the contributing guide

Research direction

Start with Sources/UttrflowCore/Cleaning/WordShape.swift:72-84 and the reproductions in Tests/UttrflowCoreTests/WordShapeTests.swift. Then run the focused WordShape and SpokenPunctuationPass tests, followed by make verify with the specified DEVELOPER_DIR. Done means abbreviation stops are preserved for added marks, ordinary clause marks still replace normally, and the CleaningPipeline.standard rows pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
desktop
Issue type
Bug
Difficulty
2/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
92/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.