Platform capability census

This is the contributor-facing census for the public v1 tool surface. Cross-platform support remains the shorter user-facing contract.

Methodology

The source of truth for static advertisement is Sources/AxonCore/ToolSurfaceSpec.swift; scripts/check-tool-surface --write is the only supported way to regenerate schema/tool-surface-v1.json. Implemented means a facade has at least one real route. Partial means that route is advertised, while unsupported target forms, parameters, delivery rungs, permissions, or sessions refuse explicitly. Not implemented means the facade does not advertise the tool; a direct socket request must still return JSON-RPC -32004 with capability-unavailable / not-implemented data rather than dispatching natively.

Static availability is not a claim that every runtime environment works. Permissions and session facts belong in health-v1 and delivery refusals. A valid but deliberately declined delivery rung is a successful structured refusal; malformed input is -32602; a statically absent capability is -32004. This keeps schema evidence, source evidence, refusal evidence, and live session evidence distinct.

Cell notation and evidence

Exhaustive 18 × 4 census

tool (socket method) Swift daemon Rust macOS Windows Linux
capture_screen (capture_screen) N/— S N/— M N/— W P/A L user-authorized ScreenCast WINDOW capture on Wayland; X11 explicitly redirects application screenshots to look
look (look) I/A S AX snapshot, screenshot, OCR P/A M AX capture plus native Vision OCR; Screen Recording and observation-form limits remain bounded [P-M-LOOK] P/A W UIA capture, Graphics Capture, and built-in OCR; session/elevation gates [P-W-LOOK] P/A L AT-SPI plus X11 capture/Tesseract OCR; Wayland refused and XWayland remains Wayland-governed [P-L-LOOK]
navigate (navigate) I/A S browser Application Scripting N/— M [N-M] N/— W [N-W] N/— L [N-L]
windows (windows) I/A S browser Application Scripting plus AX cross-check N/— M [N-M] N/— W [N-W] N/— L [N-L]
tabs (tabs) I/A S browser Application Scripting N/— M [N-M] N/— W [N-W] N/— L [N-L]
find (find) I/A S fresh AX locator resolution I/A M shared locator over AX capture I/A W shared locator over UIA capture I/A L shared locator over AT-SPI capture
wait_for_value (wait_for_value) I/A S bounded AX polling I/A M bounded AX polling I/A W bounded UIA capture polling [I-W-WAIT] I/A L bounded AT-SPI capture polling [I-L-WAIT]
wait_for_stability (wait_for_stability) I/A S bounded observation polling I/A M bounded AX polling I/A W bounded UIA capture polling [I-W-WAIT] I/A L bounded AT-SPI capture polling [I-L-WAIT]
permit (permit) I/A S macOS Accessibility prompt N/— M status/remediation only [N-M] N/— W status/remediation only [N-W] N/— L status/remediation only [N-L]
run (run) I/A S .axn runner I/A M shared .axn runner I/A W shared .axn runner I/A L shared .axn runner
save (save) I/A S action-history serialization I/A M shared action-history serialization I/A W shared action-history serialization I/A L shared action-history serialization
recording (recording.start/recording.status/recording.stop) I/A S macOS recorder in the Swift daemon I/A M shared recorder over a listen-only CGEvent tap with AX reads in the callback P/A W shared recorder over WH_KEYBOARD_LL/WH_MOUSE_LL with UI Automation enrichment at event time; a recorded artifact names its application by window title, so it replays only while that title is stable [P-W-RECORD] N/— L no observer; observeGlobalInput refuses with a typed capability reason [N-L]
click (click) I/A S semantic, point, and text-location paths P/A M semantic AXPress/text path; unsupported target forms refuse [P-M-CLICK] P/A W UIA/pixel ladder; session, elevation, and control-family limits refuse [P-W-CLICK] P/A L semantic/pixel on measured X11 paths; Wayland and unsafe toolkit paths refuse [P-L-INPUT]
type (type) I/A S AXValue I/A M AXValue I/A W UIA ValuePattern I/A L AT-SPI EditableText
keyboard (keyboard) P/A S Core Graphics; permission/foreground gates [P-S-KEY] P/A M bounded foreground Core Graphics ladder [P-M-KEY] P/A W bounded foreground/global-input ladder; interactive-session and target proof required [P-W-KEY] P/A L measured X11/toolkit ladder; Chromium and multi-window limits; all Wayland/XWayland refused [P-L-INPUT]
scroll (scroll) I/A S semantic scroll and native input paths P/A M AXScrollToVisible only; directional/amount forms refuse [P-M-SCROLL] P/A W UIA ScrollPattern with bounded fallback [P-W-SCROLL] N/— L AT-SPI has no portable delta-scroll operation [N-L]
drag (drag) I/A S native pointer gesture with restoration accounting N/— M [N-M] N/— W [N-W] N/— L [N-L]
invoke (invoke) I/A S named AX action I/A M named AX action P/A W UIA InvokePattern only [P-W-INVOKE] I/A L named AT-SPI action

Keyed refusal evidence

Locations below are stable source files rather than line numbers. A “success refusal” is JSON-RPC success whose action result has success:false, strategy:"refused", and dispatchSuccess:false; it is distinct from a transport error. Runtime health may remove a delivery candidate, but never changes the static advertised tool list.

key named evidence and wire result router/backend location and dispatch boundary runtime health/session distinction
P-S-KEY DeliveryRoutingTests and DeliveryContractTests cover policy refusal; success refusal reasons are delivery-policy/capability reasons, while malformed input is -32602. Sources/AxonCore/CommandRouter.swiftAXPrimitiveActionExecutor.swift; refusal is selected before Core Graphics dispatch. Accessibility trust and a provable foreground application are runtime gates, reported through health-v1, not static absence.
P-M-LOOK Shared fixture tests pin canonical screen-text filtering, ordering, frame omission, confidence, and limits. A 2026-08-14 signed bglab-mac probe returned native Vision text with confidence and absolute frames from look(screenText:true,screenshot:false,frames:true); the complete response also proves large OCR observations survive partial socket writes. rust/axon-mac/src/lib.rs formats both screenshot and OCR from one observe_visuals call; capture.rs reuses one CGImage, and the narrow Objective-C bridge owns Vision execution. Screen Recording and Accessibility are separate health facts. Capture denial is explicit, and OCR operation failure preserves the semantic observation as screenTextUnavailable.
P-M-CLICK Router tests preserve AX-first automatic resolution, forced screenshot OCR, and unique-match requirements. The 2026-08-14 bglab-mac probe uniquely resolved recognized Calculator text, then honestly refused delivery because v1 offered neither a foreground nor ancestry-bound background-pixel rung. rust/axon-mac/src/lib.rs routes semantic and recognized text through the same delivery ladder; capture.rs and the Vision bridge provide the retained OCR frames. Foreground/global-input support and Accessibility trust govern delivery, not recognition. Point/coordinate targets remain statically absent.
P-M-KEY Router delivery-ladder tests use success refusals with dispatchSuccess:false; missing foreground/global-input capability produces noDeliveryCandidate. rust/axon-mac/src/lib.rs (keyboard_ladder, keyboard_intent); rust/axon-mac/src/platform.rs owns Core Graphics dispatch. Accessibility and foreground/restoration proof are runtime delivery facts.
P-M-SCROLL canonical_scroll_defaults_and_unsupported_forms_refuse_before_dispatch: directional, amount, point, and text-location forms return -32004, capability directional-scroll/target capability, reason not-implemented. rust/axon-mac/src/lib.rs (Router::request scroll route); the enumeration backend proves zero scroll dispatch. AX availability is runtime health; it does not make directional scrolling statically present.
P-W-LOOK look_screenshot_and_text_share_one_capture_and_use_canonical_keys and the shared fixture tests prove one capture and canonical formatting. The 2026-08-14 bglab-win sitting returned exactly one multi-word OCR line with its union frame while omitting screenshot/image content; transcript: cairn://p/axn/cross-platform/terminal/axn171-win-final2. rust/axon-win/src/lib.rs (Router::look); platform.rs preserves one Graphics Capture; capture.rs converts Windows.Media.Ocr lines and scales their word-union bounds. Interactive graphical session, elevation parity, and desktop visibility are health-v1 facts. Requested OCR failure is explicit rather than an empty success.
P-W-CLICK Fresh-hit tests prove fail-closed behavior. The 2026-08-14 post-AXN-175 bglab-win sitting used a bare-PID target, resolved one multi-word screenshot line, dispatched foreground SendInput at its frame, and observed the button's changed text through a second OCR look; transcript: cairn://p/axn/cross-platform/terminal/axn171-win-final2. rust/axon-win/src/lib.rs routes text locations through the canonical app query and delivery ladder; platform.rs/pixel.rs own hit testing, activation, and dispatch. Session 0, non-graphical sessions, elevation mismatch, and restoration failure remove delivery rungs.
P-W-KEY keyboard_aimed_at_an_application_that_is_not_running_refuses_rather_than_typing_elsewhere: success refusal, dispatchSuccess:false; malformed keyboard input is -32602. rust/axon-win/src/lib.rs (keyboard_ladder, keyboard_intent); platform.rs owns native input. not-interactive-session and no-graphical-session are runtime health reasons; target and foreground proof remain required.
P-W-SCROLL delta_scroll_reports_position_verification_and_goal_success proves the bounded ScrollPattern route; unsupported targets/capabilities are -32004/not-implemented before native fallback. rust/axon-win/src/lib.rs (scroll_windows); rust/axon-win/src/platform.rs backend scroll. UIA pattern availability and session/elevation reachability are runtime facts.
P-W-INVOKE unsupported_invoke_names_refuse_before_native_dispatch: -32004, capability named-action, reason not-implemented; capture list remains empty. rust/axon-win/src/lib.rs invoke route; rust/axon-win/src/platform.rs InvokePattern backend. A supported name can still fail runtime target/session health without changing advertisement.
P-W-RECORD recording_start_refuses_when_native_observer_is_unavailable pins the typed -32004 refusal with its code, and recording_records_observed_input_and_stops_into_an_authored_document pins the round trip. recording-diagnostic-v2 on bglab-win (2026-09-04, live run 33900101635) measured the two facts the design rests on: this process's own SendInput is visible to its own low-level hook with the dwExtraInfo stamp intact, and enrichment absorbs a 200-keystroke or 40-notch wheel burst without approaching the bounded queue. rust/axon-win/src/global_input.rs owns the hook and enrichment threads; rust/axon-win/src/recording.rs owns the translation; platform.rs reads point/focused evidence on the existing MTA actor. Session 0 and a non-graphical session make the hook blind rather than absent, so observeGlobalInput reports session-not-interactive at runtime. The identity limitation is not a runtime fact but a vocabulary one: this backend spells an application as a process id, which cannot outlive a session, so the artifact carries the window title and bundleIdentifier stays empty rather than minting a third identity (see axn/208 on the replay side).
I-W-WAIT waits_poll_captures_and_report_satisfied_and_timeout_results proves a LoadingReady transition across separate reads and an honest zero-timeout result; wait_for_value_matches_every_readable_state_field covers value, title, description/help, and identifier matching. rust/axon-win/src/lib.rs bounded polling routes over refreshed UIA captures; rust/axon-win/src/platform.rs captures UIA HelpText. The shared facade schema advertises both waits and enforces the public duration and predicate bounds before routing.
P-L-LOOK Shared fixture and deterministic TSV tests pin formatting, scaling, failure vocabulary, and pipe backpressure. The hermetic Xvfb/Tesseract test renders text in an offset X11 window, requires absolute OCR frames, and separately proves a missing executable preserves semantics with installation remediation. rust/axon-linux/src/lib.rs reuses one visual capture; x11.rs owns pixels/frame identity and ocr.rs owns runtime Tesseract TSV execution and line grouping. X11 OCR requires the tesseract executable and language data. Wayland/XWayland remain capture-restricted; missing OCR never prevents ordinary AT-SPI operation.
P-L-INPUT click_refuses_without_a_backend_call_and_names_the_missing_mechanism and keyboard_refuses_without_a_backend_call: success refusal, reason noDeliveryCandidate, capability globalInput, dispatchSuccess:false; click counter remains zero. wayland_withholds_global_input_however_complete_the_x11_session_looks proves XWayland does not bypass the gate. rust/axon-linux/src/lib.rs delivery selection; rust/axon-linux/src/platform.rs, pixel.rs, and x11.rs own native dispatch. reports_wayland_restrictions_explicitly names wayland-restricted; no graphical session, no window manager, no XTEST, toolkit acceptance, target binding, and restoration are separate runtime facts.
I-L-WAIT waits_poll_captures_and_report_satisfied_and_timeout_results proves a LoadingReady transition across separate reads and an honest zero-timeout result; wait_for_value_matches_every_available_readable_state_field covers value, title/name, AT-SPI description, UIA help, and identifier matching without aliasing unavailable fields. The live.yml Linux facade probe starts wait_for_value before invoking Calculator, verifies the real 001 transition, then verifies predicate_timeout; live run 31764259845 passed. rust/axon-linux/src/lib.rs bounded polling routes over refreshed AT-SPI captures; .github/workflows/live.yml owns the real MCP-facade proof. The shared facade schema advertises both waits and enforces the public duration and predicate bounds before routing.
N-M excluded_tools_are_capability_errors_before_dispatch covers navigate, windows, tabs, permit, and drag: -32004, capability-specific data, reason not-implemented. rust/axon-mac/src/lib.rs EXCLUDED guard runs before the method match; no backend route exists. Backend stubs in platform.rs also return capability errors rather than native dispatch. Runtime health is irrelevant: these tools are statically absent even in a healthy session.
N-W excluded_tools_have_structured_errors_before_backend_dispatch covers navigate, windows, tabs, permit, and drag: -32004, capability-specific data, reason not-implemented; capture_queries remains empty. rust/axon-win/src/lib.rs EXCLUDED guard precedes validation, capture, and backend routing. Runtime health is irrelevant: these tools are statically absent.
N-L unimplemented_tools_have_structured_errors_before_backend_dispatch covers navigate, windows, tabs, permit, drag, and scroll: -32004, capability-specific data, reason not-implemented; scroll dispatch count remains zero. rust/axon-linux/src/lib.rs EXCLUDED guard precedes validation and backend routing. Runtime health is irrelevant: these tools are statically absent.

Linux session distinctions

X11 is eligible for the measured XTest pixel and keyboard ladders only when an interactive graphical session, EWMH window identity, toolkit behavior, and target proof all agree. GNOME/Mutter Wayland supports semantic AT-SPI access, but global input and screenshots are refused; portal presence is not proof of unattended authorization. KWin and wlroots remain experimental because there is no live evidence. XWayland applications remain subject to the enclosing Wayland session's safety rules: semantic AT-SPI access may work, but Axon does not use XTest because it cannot prove or restore focus across native Wayland windows. A session with accessibility disabled can hide Chromium-family applications entirely; that is a runtime health fact, not a static tool-list change.

Evidence maintenance

A cell is promoted by a named live lane or dated probe, never by API availability alone. The macOS, Windows, and Linux live loops and hermetic tests are catalogued in Cross-platform internals. Changes to a platform router or backend must reconcile five views: this census, the Swift specification, the generated schema, router refusal tests, and live evidence. If any disagree, the advertised claim is wrong.

Roadmap

Priority 0: shared AXN-125 convergence

  1. Port observation/change tracking and waits into Rust shared core, then enable Windows/Linux waits only after their event or polling adapters meet that contract.
  2. Port action history, serialization, recording, and global-input observation into shared core so save is not implemented three times.
  3. Centralize strict canonical parameter/target decoding and result/refusal envelopes.

The macOS cutover retains Swift-only browser scripting, permission prompting, and debug-session orchestration unless a separate product decision moves them.

Priority 1: high-value native fills

  1. Add Linux Wayland screenshots through the desktop portal with explicit authorization lifecycle and portal-authorization-required health/refusal evidence.
  2. Implement measured Linux semantic or compositor-safe scrolling; only then restore its availability flag.
  3. Design drag as its own capability with interruption, restoration, and postcondition semantics.

Priority 2: bounded parity

  1. Complete foreground delivery per the AXN-165 contract: prove activation, dispatch once, and report session.restored honestly as a best-effort fact rather than a success gate.
  2. Expand target-bound pointer/keyboard acceptance only from live toolkit/control evidence.
  3. Fill Rust macOS point-target and wheel-scroll forms only after native delivery is proven.

Intentionally platform-absent

Swift browser navigate/windows/tabs remain macOS Application Scripting features. permit remains a macOS interactive prompt; other platforms use health and installation remediation. Swift debug-session methods remain app/editor orchestration rather than public cross-platform tools. Unsafe Wayland global input remains absent unless a compositor-authorized protocol can prove target identity and restoration.