Files
cthulhu/docs/tentacle-remote-speech.md

38 KiB
Raw Permalink Blame History

Remote speech transport prerequisite

Typed speech IPC now includes an explicit reader-owned incoming mailbox and limited production observation of accessibility-event and document-reading speech. The external coordinator connects this IPC to relay speech. Production software sessions now attach an isolated incoming owner with lease, focus and local-output policy. Atomic assembly of split incoming utterances precedes owner admission. Private tests establish protocol delivery; audible acceptance remains pending. Original base reader revision: 4f9b89e on testing; matching Tentacles base: 0c74038 on master, plus the pending connection-command changes. No active or installed reader was replaced.

Wire data and ownership

RemoteSpeech has kind (speak, cancel, pause), optional priority (normal=0, next=1, now=2), parts, and optional paused boolean. Speak requires 1–128 parts and forbids paused. Cancel forbids parts, nonzero priority and paused. Pause requires paused and forbids parts and nonzero priority. Unknown fields, nulls and malformed types are rejected by both SDKs.

Each part is either text with resolved characters and language state, or end: true alone. Whitespace is significant. Language is empty for the configured default, or up to 64 ASCII letters, digits, hyphens and underscores. No voice path, backend command, sound file, object identity or arbitrary method is accepted. The canonical Go JSON representation is limited to 6144 bytes including escaping; the enclosing call retains its existing 8192-byte parameter and 64 KiB frame bounds. Python validates the same canonical byte bound.

Child remote.present takes RemotePresentation: lease, epoch, monotonically increasing operation sequence and speech. The sequence is shared with heartbeat and close. Admission requires a live controller lease with remote-present. Without a reader owner calling Session.EnableRemoteSpeech, it returns -32601. The queue never blocks the IPC reader and preserves FIFO speech/cancel/pause order. Across incomplete assemblies, queued deliveries and work in flight, each lease retains at most 64 speech frames and 256 KiB of canonical speech JSON. Overflow revokes the entire lease and returns -32002; it cannot drop a cancel while admitting newer speech. Speech does not refresh the heartbeat deadline.

For split speech, RemotePresentation also accepts utterance and final. Omitting both retains the single-call contract. A nonzero utterance identifies one assembly and equals the first fragment's operation sequence. Later fragments carry that same identifier and a strictly increasing operation sequence; the last sets final: true. Only speak can be grouped, and every fragment must have the same priority. Heartbeats may advance the shared sequence between fragments. Only one assembly may be pending; interleaved effects, changed identifiers or priorities return -32602 without committing a prefix or consuming the sequence. Replay still returns -32001. Null/unknown fields and invalid grouping metadata are rejected; both SDKs expose matching presentation validators.

No fragment reaches the owner before the final call succeeds. Finalization queues one delivery containing every frame, in order. An unfinished assembly stays bounded until completed or its lease ends; it has no independent renewal or retry. Revocation, replacement, close, policy cancellation, process loss and heartbeat expiry discard it. No relay protocol extension is involved.

The owner receives RemoteSpeechDelivery containing a cancellation context, lease, Speeches slice and idempotent Release callback. The slice is one complete effect/utterance; apply priority once, preserving all fragment states and boundaries. The owner must call Release after delivery or discard and drop its references. Dequeue alone does not return budget; stale releases cannot debit a replacement lease. It must select session shutdown, check the context and RemoteContext at delivery, apply focus/secure-input/speech policy, keep remote cancellation separate from local status, and mark remote origins so they cannot be observed again. Revocation immediately cancels contexts and drains pending mailbox items. A context may be cancelled after dequeue: dequeue is not authorization to speak later.

For export, core calls PublishRemoteSpeech with a live controlled lease, output-observe, and trusted origin local. Other origins are rejected. Core must exclude password/secret output and supply only policy-resolved local output; there is no child RPC that publishes an observed event. The method queues remote.output with lease, epoch and speech using the existing ordered sequence. Full IPC queues fail the process session, cancelling its lease. A receiving Tentacle must recheck lease/epoch and peer readiness before sending over a relay.

Limited production output observation

The Tentacle runtime now attaches its manager to the existing speech worker. Manager.CaptureRemoteSpeechOutput returns a reader-only ticket for an opened, live controlled lease with output-observe. The worker captures this ticket when it admits explicitly local speech. Work queued before consent gets no ticket; delivery never captures a replacement. The original lease, deadline and context are checked again when publishing. Production admission also holds the core availability policy lock, so lock/handoff cannot admit new output after policy cancellation, including across unlock/return. Already accepted IPC or relay writes cannot be recalled.

The zero speech origin is private. The main accessibility event presenter and reviewed document-reading producers mark requests local. Forms, preferences, connection/status messages, incoming remote text, command-result Tentacle messages and other unclassified sources stay private. The presenter classifies the original source and ancestors before context filtering/reset, so a discarded password ancestor still excludes its descendant's output. Password roles/ancestors, history exclusion, forced mode announcements, confirmations and protected messages are independently denied. Document caret movement, document navigation and assembled caret lines inspect their original source snapshots and unfiltered ancestors. Every content object in an assembled line must have a known role and no password ancestry. Say All classifies all spoken lines before admission; a sensitive or unclassified line keeps the entire batch private, including sentence-mode continuations. Local navigation failures stay private. Derived phonetic hints retain their parent's origin and original observation ticket. Separate key-name echo and notification paths still need explicit source and sensitivity review.

Visual-line assembly now retains each contributing object's complete captured ancestry in DocumentLine.SourceContexts, independently of the caret object's presentation context. Caret-line speech checks these sources, and Say All keeps them when flattening fragments into a single spoken string. A password source, password ancestor, unknown role or failed context lookup keeps the whole line private; a sensitive Say All line keeps its entire batch private. Missing source metadata does not prevent local reading.

Object review now exports successful destination presentations only with complete captured ancestry. Flat review retains the window's original snapshots and outer ancestry alongside its layout. Each line checks every contributing zone; word and character reports check their own source. Password sources/ancestors, unknown or invalid roles, defunct sources and missing/stale context keep the report local. An ancestor outside a captured menu/dialog still participates in that check. Dirty snapshot metadata conservatively keeps the captured flat window private without changing its local text. Layout text and privacy context remain from the same capture across tree replacement. Boundaries and errors remain private. Existing role chunks, pronunciation, spelling, phonetic expansion and character delivery retain their normal ordering and original lease/generation admission. Where Am I at a document position now exports only when that position retains complete captured source ancestry. The event loop stores classification with the current snapshot, document focus and epoch; it stores no lease authority. Every snapshot replacement clears classification, including same-object native updates and Say All position updates without ancestry. Focus/epoch replacement, missing or mismatched ancestry, password/unknown/invalid roles and defunct sources keep the report local. Foreground synchronization runs before this check. Lock and terminal handoff suppress the command. Missing-location status stays private. Native focus reports use the separate fresh-verification path below; other unreviewed producers remain private. Existing text, role chunks, formula reporting, pronunciation, focus, caret and Braille behavior are preserved; the speech worker captures the current lease and generation when the explicit command enters its queue.

Browse-mode current-line reporting, spelling and phonetic spelling now export only with the command's captured source ancestry. The review worker retains that ancestry from the tree producing the final movement, including after recapture. Every source role must be known; password sources/ancestors, missing or stale ancestry and sensitive neighboring fragments keep the report private. Classification does not add context announcements or change local reading, caret position, focus or Braille. Spelling retains repeated letters, character state and phonetic expansion through the existing speech owner, with its original lease/generation ticket and pronunciation policy. Errors remain private.

Native focused-line reports now capture the focused source and every parent through its application on the GLib owner, without scanning children or changing the caret. Capture refreshes role/state metadata and detects cycles, missing parents, invalid identities, provider errors and truncated walks. It has a 64-object bound and checks a 100-millisecond context between provider calls; an individual synchronous AT-SPI call still has its existing provider timeout. Failure leaves privacy metadata empty while preserving successful local text. Export requires the original focus/epoch, matching source identities and complete application ancestry; the shared classifier rejects password, unknown/invalid and defunct sources or ancestors. Spelling, phonetics, pronunciation and blank line handling use the existing speech owner and original lease/generation. Lock/handoff are rechecked after foreground synchronization and before presenting a completed report. Other unreviewed producers stay private.

Native Where Am I now requests a fresh focused snapshot and complete ancestry on the review/GLib owners, only while enabled speech has a live output observer. Idle/disabled Remote retains the immediate cached local path. Capture does not move focus or caret. The existing 64-object/100-millisecond ancestry limits apply; individual synchronous provider calls retain their existing timeout. Unsupported providers or a full request queue fall back to the original private local report.

The cached local report is preserved. Export additionally requires matching source identities and roles, complete known nonprivate ancestry, and exact equality of the freshly rendered and cached text, formula and speech chunks. Fresh ancestry alone cannot authorize cached text. Missing/truncated ancestry, provider failure, password/unknown/invalid/defunct sources and changed reports keep local output private. A snapshot replacement (even of the same object), focus/epoch change, lock or handoff retires a pending result. Verification stores no reusable native classification or lease. The speech worker captures its ordinary lease/generation ticket only when the verified report enters its queue. Pronunciation, role chunks, local speech, focus, caret and Braille retain their existing presentation paths.

Private GTK checks exercise the new snapshot operation, fresh private ancestor roles, password text exclusion, unchanged caret and truncated ancestry. Worker, application and actual source-command/private-relay tests cover fresh capture, cached/fresh mismatch, stale-result exclusion and resolved output. Physical keyboard, audible speech and real NVDA/Orca acceptance remain pending.

Say All exports each resolved chunk immediately before its completion-paced backend call. Sentence boundaries, pronunciation and the original request's lease/generation are retained. A lease replacement during reading cannot export later chunks to the new helper; local reading continues.

Reading-context entry and exit announcements now export only for an admitted, exportable local request whose retained contexts and their ancestry have known, nonprivate roles and are not defunct. Classification of the original source still precedes presentation filtering. This includes navigation, named article context, spelling and Say All, under their existing context settings, counts, deduplication and utterance order. Context labels retain the exact text submitted to the local backend; that existing path does not apply pronunciation replacement. The change adds no new source classification or backend/voice metadata.

The serialized speech delivery owner retains an observation ticket alongside its current context labels. Every known context replacement replaces this ticket; context reset clears both. Cached exit labels use the previous context's original ticket, while still requiring an eligible current request and current admission. Labels read before consent, under private/unclassified requests or under a retired lease cannot acquire later consent on exit. Nil/unknown context retains existing local state without granting new authority. Policy, expiry and speech generation are rechecked by the normal observation path. Local labels, system voice, focus, caret and Braille are unchanged; private labels remain readable locally.

Observation runs after speech-mode suppression, line/indentation processing, pronunciation, spelling expansion and backend character-path selection. It records resolved backend attempts, before backend I/O, not accepted audio or audible completion. Character mode is exported only for a single valid CHAR character; other text uses normal speech. Remote language is the default and priority is normal; voice, language/profile fidelity and pause/resume remain unimplemented. No sound cue or backend configuration is sent.

One local backend chunk becomes an observed utterance. Long UTF-8 text splits conservatively into at most 64 events, each within the encoded speech limit; only the last ends the utterance. The complete set of IPC frames must fit the shared session queue and byte budget, including in-flight bytes, before any prefix is queued. Invalid/oversized output or overflow retires the process session, matching existing output-event overflow behavior. Local speech proceeds independently. This atomic IPC admission is not atomic relay or NVDA delivery.

User/input interruption and mode/enable changes publish cancel through the same speech-owner mutex as observed utterances. An older delivery generation cannot publish behind that cancel. Remote publication never performs pipe, relay or backend I/O in the input loop. Incoming cancellation uses the isolated owner described below.

The source command now offers an explicit Return to desktop action after its reader lease opens. It closes status and releases local input capture while the original lease and heartbeat worker remain active. The Remote shortcut reopens status/Disconnect. Lock/handoff, expiry and process loss still revoke the lease. See the terminal interaction contract. Production incoming software ownership is described below; experimental-speech warnings remain in place pending live acceptance.

Verification and remaining work

Isolated synthesis prerequisite

The configured software backend now has an internal open_remote_voice primitive. It lazily creates one independent Speech Dispatcher speaking/control pair using the last successfully committed default voice settings. Ordinary reader startup creates no additional connections. Hardware and unconnected or closed backends reject acquisition; another acquisition is rejected until the previous handle has finished closing. This is not a lease authorizer and is not attached to the production incoming mailbox.

The handle offers plain-text SpeakAndWait, scoped Cancel, and idempotent Close. Speech remains outstanding through END/CANCEL, rather than just queue acknowledgement. Caller cancellation affects only the isolated client. Acquisition context cancellation after a successful open does not retire that handle: a future lease owner must explicitly close it on revoke, policy loss and shutdown. Core user interruption cancels both local output and this separate client.

A successful settings commit retires the old handle; it never silently adopts the replacement configuration. Failed preparation preserves it. Closing an old handle cannot cancel its replacement. Configuration updates now also retain the committed settings snapshot when reusing a software backend, so subsequent acquisition uses the current module and default voice rather than startup values.

Private Unix-socket SSIP tests keep local status and custom echo queued while remote cancellation, caller cancellation and handle closure run. They check independent client identities, END/CANCEL pacing, connection reuse, exclusive ownership, configuration success/failure and stale cleanup. These model protocol ownership; they do not establish real Speech Dispatcher scheduling, audible cancellation, or priority arbitration. The original plain-text primitive retains ordinary normalization. The typed renderer below adds whitespace and character/language handling; pause and NVDA priority still need stream policy.

No command invokes this primitive yet. The rendering prerequisite below adds typed utterances; binding them to the bounded reader mailbox still requires delivery-time lease/focus checks and local priority. Keep mailbox budgets until delivery/discard and retire queued work on cancellation. No live Remote speech acceptance test is available from these prerequisites alone.

Literal utterance rendering prerequisite

prepare_remote_utterance now validates a complete speak assembly, retaining its single priority for a future stream owner. It rejects non-speak effects, mixed priorities and assemblies beyond the existing 64-frame/256-KiB limits. It joins adjacent text with the same language/spelling state across IPC fragment boundaries without adding separators. An end marker or metadata change creates a synthesis boundary; repeated empty end markers produce no speech. It neither owns nor releases mailbox budget and cannot authorize presentation.

The isolated handle's SpeakUtteranceAndWait renders that prepared sequence through a new bounded speech.Client.SpeakFragmentsAndWait path. Every literal fragment is checked before backend I/O. Invalid UTF-8, NUL, C0 controls other than tab/CR/LF, DEL and invalid language tokens reject the whole sequence without a spoken prefix. Spaces and tabs remain intact; CR, LF and CRLF become SSIP CRLF line boundaries. Every dot-prefixed line is escaped, including a lone dot. SSML_MODE off makes markup and command-looking text literal data. This changes no ordinary local-text normalization.

Each fragment uses SPELLING on/off and its resolved language. Before rendering, the client reads its effective default language, output module and symbolic voice category; unsupported/malformed defaults fail closed. It restores the original module after every language change, because Speech Dispatcher language mappings may change modules. Remote language overrides clear a configured named voice and use the captured symbolic category. If a named voice has no category, explicit remote languages use SSIP category MALE1; default-language parts restore the configured voice name. Unknown categories without a configured named voice are rejected. The accepted module/default-language tokens are limited to 64 ASCII letters, digits, hyphens or underscores. Backend acceptance of a language request does not establish that the module can synthesize that language.

The speaking connection remains owned through each END/CANCEL and the complete sequence. Success restores default voice/language and spelling off. A backend failure or cancellation discards the remaining suffix and retires the isolated handle, cancelling only its client. A replacement requires explicit acquisition; there is no automatic retry or replay. Atomic IPC assembly does not make several backend fragments atomic: a later backend rejection can follow an already-spoken earlier fragment.

Only one typed assembly can render at a time. Scoped cancellation cancels its context as well as outstanding synthesis, so a cancel during a stalled metadata query cannot be lost before the first SPEAK. It never waits for metadata I/O in the caller. The renderer completes cleanup before returning ownership.

This implementation follows the SSIP speech, spelling and language contract and checks default-query/module-switch behavior against the upstream parser and settings implementation. The renderer itself makes no priority or pause decision. The stream prerequisite below now applies those decisions and retains mailbox budget, but a production owner still needs original-lease/focus/mode policy and arbitration with local output. The source Remote command remains relay-only for incoming speech; no active or installed reader was replaced.

Incoming stream prerequisite

