Skip to content

Coordinator: the finish pipeline is DictationFinish, which returns an outcome - #79

Open
dinooo13 wants to merge 1 commit into
cli/bench-coordinatorfrom
core/finish-pipeline
Open

dinooo13 wants to merge 1 commit into
cli/bench-coordinatorfrom
core/finish-pipeline

Conversation

@dinooo13

Copy link
Copy Markdown
Owner

Part of #73, point 2 (first half: the finish pipeline). Stacked on #77 (cli/bench-coordinator), which provides the harness for the before/after.

What

finish(...) leaves DictationCoordinator and becomes Sources/PladderCore/DictationFinish.swift: a @MainActor value with run(_:onStage:) async -> Outcome.

  • Input: the captured tail, the samples already fed, the source (.utterance(engine, handle) or .whole(engine)), submit, the capture-stop time and the feed wait.
  • Stage: .polishing, and .inserting(Transcript). The second is reported before insert, so the coordinator sets lastTranscript to the processed text there. That keeps 07e6d2f's fix: a paste that fails still leaves the processed text.
  • Outcome: .tooShort, .empty, .inserted(Insertion), .failed(DictationFailure).

The coordinator keeps every state flip (.transcribing, .polishing, .inserting, becomeIdle, showCopied, fail), lastTranscript, willPolish, cycleEngine, pendingUnloads, inFlight, endRecording, beginCancel and the hotkey/gesture code. hotkeyReleased builds the finish, waits for the feed hand-over and the capture stop as before, runs the finish with a stage closure, and turns the outcome into the event and the state after (finished(_:)).

Isolation. run stays @MainActor, with the same awaits in the same order: feed hand-over → capture.stop → engine → pipeline → refiner → insert. Nothing is detached or made nonisolated.

Settings read at release (as decided). The finish is built in hotkeyReleased, so the pipeline, disabledProcessors, appendTrailingSpace, minimumDuration and minimumPolishWords are the values at release. Before, they were read when the engine returned, a few hundred milliseconds later. A setting changed inside that window now applies to the next dictation.

Deviation from the plan. DictationFinish has a willPolish: Bool beside refiner. The old code had a case where willPolish is set but there is no refiner, and there timing.polish must be .zero, not nil; passing refiner: willPolish ? refiner : nil would have changed that. Outcome and Stage are Equatable for the tests.

DictationCoordinator.swift goes from 676 to 644 lines; DictationFinish.swift is 92.

Tests

The existing coordinator tests are unchanged and pass. New Tests/PladderCoreTests/DictationFinishTests.swift, 11 tests:

  • aRecordingUnderTheMinimumAbandonsTheUtteranceAndInsertsNothing
  • fedSamplesCountTowardsTheMinimum
  • processedTextEmptyInsertsNothingAndReportsEmpty
  • polishRunsFromTheMinimumWordCountAndReportsItsStage
  • underTheMinimumPolishIsZeroNotNil
  • withoutPolishTheTimingHasNoPolishStage
  • theInsertingStageCarriesTheProcessedTranscriptBeforeTheInsert
  • timingSumsTheStagesAndFeedWaitCountsAsEngineTime
  • aTrailingSpaceIsAppendedOnlyWhenMissing
  • aThrowingInsertBecomesAFailure
  • aThrowingEngineBecomesAFailureBeforeAnyStage

swift build (no warnings) and swift test (612) are green; CoordinatorHandOverTests, CoordinatorRegressionTests and DictationFinishTests passed 20 runs in a row.

Benchmark (critical path)

swift run -c release pladder-cli bench-coordinator --runs 101, Apple M1, 16 GB, macOS 27.0. The two builds ran back to back from copies of their release products.

Before, cli/bench-coordinator (d43924b), load 2.81 → 1.79:

Hold Engine Total (median) Share (median / p90 / max) Engine excess (median)
1500 ms 150 ms 160 ms 0.08 ms / 0.10 ms / 0.14 ms 0.25 ms

After, this branch (5a5a80f), load 1.72 → 1.46:

Hold Engine Total (median) Share (median / p90 / max) Engine excess (median)
1500 ms 150 ms 160 ms 0.10 ms / 0.11 ms / 0.13 ms 0.25 ms

No difference. The 0.02 ms in the share median is well under the noise line.

For the developer

  • Paced bench before (CLI: bench-coordinator times the coordinator's own share of release-to-paste #77's branch) and after (this branch): swift run -c release pladder-cli bench bench/fixtures --paced --runs 2 --pause 2
  • About 20 live dictations on an installed build of each, comparing the release-to-paste log lines: /usr/bin/log show --last 1h --style compact --predicate 'subsystem == "de.dinooo13.pladder"'

🤖 Generated with Claude Code

… outcome

Engine, processors, polish, insert and their timing move into a value built at
release; the coordinator keeps every state flip and turns the outcome into the
event and the state after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant