Circe · CUA repair pass

The repairs are real. “All fixed” is still too strong.

Keep the CUA architecture. Finish session teardown, interrupted admission, deadline fencing, effect validation and browser acquisition before calling the driver integration complete.

Verdict

This pass repairs several important failures from the migration review. I would continue with this architecture. I would not sign off on the claim that all nine P1 findings and the P2 are fully closed. The five cases below are within the current driver/mission integration, not just the deliberately deferred reasoning work.

Reviewed the uncommitted workspace atop e0f1301ccb1ae2860442d60c68c4f4e058fd9280. No Circe code was changed. The tests reported by the implementing agent were not used as proof and were not rerun. Native driver, microphone, browser and packaged-platform acceptance were not performed.

What I could credit

  • The old unknown-click case now refuses without a second action. The coordinate/type fallback implementation has been removed.
  • The old Stop → queued read → queued click sequence now fences both queued calls with mission-ended.
  • Driver calls in the host probe now carry the named mission session and override caller-supplied labels.
  • Concurrent admission through the real service/host now yields one accepted mission and one refusal.
  • The unrelated provider probe now fails with mission-owner-mismatch before reaching the driver.
  • Source shows optional observation fields, richer element state, targeted key/scroll/type calls, and a panel Stop button independent of the start RPC’s pending flag.
  • Documentation now marks Mac/Windows unqualified and acknowledges the missing embedded Mac host. That is a more accurate scope statement; it is not evidence of platform readiness.

Remaining integration defects

P1

1. Stop fences input, but loses the session it must close

Stop calls revokeMission, which immediately removes the mission from the host map. Stop only waits for calls to settle; it does not close the driver session. The following end-mission looks up the already-deleted ID and finds nothing to close. The server also ignores the host’s settled result and swallows teardown transport errors before releasing its slot.

Evidence: Actual host/transport probe: Stop and end-mission both reported settled=true; queued reads and clicks were correctly refused. Driver calls contained start_session and click with the same label, but no end_session.

Repair: Retain revoked missions in a draining/cleanup state. Share one idempotent settlement-and-close operation between Stop, end and disconnect. Release the slot and cancellation registration only when that result is known. Preserve cleanup failure or unknown settlement instead of reporting successful teardown.

apps/desktop/src/computer/ComputerHost.ts:402, 540, 554; apps/server/src/computer/ComputerService.ts:235

P1

2. Interrupted admission leaves a permanent starting slot

Admission reserves phase=starting atomically, which fixes simultaneous starts. But rollback handles only ordinary success/failure results. Interrupting the Effect while waiting for the host response bypasses that rollback. Starting contains no mission identity, and activeMission hides it, so later starts refuse while no mission is visible.

Evidence: With the real ComputerService and host, and a deliberately held transport reply: concurrent admission correctly produced one Success and one Failure. Interrupting a later pending begin left the next begin failing with “A computer mission is already active on this node,” while activeMission was undefined.

Repair: Protect reservation with an interruption-safe resource lifecycle. Allocate and store operation identity before contacting the host. On interruption, reconcile or revoke that exact host admission; do not blindly clear an uncertain acceptance. Ensure every exit has an accountable owner and a recovery path.

apps/server/src/computer/ComputerService.ts:152, 357, 387; apps/server/src/circe/Layers/CirceComputerUse.ts:178

P1

3. Expired queued requests can still execute

The transport times out after timeoutMs. The host measures from receipt and adds REQUEST_TIMEOUT_GRACE_MS=2000 before refusing a queued request. This permits an effect after the client has already stopped waiting, contradicting the stated deadline guarantee.

Evidence: Actual socket probe: a queued click timed out after 20 ms while an earlier read was held. Releasing the read after the timeout caused the queued click to execute. The driver log contained the click even though the caller had already received timeout.

Repair: Use an explicit admission deadline with conservative clock handling and no post-expiry grace for effects, or make timeout initiate acknowledged revocation. Distinguish an action already dispatched at timeout from one still in the queue. The latter must never be dispatched afterward.

apps/desktop/src/computer/ComputerHost.ts:57, 657; apps/server/src/computer/ComputerHostTransport.ts:147

P1

4. The strict verified-only predicate is still incomplete

callMutation rejects isError and dispatched-unknown but accepts the other non-error effects. A schema-valid refused or not-dispatched result therefore reaches the shortcut as success. launch_app calls still use callSoft, the read helper that swallows errors, bypassing this mutation check entirely.

Evidence: The old unknown-click probe now correctly refuses with no retry. A separate probe using isError=false/effect=refused returned “Done: click Save.” An unknown launch was swallowed and subsequent clicking continued. These are injected contract cases, not claims that a native driver routinely emits that combination.

Repair: Use an exhaustive effect switch: verified may succeed; unknown remains unknown; refused and not-dispatched fail without fallback. Apply the same result handling to launch and every mutation. If an observation resolves an uncertain launch, make that an explicit verification transition instead of swallowing the result.

apps/server/src/circe/Layers/CirceComputerUse.ts:224, 357, 366, 411, 636

P1

5. URL target acquisition can still select an unrelated window

Actions now carry the target pid/window, but selecting that target remains unsafe. After launching a URL, the code picks a matching new browser window or falls back to fresh[0]. An unrelated window that appears during launch can become the mission target. The existing-browser fallback occurs only after this path.

Evidence: Production mission adapter probe: goal “open youtube and search cats,” existing Chrome window, then an unrelated new Editor window. The next get_window_state targeted Editor pid 88/window 99. The probe stopped at the unavailable model; no physical action occurred.

Repair: Remove the arbitrary new-window fallback. Resolve the launched or existing browser by an exact app/process/window or tab relationship, and preserve ambiguity as a refusal or clarification. Never let correct per-action targeting make an incorrectly selected window look authorized.

apps/server/src/circe/Layers/CirceComputerUse.ts:362, 378

Two wiring gaps to carry into the next phase

Provider delegation is guarded but not connected. The only production beginMission caller currently creates owner.kind=client. MCP handlers require owner.kind=provider. This correctly blocks unrelated tasks, but there is no production path to authorize a provider-owned mission yet. Add explicit user-approved delegation through the ordinary provider path before claiming provider-driven computer work is usable.

The partial-observation flag is not reaching decisions. The adapter sets surface.degraded, but boundedSurface omits it and the runner does not otherwise consume it. Preserve it in the selector, plan and verification input, and decide how missing evidence affects completion. Storing the field alone does not make the planner aware of it.

apps/server/src/circe/Layers/CirceComputerUse.ts:185, 476; apps/server/src/mcp/toolkits/computer/handlers.ts:72; packages/circe-core/src/computerUse.ts:64, 263

What should happen next

  1. Close the five driver/mission defects above. Keep the successful guards and remove the remaining alternate paths around them.
  2. Make one durable operation own voice, panel and explicitly delegated provider work, with cancellation from acceptance, current retry results, and reconnect subscriptions. The admission interruption failure shows why this is a prerequisite for reliable remote use.
  3. Evaluate plan prerequisites and postconditions, remove recovery ID reuse, and require evidence for the full user goal. These acknowledged earlier defects still affect user-visible reliability.
  4. Demonstrate complete tasks in the packaged Linux app: YouTube search in an existing profile, native edit/save/reopen, stop at startup and during execution, and phone disconnect/reconnect.
  5. Then qualify the chosen Mac and Windows hosts. Physical Escape and OS-session interruption remain separate acceptance work.

“The live service → host → driver chain passes” establishes useful driver connectivity. It does not establish a complete live voice task, correct targeting under window changes, durable remote operation ownership, or truthful completion. Keep those results separate.

Wayland

The flag mentioned in the handoff is real: CUA’s official installation documentation says native Wayland is opt-in with CUA_DRIVER_RS_ENABLE_WAYLAND=1 and has compositor-specific limits. With the current in-process design, that environment belongs to the Electron process hosting CUA. Setting it only for the server is insufficient.

The Chrome extension and native desktop input are separate routes. Enabling the flag does not prove either route works in the user’s current session. I did not change environment variables, restart the app, or inspect the machine’s live permission response. Treat the pasted check_permissions result as the implementing agent’s observation until the packaged host is checked after login.

Verification boundaries

The executable probes used production ComputerService, ComputerHost, transport, mission adapter and MCP handlers, with inert runtime/service dependencies. Socket endpoints and the one audit directory were disposable and cleaned up. Admission interruption used a held transport reply to exercise the actual reservation lifecycle. The timeout probe deliberately used a short request deadline to expose the two-second grace window.

No existing test files were read as evidence. The claimed 457 passing tests, five clean package typechecks and live native chain were not independently reproduced. No live server, user database, OS input or microphone was touched. This review establishes the specific adapter outcomes above, not native acceptance.