run_remote_speech_playback now joins the isolated renderer to an explicit delivery channel. It is not registered with production sessions and provides no default authorization callback. Its caller must supply the reader's original-lease/focus/output check, bind policy loss to the delivery lifetime, and retire the session on owner failure. Policy is checked at admission, before dispatch and after backend acquisition. These callbacks are not a substitute for production local-output arbitration, which remains pending.

One owner receives complete assemblies and control effects while a separate renderer performs backend I/O. It retains the original delivery's Release until completion or discard, including through cancelled-renderer cleanup. Pending work is bounded independently to the existing 64 frames and 256 KiB, including the active assembly. Invalid assemblies, exceeded budgets and backend failure end the owner; errors omit speech and backend diagnostics. The future session caller must revoke/drain remaining ingress on failure or shutdown.

Normal speech is FIFO. NEXT moves ahead of pending normal assemblies but waits for the active assembly, including its internal end boundaries. NOW interrupts an active lower-priority assembly and precedes pending lower-priority work; equal priorities remain FIFO. Interrupted speech is discarded, without replay or suffix retry. This is deliberately limited priority support: NVDA can resume interrupted speech and preempt at utterance boundaries; this renderer does not track a backend speech position. It does not change Speech Dispatcher priority or establish scheduling parity with NVDA.

Cancel discards pending speech and cancels only the active remote renderer. Pause true does the same; it does not mute future utterances. NVDA clears its pause state on cancellation or fresh speech without requiring a separate resume message. Pause false never replays discarded text. A replacement lease discards old pending work and waits for old renderer cleanup before starting new output. Stale/cancelled deliveries are released without rendering. Lease cancellation and owner EOF or shutdown also cancel active work and release retained pending budget.

The software handle is reused within its lease after successful rendering. A cancelled rendering retires it; the next accepted assembly acquires a fresh handle after cleanup. Even an idle handle closes asynchronously on cancellation of its original lease. Replacement waits for that closure and acquires its own client; repeated old cleanup cannot cancel the replacement or local status. Remote synthesis remains outside local history and observation paths.

This prerequisite changes no Xorg capture, keyboard focus, installed reader or production session routing. Private SSIP fixtures establish scoped protocol ownership and cancellation, without audible speech or real server scheduling. Production mailbox attachment, local output priority, physical input and live NVDA/Orca acceptance remain pending.

Production incoming owner and local priority

Both local-launch and managed-session startup now attach one incoming owner when remote-present, the reader availability policy, focus gate and configured software speaker are present. Its lifecycle watcher joins cleanup on session revocation or reader shutdown. It creates no synthesis connection until accepted speech needs one. Hardware acquisition fails closed and retires the original lease; no hardware device is opened by the incoming owner.

Every delivery and synthesis handoff rechecks the original process-owned lease with remote-present, its deadline/context, lock/handoff, internal-form capture, speech enable and speech mode. Denied work is discarded. Local pending/active requests and the interruption barrier also deny new speech admission. Incoming text bypasses local history and observation entirely; remote cancel/pause affects only its isolated speaking client. Backend failures retire only the failing lease epoch. Cleanup completes before the renderer starts speech under fresh consent; a stale failure cannot revoke a replacement approval.

Local speech admission or interruption synchronously retires incoming admission tickets. Active remote rendering cancels; older queued work is discarded without replay. The first frame of an IPC assembly receives a reader-owned monotonic admission timestamp. Local output records a cutoff, so an assembly or mailbox item accepted before that output cannot acquire a fresh ticket when dequeued later. The local-generation check is synchronous even before its asynchronous cancellation callback runs. Mailbox budget remains retained through cleanup. Speech accepted after the cutoff may proceed once local queue policy permits.

Ordinary software speech returns at queue acknowledgement, not audio completion. The isolated client therefore sets SSIP TEXT before speaking; local reader clients retain MESSAGE. Under the SSIP priority contract, TEXT waits behind MESSAGE, and a new MESSAGE cancels older TEXT output. The relay's normal/NEXT/NOW values only order remote work and cannot raise its SSIP priority. A server cancellation discards the current assembly and its suffix; it does not itself revoke consent. Failed priority setup closes the candidate. This establishes the requested protocol policy, not real-server scheduling or audible priority acceptance. Other applications using TEXT priority can also cancel remote output. Pause and priority retain the limited discard semantics of the stream prerequisite; there is no replay or resume parity.

Private source-command/TLS/SSIP checks exercise complete multilingual spelling and language output after Return to desktop, and cancel active synthesis on Disconnect, lock, handoff and disable. Focused tests cover local announcements, interruption, speech modes, disable/secure input, old mailbox/assembly cutoffs, rejected local results, backend priority rejection and server cancellation. Physical local approval, audible output, real Speech Dispatcher scheduling, Braille and NVDA/Orca interoperability remain separate acceptance gates.

Automated evidence

For the isolated synthesis prerequisite, focused application/backend race checks passed, including ten repetitions of the new cases. Existing speech regression checks, affected-package vet, a separate reader build and Tentacles make check also passed. No installed or active reader was replaced, and no audible or physical-input acceptance was performed.

The literal rendering increment passed the complete speech-backend race suite and ten repetitions of the isolated-client/rendering race cases. Private SSIP fixtures cover exact whitespace, mixed CR/LF, dot-only/command-looking lines, markup as literal text, repeated-character spelling, language/module/default restoration, named-voice fallback, long split text, end boundaries, assembly limits, invalid later fragments, unavailable defaults, rejected settings and remote/caller/configuration cancellation with local status/echo retained. The stalled-metadata cancellation case also rejects overlapping typed assemblies and verifies that neither a prefix nor suffix reaches synthesis. The existing speech regression harness, affected-package vet, separate reader build and Tentacles make check passed. These are protocol fixtures, without real Speech Dispatcher, audio, physical input or Braille acceptance.

The incoming-stream increment passed ten repetitions of its focused race cases and the existing application Remote speech and echo race checks. Deterministic fixtures cover NEXT/NOW ordering, equal-priority FIFO, cancel/pause while rendering, no replay, queued revocation, replacement leases, policy rejection, frame/byte overflow, backend failure and EOF cleanup. Private SSIP checks retain local status across stream cancellation, reject policy loss during acquisition, close idle voices on revoke and protect replacement voices from old cleanup. The existing speech regression harness, application vet, separate reader build and Tentacles make check passed. No live acceptance or full reader suite was performed for this unwired increment.

Run from this checkout:

GOCACHE=/tmp/cthulhu-cache GOPROXY=off go test -race ./internal/tentaclewire ./internal/tentacle ./sdk/... -count=1 -timeout=240s

Real Go/Python child processes cover typed speech and observed events, absent owners, role/grant denial, replay, ordering, revocation and queue overflow. Shared validation cases cover null/type/unknown-field rejection, grouping metadata and escaped byte bounds. Assembly tests cover no-prefix admission, interleaving, replay, priority consistency, heartbeats, in-flight accounting, idempotent/stale release, byte/frame overflow and retirement. A private TLS fixture in TestRemoteConnectionCommandPrivateRelay also passes long escaped multilingual speech through the actual source command and reader host to one complete test-owner delivery. Existing lifecycle/SDK tests exercise old 1.0 behavior, process failure, timeout, event overflow and installation contracts.

The observation increment adds Go/Python SDK tests for captured authority, UTF-8/escaped splitting, cancel order and stale tickets; isolated host tests for all-or-none queue admission, in-flight bytes, expired/pending leases and policy round trips; and speech-worker tests for resolved pronunciation/characters, chunks/spelling, private origins, modes, generation and lock/handoff retirement. The source-command/private-relay fixture also exercises production observation attachment, transformed long output, private exclusion and cancel order with a test speech backend. It does not drive physical input or audible synthesis. Queue-level regressions additionally hold the backend across reconnection: output admitted under old consent or before fresh consent cannot acquire the replacement lease. An oversized observed utterance retires Remote while both that local utterance and subsequent local reading still reach the test backend. Ten repetitions of the observation race cases passed, along with the complete host/wire/both-SDK race suites (host: about 127 seconds), focused application speech/Remote/Tentacle race checks, affected-package vet, a separate reader build, the existing speech regression harness and Tentacles make check.

The production incoming-owner increment passed ten repetitions of its focused Remote speech/source-command race cases, the complete speech-backend and host/wire/both-SDK race suites (host: about 128 seconds), shared application speech/Tentacle race regressions, the existing speech regression harness, affected-package vet, a separate reader build and Tentacles make check. Backend failure coverage also verifies explicit status close and a fresh setup workflow without restarting the process. No full reader suite or live acceptance was performed.

