Skip to content

feat(agents): add macOS accessibility knowledge topics - #3158

Open
Jason Morse (JasonVMo) wants to merge 2 commits into
user/jasonvmo/agent-knowledgefrom
user/jasonvmo/a11y-knowledge
Open

Jason Morse (JasonVMo) wants to merge 2 commits into
user/jasonvmo/agent-knowledgefrom
user/jasonvmo/a11y-knowledge

Conversation

@JasonVMo

Copy link
Copy Markdown

Summary:

Stacked on #3157. Review only the top commit (fd13876983).

macOS accessibility in this fork depends on AppKit behavior that isn't obvious from headers. For example, a getter can return AXUnknown while AX clients see a different role. Paper and Fabric also resolve roles differently. Agents and contributors keep rediscovering these details, or they copy iOS patterns that don't hold on macOS. This PR records the verified behavior as knowledge topics. It also adds a repeatable way to check what VoiceOver and other AX clients actually see.

New topics, loaded from a new AGENTS.md row for accessibility, focus, or Tab-order work:

  • a11y.appkit: AppKit AX behavior observed out of process. Rules:
    • Never copy an unset role.
    • Read a control's default role from its cell.
    • Gate state attributes.
    • Target the AX element.
    • Verify out of process.
  • a11y.roles: Role resolution in Paper and in Fabric, side by side. Rules:
    • Resolve role, then accessibilityRole, then the element's default role.
    • Restore the default role when both props are unset.
    • Don't import Paper's RCTConvert into Fabric.
  • a11y.state: How accessibilityState maps to AX attributes and value words. Rules:
    • Stop advertising state that's been removed.
    • Advertise AXPress only when the element handles it.
  • a11y.text-input: editable={false} semantics on every platform, plus the single-line (RCTUITextField) and multiline (RCTWrappedTextView) backings.
  • a11y.keyboard: focusable, first responder, and the key view loop, including why a Fabric TextInput takes two Tab stops on main.
  • testing.ax-probes: A procedure with a small Swift AX probe. It dumps elements by testID, reports focus, and clicks Fabric views, all driven with osascript keys.

The topics state facts and the open questions in #3136 without recommending how to resolve them. They watch #3136, so they'll be updated when it lands.

This PR also corrects testing.rntester-macos. yarn start fails under the repository's nodeLinker: pnpm, because Yarn resolves the react-native-macos binary under .yarn/__virtual__, which the pnpm linker never creates. Running cli.js directly without NODE_PATH starts Metro without its /status route, so the app reports "No script URL provided". The topic now gives a command that works.

Knowledge files only, with no product code changes, so there's no corresponding facebook/react-native PR.

Test Plan:

Environment: macOS 27.2 (26B123), Xcode 26.4.1, Node 24.18.0. Run at fd13876983.

Knowledge checks (the steps CI runs):

Command Result
node .agents/knowledge/tools/knowledge.mts check --ci Passed: 26 files checked: 0 errors, 0 warnings.
node --test .agents/knowledge/tools/knowledge.test.mts Passed: 21 pass, 0 fail
yarn markdownlint-cli2 --no-globs AGENTS.md ".agents/knowledge/**/*.md" ".claude/skills/rnmacos-*/**/*.md" .github/copilot-instructions.md Passed: Summary: 0 error(s)
yarn format-check Passed (exit 0)

Fact verification:

  • Checked every code claim against main. The asserts front matter keeps key lines tied to source, and I confirmed it's enforced: a deliberately broken assert produced a warning.
  • Verified AppKit claims with the probe against small standalone AppKit apps: NSView, NSTextField, and NSTextView, before and after setting roles and state.

Procedure verification in RNTester-macOS on main:

Step Result
RCT_NEW_ARCH_ENABLED=1 pod install (Homebrew CocoaPods 1.17.0; local Ruby couldn't bundle install), then .ado/scripts/xcodebuild.sh packages/rn-tester/RNTesterPods.xcworkspace macosx Debug RNTester-macOS build Build Succeeded
NODE_PATH="$PWD/node_modules" node ../react-native/cli.js start from packages/rn-tester, then curl localhost:8081/status packager-status:running (without NODE_PATH: Cannot GET /status)
testing.ax-probes procedure, run verbatim from a fresh launch Navigated APIs → Accessibility; example_search role=AXTextField enabled=1
ax dump of the TextInput module Both secureTextEntry inputs report role=AXUnknown enabled=1, which matches the RCTCopyBackedTextInput fact
Tab from example_search in the TextInput module, with autorecalculatesKeyViewLoop = YES added to RNTester's AppDelegate locally (not committed) Focus alternates AXScrollArea, AXTextField, AXScrollArea, AXTextField…; Shift-Tab returns to AXScrollArea. This matches a11y.keyboard
Same Tab test without that change Focus stays on example_search, which matches the a11y.keyboard.test-recalculated-loop rule

@JasonVMo
Jason Morse (JasonVMo) force-pushed the user/jasonvmo/a11y-knowledge branch from fd13876 to 1cf8630 Compare October 29, 2026 17:10
Add verified a11y topics (appkit, roles, state, text-input, keyboard) and a
testing.ax-probes procedure with an out-of-process AX probe. Route
accessibility work from AGENTS.md. Fix the RNTester Metro command, since
`yarn start` fails under the pnpm linker.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JasonVMo
Jason Morse (JasonVMo) force-pushed the user/jasonvmo/a11y-knowledge branch from 1cf8630 to c64c608 Compare October 29, 2026 19:49

@Saadnajmi Saad Najmi (Saadnajmi) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first look feels like this is quite specific to the PR we submitted, which makes me worry how fast this might grow if we do a similar thing for every PR. Ex: It seems very focused on TextInput, because that PR was probably fixing some TextInput bugs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants