Skip to content

feat(docs): add a capabilities matrix page generated from runtime facts - #3942

Open
thymikee wants to merge 3 commits into
mainfrom
t3/capabilities-matrix-page
Open

thymikee wants to merge 3 commits into
mainfrom
t3/capabilities-matrix-page

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Adds website/docs/docs/capabilities.md: which commands each target supports — iOS sim/physical, Android emulator/physical, Android TV/Fire OS, tvOS, Vega OS, HarmonyOS, macOS desktop/app, Linux, web, and Limrun, BrowserStack, AWS Device Farm, TestMu AI sessions. Identical targets merge into one column; each row links to the doc section that proves it.

Every cell is generated, not written: scripts/capability-matrix/ loads each platform/provider runtime against a stub host and a scripted provider wire, calls the same inspectFacts(device) the capabilities command answers from, and maps facts to commands through the registry rule (command-registry/fact-support, extracted from the capabilities inventory so both share one owner; it judges the declared uses given to it, so importing it adds no eagerly-loaded modules). Non-fact data (labels, limit notes, doc links) lives in annotations.ts keyed by command id, so removing or renaming a capability fails the gate. Host seams that decide cells fail closed: a runtime that starts reading a new seam fails the run rather than reading a fabricated answer.

Also: check:capabilities-matrix in the docs lane (regenerate-and-diff + repo-guard with negative proofs), sidebar + README links, llms.txt coverage comes from the sidebar.

Follow-ups, not blocked here:

  • <link rel="alternate" type="text/markdown"> per page would need a @callstack/rspress-preset change (.md mirrors already ship and llms.txt lists the page).
  • The release lane never rewrites the page's version/date stamp, so it silently ages; the drift mask hides that by design. A release-time generate:capabilities --write would fix it.
  • HarmonyOS facts mark perf frames/memory-snapshot available while commands.md says those report unavailable inside the run; a row note now says a Yes means the command runs, not every metric. Deciding the HarmonyOS facts themselves is separate work.

Validation

  • Head cbd21be5d4 (base ed798f9cfe): pnpm check:affected --run --head cbd21be5d4 — all runnable checks passed, exactCommit=true (receipt .tmp/validation/run-QeHekO).
  • Docs-only page; runtime device lanes are GitHub-authoritative at the PR head.
  • Guard proven to fail: flipped cell, dropped/renamed annotation command, and a synthetic split row each turn it red; the version/date mask alone stays quiet.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-3942/

Built to branch gh-pages at 2026-10-25 20:23 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.58 MB 5.58 MB +390 B
Package (unpacked) 5.58 MB 5.58 MB +390 B
Package (download) 1.69 MB 1.69 MB +85 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 40.9 ms 37.9 ms -3.0 ms
CLI --help 81.4 ms 78.6 ms -2.8 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 18 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/capability-matrix/run.ts Outdated
Comment thread .github/workflows/docs.yml
Comment thread scripts/capability-matrix/run.ts Outdated
Comment thread website/docs/docs/capabilities.md Outdated
Comment thread scripts/capability-matrix/targets.ts
Comment thread .github/workflows/docs.yml
Comment thread website/docs/docs/capabilities.md Outdated
Comment thread website/docs/docs/capabilities.md Outdated
@thymikee

Copy link
Copy Markdown
Member Author

The code looks sound at cbd21be and CI is green (23 checks, none failing); there are no conflicts. I did not run the generator or the daemon capabilities command, so I did not confirm that each column equals what a live capabilities --platform X returns (for example web network mode, where the stub does not scope the web provider as a web request does). I also did not build the site to check llms.txt and sidebar rendering.

Not blocking, take or leave: the stub host in seams.ts re-states the simulator-vs-runner split that createAppleScreenRecordingHost(host).availability already owns, so every seam the stub answers for real should come from the production host composer. The row notes in annotations.ts restate per-target cell values (for example "which is why its column reads No"), so the drift check can never see them contradict the cells; keep notes to target-neutral limits, or have renderPage assert the named target's cell. In collect.ts the helper sits between imports and its rule is explained twice, and the same declaredUsesAreAvailable(commandRuntimeUseRequirements(command), facts) composition appears at three sites, so one registry export would give it a single shape. isDocs in model.ts becomes private with no hunk needing it; name it in the PR body or drop the hunk.

The Cubic threads that still hold are the client.command name, the perf and logs sentence, the per-target error context (or the run.ts comment), and the native-apps sentence; one more P2 on duplicate cloud labels is latent today but still applies. The two Node-version and drift-gate P1 threads (1, 2) and the test-path P2 (3) do not apply, so you can resolve them: this is repo tooling run on the toolchain Node, and the page drift check runs in unit-ci and the docs lane.

Could this be smaller? The PR adds about 1,730 net production lines, and roughly 140 lines of SCOPE_ROWS plus the page header in generate.ts are prose held in TypeScript and re-emitted unchanged, so the drift check guards text it does not decide. Would a hand-written capabilities.md with a generated region between markers work, where only the tables are filled and annotations.ts shrinks to row, label, commands and link? Then the prose fixes above become plain doc edits. Could the stub host also be built from the production composers (createAppleScreenRecordingHost, createNetworkRuntimeHost) with only process and tool seams stubbed, so a daemon seam change moves the page? That second part may need a host-builder entry in src/ that accepts injected seams, or importing the per-seam composers from scripts.

Before merge, please fix the four holding threads and answer the smaller-design question.

@thymikee
thymikee force-pushed the t3/capabilities-matrix-page branch from cbd21be to 02e1a6c Compare October 25, 2026 11:34
@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto current main and pushed 02e1a6c8 (--force-with-lease), with the review round folded into three clean commits: the registry rule, the page and generator, and the gates/enforcement on top. Item by item:

Holding threads — all four fixed and each thread answered and resolved:

  • client.command name (r4368945978): adopted in the stronger wording the independent review also asked for — the row now describes the typed device/session surface through domain methods and client.command, and says the client does not expose every CLI entry point.
  • perf/logs sentence (r4368945989): the row became "Native profiles and traces" and splits perf cpu profile / perf trace from the session-diagnostic trace command; the "perf and logs cover the other platforms" fallback is gone.
  • Per-target error context (r4368945975): each target's collection is wrapped and re-raises as Capability facts failed for target "<id>" (<label>): <reason>.
  • Native-apps sentence (r4368945991): rewritten so native and Flutter apps use what their target exposes, and only React Native / Expo development builds add the React rows.
  • Duplicate cloud labels (r4368945985): unmerged provider columns keep their per-target label; mergeLabel applies only when one provider's columns actually merge. Column merging now signatures on available operations, not command sets.
  • The two P1s and the test-path P2 you marked not-applying (r4368945971, r4368945972, r4368945987): fixed anyway since each was a cheap hardening — Limrun stubbing moved from registerHooks to module.register (works on the 22.12 floor), and the capabilities-matrix gate id now runs the repo-guard itself, so the docs lane executes the negative tests and the same guard rides unit-ci for source changes.

Non-blocking list — all four taken:

  • The stub host's Apple recording seam now delegates to createAppleScreenRecordingHost(...).availability, so the production composer owns the simulator-vs-runner split. The web network seam is also scoped through withWebProvider like the web recording seam, which answers the web network-mode concern you raised — the fact is now collected the way a web request composes it.
  • Row notes no longer state per-target cell values. Notes are target-neutral; per-column honesty moved into checked cells entries on the row: the cell text names the operations behind it (admits/refuses/commands/refusesCommands) and generation fails when a named fact stops holding. renderPage also refuses a cell declared for a target that no longer renders its own column.
  • The three-site composition is one registry export: commandIsAvailableFromFacts(command, facts) in @agent-device/command-registry/planned-operations, used by the daemon inventory and the collector. The separate fact-support export is gone.
  • isDocs: the hunk that made it private is dropped; the tree keeps the original export unchanged.

Smaller design — I went the other way deliberately, for one reason: the annotations are no longer prose the drift check carries, they are inputs the generator validates. SCOPE_ROWS text is compared exactly, and every qualified cell must still name operations the facts admit or refuse (mutation tests: removing one keyboard op while another remains now fails generation, which was the concrete hole). A hand-written page with a generated region keeps the prose unverified and still needs the cell-validation machinery; what shrank instead is the failure surface — the page can no longer disagree with the facts in either direction. The generator half is mechanical (~350 lines of collect/generate/seams), and the annotation file is the only place a reviewer must read prose. I'd rather keep one source of truth with checked prose than two sources with unchecked prose.

Also in this head, from the independent review (verdict was rework; all P1s and P2s addressed, cell qualifications now live in cells because models quote cells):

  • "Yes" semantics: intro says availability does not establish support for every operation/subcommand/model; operation drift is caught by the cell validation, keyed on operations rather than commands.
  • HarmonyOS keyboard cells read "Dismiss and enter; status unsupported"; hosted iOS keyboard cells read "Dismiss only; status and enter unsupported" (verified against platform-harmonyos/runtime.ts and provider-webdriver/platform-runtime.ts).
  • Physical iOS column is labelled "iOS device (CoreDevice)" with the XCTest-backend sentence in the header.
  • Vega open/close cell: "App open/close only; URLs and deep links rejected" (platform-vega rejects URL targets).
  • Network row relabelled "HTTP(S) request inspection" with the log-derived vs browser-history vs TestMu post-session-artifact note; perf cells qualify HarmonyOS (RSS only) and physical iOS (memgraph unavailable — verified in buildAppleMemorySnapshotSupport); macOS appstate cells read "Bound app sessions answer; sessionless discovery unsupported"; Android clipboard cells read "Build-dependent; unavailable on AOSP builds without cmd clipboard"; fold/action-button cells name the model requirement; replay/batch/Maestro scope text replaced with the restricted wording; hardware/gesture notes cover per-button and multi-touch limits.
  • Version masking: the drift check masks only the generation date; the version stamp is compared, and the release workflow's version commit regenerates the page so the stamp moves with the bump.
  • Column header renamed to "Capability and reference".

One thing I did not do: the cross-check's "General" asks (host OS/toolchain requirements, supported OS versions, signing/setup requirements as new content) mostly belong to other pages and are only reflected where a cell needed them (macOS audio host note, Screen Recording permission, clipboard build dependency). pnpm check:affected --run --head 02e1a6c8 passes with exactCommit=true; CI on the new head will confirm the docs lane end to end.

@thymikee
thymikee force-pushed the t3/capabilities-matrix-page branch from 02e1a6c to e9145dc Compare October 25, 2026 12:01
@thymikee

Copy link
Copy Markdown
Member Author

Round 2 addressed on e9145dc98 (pushed with --force-with-lease; three clean commits again: registry rule, page + generator, gates/enforcement).

First, an honest explanation for two of the four items: the round-1 follow-up fixes (the release.yml regenerate line, the Action Button / fold / TV clipboard cells) were authored but lost from the pushed head — my history-rebuild script snapshotted HEAD before committing the working-tree edits, so it checked out file states from the predecessor's WIP instead of mine, and those files silently reverted. The trees at 02e1a6c8 therefore still showed what round 2 saw. That was my bug, not a disagreement; the fixes are restored and now verifiably in the head, and the rebuild was byte-verified this time (git diff against the round-2 state is empty).

1. P1 release restamp — the release step now runs pnpm generate:capabilities inside each retry, after advance, outside the captured next="$(...)" stdout, and before git diff --quiet / git add -u (so the restamp lands in the same version commit). Proven locally on the committed tree, no tags, no release:

  • node scripts/release-version.mjs advance 0.21.32 → version files move to 0.21.33-dev, page stamp stays 0.21.32-dev, and the guard fails — the exact stale failure mode you described.
  • Running the regenerate line the workflow now runs → page restamps to agent-device 0.21.33-dev, git diff shows the page (so git add -u carries it), and the guard passes (11/11). Simulation reverted; tree clean.

2. P2 mutation escape — plain Yes cells are now operation-proved. FactRow.advertises declares each row's promised operations, defaulting to the union of the row commands' declared uses from the registry; a plain Yes renders only while the column admits every promised operation, so removing keyboardDismiss on Android fails generation with Cell "keyboard" x "android-emulator" would read Yes while the facts refuse the advertised operations [keyboardDismiss]. The mutation test now removes each of the three keyboard operations from Android emulator (a plain-Yes column) and asserts generation throws. Where the row's label does not claim every alternative use (snapshot custom-actions, headless boot, runtime hints, multi-touch plans, orientation unlock, audioProbeQuery, no-app captures), the row narrows advertises to what its label promises; those narrowed lists are the exact operation intersections the current facts already satisfy per Yes column, so nothing hides a real refusal behind a narrowing.

3. P2 quoted cells — Android TV emulator and Android TV / Fire OS clipboard cells now read "Build-dependent; unavailable on AOSP builds without cmd clipboard" (same probe path, clipboardFact in packages/platform-android/src/runtime.ts, applies to kind: 'device' TV hardware identically). "Models with an Action Button only" and "Foldable models only" are in the cells themselves (restored; verified rendering on capabilities.md rows for iOS Action Button and the foldable Yes columns).

4. P2 HarmonyOS settings — both HarmonyOS cells now read "Clear app state only; other settings and reads unsupported", with admits: [setSetting] / refuses: [readSetting] still enforced. Verified against the runtime: HarmonyOS declares only write: { 'clear-app-state': available } and its unavailable fact names clear-app-state as the sole supported helper.

Validation: pnpm check:affected --run --head e9145dc98 passes, exactCommit=true, capabilities-matrix passed (receipt run-3OdO2f).

@thymikee
thymikee force-pushed the t3/capabilities-matrix-page branch from e9145dc to 87b4067 Compare October 25, 2026 12:13
@thymikee

Copy link
Copy Markdown
Member Author

Round 3's single finding fixed on 87b40672e (pushed with --force-with-lease; same three-commit shape).

The rule, applied generally. answer now proves every Yes at operation level, plain or qualified: a qualified Yes whose text does not name its own operations is held to the row's advertised operations, exactly like a plain one. A qualification that declares admits/refuses is the row's explicit narrowing for that cell — those lists become the proof, and they are checked in both directions (an admitted operation that stops holding fails, a refused one that appears fails). So the escape was closed where it existed: a commands-only qualification proved command admission and nothing else.

Clipboard cells. All four Android clipboard cells (emulator, device, TV emulator, TV device) now declare admits: ['readClipboard', 'writeClipboard'], matching what their text claims — clipboard's declared uses are exactly those two operations, one per action.

Mutation tests extended in the repo-guard, all asserting generation throws:

  • readClipboard and writeClipboard removed from the Android emulator facts, one at a time (the exact escape you reproduced).
  • One generic non-clipboard case for the commands-only qualification path: perfFrames, perfMemorySnapshot, and perfProfileReport removed from the iOS device facts one at a time — perf stays admitted through its other uses and the perf cell qualifies by command only, so only the new advertised-operations check can catch these.

The other cells that qualify a Yes by command only (Vega open/close, Action Button, fold) were already covered by the generator run against real facts: with the general rule active, generation renders the same page, so the operations their labels promise currently hold; and if any of them stops holding, generation now fails at that cell instead of re-rendering the sentence.

Guard: 12/12. pnpm check:affected --run --head 87b40672e passes with exactCommit=true (receipt run-Y4Fd1E), capabilities-matrix passed.

@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the update. At 87b4067 the earlier findings from the first review are fixed, and I found no new blocking problem in the code. All 33 checks pass, and there are no conflicts. I did not run the generator or the repo-guard, so the mutation outcomes below are reasoned from the code. I also did not compare the columns against a live capabilities --platform X per target, and I did not run the release step or Node 22.12.

The four earlier P1/P2 threads that cubic raised on seams.ts, the matrix gate in unit-core, namingTarget and the Node API row look fixed at this head. So do the mergeLabel, docs.yml gate, perf/logs row and React scope threads. Please resolve them: #3942 (comment), #3942 (comment), #3942 (comment), #3942 (comment), #3942 (comment), #3942 (comment), #3942 (comment), #3942 (comment).

Not blocking, and you can take or leave these: (1) answer() skips the advertised check whenever a qualification declares admits or refuses, so a qualified Yes does not have to account for every advertised operation. For example, the HarmonyOS perf cells (annotations.ts:327-336) only refuse two operations. If perfMemorySample is removed from the harmonyos-emulator facts, perf stays admitted through perfFrames, the refuses still hold, and the page keeps claiming an RSS sample the target no longer offers while every gate passes. The rule should be total: for a qualified Yes, advertised operations must be a subset of admits plus refuses, with admits checked present and refuses checked absent. Please update the HarmonyOS perf cells to match the facts, and add one mutation that removes perfMemorySample from harmonyos-emulator and asserts generation throws. (2) The macOS appstate cells use refusesCommands: ['appstate'] to replace a No with 'Bound app sessions answer; sessionless discovery unsupported' (annotations.ts:259). The positive half repeats the carve-out in isSessionOwnedAppStateAvailable (inventory.ts:310-318) and nothing checks it. Either render the plain No and move the sentence to the row note, or read the carve-out from an exported predicate. (3) In run.ts:424, operations ? ... : [] tests a value collectCommandSupport always returns, and the field comment says 'in registry order' while the list is sorted alphabetically.

Could one rule replace the four overlapping guards (row.advertises, admits/refuses, commands/refusesCommands, and the skip branch)? Each row has an advertised operation set, defaulting from the registry. A plain Yes needs all of it. A qualified cell splits exactly that set into admits (present) and refuses (absent). With that, commands is redundant with a base Yes, refusesCommands goes away, and answer() has one check with no bypass. That would also close the first note above.

The open item is the question above: please adopt the single partition rule or say why the four guards should stay. Either way, the HarmonyOS perf cells and a mutation for them would close the hole in the first note.

The `capabilities` inventory decided command support from a device's
runtime facts inside its own module. Move that rule into the command
registry package, where the use requirements it reads already live, so
every reader of the same facts answers the same way. The rule reads the
command's declared uses itself, so a reader cannot compose the pair
wrong, and it keeps its fail-closed guard: an operation the inspected
owner does not state is unavailable.
The page answers which commands each target supports: iOS simulator and
physical devices, Android emulator and devices, Android TV and Fire OS,
tvOS, Vega OS, HarmonyOS, macOS desktop and apps, Linux, web, and the
four hosted providers. Every cell comes from the same inspectFacts
decision the `capabilities` command answers from, collected per target
against a stub host and a scripted provider wire, so the page cannot
claim a command the runtime would refuse. One partition rule decides
every cell: a row advertises an operation set that defaults to its
commands' declared uses, a plain Yes needs the whole set admitted, and a
qualified cell splits that same set into admits (checked present) and
refuses (checked absent), so removing one operation fails generation
even when the command stays admitted through another use. The seam
fails the run when a runtime starts deciding a cell from a host seam the
matrix has not declared, and the web and Apple recording seams answer
through the same composition the daemon builds. Rows, labels, limits,
and doc links live in one annotations file keyed by command name.
Registers the check id `capabilities-matrix`, wired to the repo-guard
that regenerates the page, diffs it, proves the drift report fires on a
changed cell, and proves the partition rule at every cell by mutation:
removing one admitted operation, or granting one refused operation,
fails generation at that cell for plain and qualified cells alike. It
runs in the docs lane before the checks that read the page, and the same
guard rides unit-ci for source changes. The page's version stamp is
compared, and the release workflow's version step regenerates the page
after the bump, outside the captured version stdout, so the stamp moves
inside the same version commit; only the generation date stays masked.
The page's own path now selects the gate in the affected-checks model.
@thymikee
thymikee force-pushed the t3/capabilities-matrix-page branch from 87b4067 to e1b5aea Compare October 25, 2026 20:23
@thymikee

Copy link
Copy Markdown
Member Author

Adopted the single partition rule on e1b5aea34 (pushed with --force-with-lease; rebased onto current main, which now carries #3940 and #3948).

The single rule. answer() has one check, assertPartition, and no bypass:

  • Each row has one advertised operation set — its commands' declared uses from the registry, narrowed by advertises only where the label claims less than the union of alternative uses.
  • A plain Yes needs the whole set admitted.
  • A qualified cell splits exactly that set: every advertised operation must appear in admits (checked present) or refuses (checked absent), each named exactly once, and nothing may be named that the row does not advertise.
  • commands and refusesCommands are gone from CellQualification; a qualified cell stands in for a Yes only — a No column taking a qualification is now a generation error.

orientation lost its advertises: ['setOrientation'] narrowing because its cells name unlockOrientation; with the set back to the full declared uses, the "Set only; unlock unsupported" cells partition it exactly. The web audio cell now refuses all three host-capture operations instead of one, and the hardware-buttons cell's refusal list gained the three media keys the facts also refuse.

Note 1 — HarmonyOS perf cells. Verified against the facts and the runtime: the HarmonyOS binding implements perfFrames/perfMemorySample/perfMemorySnapshot with frames and the snapshot artifact carrying unavailable payloads, and declares the capture/profile operations unavailable. The cells now read "Process RSS memory sample only; frames, memory-snapshot artifacts, CPU profiles, and native captures report unavailable" — a sentence I corrected rather than kept: frames and snapshots run and report unavailability in their payload, and the split now accounts for all seven perf operations, admits: [perfFrames, perfMemorySample, perfMemorySnapshot], refuses the four capture/profile ones. The mutation you described is in the guard: removing perfMemorySample from harmonyos-emulator throws at "perf" x "harmonyos-emulator", even though perf stays admitted through frames and every declared refusal still holds — the exact hole.

Note 2 — macOS appstate. Took the simpler option: the cells are gone, both macOS columns render the plain No the facts answer, and the bound-session carve-out moved to the row note ("On iOS and macOS a bound session that owns an app identity or surface answers this from its own state, even where the sessionless probe cannot see a foreground app"). No accuracy lost — the note is the daemon behavior, the cell is the fact, and neither pretends to check the other.

Note 3 — run.ts. The operations ? … : [] branch is dropped (collectCommandSupport always returns the map) and the comment now says "sorted alphabetically".

Page diff vs 87b4067 (full page; only these lines move):

-| [Foreground app state](...) | Bound app sessions answer; sessionless discovery unsupported | Bound app sessions answer; sessionless discovery unsupported | No | No |
+| [Foreground app state](...) | No | No | No | No |
+ **Foreground app state**: On iOS and macOS a bound session that owns an app identity or surface answers this from its own state, …   (new row-note)
-| [Performance samples (...)](...) | ... | Process RSS sample only; frames, CPU profiles, and memory snapshots report unavailable | Process RSS sample only; ... |
+| [Performance samples (...)](...) | ... | Process RSS memory sample only; frames, memory-snapshot artifacts, CPU profiles, and native captures report unavailable | Process RSS memory sample only; ... |
-| [HTTP(S) request inspection](...) | Yes | Yes | Yes | Yes | Yes | Yes |   (hosted table)
+| [HTTP(S) request inspection](...) | No | No | No | No | Yes | Yes |
- **HTTP(S) request inspection**: Native app sessions inspect the HTTP(S) entries present in the session app log; …
+ **HTTP(S) request inspection**: Local native app sessions inspect the HTTP(S) entries present in the session app log; … The three WebDriver provider clouds refuse a live dump: their traffic is on the provider side, and TestMu AI can return provider network-log artifacts after the session ends. …

The hosted network row is #3940 doing its job in the facts: the three WebDriver clouds now refuse the live dump, so their columns read No and the note says where their traffic lives; Limrun keeps its own live-session answers. (The P2-4 instruction had said to reflect #3940 once it was on main — this rebase is that moment.)

On the four guards vs one: agreed — the merge also removed a trap I had introduced at 87b4067, where a commands-only qualification silently skipped the operation proof. The split is total now, and the guard's mutation set covers each shape: plain Yes (keyboard ops on Android), commands-implicit qualified Yes (perf ops on iOS device), explicit split (HarmonyOS RSS loss), refusal granted (iOS keyboardStatus appears), and clipboard op removal.

Housekeeping: all eight earlier threads confirmed resolved (0 unresolved review threads). isDocs is private again — its main-side export has no consumer and fallow flagged it once this PR touched the file, so the export hunk is dropped. Rebase notes: #3948 reworked inventory.ts around the same rule; commit 1 now re-applies the move to commandIsAvailableFromFacts on top of it (the apps-logs reason test passes). Fallow's complexity finding on the first assertPartition shape was fixed by splitting the declaration check from the facts check rather than by a suppression.

Guard: 12/12. pnpm check:affected --run --head HEAD passes at e1b5aea34 with exactCommit=true (receipt run-KM3k5Y).

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.

1 participant