The subsequent fragment-privacy review adds a real accessibility-tree regression with an ordinary caret and a neighboring password descendant on the same visual row. Running that regression against the previous classifier with a Go source overlay reproduced export admission; the corrected classifier denies it. Presentation/SDK tests cover password sources, password ancestry, unknown roles, missing contexts and unchanged local reading. Review tests retain every source through Say All flattening and preserve local text after ancestry becomes stale. The source-command/private-relay fixture also checks private joined caret/Say All output never reaches relay speech frames.

Verification for this review passed: the complete AT-SPI/review race suites, focused application Remote/speech/document/caret/current-line/flat/object-review race regressions, ten repetitions of the document privacy race cases, source-command/private-relay race checks, the speech regression harness, vet for the affected packages, a separate reader build in /tmp, and Tentacles make check. The repository check now includes document/Say All observation regressions. Native/hardware checks and the full reader suite were not run for this review. Physical input, audible speech, physical Braille and real NVDA/Orca interoperability remain unverified.

The browse current-line increment passed focused application/input reading and Remote race regressions, the complete review and external Remote race suites, the existing speech regression harness, affected-package vet, a separate reader build and Tentacles make check. The new tests cover source/neighbor privacy, native and error exclusion, recaptured ancestry, unchanged local text after context invalidation, pronunciation, repeated-letter character spelling, phonetic spelling and blank lines. The source-command/private-relay fixture exports an ordinary report and rejects a private joined neighbor. No full reader suite, native desktop, hardware or audible acceptance was performed for this increment. See the live test steps.

The object/flat-review increment passed complete AT-SPI/review race suites, focused application reading/speech/Remote race regressions, affected-package vet, the existing speech regression harness, a separate reader build and Tentacles make check. Source-tree cases check private and unknown ancestors, every joined zone, outer menu ancestry, stale metadata and unchanged local reading. Worker tests compare resolved local and exported speech, including spelling/phonetics. The actual source command/private relay verifies ordinary exports, private/stale exclusion and separate role utterance boundaries. An overlay restoring the previous unclassified producers reproduces missing ordinary export. No full reader suite, native desktop, hardware or audible checks were performed.

The document Where Am I increment passed focused application reading/focus/ speech/Remote race regressions, complete input/review/AT-SPI and external Remote race suites, ten repetitions of the new source/privacy cases, the speech regression harness, application vet, a separate reader build and Tentacles make check. The ordinary-source regression failed before implementation. Owned-tree cases check private/missing/stale ancestry, same-object native updates, Say All position replacement, focus/epoch retirement and lock/handoff, including handoff discovered by foreground synchronization. The source-command/private-relay fixture verifies ordinary exports, private exclusion and separate role boundaries. The full application race suite timed out after 180 seconds in package-upgrade tests; it is not a pass. The source-command race run needed approval for local fixture sockets; its approved rerun passed. No native desktop, hardware or audible checks were performed. See the live Where Am I steps.

The reading-context increment reproduced missing ordinary export before the implementation. Focused application Remote, speech, reading, context-settings, Say All, spelling and interruption race regressions passed, including ten repetitions of context privacy, reconnect and source-command/private-relay cases. The external Remote race suite, existing speech regression harness, application vet, a separate reader build and Tentacles make check passed. The relay fixture now supplies reading settings and verifies ordinary context entry/exit output while private labels and their cached exits stay local. No full reader suite, native desktop, hardware or audible acceptance was performed. See the live context steps.

Next: verify real Speech Dispatcher scheduling and audible local/remote arbitration. Physical input capture/release and ordinary reading while connected still need acceptance. Review the remaining output producers before assigning exportable origins. Atomic IPC assembly is implemented; it does not establish backend priority/cancel behavior or audible delivery. The stream prerequisite implements cancel-on-pause and limited priority as documented above; full NVDA priority/resumption fidelity is not established. Then verify normal reading, key echo, read-all, spelling, speech modes and local status while connected; lock/handoff and disconnect must discard remote work. Physical consent, audible speech and real NVDA/Orca acceptance remain pending.