Problem
When Accessibility does not expose AXSelectedText, the macOS fallback must use Command+C. The former AppleScript round trip kept only one preferred clipboard value and restored it unconditionally, which could lose rich representations or overwrite a newer clipboard generation.
Hardened direction
- Snapshot every pasteboard item, declared type, type order, item order, and exact data bytes before sending Copy.
- Fail before Copy if any declared representation cannot be materialized or the snapshot is unstable or exceeds the bounded in-memory limit.
- Do not seed a sentinel. An unchanged
changeCount never reads stale clipboard text.
- Accept only the immediately following generation as a copy candidate. Reject generation jumps and any additional observed change.
- Return candidate text only after the candidate stays stable and the original snapshot is restored successfully.
- Restore with
NSPasteboardContentsCurrentHostOnly because macOS does not expose the original host-only option.
- Keep System Events only for the static Command+C event and retain explicit Accessibility and Automation permission guidance.
- Run pasteboard object creation, access, restoration, and release on the macOS main thread; wait for the external System Events process off the UI thread.
- Bound the System Events wait with a 15-second watchdog. Once Copy has been dispatched, any timeout, no-selection observation window, generation anomaly, native failure, or abandoned request quarantines copy fallback until app restart rather than admitting another transaction.
Public API limits
NSPasteboard does not expose writer identity or an atomic compare-and-swap operation. System Events and CoreGraphics also do not acknowledge that the target application's copy handler has completed. Therefore:
- The first generation observed after Command+C cannot be proven to come from the target application rather than a simultaneous third-party write.
- A write can land between the final generation check and
prepareForNewContentsWithOptions; this check-to-write TOCTOU window cannot be eliminated with public APIs.
- A target application may process an already-dispatched Copy after the observation window or after
osascript is terminated. Quarantine prevents that late event from entering a newer Wordwise fallback transaction, but it may still change the current system clipboard.
- The implementation can preserve every unexpected generation it observes before restoration starts and can isolate ambiguous post-Copy failures, but it cannot truthfully guarantee that every concurrent or delayed write is never overwritten.
An absolute guarantee requires disabling the copy fallback, leaving the copied value in place instead of restoring, or adding user confirmation before a fallback candidate can be translated.
Implementation acceptance
- Plain text, RTF/HTML, image, file URL, empty, and multi-item snapshots round-trip byte-for-byte in native named-pasteboard tests.
- No selection with an unchanged generation reports that no text was detected without reading old clipboard text, releases the in-memory snapshot, and quarantines copy fallback until restart.
- Generation jumps, post-candidate changes, incomplete snapshots, native exceptions, and restore failures fail closed and never return candidate text for translation.
- Timeout, wait failure, no-selection, generation anomaly, native failure, and abandoned-request paths cannot admit a newer fallback transaction in the same app process.
- Error messages and logs contain no selected text or previous clipboard content.
- Automated policy tests cover unchanged, exact-next, jump, wraparound, restore guard, empty snapshot, multi-item/type, observed newer-generation, and process watchdog paths.
- Documentation discloses memory-only snapshot handling, current-host-only restoration, first-change attribution, delayed-event handling, quarantine, and the TOCTOU boundary.
- PR and release workflows run Rust gates; PR CI builds the
.app and checks bundled usage text; signed release CI verifies Developer ID authority, Team ID, and the Apple Events entitlement.
Release validation before closing
Keep this issue open as a public-release blocker until a signed and notarized candidate is manually tested for AX success, forced fallback with every representation class, Automation denied, no selection and quarantine, watchdog timeout and quarantine, observed concurrent update, permission revoke/regrant, first-run TCC text, effective entitlements, and actual cross-application shortcut behavior. Closing also requires an explicit product decision accepting the documented public-API residual risk or choosing a stricter fallback policy.
Problem
When Accessibility does not expose
AXSelectedText, the macOS fallback must use Command+C. The former AppleScript round trip kept only one preferred clipboard value and restored it unconditionally, which could lose rich representations or overwrite a newer clipboard generation.Hardened direction
changeCountnever reads stale clipboard text.NSPasteboardContentsCurrentHostOnlybecause macOS does not expose the original host-only option.Public API limits
NSPasteboarddoes not expose writer identity or an atomic compare-and-swap operation. System Events and CoreGraphics also do not acknowledge that the target application's copy handler has completed. Therefore:prepareForNewContentsWithOptions; this check-to-write TOCTOU window cannot be eliminated with public APIs.osascriptis terminated. Quarantine prevents that late event from entering a newer Wordwise fallback transaction, but it may still change the current system clipboard.An absolute guarantee requires disabling the copy fallback, leaving the copied value in place instead of restoring, or adding user confirmation before a fallback candidate can be translated.
Implementation acceptance
.appand checks bundled usage text; signed release CI verifies Developer ID authority, Team ID, and the Apple Events entitlement.Release validation before closing
Keep this issue open as a public-release blocker until a signed and notarized candidate is manually tested for AX success, forced fallback with every representation class, Automation denied, no selection and quarantine, watchdog timeout and quarantine, observed concurrent update, permission revoke/regrant, first-run TCC text, effective entitlements, and actual cross-application shortcut behavior. Closing also requires an explicit product decision accepting the documented public-API residual risk or choosing a stricter fallback policy.