uttrflow / uttrflow/uttrflow-swift

Paste confirmation ignores cancellation and polls without its 40 ms delay

Open
#796 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## Problem

`PasteConfirmation.waitFor` ignores errors from `clock.sleep(for: interval)` at `Sources/UttrflowInput/PasteConfirmation.swift:54`. With the default `ContinuousClock`, cancellation makes subsequent sleeps throw immediately. The loop still calls `focus.tail`, normalizes the returned text, and repeats until its elapsed-time budget expires.

A cancelled confirmation therefore loses its intended 40 ms pacing and continues unnecessary field reads. With a real Accessibility reader these are synchronous calls into another application; their cost limits the iteration rate, but cancellation does not stop them.

## Reproduction and evidence

Reviewed main commit `8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d`. A Swift test exercised the unmodified production `PasteConfirmation` with its default clock and budget, and a thread-safe fake `AccessibilityFocus` that returns `.text("unchanged")` while counting calls:

1. Start `Task { await PasteConfirmation(focus: focus).waitFor("new words") }`.
2. Wait until the initial field read has happened, then cancel the task.
3. Await its value and assert that cancellation permits at most one additional in-flight read.

Observed **1,070,805 additional reads** and `.gaveUp` after **1.600000625 seconds**. The cancellation assertion failed; all **10 existing PasteConfirmation tests passed**. Their scripted clock advances simulated time without throwing on cancellation, so they do not cover this behavior.

This count is for a fast fake reader, not a measurement of native Accessibility throughput or app CPU usage.

## Caller and scope

`PasteboardTextInsertionEngine.insert` awaits this confirmation after sending the paste. Insertion is wrapped by `withStageTimeout` in `DictationPipeline`; the timeout helper cancels the work task when its deadline wins. Confirmation should cooperate if cancellation reaches it. A normal confirmation finishes before the 15-second stage limit; this test does not claim every dictation or the UI cancel action triggers the defect.

Unlike #213 (charging field-read time to the budget), this concerns losing the polling delay on cancellation. Unlike #228 (preventing late insertion side effects), it concerns the observation loop after a paste has already been sent.

## Acceptance criteria

- Exit confirmation promptly when cancelled, including cancellation during sleep, without further repeated field reads.
- Represent or propagate cancellation consistently through the insertion API; do not label cancellation as a successful confirmation.
- Add cancellation coverage using a clock that throws on cancellation, covering both pre-cancelled entry and cancellation while waiting.
- Preserve the existing elapsed-time, unreadable-field, and successful-arrival behavior.

Priority: P2 — avoidable resource use after cancellation.

Contributor guide

Open the contributing guide

Research direction

Start with PasteConfirmation.waitFor in Sources/UttrflowInput/PasteConfirmation.swift:54 and the existing PasteConfirmation tests, then trace PasteboardTextInsertionEngine.insert through DictationPipeline's withStageTimeout. Add coverage for pre-cancelled entry and cancellation during sleep, ensuring cancellation stops further field reads without reporting success while preserving elapsed-time, unreadable-field, and successful-arrival behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
accessibility, desktop, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.