Skip to content

refactor(mobile): send the transport pairing and status domain through typed RpcOperations - #20667

Merged
Jinwoo-H merged 21 commits into
mainfrom
step4-transport
Sep 15, 2026
Merged

Jinwoo-H merged 21 commits into
mainfrom
step4-transport

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​43 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​43
Prod 360 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​16616 $\color{#cf222e}{\Huge{\mathbf{−}}}$​379 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​16237

Diff composition (head 6ca467c9ce, vs merge-base with main)

The line count is almost entirely recorded golden JSON, not code. Product source is the small row.

Part Files Added Deleted What it is
Goldens, newly recorded 35 +14,436 0 Recorded RPC transcripts for the migrated call sites; pinned against the baseline before the refactor, unchanged after it
Goldens, header re-digest 306 +306 -306 Existing goldens where only the recorderSha256 header line moved; zero body lines changed (verified with the inert-proof diff in the sections below)
pilot-scenarios.json 1 +1,135 0 Scenario manifest entries for the new goldens
Test and recorder infra 8 +631 -3 .test.ts files and mobile/src/test-support/rpc-recording/
Product source 11 +151 -70 The actual migration: mobile/src non-test files
Total 361 +16,659 -379

What

Step 4, transport domain: the pairing / probing / capability call sites in mobile/src/transport/ move onto typed RpcOperations. Three operations over three methods in two new modules, zero new acceptance policies, one reader per method:

  • host-status-probe-operations.ts — hostStatusProbe on status.get, success-result-or-skip. All three transport callers treat a refusal as an absent answer. It is a third named policy on status.get, separate from the two in tasks, because an operation name is what a decode failure reports and transport must not import tasks.
  • mobile-relay-pairing-operations.ts — relayCredentialProvision and relayPairingEndpointsRead, both require-result-or-throw, which reuses the exact code: message text the four call sites each spelled by hand. Three sites still read the raw envelope for method_not_found before interpreting, because an unknown method means the host build has no relay, not that the install failed.

Reply parsing stays where it was (step 7): every reader is rpcUncheckedPayloadReader and the zod contract schemas still run at the call sites.

Inventory

src/transport/: 18 files / 39 refs → 12 files / 28 refs. Six entries deleted; runtime-capability-probe.ts 2 → 1. The nine RPC-client-stack files that implement the port are unchanged and each now carries its own reason in the inventory comment. Three genuine holdouts, reasons in the inventory:

  • pairing-relay-candidate.ts (4) decorates a PairingCandidateClient with director recovery and forwards whatever method it is handed; it implements the port. Its one chosen method string now comes from hostStatusProbe.operation.method.
  • mobile-runtime-capability-negotiation.ts (2) sends through the physical clients' authenticated-but-not-yet-connected path, which is not an RpcClient (it deadlocks on waitForConnected if given one) and no recording can reach it, so a migration could not be shown to preserve behaviour.
  • runtime-capability-probe.ts (1) is a parameter type only: the request goes through hostStatusProbe, but push-registration.ts holds only a sender, so widening to RpcClient breaks another domain's file.

Evidence

Oracle parity. Commit 1 records the scenarios against main's product code (git worktree add --detach at the pinned baseline with this branch's recorder and manifest overlaid); commit 2 (the refactor, 11 product files) touches zero goldens; commit 3 adds one scenario recorded the same way. Re-verified after the fact: a fresh overlay run at the baseline emitted all 242 goldens and diff -rq against the tree reported no differences, and the recording suite replays 296 green on the migrated tree. Note for repeaters: git archive cannot serve as the overlay tree because the record fence shells out to git diff; use git worktree add --detach.

Mutation census, each applied / run / reverted:

mutation failures
status.get repointed to ui.get 20
pairing.provisionRelay repointed 15
pairing.getEndpoints repointed 18
hostStatusProbe policy swapped 18
relayCredentialProvision policy swapped 3
relayPairingEndpointsRead policy swapped 5
pre-profile coordinator interprets inside the request chain 2
delivery-unknown mark dropped by wrapping 43
cutover flag forced false 1

No survivors; every migrated reference reaches a recorded wire.

Reply matrix. Seven new families, all matrixed (15 matrix goldens); family census and live-site inventory check green.

Measurements

~44 min start to green · 3 readers, no method needing two · 0 new policies · goldens 208 / 3.8 MB → 242 / 4.3 MB, scenarios 109 → 128, families 34 → 41 · awkward: host-status-gates.ts needs a SAFETY cast because the reader returns unknown and the caller re-types it; the refusal-tolerant provision sites must inspect the raw envelope before interpreting, so request and interpret cannot be one expression; the relay modules build dependencies at module scope, so recording them needed native substitutes for expo-crypto, secure-store and async-storage.

Not done, deliberately

  • PushClient in src/notifications/push-registration.ts not widened (another domain; would take runtime-capability-probe.ts to zero). Ticket.
  • mobile-relay-rpc-session.ts's own pairing.getEndpoints acceptance throws on error.code alone and disagrees with the four migrated sites. Behaviour change; ticket.
  • No latent bug the goldens pin was fixed.

Adapters are placed where main has them today; this branch merges main and re-records once when #20662 (adapter digest) lands.

Gates

pnpm --dir mobile typecheck 0 · pnpm --dir mobile test 581 files, 5057 passed / 6 skipped · pnpm tc 0 · check:code-quality:changed 0 over 18 files · check:react-doctor:changed clean · oxfmt --check . from mobile/ clean · oxlint clean on changed files and the whole mobile/ tree.

After #20662 landed (2026-09-15)

Merged main at 6a11a0b8e6; adapters moved into adapters/ and registered. Round-1 items applied: the three partial native substitutes are now throwing proxies (an unlisted member read fails instead of recording undefined; the ES-module interop probe for __esModule is the one exempt key, because import * as reads it before copying members), the unread expo-secure-store substitute is deleted, nine dead fixture exports dropped, credentialHash deduped. Re-recorded at main's pin c6a72169843e (an interim repin to 6a11a0b8e6 was reverted in 55c7d2916a; the two pins record identical bytes).

Golden movement: all 242 goldens changed against the pre-merge head and all 208 pre-existing goldens changed against main, every changed line a header line (recorderSha256 against main; baseline/recorderSha256/adapterSha256 against the pre-merge head). The engine re-digest is unavoidable for this PR: it adds native-mounting-substitutes.ts and relay-pairing-fixtures.ts and a loader hook that consults the first, because the relay modules build their dependencies at module scope and no adapter can substitute them, and the seam forbids adapters importing a sibling for shared fixtures. Consequence: branches still open when this lands re-record headers once. Recording suite 308 passed against the migrated tree; full mobile suite 5079 passed, 6 skipped; all gates green.

Review round 2 (2026-09-15)

Ship after fixes; applied in 59cfcc64b1 and re-recorded in 6fda563942. The store substitute's throwing proxy answered the module system's __esModule probe with a throwing function, so a default import bound proxy.default and AsyncStorage.getItem read as undefined (a TypeError instead of the named "native store reached" error); both traps now leave that marker undefined, and native-mounting-substitutes.test.ts pins the named error under a default import, the marker, and an unlisted member. recordingRandomBytes was the expo-crypto substitute's own default handed back to it, so it is deleted along with its stranded comment. Re-record moved recorderSha256 on all 242 goldens and adapterSha256 on the 10 relay credential goldens, zero non-header lines. Against main the branch remains 208 modified (recorderSha256 only) and 34 added.

Bot-comment triage before merge (2026-09-15)

Fixed on this branch (8d4b6e06c2, d6baaf3a7b, 4df87db5a4, 6ca467c9ce):

  • Pairing-race adapter returned the raw PairingCandidate, whose client has function members, so the recorder threw Unsupported observation: function and every pairing-race golden baked an unhandled-rejection effect with the race settlement left pending. The adapter now returns winner.path; all four race goldens record a fulfilled (direct/relay) or rejected (both-refused) settlement with the loser closed.
  • direct-wins-the-tie recorded relay. The runner flushes after every step and the scenario DSL has no way to deliver two completions in one microtask, so the product's same-tick direct preference is not recordable without an engine change. Renamed to transport-pairing-race-relay-completes-first and added the mirror transport-pairing-race-direct-completes-first; the two matrix goldens inherit the checkpoint name.
  • Substitutes doc named the wrong module ("the secret store"); it now names the async-storage substitute. This is an engine file, so it re-digests every golden header; split into its own commit + re-record so the adapter-only proof at d6baaf3a7b stays clean (51 headers moved, all from refactor(mobile): send the github.* PR surface and the diff-review loaders through typed RpcOperations #20668's merge).

Declined: gates-drop "checkpoints identical" (the hook's public output after a drop is identical by design); __esModule interop (already fixed with a test).

Proofs vs main 44268d9616: 306 M all recorderSha256-only, 0 non-header lines, 35 A, 0 D; product source byte-identical to the pre-fix head except the merge-touched inventory. Gates green (mobile typecheck, pnpm tc, mobile test 5181 passed, code-quality:changed, react-doctor, oxfmt, oxlint); recording suite re-run by the lead: 11 files / 410 passed.

…main

Adds seven recording families for `mobile/src/transport/`, recorded from main's
unmigrated product code before any refactor: the protocol-gate hook, the retrying
capability probe, the pairing candidate race, credential rotation, direct-to-relay
upgrade, startup pairing recovery and first pairing.

The relay modules build their `defaultDependencies` at module scope, so merely
referencing `Platform.OS` or a storage-backed loader threw before an adapter could
override it. `native-mounting-substitutes.ts` separates reference from use: react,
zod and @noble/hashes are the real libraries, expo-crypto routes through the Web
Crypto the scheduler already pins, and the two secret stores throw when called.

`baseline` repins to fc525c3 because main's tree no longer matches the pinned
50e752f. All 241 goldens re-recorded; main's 208 move only `baseline` and
`recorderSha256`, and no `scenarioSha256` or recording body moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…h typed RpcOperations

Migrates 11 of the 18 raw-port references in `mobile/src/transport/`: the protocol
gate, the runtime capability probe, the pairing candidate race, credential rotation,
the direct-to-relay upgrade, startup pairing recovery and first pairing.

Three operations over three methods, zero new acceptance policies. `status.get` gains
a third named policy — `status.transport-probe-or-skip` — because all three transport
callers treat a refusal as an absent answer, which is the Tasks create-drawer policy
but not the Tasks hydration one; transport cannot import tasks, and the name is what a
decode failure reports. `pairing.provisionRelay` and `pairing.getEndpoints` are
`require-result-or-throw`, which reuses the same `code: message` text the four call
sites each spelled by hand. Three sites read the raw envelope for `method_not_found`
before interpreting, because an unknown method means "this build has no relay" rather
than "the install failed". Reply parsing stays at the call sites: the readers are
unchecked and the zod contract schemas run where they ran before.

No wire change and no behaviour change: all 241 goldens replay green and this commit
touches none of them.

Two files stay on the raw port and now carry their own reason in the inventory.
`pairing-relay-candidate.ts` decorates a PairingCandidateClient with director recovery,
so it implements the port rather than calling it; its one chosen method string now
comes from hostStatusProbe. `mobile-runtime-capability-negotiation.ts` sends over the
physical clients' pre-`connected` authenticated path, which no recording can reach.
`runtime-capability-probe.ts` drops to its parameter type alone.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…rozen baseline

The reply matrix drives every scripted reply, so it already covers a transport
rejection at each migrated site. It cannot cover the one signal the probe reads out
of a rejection: `isLogicalClientCutoverError` chooses a 250 ms re-ask over the 1 s
failure backoff, and no golden made that choice observable.

`transport-capability-probe-cutover-reasks-fast` migrates the logical client mid-probe
and binds the replacement request at exactly 250 ms, so a re-ask moved in either
direction fails the binding rather than recording a different number. The recorded
rejection carries `LogicalClientCutoverError`, its `Connection closed` cause and
`isRpcDeliveryUnknown`.

Recorded from a `git archive` of the pinned baseline with this branch's recorder laid
over it, per the recorder README: the migrated tree can no longer satisfy the fence.
The same run reproduced the other 241 goldens byte-for-byte.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
@pullfrog

pullfrog Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@greptile-apps

greptile-apps Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Too many files changed for review (361 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change introduces shared typed RPC operations for host status, relay credential provisioning, and relay endpoint reads. Existing transport callers now use these operations. Recording support adds native substitutes, deterministic relay fixtures, mount adapters, and pilot scenarios for transport and pairing flows. New golden fixtures capture these scenarios. Existing golden fixtures refresh recorder hashes while retaining recorded scenario data.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 6fda5

The new transport recordings do not reliably verify direct tie-breaking, race settlement, or gate invalidation after disconnect. Correct these test harness and scenario defects before merging so regressions in pairing and status handling are not accepted.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.90% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 23 files. (242 skippe… Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description provides extensive technical scope, rationale, testing evidence, and validation results. However, it does not follow the required template fully: it omits the required Linked Issue ent… Add the required template sections and complete them. Include a real issue reference under Linked Issue using Fixes #<number>. Add Visual Proof with N/A and a brief explanation if there is no visual or interaction change. Add a concise …
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: migrating mobile transport pairing and status call sites to typed RpcOperations.
Full details: Docstring Coverage

Explanation

Docstring coverage is 35.90% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 23 files. (242 skipped: 242 unsupported.)

Full details: Description check

Explanation

The description provides extensive technical scope, rationale, testing evidence, and validation results. However, it does not follow the required template fully: it omits the required Linked Issue entry, Visual Proof section with N/A justification, explicit ELI5 section, and checklist confirmations.

Resolution

Add the required template sections and complete them. Include a real issue reference under Linked Issue using Fixes #&lt;number&gt;. Add Visual Proof with N/A and a brief explanation if there is no visual or interaction change. Add a concise ELI5 section and complete the Testing, AI Disclosure, Agent skill upstream boundary, Notes, and Checklist sections as applicable.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 1ede4e34-6cc2-4128-a4e7-32ed7b1626a4

📥 Commits

Reviewing files that changed from the base of the PR and between c6a7216 and 45792fe.

📒 Files selected for processing (261)
  • mobile/rpc-foundation/goldens/b1.json
  • mobile/rpc-foundation/goldens/b2.json
  • mobile/rpc-foundation/goldens/b3.json
  • mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/inventory-repeat-query.json
  • mobile/rpc-foundation/goldens/lifecycle-b3.json
  • mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json
  • mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json
  • mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json
  • mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json
  • mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json
  • mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json
  • mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json
  • mobile/rpc-foundation/goldens/sc-base-ref-default.json
  • mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json
  • mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json
  • mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json
  • mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json
  • mobile/rpc-foundation/goldens/sc-commit-message-canceled.json
  • mobile/rpc-foundation/goldens/sc-commit-message-generated.json
  • mobile/rpc-foundation/goldens/sc-create-existing-review.json
  • mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json
  • mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json
  • mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json
  • mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json
  • mobile/rpc-foundation/goldens/sc-eligibility-fetched.json
  • mobile/rpc-foundation/goldens/sc-history-loaded.json
  • mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json
  • mobile/rpc-foundation/goldens/sc-pr-link-read.json
  • mobile/rpc-foundation/goldens/sc-pr-link-set.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-publish.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-push.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json
  • mobile/rpc-foundation/goldens/sc-reveal-first-poll.json
  • mobile/rpc-foundation/goldens/sc-reveal-timeout.json
  • mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json
  • mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-review-commit-rejected.json
  • mobile/rpc-foundation/goldens/sc-review-commit.json
  • mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json
  • mobile/rpc-foundation/goldens/sc-review-status-normalized.json
  • mobile/rpc-foundation/goldens/schedules-b3.json
  • mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json
  • mobile/rpc-foundation/goldens/settings-home-coalesced.json
  • mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refused.json
  • mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json
  • mobile/rpc-foundation/goldens/settings-new-tab-refused.json
  • mobile/rpc-foundation/goldens/settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json
  • mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refused.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-refused.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-write.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json
  • mobile/rpc-foundation/goldens/tw-capabilities-advertised.json
  • mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json
  • mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-created.json
  • mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json
  • mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json
  • mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json
  • mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json
  • mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json
  • mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json
  • mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json
  • mobile/rpc-foundation/pilot-scenarios.json
  • mobile/src/test-support/rpc-recording/native-mounting-substitutes.ts
  • mobile/src/test-support/rpc-recording/operation-module-loader.ts
  • mobile/src/test-support/rpc-recording/pairing-journal-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/pilot-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/relay-credential-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/relay-pairing-fixtures.ts
  • mobile/src/test-support/rpc-recording/transport-status-mount-adapters.ts
  • mobile/src/transport/host-status-gates.ts
  • mobile/src/transport/host-status-probe-operations.ts
  • mobile/src/transport/mobile-relay-credential-rotation.ts
  • mobile/src/transport/mobile-relay-direct-upgrade.ts
  • mobile/src/transport/mobile-relay-pairing-operations.ts
  • mobile/src/transport/mobile-relay-pairing-recovery.ts
  • mobile/src/transport/pairing-candidate-race.ts
  • mobile/src/transport/pairing-relay-candidate.ts
  • mobile/src/transport/pre-profile-pairing-coordinator.ts
  • mobile/src/transport/runtime-capability-probe.ts
  • mobile/src/transport/unvalidated-rpc-request-port-inventory.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.

Comment on lines +443 to +451
"id": "transport-pairing-race-direct-wins.normal:direct-wins-the-tie",
"observation": {
"sender": ["26f802fad080", "36caf183b988"],
"payloads": ["1e5b32902af7", "c0c86e67c300"],
"settlements": {
"race": "9270aeb7d9c6"
},
"state": "a7d5becc0aed",
"effects": ["f203320e31e9", "2d40d1d38104"]

@coderabbitai coderabbitai Bot Sep 14, 2026 •

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Batch both status completions before one flush.

runRecording flushes after every step. The relay completion therefore queues and runs racePairingCandidates’ selection microtask before the direct completion becomes visible. Both matrix checkpoints use this ordering, so both select relay and close the direct candidate. Batch both completions before one flush, then regenerate all affected goldens. Do not change only the expected state.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Valid. Fixing on this branch before merge: the pairing-race adapter will return a serializable projection (winner.path) so the race settlement records and no Unsupported observation effect is baked in, and the tie scenarios will either deliver both completions before one flush or be renamed to what they actually record with a mirror direct-first scenario added.

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.

@Jinwoo-H, thanks for confirming.

The fix should remove the Unsupported observation: function record and capture the race result as winner.path. The updated tie scenarios should also make the completion order explicit, or use scenario names that match the recorded ordering. I will verify the regenerated golden recordings after the update.

You are interacting with an AI system.

"value": {
"category": "Error",
"isRpcDeliveryUnknown": false,
"message": "Unsupported observation: function"

@coderabbitai coderabbitai Bot Sep 14, 2026 •

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the scenario registration and the recorder paths that create or observe
# `unhandled-rejection` and `Unsupported observation` records.
rg -n -C 5 \
  'transport-pairing-race-relay-wins-when-direct-refused|unhandled-rejection|Unsupported observation: function|candidate-closed' \
  mobile/rpc-foundation mobile/src/test-support

Repository: stablyai/orca

Length of output: 38326


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- Unsupported observation bindings ---'
rg -n -C 8 'Unsupported observation|observe\(|observation' mobile/src mobile/rpc-foundation --glob '*.ts' --glob '*.tsx' --glob '*.json' | head -n 500
printf '%s\n' '--- Pairing race scenario references ---'
rg -n -C 12 'transport-pairing-race|relay-wins-when-direct-refused|direct-refused|pairing-race' mobile/src mobile/rpc-foundation --glob '*.ts' --glob '*.tsx' --glob '*.json' | head -n 500
printf '%s\n' '--- Recorder support files ---'
find mobile/src/test-support/rpc-recording -maxdepth 2 -type f -print | sort

Repository: stablyai/orca

Length of output: 50370


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- exact Unsupported observation matches ---'
rg -n -l --fixed-strings 'Unsupported observation' . --glob '!*.json' --glob '!node_modules/**' | while read -r f; do
  echo "--- $f"
  rg -n -C 12 --fixed-strings 'Unsupported observation' "$f"
done
printf '%s\n' '--- exact scenario-id matches in source ---'
rg -n -l --fixed-strings 'transport-pairing-race-relay-wins-when-direct-refused' mobile --glob '*.ts' --glob '*.tsx' --glob '*.json' | while read -r f; do
  echo "--- $f"
  rg -n -C 20 --fixed-strings 'transport-pairing-race-relay-wins-when-direct-refused' "$f"
done
printf '%s\n' '--- pairing-race source files and symbols ---'
rg -n -l 'pairing-race|pairingCandidateRace|candidateRace' mobile/src --glob '*.ts' --glob '*.tsx' | sort

Repository: stablyai/orca

Length of output: 4077


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- scenario definition ---'
sed -n '7030,7125p' mobile/rpc-foundation/pilot-scenarios.json
printf '%s\n' '--- recording value capture ---'
cat -n mobile/src/test-support/rpc-recording/recording-values.ts
printf '%s\n' '--- recording runner ---'
cat -n mobile/src/test-support/rpc-recording/run-recording.ts
printf '%s\n' '--- transport-status adapter ---'
cat -n mobile/src/test-support/rpc-recording/transport-status-mount-adapters.ts
printf '%s\n' '--- pairing race implementation ---'
cat -n mobile/src/transport/pairing-candidate-race.ts

Repository: stablyai/orca

Length of output: 21502


Do not accept the recorder's unhandled rejection as a golden effect.

runRecording passes the race promise to observeSettlement. racePairingCandidates resolves it with a candidate whose client.close is the candidateClient callback. captureValue(result) then throws Unsupported observation: function. The rejected Promise returned by that fulfillment handler is not handled, so the recorder records unhandled-rejection even though the outcome is relay. Return a serializable winner projection from the adapter before regenerating the golden.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Valid. Fixing on this branch before merge: the pairing-race adapter will return a serializable projection (winner.path) so the race settlement records and no Unsupported observation effect is baked in, and the tie scenarios will either deliver both completions before one flush or be renamed to what they actually record with a mirror direct-first scenario added.

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.

@Jinwoo-H, thanks for confirming the issue.

The serializable winner.path projection addresses the unhandled rejection in the recording path. Updating the tie scenarios will also make their names and recorded scheduling behavior consistent.

Please post an update after the branch contains the change. I will then verify the adapter behavior and the regenerated golden.

🐇

You are interacting with an AI system.

Five pairing sites nested `parse(interpret(await request(...)))` three deep with the
await innermost, so the send was the last thing a reader found. Bind the reply first
and interpret it on its own line, which is the shape main had before the migration.

No behaviour change: the request still settles before interpretation and the barrier
is unmoved, so no golden shifts.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Takes main's version of every golden this branch also carries, keeps this branch's own
34, and keeps both sides' scenarios. `pilot-mount-adapters.ts` takes main's merger
wholesale, and the three transport adapter modules move into #20662's `adapters/` seam
and register there, so each of their goldens is pinned by `adapterSha256` on its own
module instead of by `recorderSha256` on all of them.

The loader keeps this branch's native mounting substitutes and drops the mutant table
import main removed: the relay modules build their dependencies at module scope, so
nothing under `adapters/` can mount them without a substitution the engine performs.
`relay-pairing-fixtures.ts` stays on the engine side because the seam forbids an adapter
module from importing a sibling and three modules share it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…ng undefined

Three substitutes were plain object literals standing in for whole modules, so any member
the table did not list read as `undefined` rather than failing — the opposite of what the
file's own default proxy does, and a silent one: `react-native` alone is requested 354
times across the goldens, and `platformSelect`, `view` and `styleSheet` all read undefined.
A recording that reaches an unsubstituted native member is not evidence of anything,
because the product on a device would call it.

Also drops the `expo-secure-store` substitute, which no recording requests, and corrects
the doc's "fails loudly" claim: the throw is real, but `host-app-version-store.ts` catches
it and degrades to its unread state, which is what it does on a device too.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…reads

Nine fixture constants and `relayEndpoint` were exported with no reader outside their own
module, and `credentialHash` was copied verbatim into both relay adapter modules. The
adapter seam forbids one module under `adapters/` from importing a sibling, so the shared
form has to live with the fixtures both already import.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Repins `baseline` to 6a11a0b and records all 242 from a detached worktree of that
commit with this branch's recorder overlaid, per the README's migration-branch procedure.

Two header fields move and nothing else: `baseline`, and `recorderSha256` for the 208
goldens main also carries, because this branch adds `native-mounting-substitutes.ts` and
`relay-pairing-fixtures.ts` to the engine and teaches the loader to consult the first.
Against the pre-merge tree every one of the 242 is header-only, so no recorded byte moved.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
`import * as ExpoCrypto from 'expo-crypto'` transpiles to an interop helper that reads
`__esModule` before copying members, so throwing on it fails the module system's own probe
rather than an unsubstituted API read. Four relay families could not record.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The changed-code casting gate rejects `key as string`, and the trap has a typed read that
needs no assertion.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
`recorderSha256` only; every recorded byte is unchanged from the previous re-record.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
Two source conflicts and 208 golden conflicts, all mechanical.

`operation-module-loader.ts`: both sides dropped the shared React import; kept our
`native-mounting-substitutes` import, since #20667 has not landed and the substitutes module is
still this branch's to carry.

`adapters/mounted-operation-modules.ts`: took our register, which is main's nine entries verbatim
plus this branch's five. Every line of main's version is present in ours.

All 208 conflicting goldens exist on main and took main's version. This branch's own 55 goldens
were added, not conflicted, so they still carry the pre-#20662 header shape; the re-record that
follows brings them onto the new digests.

`pilot-scenarios.json` merged to the exact union of both sides, 136 scenarios, and both sides
already pinned the same baseline.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
Recorded from a detached worktree at c6a7216
with this branch's rpc-recording/ and pilot-scenarios.json overlaid, per the
README's migration-branch procedure. Only header fields moved; no body line in
any golden changed.

recorderSha256 moved on all 208 pre-existing goldens: this branch adds
native-mounting-substitutes.ts at the top of rpc-recording/ and routes
operation-module-loader.ts through it, and both are recorder-engine inputs.
That resolves itself when #20667 lands the same substitution on main.

adapterSha256 moved on exactly four goldens, all from the late-worktree-list
scenario's edit to adapters/agent-history-mount-adapters.ts:
aivault-history-scan-fulfilled, aivault-history-scan-unsupported,
matrix-aivault.history-aivault.listsessions-1,
matrix-aivault.history-status.get-1.

aivault-history-scan-worktrees-late.json is new.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The merge brought main's pin c6a7216. Repinning it to the branch's own
merge-base rewrote the `baseline` header of all 208 pre-existing goldens for no
behavioural reason, so put main's pin back and re-record at that commit through
a detached baseline worktree with this branch's recorder overlaid.

Only `baseline` moved in all 242 goldens; no recorded frame, settlement or
sender line changed. Against origin/main the 208 pre-existing goldens now differ
in `recorderSha256` alone, which this branch's two added engine files force.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog 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.

ℹ️ No critical issues — two doc/comment nits inline.

Reviewed changes

Reviewed the six commits pushed since the prior run (main merge e7743c66fd through 70410a626c). This delta is test-support only: no file under mobile/src/ outside test-support changed, so there is no product-behavior surface.

  • Re-recorded every golden at main's tip — all 242 goldens move their header to baseline: 6a11a0b8e6 / the new recorderSha256; no values line changed, so the recorded wire behavior is unchanged.
  • Made the native substitutes strict — partialNativeModule now throws on any unlisted member read for react-native, expo-crypto and @noble/hashes/sha256, so an unmounted native API fails loudly instead of recording undefined.
  • Dropped the unused expo-secure-store substitute — only @react-native-async-storage/async-storage remains as a call-time-throwing store.
  • Deduped the credential-hash fixture loader — credentialHash moves into relay-pairing-fixtures.ts, both adapters call it, and the unread fixture exports are de-exported.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

*
* Every substitute that stands in for part of a module keeps the default's shape: a member nobody
* listed throws on the read rather than resolving to `undefined`, because an undefined native
* member is not a recording of anything — the product would call it. The secret store is the one

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.

With expo-secure-store now absent from the table, the only store left that throws on call is @react-native-async-storage/async-storage — which is not "the secret store" — and a recording that reaches expo-secure-store now fails at member read with the loader's generic Unspecified native mounting dependency error, not the store error this paragraph promises. Reword to name the async-storage substitute.

@coderabbitai coderabbitai 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.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 5e825368-b2c9-4c80-924e-34bf539da441

📥 Commits

Reviewing files that changed from the base of the PR and between 1970deb and 70410a6.

📒 Files selected for processing (250)
  • mobile/rpc-foundation/goldens/b1.json
  • mobile/rpc-foundation/goldens/b2.json
  • mobile/rpc-foundation/goldens/b3.json
  • mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/inventory-repeat-query.json
  • mobile/rpc-foundation/goldens/lifecycle-b3.json
  • mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json
  • mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json
  • mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json
  • mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json
  • mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json
  • mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json
  • mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json
  • mobile/rpc-foundation/goldens/sc-base-ref-default.json
  • mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json
  • mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json
  • mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json
  • mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json
  • mobile/rpc-foundation/goldens/sc-commit-message-canceled.json
  • mobile/rpc-foundation/goldens/sc-commit-message-generated.json
  • mobile/rpc-foundation/goldens/sc-create-existing-review.json
  • mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json
  • mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json
  • mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json
  • mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json
  • mobile/rpc-foundation/goldens/sc-eligibility-fetched.json
  • mobile/rpc-foundation/goldens/sc-history-loaded.json
  • mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json
  • mobile/rpc-foundation/goldens/sc-pr-link-read.json
  • mobile/rpc-foundation/goldens/sc-pr-link-set.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-publish.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-push.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json
  • mobile/rpc-foundation/goldens/sc-reveal-first-poll.json
  • mobile/rpc-foundation/goldens/sc-reveal-timeout.json
  • mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json
  • mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-review-commit-rejected.json
  • mobile/rpc-foundation/goldens/sc-review-commit.json
  • mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json
  • mobile/rpc-foundation/goldens/sc-review-status-normalized.json
  • mobile/rpc-foundation/goldens/schedules-b3.json
  • mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json
  • mobile/rpc-foundation/goldens/settings-home-coalesced.json
  • mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refused.json
  • mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json
  • mobile/rpc-foundation/goldens/settings-new-tab-refused.json
  • mobile/rpc-foundation/goldens/settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json
  • mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refused.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-refused.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-write.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json
  • mobile/rpc-foundation/goldens/tw-capabilities-advertised.json
  • mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json
  • mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-created.json
  • mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json
  • mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json
  • mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json
  • mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json
  • mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json
  • mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json
  • mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json
  • mobile/rpc-foundation/pilot-scenarios.json
  • mobile/src/test-support/rpc-recording/adapters/mounted-operation-modules.ts
  • mobile/src/test-support/rpc-recording/adapters/pairing-journal-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/adapters/relay-credential-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/adapters/transport-status-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/native-mounting-substitutes.ts
  • mobile/src/test-support/rpc-recording/operation-module-loader.ts
  • mobile/src/test-support/rpc-recording/relay-pairing-fixtures.ts
🚧 Files skipped from review as they are similar to previous changes (110)
  • mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json
  • mobile/rpc-foundation/goldens/sc-reveal-timeout.json
  • mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json
  • mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json
  • mobile/rpc-foundation/goldens/sc-review-commit-rejected.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json
  • mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json
  • mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json
  • mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json
  • mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json
  • mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-refused.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json
  • mobile/rpc-foundation/goldens/sc-create-existing-review.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json
  • mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json
  • mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json
  • mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json
  • mobile/rpc-foundation/goldens/inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json
  • mobile/rpc-foundation/goldens/b3.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json
  • mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/b2.json
  • mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-publish.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json
  • mobile/rpc-foundation/goldens/sc-review-commit.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json
  • mobile/rpc-foundation/goldens/settings-task-write.json
  • mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json
  • mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json
  • mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json
  • mobile/rpc-foundation/goldens/tw-capabilities-advertised.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json
  • mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json
  • mobile/rpc-foundation/goldens/sc-history-loaded.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json
  • mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json
  • mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment on lines +118 to +125
"2d40d1d38104": {
"name": "unhandled-rejection",
"value": {
"category": "Error",
"isRpcDeliveryUnknown": false,
"message": "Unsupported observation: function"
}
},

@coderabbitai coderabbitai Bot Sep 15, 2026 •

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Project a serializable race result in the pairing-race adapter. racePairingCandidates resolves a PairingCandidate whose client contains function-valued members. The adapter returns this raw promise, so observeSettlement passes the candidate to captureValue, which throws Unsupported observation: function. Both matrix goldens keep the race settlement pending and record an unhandled-rejection effect.

Return winner.path from the settlement promise. Rethrow rejected races after updating outcome. Then re-record both goldens.

Minimal correction
-          settled.then(
+          return settled.then(
             (winner) => {
               outcome = winner.path
+              return winner.path
             },
             (error: unknown) => {
               outcome = `failed: ${error instanceof Error ? error.message : String(error)}`
+              throw error
             }
           )
-          return settled

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Valid. Fixing on this branch before merge: the pairing-race adapter will return a serializable projection (winner.path) so the race settlement records and no Unsupported observation effect is baked in, and the tie scenarios will either deliver both completions before one flush or be renamed to what they actually record with a mirror direct-first scenario added.

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.

@Jinwoo-H, thanks for confirming the finding.

The proposed change addresses the non-serializable settlement value. It also removes the recorded Unsupported observation: function rejection. The scenario update should make the tie behavior explicit and test both candidate orderings.

You are interacting with an AI system.

The store substitute answered every key with a throwing function, including
`__esModule`. TypeScript's `__importDefault` reads that marker and, finding it
truthy, binds the default import to the trap's own function instead of the
module record, so all five consumers saw `AsyncStorage.getItem` as undefined.
A recording that reached `host-app-version-store.ts` failed with
`TypeError: AsyncStorage.getItem is not a function` rather than the named
`Native store reached during recording: ….getItem`, and an `await import()` of
the store rejected on `.then` for the same reason. Both traps now leave the
marker undefined, and the doc states that invariant.

Also drop `recordingRandomBytes`: its body and the `expo-crypto` substitute's
are the same call into the seeded `getRandomValues`, and both relay entry
points already default `randomBytes` to `ExpoCrypto.getRandomBytes`, so the two
injections were passing the default back to itself. Its orphaned doc comment,
left stacked above `credentialHash` by the dedupe commit, goes with it.

The new suite does not record, so `recorderSha256` does not pin it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
`recorderSha256` moved in all 242 goldens, and `adapterSha256` in the 10
`relay.credential-rotation` and `relay.direct-upgrade` goldens whose adapter
stopped injecting `randomBytes`. Nothing else moved: no recorded frame,
settlement, effect or sender line differs, which is the claim that the default
the adapter was passing back to itself and the substitute it resolved to were
always the same function.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog 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.

ℹ️ No new issues in this delta. One prior doc/comment nit (native-mounting-substitutes.ts still calls the async-storage substitute "the secret store") remains unaddressed and its thread is left open, so this is not an approval.

Reviewed changes

Reviewed the two commits pushed since the prior review (55c7d2916a..6fda563942). This delta stays entirely inside mobile/src/test-support/ plus golden headers — no product-code change.

  • Fixed the store substitute's __esModule trap — unusableNativeStore now answers the module system's interop probe with undefined, so a transpiled default import binds the module object instead of the trap's own throwing function. Previously importDefault(...).default bound a function and AsyncStorage.getItem read as undefined, producing a TypeError instead of the named "Native store reached during recording" error; partialNativeModule already handled the marker correctly.
  • Added native-mounting-substitutes.test.ts — pins the named store error under a default import, __esModule === undefined for both trap types, and the unlisted-member throw. Confirmed falsifiable: reverting the unusableNativeStore fix fails 2 of the 3 tests.
  • Deleted the redundant recordingRandomBytes — both credential callers already default randomBytes to ExpoCrypto.getRandomBytes, which the expo-crypto substitute routes through the pinned Web Crypto, so the adapter injection (and the fixture helper with its stranded comment) is gone.
  • Re-recorded the goldens — every change is a header line: recorderSha256 on all 242, adapterSha256 only on the 10 relay-credential goldens, zero values changes. The recording suite replays green on this tree (311 passed / 3 skipped).

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (3)

🟠 Major · Return a serializable result from the pairing-race adapter. · mobile/src/test-support/rpc-recording/adapters/transport-status-mount-adapters.ts:77-109

77-109: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return a serializable result from the pairing-race adapter.

racePairingCandidates resolves to a raw PairingCandidate, whose candidateClient(...) value contains functions. When action returns settled, recording recursively projects those functions and raises Unsupported observation: function. Race recordings can therefore remain pending instead of recording the winner.

Return the chained promise. Return winner.path on success. Record the failure outcome, then rethrow the same error.

          return settled.then(
            (winner) => {
              outcome = winner.path
              return winner.path
            },
            (error: unknown) => {
              outcome = `failed: ${error instanceof Error ? error.message : String(error)}`
              throw error
            }
          )
🟡 Minor · Expose the verification state in the gate recording. · mobile/src/test-support/rpc-recording/adapters/transport-status-mount-adapters.ts:14-51

14-51: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Expose the verification state in the gate recording.

The adapter serializes capabilities and pending, but statusPending becomes false when connState changes to connecting. The connection-drop scenario therefore records the same state value before and after the transition, so the golden can miss gates that remain verified. Add a serializable verification-state field and assert its unverified value while retaining capabilities.

🟡 Minor · Schedule both status completions before one flush. · mobile/rpc-foundation/pilot-scenarios.json:6977-7085

6977-7085: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Schedule both status completions before one flush. runRecording flushes after every step, so the current relay-status completion selects relay before direct-status completes. The direct-tie golden and its matrix direct-status recordings therefore do not exercise direct winning the tie. Dispatch both completions before the next flush, then regenerate the dependent recordings.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: bcab2719-db5a-492e-a6d4-435345581bf8

📥 Commits

Reviewing files that changed from the base of the PR and between 55c7d29 and 6fda563.

📒 Files selected for processing (246)
  • mobile/rpc-foundation/goldens/b1.json
  • mobile/rpc-foundation/goldens/b2.json
  • mobile/rpc-foundation/goldens/b3.json
  • mobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/inventory-repeat-query.json
  • mobile/rpc-foundation/goldens/lifecycle-b3.json
  • mobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json
  • mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json
  • mobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json
  • mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json
  • mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json
  • mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json
  • mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-commits.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json
  • mobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.json
  • mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json
  • mobile/rpc-foundation/goldens/sc-base-ref-default.json
  • mobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.json
  • mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json
  • mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json
  • mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json
  • mobile/rpc-foundation/goldens/sc-commit-message-canceled.json
  • mobile/rpc-foundation/goldens/sc-commit-message-generated.json
  • mobile/rpc-foundation/goldens/sc-create-existing-review.json
  • mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json
  • mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json
  • mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json
  • mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json
  • mobile/rpc-foundation/goldens/sc-eligibility-fetched.json
  • mobile/rpc-foundation/goldens/sc-history-loaded.json
  • mobile/rpc-foundation/goldens/sc-pr-link-hosted-review.json
  • mobile/rpc-foundation/goldens/sc-pr-link-read.json
  • mobile/rpc-foundation/goldens/sc-pr-link-set.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-publish.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-push.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json
  • mobile/rpc-foundation/goldens/sc-reveal-first-poll.json
  • mobile/rpc-foundation/goldens/sc-reveal-timeout.json
  • mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json
  • mobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.json
  • mobile/rpc-foundation/goldens/sc-review-commit-rejected.json
  • mobile/rpc-foundation/goldens/sc-review-commit.json
  • mobile/rpc-foundation/goldens/sc-review-status-entries-not-array.json
  • mobile/rpc-foundation/goldens/sc-review-status-normalized.json
  • mobile/rpc-foundation/goldens/schedules-b3.json
  • mobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refused.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.json
  • mobile/rpc-foundation/goldens/settings-home-coalesced.json
  • mobile/rpc-foundation/goldens/settings-home-providers-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refused.json
  • mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json
  • mobile/rpc-foundation/goldens/settings-new-tab-refused.json
  • mobile/rpc-foundation/goldens/settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json
  • mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refused.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refused.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-linear.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-refused.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json
  • mobile/rpc-foundation/goldens/settings-task-write.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-refused.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-ready.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json
  • mobile/rpc-foundation/goldens/tw-capabilities-advertised.json
  • mobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.json
  • mobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json
  • mobile/rpc-foundation/goldens/tw-create-retry-created.json
  • mobile/rpc-foundation/goldens/tw-create-retry-name-collision.json
  • mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json
  • mobile/rpc-foundation/goldens/tw-create-retry-warning-kept.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-soft-error.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-resolved.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-always.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json
  • mobile/rpc-foundation/goldens/tw-smart-search-all-providers.json
  • mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json
  • mobile/rpc-foundation/goldens/tw-smart-search-linear-listed.json
  • mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.json
  • mobile/src/test-support/rpc-recording/adapters/relay-credential-mount-adapters.ts
  • mobile/src/test-support/rpc-recording/native-mounting-substitutes.test.ts
  • mobile/src/test-support/rpc-recording/native-mounting-substitutes.ts
  • mobile/src/test-support/rpc-recording/relay-pairing-fixtures.ts
💤 Files with no reviewable changes (1)
  • mobile/src/test-support/rpc-recording/relay-pairing-fixtures.ts
🚧 Files skipped from review as they are similar to previous changes (142)
  • mobile/rpc-foundation/goldens/settings-home-coalesced.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.json
  • mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json
  • mobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.json
  • mobile/rpc-foundation/goldens/sc-history-loaded.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.json
  • mobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.json
  • mobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.json
  • mobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.json
  • mobile/rpc-foundation/goldens/settings-new-tab-transport-error.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/tw-workspace-sparse-saved.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.json
  • mobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.json
  • mobile/rpc-foundation/goldens/sc-commit-message-generated.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-transport-error.json
  • mobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.json
  • mobile/rpc-foundation/goldens/sc-reveal-timeout.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/sc-review-commit-rejected.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.json
  • mobile/rpc-foundation/goldens/sc-base-ref-default.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.json
  • mobile/rpc-foundation/goldens/settings-home-providers-transport-error.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-skipped.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.json
  • mobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-both-refused.json
  • mobile/rpc-foundation/goldens/b1.json
  • mobile/rpc-foundation/goldens/settings-task-workspace-transport-error.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refused.json
  • mobile/rpc-foundation/goldens/tw-create-retry-created.json
  • mobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.json
  • mobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json
  • mobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/sc-eligibility-fetched.json
  • mobile/rpc-foundation/goldens/tw-task-preferences-resume-write.json
  • mobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.json
  • mobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connected.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.json
  • mobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.json
  • mobile/rpc-foundation/goldens/tw-capabilities-advertised.json
  • mobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.json
  • mobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.json
  • mobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/b2.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.json
  • mobile/rpc-foundation/goldens/sc-create-rejected-empty-message.json
  • mobile/rpc-foundation/goldens/sc-create-pushes-then-creates.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.json
  • mobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-refused.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.json
  • mobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.json
  • mobile/rpc-foundation/goldens/probe-new-tab-both-refused.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.json
  • mobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.json
  • mobile/rpc-foundation/goldens/tw-hosted-base-resolved.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.json
  • mobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.json
  • mobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.json
  • mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json
  • mobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refused.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.json
  • mobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.json
  • mobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.json
  • mobile/rpc-foundation/goldens/sc-create-refused-empty-message.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.json
  • mobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.json
  • mobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.json
  • mobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.json
  • mobile/rpc-foundation/goldens/settings-task-hydration-transport-error.json
  • mobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.json
  • mobile/rpc-foundation/goldens/sc-review-commit-inner-failure.json
  • mobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.json
  • mobile/rpc-foundation/goldens/pairing-pre-profile-times-out.json
  • mobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.json
  • mobile/rpc-foundation/goldens/sc-base-ref-unavailable.json
  • mobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.json
  • mobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.json
  • mobile/rpc-foundation/goldens/transport-capability-probe-publishes.json
  • mobile/rpc-foundation/goldens/inventory-repeat-query.json
  • mobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.json
  • mobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.json
  • mobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.json
  • mobile/rpc-foundation/goldens/sc-create-existing-review.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.json
  • mobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.json
  • mobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.json
  • mobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.json
  • mobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.json
  • mobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.json
  • mobile/rpc-foundation/goldens/b3.json
  • mobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.json
  • mobile/rpc-foundation/goldens/schedules-b3.json
  • mobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.json
  • mobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.json
  • mobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.json
  • mobile/rpc-foundation/goldens/inventory-lifecycle.json
  • mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json
  • mobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.json

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Three conflicts, all structural:

- `adapters/mounted-operation-modules.ts`: union of both sides' entries. #20705
  added `new-workspace` and `worktree-catalog`; this branch adds
  `transport-status`, `relay-credential` and `pairing-journal`. The two relay
  entries move into alphabetical position so the register stays sorted.
- `pilot-scenarios.json`: union of both sides' scenarios, 109 shared + 21 from
  main + 19 from here = 149. Neither side edited or removed a scenario and no id
  collides, so the merge is additive. Both sides already pinned baseline
  c6a7216.
- `unvalidated-rpc-request-port-inventory.ts`: this branch's `src/transport/`
  holdouts, which record migrations main has not seen, plus main's deletion of
  the `src/worktree/` section, whose three files #20705 migrated.

Main's 47 new goldens arrive unchanged and fail `recorderSha256` here, for the
reason already ruled structural for the other 208: this branch adds two files to
the recorder engine. The next commit re-records them.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Only the 47 goldens #20705 added moved, and only on `recorderSha256`: they
carry main's engine digest, and this branch adds two files to the engine. The
other 242 came back byte-identical, so the merge changed nothing any of them
observed. No recorded frame, settlement, effect or sender line differs anywhere.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog 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.

ℹ️ No new issues in this delta. The merge and re-digest both check out; the one prior doc/comment nit (native-mounting-substitutes.ts still calls the async-storage substitute "the secret store") is untouched by these commits and its thread is left open, so this is not an approval.

Reviewed changes

Reviewed the two commits pushed since the prior pullfrog review (6fda563942..f19081fb08): a merge of main (36ef93a64f, the parallel step-4 "small domains" PR #20705) plus one golden re-digest. No product code is authored in this delta beyond the merge integration; the branch's own src/transport/ migration is unchanged.

  • Merged main and resolved three structural conflicts — adapters/mounted-operation-modules.ts unions both registers (this branch's transport-status/relay-credential/pairing-journal entries join main's five new adapters, alphabetized); pilot-scenarios.json is an additive union (149 scenarios, no duplicate ids); unvalidated-rpc-request-port-inventory.ts keeps the branch's src/transport/ holdouts and drops main's now-migrated src/worktree/ entries.
  • Re-digested main's 47 new goldens on recorderSha256 only — this branch adds two files to the recorder engine, so the header digest moves; baseline, adapterSha256, scenarioSha256 and every values line are unchanged, and the other 242 goldens came back byte-identical.
  • Absorbed main's transport framework files — rpc-operation.ts, rpc-accepted-result.ts (new on main, extracted out of rpc-operation-contract.ts), relay-dial-stage.ts and peers resolve to main's versions; the branch had not modified them, so nothing of the branch's migration was overwritten.

Verified independently on the merged tree (mobile deps installed offline): the recording oracle (pilot-recordings, family-recordings, golden-header-digest, adapter-seam) passes 303 tests and the raw-port inventory ratchet passes 8, so the hand-resolved inventory counts and the re-digested headers agree with the recorded wire.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

One conflict, `pilot-scenarios.json`, resolved as a verified union: 130 shared,
21 from #20668, 19 from here, 170 total, no id collision and no scenario edited
or removed on either side. The register and the port inventory auto-merged into
the union each side intended.

#20668 changed no recorder engine file, so this branch's `recorderSha256` is
unchanged and only the 51 goldens it added need the header re-digest that the
next commit records.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…os truthfully

The race adapter returned the raw `PairingCandidate`, whose `client` is a live
object the recorder cannot serialize, so `captureValue` threw and every
pairing-race golden baked an `Unsupported observation: function`
unhandled-rejection effect and left the race settlement `pending`. It now
settles on `winner.path`, which is the entire decision, and rethrows so the
both-refused rejection keeps its identity.

`transport-pairing-race-direct-wins` also did not record a tie. The runner
flushes after every step, so the two completions can never land in one
microtask and the scenario only ever exercised relay completing first. Rather
than change the engine to script a simultaneous delivery, the scenario and its
checkpoint are renamed to what they record, and a mirror scenario completes
direct first. The two matrix checkpoints inherit the base's name, so they follow.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…race

66 goldens moved, in four groups:

- 51 from #20668, `recorderSha256` only and zero non-header lines: they carry
  main's engine digest and adopt this branch's.
- 10 other `transport-status` goldens, `adapterSha256` only and zero non-header
  lines: their adapter module changed, their recordings did not.
- `transport-pairing-race-relay-wins-when-direct-refused` and the two
  `matrix-transport.pairing-race-*` goldens: the race settlement is now
  fulfilled with `'relay'`/`'direct'` instead of pending, and the
  `Unsupported observation: function` unhandled-rejection effect is gone. The
  two matrix goldens also move `scenarioSha256`, having inherited the base
  scenario's renamed checkpoint.
- `transport-pairing-race-direct-wins` is renamed to
  `transport-pairing-race-relay-completes-first`, and
  `transport-pairing-race-direct-completes-first` is new.

`transport-pairing-race-both-refused` did not move: its race rejects, and a
rejection was always serializable. `recorderSha256` moved nowhere except in
those 51, which is the proof the engine is untouched by this step.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…in for

The paragraph called it "the secret store", which is `expo-secure-store` — not
in the table, and handled by the loader's throw-on-read default. The substitute
that answers with throwing functions is async storage.

This is an engine file, so the next commit re-digests every golden.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
`recorderSha256` in all 341 and nothing else: the reworded paragraph is a
comment in an engine file, so it moves the digest without moving a recording.
No frame, settlement, effect or sender line differs anywhere.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
@Jinwoo-H
Jinwoo-H merged commit 9878482 into main Sep 15, 2026
21 of 22 checks passed
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
#20667 transport, #20668 github PR surface and #20705. Resolved as unions: the adapter register
keeps both sides' entries, `pilot-scenarios.json` keeps main's 171 and this branch's 36 for 207 with
`baseline` still main's c6a7216, and the 208 pre-existing goldens take main's bytes because
this branch re-records them from that same pin anyway.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
Resolutions:

- `native-mounting-substitutes.ts`, `operation-module-loader.ts` and `relay-pairing-fixtures.ts`:
  main's version verbatim. This branch carried #20667's substitution ahead of it landing, which is
  why every pre-existing golden's `recorderSha256` differed from main. Taking main's engine collapses
  that: `git diff origin/main -- rpc-recording ':!**/adapters/**' ':!**/mutants/**'` is now empty.
- `adapters/mounted-operation-modules.ts`, `pilot-scenarios.json` and
  `unvalidated-rpc-request-port-inventory.ts`: unions. 24 registered modules, 199 scenarios (main's
  171 plus this branch's 28, no id collisions and no base scenario edited on either side), and an
  inventory where each disagreeing line was changed by exactly one side.
- `rpc-operation.ts`: both sides refactored the send-argument types. Main's `RpcParamsOmittable`
  is kept so single-flight and direct sends share one omittability rule; this branch's `| null`
  admission is kept on the `void` branch, because a frame carrying `params: null` is not the frame
  that omits the key.
- The 208 conflicted goldens: main's version. All conflicted on `recorderSha256` alone; the next
  commit re-records.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
…at the pin

Taking #20667's landed `native-mounting-substitutes.ts` verbatim dropped the substitutes this
branch's domains need, and five scenarios stopped recording: `speech-audio-chunk-acknowledged`,
`speech-dictation-session-transcript`, `speech-dictation-session-cancelled`,
`terminal-viewport-refit-applied` and `terminal-viewport-refit-legacy-desktop` all failed with
`Missing or completed request`, because the hook throws on the native member before it sends.

Both branches created that file independently; neither is a version of the other. Main's structure
is kept whole — `partialNativeModule`, the `__esModule` rule, the async-storage trap — and this
branch's boundaries are added inside it: `buffer`, `AppState` and `useWindowDimensions` on
`react-native`, `@orca/expo-two-way-audio`, `expo-keep-awake`, and `expo-secure-store` as a second
unusable store. Each is inert; no listener is ever fired and no audio is produced.

That is an engine edit, so every golden re-digests. Recorded at main's pin
c6a7216 from a detached worktree with this recorder overlaid, and
the product tree there was byte-identical to the pin. All 397 goldens moved on `recorderSha256`
alone and nothing else:

    git diff -U0 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256|goldenFormatVersion|baseline|scenarioSha256|lockfileSha256)":' | wc -l
    0

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
…ser domains through typed RpcOperations (#20702)

* refactor(mobile): pin each RPC golden to its own mount adapter, not every domain's

`recorderSha256` covered the whole recorder directory, mount adapters included, so a domain PR
that adds its adapter module moved the header of all 153 goldens. #20568 did exactly that and its
merge with main conflicted on that one line in 153 files; every future domain PR would collide
with every other in flight the same way.

Split the directory at a real seam instead of a filename convention: `adapters/` holds one module
per domain, registered in `adapters/mounted-operation-modules.ts`, and `recorderSha256` now covers
the engine only. A new `adapterSha256` covers the source of the module that mounts each operation
a golden's scenarios drive, read off the same `mounts` calls that build the table the recording
runs against, so the pin cannot name a file the runner did not use.

Adding a domain's module now re-digests nothing already recorded; editing one fails exactly the
goldens mounted through it. `adapter-seam.test.ts` keeps the split from drifting: an engine file
inside `adapters/`, an adapter defined in an engine file, a register entry naming the wrong file,
and an adapter importing a sibling each fail.

The five adapters that were inline in `pilot-mount-adapters.ts` move into their own modules, which
leaves that file as the registry and nothing else. `GOLDEN_FORMAT_VERSION` goes to 5 for the new
header field; the goldens re-record in the next commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens under the split recorder/adapter digest

Header-only. Every changed line is `recorderSha256` (the engine digest no longer covers
`adapters/`), the new `adapterSha256`, or `goldenFormatVersion` 4 -> 5; `baseline` is unchanged and
recording ran against the same pinned product tree.

    git diff -U0 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256|goldenFormatVersion)":' | wc -l
    0

The seven `adapterSha256` values partition the 153 goldens by the module each was recorded
through: 58 settings, 37 hosted review, 21 source control, 11 new-tab agents, 9 file inventory,
9 tasks, 8 workspace settings.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): stop pinning goldens to recorder inputs no recording can read

The adapter split left three per-domain edits still moving all 153 headers: the mutant table, the
per-family mutant registry beside it, and the probe-hole witness. None can change a recording --
the loader consults a mutant only when a mutant test asks for one, and no suite but the two
recording drivers writes a golden -- so pinning them claimed a provenance the goldens do not have
and charged every domain a full re-record for it.

`mutants/` now holds the table, the registry, the reference states, the mutant suites and the
probe-hole witness, and `recorderSha256` skips it. What makes that sound is that no recording can
reach it: `operationModuleLoader` takes a resolved mutation spec instead of importing a table by
name, so nothing on the recording path names `mutants/` at all. `mutants/mutant-seam.test.ts`
checks exactly that, and fails if an engine file names the directory or anything outside imports
from it.

`recorderSha256` also pins only the suites in `recording-drivers.ts`, which
`scripts/rpc-recording.mts` records from, so the two cannot drift. A suite that reads goldens, or
writes one to a scratch directory, is no longer provenance for a recorded file.

`OPERATION_EXPOSURES` went the other way, because it does change what a recording loads: withhold
the resume-metadata exposure and exactly four goldens fail. Each domain module now declares its own
exposures and gets its own loader, so `adapterSha256` pins the ones that reached each golden.

Two assertions in the digest boundary test were vacuous: `join(root, '.')` normalises back to
`root` and hit `recorderSha256`'s per-root cache, so the prose-is-ignored claim never recomputed
anything. Each call now spells the root differently.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens under the mutant and driver exclusions

Header-only, and no format bump: the header shape is unchanged. `recorderSha256` moves on all 153
because the engine set shrank, and `adapterSha256` moves on the 58 settings goldens because that
module now carries its own exposure declaration.

    git diff -U0 HEAD~1 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256)":' | wc -l
    0

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(mobile): restore the preferences actions the merge resolution dropped

#20568 added `resume` and `trust` actions to the `settings.task-preferences`
adapter while it still lived in `pilot-mount-adapters.ts`. This branch had already
moved that adapter into `adapters/task-mount-adapters.ts`, so resolving the
`pilot-mount-adapters.ts` conflict in favour of the registry merge silently
discarded them and `tw-task-preferences-resume-write` failed to record at all
("Missing or completed request: ui.set#1").

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens at main's tip after the merge

All 208 goldens, header-only. `baseline` moves from 50e752f to main's tip
c6a7216, `goldenFormatVersion` from 4 to 5, `recorderSha256` to the value of
the engine with `adapters/` and `mutants/` carved out, and `adapterSha256` is new
on every file. Nine distinct adapter digests over 208 goldens: each golden now
pins only the module that mounts it.

No observation moved. The whole-diff census against origin/main reports exactly
four changed keys and nothing else:

  208 "adapterSha256":   416 "baseline":
  416 "goldenFormatVersion":   416 "recorderSha256":

Recorded in place rather than through the README's detached-baseline dance: this
branch changes no product file, so its tree at the merge is byte-identical to
c6a7216 under mobile/src, src/shared and the lockfile, and the parity claim
stays non-circular. README says so now.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): hold the recording drivers to the engine's mutant-seam rule

The name scan exempted every `.test.ts` on the ground that a test cannot change a
recording. Two of them can: the recording drivers are the recording path. A driver
that read the mutant table by path rather than importing it passed both seam checks
— the import scan sees no import, and the name scan waved it through as a test:

  const table = resolve(import.meta.dirname, 'mutants/operation-mutations.ts')
  console.log(readFileSync(table, 'utf8').length)

at the top of `pilot-recordings.test.ts` gave 2 passed before, and after this change
fails with ["pilot-recordings.test.ts"].

Only non-driver tests are exempt now. This file lives in `mutants/`, which
`recorderSha256` skips, so no golden moves: the recorder suite is green on the
existing 208 with zero dirty.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop the registry parameter no caller varies

`pilotMountAdapters` took `registered` so a caller could mount a different module
set; all six callers take the default. The header-digest tests vary the registry
through `goldenRecording`, which keeps its own parameter and is where the stub
roots need it. Engine source, so `recorderSha256` moves and the goldens follow in
the next commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens after the registry parameter came out

All 208, `recorderSha256` only. The re-record against the previous commit moves
416 lines, every one of them that field:

  416 "recorderSha256":

Against origin/main the picture is unchanged from the merge: 208 goldens, 0 added
or deleted, 0 non-header lines, and exactly four keys differing —

  208 "adapterSha256"   416 "baseline"   416 "goldenFormatVersion"   416 "recorderSha256"

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): wrap the recording README at the width the rest of it uses

Seven lines this branch added ran past 100 columns, worst 124. No wording changed.
Markdown is outside `recorderSha256`, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): name the worktree overlay, not the archive that cannot work

`git archive` was offered alongside a detached checkout as a way to lay this
branch's recorder over the pinned baseline. It cannot work: the fence in
scripts/rpc-recording.mts runs `git diff --quiet <baseline>` and an untracked-file
check, both of which need a real `.git`. In an archive tree git exits non-zero for
lack of a repository and the script reports "Product sources or lockfile differ
from the pinned main baseline", which reads as a product mismatch that is not
there. The transport agent lost time to exactly that.

Names `git worktree add --detach` only, and says what the misleading failure looks
like if someone tries an archive anyway. Markdown is outside `recorderSha256`, so
no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): close two ways an adapter module escapes its own digest

Two holes, one class: the seam was checked by how an import was spelled and by
what the register's values evaluated to, never by where they resolve or where they
were written.

Inward imports: the scan dropped every specifier starting with `..`, so
`'../adapters/settings-mount-adapters'` climbed out of the directory and back into
it unseen. A reviewer had `new-tab-agent-mount-adapters.ts` project a value read
from the settings module, edited that module, and watched the mounted state change
while the new-tab adapter digest held. Specifiers now resolve against the
directory and anything landing back inside it fails:

  ["new-tab-agent-mount-adapters.ts imports ../adapters/settings-mount-adapters"]

The register: `adapters/mounted-operation-modules.ts` is pinned by nothing —
`recorderSha256` skips the directory and `adapterSha256` reads each entry's
`source`. An `exposes` written inline there drives the mounted product module with
no digest covering it. The same reviewer replaced the new-tab entry's `exposes`
with a literal overriding `loadMobileNewTabAgentOptions`; twelve fence tests
passed. Both `mounts` and `exposes` must now be identifiers the register imports
from that entry's own module:

  ["new-tab-agent-mount-adapters.ts writes exposes inline instead of importing it"]

Checked on the register's syntax, not its values, because an inline literal and an
imported binding are indistinguishable once evaluated.

Pinning the register in the engine digest would also close it, and is the wrong
trade: every domain adding a register line would re-digest all 208 goldens, which
is the conflict this PR exists to remove. Keeping the register an index costs
nothing and keeps a domain's line local.

Both fixes live in a `.test.ts` outside the drivers, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): prove the mutant seam from the drivers out, not by spelling

The seam rested on a grep for the literal `mutants`, which the exported
`MUTANT_DIRECTORY` spells without containing. A reviewer had
`pilot-mount-adapters.ts` read the mutant table through that constant and both
checks passed. The README's claim — that nothing on the recording path names the
directory — was false as written.

Three changes, in order of strength:

Reachability is now proved forward. The suite walks the static import graph from
the two recording drivers and fails if any module under `mutants/` is in it. That
answers the real question, what a golden's bytes can depend on, instead of the old
inward scan's question, who mentions this directory. Non-emptiness is asserted on
both sides so a graph that resolved nothing cannot pass by reaching nothing.

The name scan covers both spellings, for paths a module can be read by rather than
imported. The reviewer's probe now fails as ["pilot-mount-adapters.ts"].

`MUTANT_DIRECTORY` is no longer exported. Its two consumers were both tests of the
digest, and they now spell the path instead, which is strictly better for them: a
test that imports the constant follows a rename silently, while one that spells it
fails on a rename — and that specific directory name is the whole soundness
argument. This edits `recorder-digest.ts`, so the goldens re-record in the next
commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens after MUTANT_DIRECTORY stopped being exported

All 208, `recorderSha256` only. Against the previous commit the diff is 416 lines
and every one of them is that field:

  416 "recorderSha256":

Against origin/main, unchanged: 208 goldens, 0 added or deleted, 0 non-header
lines, four keys differing —

  208 "adapterSha256"   416 "baseline"   416 "goldenFormatVersion"   416 "recorderSha256"

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): state the mutant seam's actual argument, and its edge

The README claimed nothing on the recording path names `mutants/`. That was the
old inward scan's claim and a reviewer falsified it with the exported constant. It
now describes what the check does: a forward walk of the import graph from the two
recording drivers, plus a name scan in both spellings for read-by-path, plus the
constant no longer being exported. It also names the case neither closes — a path
assembled from fragments at runtime.

Markdown is outside `recorderSha256`, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): prove the engine/adapter seam in both directions

The inward scan only held adapters to the seam. An engine file importing an
adapter executes code its own digest skips and that every golden recorded
through another domain leaves out of `adapterSha256`, so the register is now
the only crossing allowed from the engine side.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): name what the driver walk missed instead of counting it

Seeding `seen` with the drivers made the driver-presence check true by
construction, and the size bound compared a graph inflated by `typeof import`
product modules against a recorder-sized number. Both go; the walk now reports
the recording files it failed to reach, which is empty today and names an
orphan engine file the moment one appears.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): reflow four paragraphs left ragged by the rewrap

Orphan fragments only, no wording change: the golden-schema field list, the
mutant-evidence paragraph, the probe-witness sentence and the re-anchor note.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): record the dictation, terminal, notification and browser domains against main

Adds scenarios and mount adapters for six small feature areas before any product file
moves, so the migration that follows has a frozen main oracle to be measured against:
dictation setup and session, agent history, terminal input and viewport refit, push
registration and the hosted browser's pointer, keyboard and dialog commands.

Five new adapter modules, one per domain, each pinned by its own `adapterSha256`. Two
declare exposures: `sendRegister`/`sendUnregister` are module-private in push-registration
and their exported callers read the keychain host catalog first, and the history hook
reaches its client through the shared per-host context rather than a parameter, so the
provider is the mounting boundary.

`native-mounting-substitutes.ts` is copied verbatim from the transport migration (#20667)
so the two branches merge, extended with the members these hooks read: `AppState` and
`useWindowDimensions` on react-native, the two-way audio module, `expo-keep-awake`, and
`buffer`. Every device event source is inert — no listener is fired — because each send is
driven through the operation's own API instead.

Wiring the substitute table into the loader moves `recorderSha256`, so every golden's
header re-digests. The product tree is unchanged, so `baseline` is unchanged and the 208
existing goldens move exactly one line each; no observation moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): send the dictation, terminal, notification and browser domains through typed RpcOperations

Replaces 42 raw `sendRequest` reaches across six feature areas with declared operations:
dictation setup and session, agent history, terminal input and viewport refit, push
registration and the hosted browser's page commands. No wire change — every golden
recorded in the previous commit still compares byte-identical, which is the evidence.

Acceptance is preserved site by site rather than unified. Every dictation and browser
refusal already raised the host's message with a screen fallback, so those share one
policy and keep their own copy at the call site. The terminal's two input sites read one
boolean off an object result, which `object-result-or-null` gives them without a throw.
The worker-takeover report and the three enrichment reads in the resume sheet are skips.

Two latent behaviours are preserved deliberately rather than tidied. `repo.list` reads
`.repos` at the return statement, so a null result throws a raw TypeError there and not a
wrapped refusal message; the three enrichment reads beside it are optional-chained and
tolerate the same null. `speech.dictation.finish` checks its refusal before the staleness
guard and reads `.text` after it, so the member read stays at the call site.

`RpcSendArguments` now admits an explicit `null` where the catalog declares no params.
Four shipped senders put `params: null` on the wire for such a method, and a frame carrying
a null is not the frame that omits the key; without this the migration would have rewritten
those bytes. `src/settings/native-voice-settings-operations.ts` widens its client type for
the same reason — it holds the port only to hand it to dictation.

Three references are left behind, each listed in the inventory with what blocks it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): matrix the dialog-dismiss site and name the three reaches left behind

The reply matrix drives every reply a family's base scenario scripts, and the browser dialog
family scripted only the accept leg, so repointing `browser.dialogDismiss` at another method
survived the whole suite. A scenario for the dismiss leg closes it; the mutation is killed now.

The inventory loses five section headers that no longer list anything, and the three entries
that resisted migration each carry what blocks them: a `worktree.ps` inside the history screen
component's own effect, an unsubscribe closed over inside a `subscribe` callback, and a
`notifications.getMissedSince` gated behind the OS notification tray and the keychain host
catalog. Faking either of the last two would record device state, not a wire.

Eighteen goldens move on `adapterSha256` alone: removing two inner casts from the browser and
history adapters re-digests the goldens mounted through them and nothing else. All 263 were
re-recorded from the pinned baseline and compare byte-identical to the previous recording
apart from those headers and the new file.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): fold five refusal-to-message spellings into one helper

The same three lines appeared five times in this PR: interpret a reply, and turn a refusal into a
plain Error carrying the host's message or the screen's copy. `interpretOrThrowRefusalMessage`
lives beside `refusedRpcMessageOrFallback` and takes the interpretation as a thunk, so the caller
still awaits the request outside the catch and a transport rejection reaches it as the object the
transport threw, delivery-unknown mark intact.

That removes the browser hook's own `assertBrowserCommandAccepted`, whose first parameter named an
operation the helper then used only to call `interpret`. Every browser page command is built by one
factory with one acceptance, so substituting one operation's `interpret` for another's was
unobservable and the parameter read as load-bearing when it was not. The call sites now name the
operation where they request and where they interpret, the shape the dictation sites already used.

Main's eight source-control spellings are deliberately untouched; folding those in is its own PR.

No wire change and no golden moves: `git diff --name-status origin/recorder-adapter-digest...HEAD
-- mobile/rpc-foundation` is byte-identical before and after at 55 added and 209 modified.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): record the history screen holding for a late worktree list

The hook holds loading when a scoped tab has no active worktree and the worktree list has not
arrived, rather than firing an unscoped scan that would briefly show unrelated host history. The
adapter hardcoded `worktreesLoaded` to true and its `worktrees-loaded` action was dead, so no
golden reached that branch and the hold was unrecorded.

The list and the flag now move together, which is how the screen learns them, and `mount` takes
`worktreesLoaded: false` to start unloaded. The new scenario records the hold, then the late load.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the goldens at the pinned baseline

Recorded from a detached worktree at c6a7216
with this branch's rpc-recording/ and pilot-scenarios.json overlaid, per the
README's migration-branch procedure. Only header fields moved; no body line in
any golden changed.

recorderSha256 moved on all 208 pre-existing goldens: this branch adds
native-mounting-substitutes.ts at the top of rpc-recording/ and routes
operation-module-loader.ts through it, and both are recorder-engine inputs.
That resolves itself when #20667 lands the same substitution on main.

adapterSha256 moved on exactly four goldens, all from the late-worktree-list
scenario's edit to adapters/agent-history-mount-adapters.ts:
aivault-history-scan-fulfilled, aivault-history-scan-unsupported,
matrix-aivault.history-aivault.listsessions-1,
matrix-aivault.history-status.get-1.

aivault-history-scan-worktrees-late.json is new.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): ask these call sites for a request port, not a whole client

The migration widened seven files from `Pick<RpcClient, 'sendRequest'>` to the
full `RpcClient` for no reason: a bound operation's `request` takes
`UnvalidatedRpcRequestPort`, which is structurally that same single member.
Name the port instead, so the signature says what each function actually needs
and a caller holding only a port still satisfies it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): make these adapters reject an action no scenario dispatches

Each of the five new mount adapters ended its `action` with an unnamed default,
so a misspelled or engine-introduced name silently ran the last branch: probing
`remount` on the history adapter ran `onRefresh()` and put `status.get` on the
wire. Twelve pre-existing adapters throw `Unknown X action` instead; these now
do too.

Deleted with it: every branch no scenario can reach. `unmount` is dispatched
only by the lifecycle derivation in derived-goldens.ts, which is restricted to
LIFECYCLE_BASES, and none of these scenarios is in it; teardown goes through
`dispose`, which already unmounts. Same for the history adapter's `retry`,
`select-scope` and refresh, the dictation start flow's `disable`, and the
viewport adapter's `reconnect`. None of them handled `remount`, which the
schedule driver always pushes after `unmount`, so the pairing was never whole.

The three single-action entries keep `_name`, matching six pre-existing
entries that do the same.

Goldens move on adapterSha256 only and are re-recorded separately.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop four fallback strings no catch can read

Both pointer paths wrap their whole sequence in `catch {}` with an empty body,
so the message the refusal helper builds is discarded. `interpret` already
throws on a refusal under `require-result-or-throw-message`, and the helper only
rewrote the text, so removing it keeps the same control flow: the sequence still
stops at the refused leg, the later commands still go unsent, and `setError`
still does not run. The mousemove matrix golden records `error: null` under
every refusal and transport shape either way.

The helper stays where the throw reaches a caller: dictation and agent history.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): read the viewport outcome where the reader is

`readTerminalUpdateViewportOutcome` had one caller, the reader that wraps it, so
the name bought a second file to open and nothing else. Inline the two
comparisons and keep the outcome type where it was.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record this branch's goldens after the adapter cleanup

Recorded from a detached worktree at c6a7216,
this branch's rpc-recording/ and pilot-scenarios.json overlaid, per the README's
migration-branch procedure. 266 recordings, all reproduced.

adapterSha256 moved on all 56 goldens this branch owns, because the named throws
and the deleted unreachable branches changed all five adapter modules. No other
header field moved, and no body line in any golden changed: the wire, the
effects and the state snapshots are identical, which is the claim the five
review deletions rest on.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): reach the narrow port type without naming the port module

The previous commit named `UnvalidatedRpcRequestPort` by importing the port
module, and that fails the boundary ratchet: it counts an import of
`unvalidated-rpc-request-port` as reach, so all six unlisted files became
offenders and the history panel went from 1 reference to 2.

Main's `Pick<RpcClient, 'sendRequest'>` fails it for the same reason, by a
different rule: a bare `'sendRequest'` string literal is counted too. That is
why the migration widened these signatures in the first place, so the review
finding's premise that it was done for no cause is wrong. Only the full
`RpcClient` scored zero.

Re-export the port type from `rpc-client` instead. An export declaration with no
module specifier is not counted, the seven signatures still say they need one
sender rather than a whole client, and the inventory does not move. Holding a
client already carries the same reach, so nothing new is opened.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* Revert "refactor(mobile): reach the narrow port type without naming the port module"

This reverts 92e679f and 16c9c23 as one commit, returning the seven
client signatures to the full RpcClient the migration gave them.

unvalidated-rpc-request-port-boundary.test.ts:192 pins an import of the port
type as one reach by design: naming the type is exactly what the ratchet
retires, so re-exporting it from rpc-client opened an uncounted path for every
future file. Widening to the client is the design's intended end state.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): restore this branch's native substitutes and re-record at the pin

Taking #20667's landed `native-mounting-substitutes.ts` verbatim dropped the substitutes this
branch's domains need, and five scenarios stopped recording: `speech-audio-chunk-acknowledged`,
`speech-dictation-session-transcript`, `speech-dictation-session-cancelled`,
`terminal-viewport-refit-applied` and `terminal-viewport-refit-legacy-desktop` all failed with
`Missing or completed request`, because the hook throws on the native member before it sends.

Both branches created that file independently; neither is a version of the other. Main's structure
is kept whole — `partialNativeModule`, the `__esModule` rule, the async-storage trap — and this
branch's boundaries are added inside it: `buffer`, `AppState` and `useWindowDimensions` on
`react-native`, `@orca/expo-two-way-audio`, `expo-keep-awake`, and `expo-secure-store` as a second
unusable store. Each is inert; no listener is ever fired and no audio is produced.

That is an engine edit, so every golden re-digests. Recorded at main's pin
c6a7216 from a detached worktree with this recorder overlaid, and
the product tree there was byte-identical to the pin. All 397 goldens moved on `recorderSha256`
alone and nothing else:

    git diff -U0 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256|goldenFormatVersion|baseline|scenarioSha256|lockfileSha256)":' | wc -l
    0

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record at the pin after the #20685 merge

The merged recorder is a third engine: main's post-tasks-2 files plus this
branch's native-mounting-substitutes.ts, so recorderSha256 moves once and every
golden re-digests. Recorded from the pinned baseline c6a7216 in a detached
worktree with this tree's recorder laid over it, so the product source is still
main's pre-migration tree. All 509 goldens move on recorderSha256 alone; no
recorded wire byte changes.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
…failure

A projection refusal settles as data, not as a failed run: `captureValue` throws
inside the action, the recorder captures it as an `unhandled-rejection` effect,
and `--record` writes a green golden whose action never settled. The class has
landed twice — `transport.pairing-race` in #20667 and `worktree.catalog-snapshot`
in the previous commit — and reverting either one plus a re-record would go green
with the broken golden back.

Read every golden and fail on an `unhandled-rejection` effect or on either of
`captureValue`'s refusal texts in any observation field. A positive control in the
same case asserts both detectors fire, so the absence claim is load-bearing rather
than vacuous.

Not a recording driver, so `recorderSha256` excludes it and no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Jinwoo-H added a commit that referenced this pull request Sep 15, 2026
… RPC recorder (#20873)

* test(rpc-recording): record the real worktree catalog snapshot result

`worktree.catalog-snapshot`'s action returned `WorktreeCatalogSnapshotClient.fetch`'s
raw result. That value nests the live `RpcClient` under `pending.client`, so
`captureValue` threw `Unsupported observation: function`: both goldens baked an
`unhandled-rejection` effect and left the action's settlement `pending`, proving
nothing about what the fetch returns.

Project the result through `projectObservable` on the settlement path, the same
way `state()` already shows it and the same way #20667 fixed
`transport.pairing-race`. Rejections still propagate unchanged, so the two
transport-rejection matrix partitions keep their recorded errors.

The matrix golden now discriminates all eleven reply partitions: a full
admission, six invalid ones, three `request_failed` codes and two rejections.
`recorderSha256` does not move; `adapterSha256` moves on the six goldens mounted
through this adapter module, four of which have no other change.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): fail the suite when a golden records a recorder failure

A projection refusal settles as data, not as a failed run: `captureValue` throws
inside the action, the recorder captures it as an `unhandled-rejection` effect,
and `--record` writes a green golden whose action never settled. The class has
landed twice — `transport.pairing-race` in #20667 and `worktree.catalog-snapshot`
in the previous commit — and reverting either one plus a re-record would go green
with the broken golden back.

Read every golden and fail on an `unhandled-rejection` effect or on either of
`captureValue`'s refusal texts in any observation field. A positive control in the
same case asserts both detectors fire, so the absence claim is load-bearing rather
than vacuous.

Not a recording driver, so `recorderSha256` excludes it and no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): ban only the recorder's own refusals, not product bugs

The gate also failed on any effect named `unhandled-rejection`, which bans a real
observation: detached-rejection capture exists to pin a main bug in a golden, and
`unhandled-recording.test.ts` pins the capture precisely because no golden carries
one today. Banning the name would make recording a genuine product failure a test
failure.

Drop that detector. `refusalText` alone catches all seven checkpoints of the
worktree-catalog regression, because the refusal is the message of the captured
error rather than the effect's name. The positive control now seeds that shape —
the refusal inside a recorded error under `effects` — so the surviving detector is
still proven to fire. Vacuity guards on golden and checkpoint counts are unchanged.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): re-record the worktree catalog goldens over main's engine digest

#20874 re-digested `recorderSha256` on all 509 goldens, so the six goldens mounted
through the worktree catalog adapter had to be recorded again from the pin rather
than merged. Bodies and `adapterSha256` are unchanged from the pre-merge recording.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
ivanvykopal pushed a commit to ivanvykopal/orca that referenced this pull request Sep 20, 2026
…h typed RpcOperations (stablyai#20667)

* test(mobile): record the transport pairing and status domain against main

Adds seven recording families for `mobile/src/transport/`, recorded from main's
unmigrated product code before any refactor: the protocol-gate hook, the retrying
capability probe, the pairing candidate race, credential rotation, direct-to-relay
upgrade, startup pairing recovery and first pairing.

The relay modules build their `defaultDependencies` at module scope, so merely
referencing `Platform.OS` or a storage-backed loader threw before an adapter could
override it. `native-mounting-substitutes.ts` separates reference from use: react,
zod and @noble/hashes are the real libraries, expo-crypto routes through the Web
Crypto the scheduler already pins, and the two secret stores throw when called.

`baseline` repins to fc525c3 because main's tree no longer matches the pinned
50e752f. All 241 goldens re-recorded; main's 208 move only `baseline` and
`recorderSha256`, and no `scenarioSha256` or recording body moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): send the transport pairing and status domain through typed RpcOperations

Migrates 11 of the 18 raw-port references in `mobile/src/transport/`: the protocol
gate, the runtime capability probe, the pairing candidate race, credential rotation,
the direct-to-relay upgrade, startup pairing recovery and first pairing.

Three operations over three methods, zero new acceptance policies. `status.get` gains
a third named policy — `status.transport-probe-or-skip` — because all three transport
callers treat a refusal as an absent answer, which is the Tasks create-drawer policy
but not the Tasks hydration one; transport cannot import tasks, and the name is what a
decode failure reports. `pairing.provisionRelay` and `pairing.getEndpoints` are
`require-result-or-throw`, which reuses the same `code: message` text the four call
sites each spelled by hand. Three sites read the raw envelope for `method_not_found`
before interpreting, because an unknown method means "this build has no relay" rather
than "the install failed". Reply parsing stays at the call sites: the readers are
unchecked and the zod contract schemas run where they ran before.

No wire change and no behaviour change: all 241 goldens replay green and this commit
touches none of them.

Two files stay on the raw port and now carry their own reason in the inventory.
`pairing-relay-candidate.ts` decorates a PairingCandidateClient with director recovery,
so it implements the port rather than calling it; its one chosen method string now
comes from hostStatusProbe. `mobile-runtime-capability-negotiation.ts` sends over the
physical clients' pre-`connected` authenticated path, which no recording can reach.
`runtime-capability-probe.ts` drops to its parameter type alone.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): pin the capability probe's cutover re-ask against the frozen baseline

The reply matrix drives every scripted reply, so it already covers a transport
rejection at each migrated site. It cannot cover the one signal the probe reads out
of a rejection: `isLogicalClientCutoverError` chooses a 250 ms re-ask over the 1 s
failure backoff, and no golden made that choice observable.

`transport-capability-probe-cutover-reasks-fast` migrates the logical client mid-probe
and binds the replacement request at exactly 250 ms, so a re-ask moved in either
direction fails the binding rather than recording a different number. The recorded
rejection carries `LogicalClientCutoverError`, its `Connection closed` cause and
`isRpcDeliveryUnknown`.

Recorded from a `git archive` of the pinned baseline with this branch's recorder laid
over it, per the recorder README: the migrated tree can no longer satisfy the fence.
The same run reproduced the other 241 goldens byte-for-byte.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): read the pairing sends top to bottom

Five pairing sites nested `parse(interpret(await request(...)))` three deep with the
await innermost, so the send was the last thing a reader found. Bind the reply first
and interpret it on its own line, which is the shape main had before the migration.

No behaviour change: the request still settles before interpretation and the barrier
is unmoved, so no golden shifts.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): make an unlisted native member throw instead of recording undefined

Three substitutes were plain object literals standing in for whole modules, so any member
the table did not list read as `undefined` rather than failing — the opposite of what the
file's own default proxy does, and a silent one: `react-native` alone is requested 354
times across the goldens, and `platformSelect`, `view` and `styleSheet` all read undefined.
A recording that reaches an unsubstituted native member is not evidence of anything,
because the product on a device would call it.

Also drops the `expo-secure-store` substitute, which no recording requests, and corrects
the doc's "fails loudly" claim: the throw is real, but `host-app-version-store.ts` catches
it and degrades to its unread state, which is what it does on a device too.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): keep one credential hash and only the fixtures someone reads

Nine fixture constants and `relayEndpoint` were exported with no reader outside their own
module, and `credentialHash` was copied verbatim into both relay adapter modules. The
adapter seam forbids one module under `adapters/` from importing a sibling, so the shared
form has to live with the fixtures both already import.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record every golden at main's tip

Repins `baseline` to 6a11a0b and records all 242 from a detached worktree of that
commit with this branch's recorder overlaid, per the README's migration-branch procedure.

Two header fields move and nothing else: `baseline`, and `recorderSha256` for the 208
goldens main also carries, because this branch adds `native-mounting-substitutes.ts` and
`relay-pairing-fixtures.ts` to the engine and teaches the loader to consult the first.
Against the pre-merge tree every one of the 242 is header-only, so no recorded byte moved.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): let the interop marker through the native substitute proxy

`import * as ExpoCrypto from 'expo-crypto'` transpiles to an interop helper that reads
`__esModule` before copying members, so throwing on it fails the module system's own probe
rather than an unsubstituted API read. Four relay families could not record.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): read the substitute through Reflect instead of a cast

The changed-code casting gate rejects `key as string`, and the trap has a typed read that
needs no assertion.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record for the substitute's typed read

`recorderSha256` only; every recorded byte is unchanged from the previous re-record.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): restore main's baseline pin and re-record

The merge brought main's pin c6a7216. Repinning it to the branch's own
merge-base rewrote the `baseline` header of all 208 pre-existing goldens for no
behavioural reason, so put main's pin back and re-record at that commit through
a detached baseline worktree with this branch's recorder overlaid.

Only `baseline` moved in all 242 goldens; no recorded frame, settlement or
sender line changed. Against origin/main the 208 pre-existing goldens now differ
in `recorderSha256` alone, which this branch's two added engine files force.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): let a default import of the secret store reach its members

The store substitute answered every key with a throwing function, including
`__esModule`. TypeScript's `__importDefault` reads that marker and, finding it
truthy, binds the default import to the trap's own function instead of the
module record, so all five consumers saw `AsyncStorage.getItem` as undefined.
A recording that reached `host-app-version-store.ts` failed with
`TypeError: AsyncStorage.getItem is not a function` rather than the named
`Native store reached during recording: ….getItem`, and an `await import()` of
the store rejected on `.then` for the same reason. Both traps now leave the
marker undefined, and the doc states that invariant.

Also drop `recordingRandomBytes`: its body and the `expo-crypto` substitute's
are the same call into the seeded `getRandomValues`, and both relay entry
points already default `randomBytes` to `ExpoCrypto.getRandomBytes`, so the two
injections were passing the default back to itself. Its orphaned doc comment,
left stacked above `credentialHash` by the dedupe commit, goes with it.

The new suite does not record, so `recorderSha256` does not pin it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record for the secret-store and randomBytes fixes

`recorderSha256` moved in all 242 goldens, and `adapterSha256` in the 10
`relay.credential-rotation` and `relay.direct-upgrade` goldens whose adapter
stopped injecting `randomBytes`. Nothing else moved: no recorded frame,
settlement, effect or sender line differs, which is the claim that the default
the adapter was passing back to itself and the substitute it resolved to were
always the same function.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): adopt this branch's recorder digest in main's new goldens

Only the 47 goldens stablyai#20705 added moved, and only on `recorderSha256`: they
carry main's engine digest, and this branch adds two files to the engine. The
other 242 came back byte-identical, so the merge changed nothing any of them
observed. No recorded frame, settlement, effect or sender line differs anywhere.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): record the pairing race's decision and name its scenarios truthfully

The race adapter returned the raw `PairingCandidate`, whose `client` is a live
object the recorder cannot serialize, so `captureValue` threw and every
pairing-race golden baked an `Unsupported observation: function`
unhandled-rejection effect and left the race settlement `pending`. It now
settles on `winner.path`, which is the entire decision, and rethrows so the
both-refused rejection keeps its identity.

`transport-pairing-race-direct-wins` also did not record a tie. The runner
flushes after every step, so the two completions can never land in one
microtask and the scenario only ever exercised relay completing first. Rather
than change the engine to script a simultaneous delivery, the scenario and its
checkpoint are renamed to what they record, and a mirror scenario completes
direct first. The two matrix checkpoints inherit the base's name, so they follow.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the merge's headers and the repaired pairing race

66 goldens moved, in four groups:

- 51 from stablyai#20668, `recorderSha256` only and zero non-header lines: they carry
  main's engine digest and adopt this branch's.
- 10 other `transport-status` goldens, `adapterSha256` only and zero non-header
  lines: their adapter module changed, their recordings did not.
- `transport-pairing-race-relay-wins-when-direct-refused` and the two
  `matrix-transport.pairing-race-*` goldens: the race settlement is now
  fulfilled with `'relay'`/`'direct'` instead of pending, and the
  `Unsupported observation: function` unhandled-rejection effect is gone. The
  two matrix goldens also move `scenarioSha256`, having inherited the base
  scenario's renamed checkpoint.
- `transport-pairing-race-direct-wins` is renamed to
  `transport-pairing-race-relay-completes-first`, and
  `transport-pairing-race-direct-completes-first` is new.

`transport-pairing-race-both-refused` did not move: its race rejects, and a
rejection was always serializable. `recorderSha256` moved nowhere except in
those 51, which is the proof the engine is untouched by this step.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): name the module the throwing-on-call substitute stands in for

The paragraph called it "the secret store", which is `expo-secure-store` — not
in the table, and handled by the loader's throw-on-read default. The substitute
that answers with throwing functions is async storage.

This is an engine file, so the next commit re-digests every golden.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-digest every golden for the substitutes doc reword

`recorderSha256` in all 341 and nothing else: the reworded paragraph is a
comment in an engine file, so it moves the digest without moving a recording.
No frame, settlement, effect or sender line differs anywhere.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
ivanvykopal pushed a commit to ivanvykopal/orca that referenced this pull request Sep 20, 2026
…ser domains through typed RpcOperations (stablyai#20702)

* refactor(mobile): pin each RPC golden to its own mount adapter, not every domain's

`recorderSha256` covered the whole recorder directory, mount adapters included, so a domain PR
that adds its adapter module moved the header of all 153 goldens. stablyai#20568 did exactly that and its
merge with main conflicted on that one line in 153 files; every future domain PR would collide
with every other in flight the same way.

Split the directory at a real seam instead of a filename convention: `adapters/` holds one module
per domain, registered in `adapters/mounted-operation-modules.ts`, and `recorderSha256` now covers
the engine only. A new `adapterSha256` covers the source of the module that mounts each operation
a golden's scenarios drive, read off the same `mounts` calls that build the table the recording
runs against, so the pin cannot name a file the runner did not use.

Adding a domain's module now re-digests nothing already recorded; editing one fails exactly the
goldens mounted through it. `adapter-seam.test.ts` keeps the split from drifting: an engine file
inside `adapters/`, an adapter defined in an engine file, a register entry naming the wrong file,
and an adapter importing a sibling each fail.

The five adapters that were inline in `pilot-mount-adapters.ts` move into their own modules, which
leaves that file as the registry and nothing else. `GOLDEN_FORMAT_VERSION` goes to 5 for the new
header field; the goldens re-record in the next commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens under the split recorder/adapter digest

Header-only. Every changed line is `recorderSha256` (the engine digest no longer covers
`adapters/`), the new `adapterSha256`, or `goldenFormatVersion` 4 -> 5; `baseline` is unchanged and
recording ran against the same pinned product tree.

    git diff -U0 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256|goldenFormatVersion)":' | wc -l
    0

The seven `adapterSha256` values partition the 153 goldens by the module each was recorded
through: 58 settings, 37 hosted review, 21 source control, 11 new-tab agents, 9 file inventory,
9 tasks, 8 workspace settings.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): stop pinning goldens to recorder inputs no recording can read

The adapter split left three per-domain edits still moving all 153 headers: the mutant table, the
per-family mutant registry beside it, and the probe-hole witness. None can change a recording --
the loader consults a mutant only when a mutant test asks for one, and no suite but the two
recording drivers writes a golden -- so pinning them claimed a provenance the goldens do not have
and charged every domain a full re-record for it.

`mutants/` now holds the table, the registry, the reference states, the mutant suites and the
probe-hole witness, and `recorderSha256` skips it. What makes that sound is that no recording can
reach it: `operationModuleLoader` takes a resolved mutation spec instead of importing a table by
name, so nothing on the recording path names `mutants/` at all. `mutants/mutant-seam.test.ts`
checks exactly that, and fails if an engine file names the directory or anything outside imports
from it.

`recorderSha256` also pins only the suites in `recording-drivers.ts`, which
`scripts/rpc-recording.mts` records from, so the two cannot drift. A suite that reads goldens, or
writes one to a scratch directory, is no longer provenance for a recorded file.

`OPERATION_EXPOSURES` went the other way, because it does change what a recording loads: withhold
the resume-metadata exposure and exactly four goldens fail. Each domain module now declares its own
exposures and gets its own loader, so `adapterSha256` pins the ones that reached each golden.

Two assertions in the digest boundary test were vacuous: `join(root, '.')` normalises back to
`root` and hit `recorderSha256`'s per-root cache, so the prose-is-ignored claim never recomputed
anything. Each call now spells the root differently.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens under the mutant and driver exclusions

Header-only, and no format bump: the header shape is unchanged. `recorderSha256` moves on all 153
because the engine set shrank, and `adapterSha256` moves on the 58 settings goldens because that
module now carries its own exposure declaration.

    git diff -U0 HEAD~1 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256)":' | wc -l
    0

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(mobile): restore the preferences actions the merge resolution dropped

stablyai#20568 added `resume` and `trust` actions to the `settings.task-preferences`
adapter while it still lived in `pilot-mount-adapters.ts`. This branch had already
moved that adapter into `adapters/task-mount-adapters.ts`, so resolving the
`pilot-mount-adapters.ts` conflict in favour of the registry merge silently
discarded them and `tw-task-preferences-resume-write` failed to record at all
("Missing or completed request: ui.set#1").

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens at main's tip after the merge

All 208 goldens, header-only. `baseline` moves from 50e752f to main's tip
c6a7216, `goldenFormatVersion` from 4 to 5, `recorderSha256` to the value of
the engine with `adapters/` and `mutants/` carved out, and `adapterSha256` is new
on every file. Nine distinct adapter digests over 208 goldens: each golden now
pins only the module that mounts it.

No observation moved. The whole-diff census against origin/main reports exactly
four changed keys and nothing else:

  208 "adapterSha256":   416 "baseline":
  416 "goldenFormatVersion":   416 "recorderSha256":

Recorded in place rather than through the README's detached-baseline dance: this
branch changes no product file, so its tree at the merge is byte-identical to
c6a7216 under mobile/src, src/shared and the lockfile, and the parity claim
stays non-circular. README says so now.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): hold the recording drivers to the engine's mutant-seam rule

The name scan exempted every `.test.ts` on the ground that a test cannot change a
recording. Two of them can: the recording drivers are the recording path. A driver
that read the mutant table by path rather than importing it passed both seam checks
— the import scan sees no import, and the name scan waved it through as a test:

  const table = resolve(import.meta.dirname, 'mutants/operation-mutations.ts')
  console.log(readFileSync(table, 'utf8').length)

at the top of `pilot-recordings.test.ts` gave 2 passed before, and after this change
fails with ["pilot-recordings.test.ts"].

Only non-driver tests are exempt now. This file lives in `mutants/`, which
`recorderSha256` skips, so no golden moves: the recorder suite is green on the
existing 208 with zero dirty.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop the registry parameter no caller varies

`pilotMountAdapters` took `registered` so a caller could mount a different module
set; all six callers take the default. The header-digest tests vary the registry
through `goldenRecording`, which keeps its own parameter and is where the stub
roots need it. Engine source, so `recorderSha256` moves and the goldens follow in
the next commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens after the registry parameter came out

All 208, `recorderSha256` only. The re-record against the previous commit moves
416 lines, every one of them that field:

  416 "recorderSha256":

Against origin/main the picture is unchanged from the merge: 208 goldens, 0 added
or deleted, 0 non-header lines, and exactly four keys differing —

  208 "adapterSha256"   416 "baseline"   416 "goldenFormatVersion"   416 "recorderSha256"

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): wrap the recording README at the width the rest of it uses

Seven lines this branch added ran past 100 columns, worst 124. No wording changed.
Markdown is outside `recorderSha256`, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): name the worktree overlay, not the archive that cannot work

`git archive` was offered alongside a detached checkout as a way to lay this
branch's recorder over the pinned baseline. It cannot work: the fence in
scripts/rpc-recording.mts runs `git diff --quiet <baseline>` and an untracked-file
check, both of which need a real `.git`. In an archive tree git exits non-zero for
lack of a repository and the script reports "Product sources or lockfile differ
from the pinned main baseline", which reads as a product mismatch that is not
there. The transport agent lost time to exactly that.

Names `git worktree add --detach` only, and says what the misleading failure looks
like if someone tries an archive anyway. Markdown is outside `recorderSha256`, so
no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): close two ways an adapter module escapes its own digest

Two holes, one class: the seam was checked by how an import was spelled and by
what the register's values evaluated to, never by where they resolve or where they
were written.

Inward imports: the scan dropped every specifier starting with `..`, so
`'../adapters/settings-mount-adapters'` climbed out of the directory and back into
it unseen. A reviewer had `new-tab-agent-mount-adapters.ts` project a value read
from the settings module, edited that module, and watched the mounted state change
while the new-tab adapter digest held. Specifiers now resolve against the
directory and anything landing back inside it fails:

  ["new-tab-agent-mount-adapters.ts imports ../adapters/settings-mount-adapters"]

The register: `adapters/mounted-operation-modules.ts` is pinned by nothing —
`recorderSha256` skips the directory and `adapterSha256` reads each entry's
`source`. An `exposes` written inline there drives the mounted product module with
no digest covering it. The same reviewer replaced the new-tab entry's `exposes`
with a literal overriding `loadMobileNewTabAgentOptions`; twelve fence tests
passed. Both `mounts` and `exposes` must now be identifiers the register imports
from that entry's own module:

  ["new-tab-agent-mount-adapters.ts writes exposes inline instead of importing it"]

Checked on the register's syntax, not its values, because an inline literal and an
imported binding are indistinguishable once evaluated.

Pinning the register in the engine digest would also close it, and is the wrong
trade: every domain adding a register line would re-digest all 208 goldens, which
is the conflict this PR exists to remove. Keeping the register an index costs
nothing and keeps a domain's line local.

Both fixes live in a `.test.ts` outside the drivers, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): prove the mutant seam from the drivers out, not by spelling

The seam rested on a grep for the literal `mutants`, which the exported
`MUTANT_DIRECTORY` spells without containing. A reviewer had
`pilot-mount-adapters.ts` read the mutant table through that constant and both
checks passed. The README's claim — that nothing on the recording path names the
directory — was false as written.

Three changes, in order of strength:

Reachability is now proved forward. The suite walks the static import graph from
the two recording drivers and fails if any module under `mutants/` is in it. That
answers the real question, what a golden's bytes can depend on, instead of the old
inward scan's question, who mentions this directory. Non-emptiness is asserted on
both sides so a graph that resolved nothing cannot pass by reaching nothing.

The name scan covers both spellings, for paths a module can be read by rather than
imported. The reviewer's probe now fails as ["pilot-mount-adapters.ts"].

`MUTANT_DIRECTORY` is no longer exported. Its two consumers were both tests of the
digest, and they now spell the path instead, which is strictly better for them: a
test that imports the constant follows a rename silently, while one that spells it
fails on a rename — and that specific directory name is the whole soundness
argument. This edits `recorder-digest.ts`, so the goldens re-record in the next
commit.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the RPC goldens after MUTANT_DIRECTORY stopped being exported

All 208, `recorderSha256` only. Against the previous commit the diff is 416 lines
and every one of them is that field:

  416 "recorderSha256":

Against origin/main, unchanged: 208 goldens, 0 added or deleted, 0 non-header
lines, four keys differing —

  208 "adapterSha256"   416 "baseline"   416 "goldenFormatVersion"   416 "recorderSha256"

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): state the mutant seam's actual argument, and its edge

The README claimed nothing on the recording path names `mutants/`. That was the
old inward scan's claim and a reviewer falsified it with the exported constant. It
now describes what the check does: a forward walk of the import graph from the two
recording drivers, plus a name scan in both spellings for read-by-path, plus the
constant no longer being exported. It also names the case neither closes — a path
assembled from fragments at runtime.

Markdown is outside `recorderSha256`, so no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): prove the engine/adapter seam in both directions

The inward scan only held adapters to the seam. An engine file importing an
adapter executes code its own digest skips and that every golden recorded
through another domain leaves out of `adapterSha256`, so the register is now
the only crossing allowed from the engine side.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): name what the driver walk missed instead of counting it

Seeding `seen` with the drivers made the driver-presence check true by
construction, and the size bound compared a graph inflated by `typeof import`
product modules against a recorder-sized number. Both go; the walk now reports
the recording files it failed to reach, which is empty today and names an
orphan engine file the moment one appears.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* docs(mobile): reflow four paragraphs left ragged by the rewrap

Orphan fragments only, no wording change: the golden-schema field list, the
mutant-evidence paragraph, the probe-witness sentence and the re-anchor note.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): record the dictation, terminal, notification and browser domains against main

Adds scenarios and mount adapters for six small feature areas before any product file
moves, so the migration that follows has a frozen main oracle to be measured against:
dictation setup and session, agent history, terminal input and viewport refit, push
registration and the hosted browser's pointer, keyboard and dialog commands.

Five new adapter modules, one per domain, each pinned by its own `adapterSha256`. Two
declare exposures: `sendRegister`/`sendUnregister` are module-private in push-registration
and their exported callers read the keychain host catalog first, and the history hook
reaches its client through the shared per-host context rather than a parameter, so the
provider is the mounting boundary.

`native-mounting-substitutes.ts` is copied verbatim from the transport migration (stablyai#20667)
so the two branches merge, extended with the members these hooks read: `AppState` and
`useWindowDimensions` on react-native, the two-way audio module, `expo-keep-awake`, and
`buffer`. Every device event source is inert — no listener is fired — because each send is
driven through the operation's own API instead.

Wiring the substitute table into the loader moves `recorderSha256`, so every golden's
header re-digests. The product tree is unchanged, so `baseline` is unchanged and the 208
existing goldens move exactly one line each; no observation moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): send the dictation, terminal, notification and browser domains through typed RpcOperations

Replaces 42 raw `sendRequest` reaches across six feature areas with declared operations:
dictation setup and session, agent history, terminal input and viewport refit, push
registration and the hosted browser's page commands. No wire change — every golden
recorded in the previous commit still compares byte-identical, which is the evidence.

Acceptance is preserved site by site rather than unified. Every dictation and browser
refusal already raised the host's message with a screen fallback, so those share one
policy and keep their own copy at the call site. The terminal's two input sites read one
boolean off an object result, which `object-result-or-null` gives them without a throw.
The worker-takeover report and the three enrichment reads in the resume sheet are skips.

Two latent behaviours are preserved deliberately rather than tidied. `repo.list` reads
`.repos` at the return statement, so a null result throws a raw TypeError there and not a
wrapped refusal message; the three enrichment reads beside it are optional-chained and
tolerate the same null. `speech.dictation.finish` checks its refusal before the staleness
guard and reads `.text` after it, so the member read stays at the call site.

`RpcSendArguments` now admits an explicit `null` where the catalog declares no params.
Four shipped senders put `params: null` on the wire for such a method, and a frame carrying
a null is not the frame that omits the key; without this the migration would have rewritten
those bytes. `src/settings/native-voice-settings-operations.ts` widens its client type for
the same reason — it holds the port only to hand it to dictation.

Three references are left behind, each listed in the inventory with what blocks it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): matrix the dialog-dismiss site and name the three reaches left behind

The reply matrix drives every reply a family's base scenario scripts, and the browser dialog
family scripted only the accept leg, so repointing `browser.dialogDismiss` at another method
survived the whole suite. A scenario for the dismiss leg closes it; the mutation is killed now.

The inventory loses five section headers that no longer list anything, and the three entries
that resisted migration each carry what blocks them: a `worktree.ps` inside the history screen
component's own effect, an unsubscribe closed over inside a `subscribe` callback, and a
`notifications.getMissedSince` gated behind the OS notification tray and the keychain host
catalog. Faking either of the last two would record device state, not a wire.

Eighteen goldens move on `adapterSha256` alone: removing two inner casts from the browser and
history adapters re-digests the goldens mounted through them and nothing else. All 263 were
re-recorded from the pinned baseline and compare byte-identical to the previous recording
apart from those headers and the new file.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): fold five refusal-to-message spellings into one helper

The same three lines appeared five times in this PR: interpret a reply, and turn a refusal into a
plain Error carrying the host's message or the screen's copy. `interpretOrThrowRefusalMessage`
lives beside `refusedRpcMessageOrFallback` and takes the interpretation as a thunk, so the caller
still awaits the request outside the catch and a transport rejection reaches it as the object the
transport threw, delivery-unknown mark intact.

That removes the browser hook's own `assertBrowserCommandAccepted`, whose first parameter named an
operation the helper then used only to call `interpret`. Every browser page command is built by one
factory with one acceptance, so substituting one operation's `interpret` for another's was
unobservable and the parameter read as load-bearing when it was not. The call sites now name the
operation where they request and where they interpret, the shape the dictation sites already used.

Main's eight source-control spellings are deliberately untouched; folding those in is its own PR.

No wire change and no golden moves: `git diff --name-status origin/recorder-adapter-digest...HEAD
-- mobile/rpc-foundation` is byte-identical before and after at 55 added and 209 modified.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): record the history screen holding for a late worktree list

The hook holds loading when a scoped tab has no active worktree and the worktree list has not
arrived, rather than firing an unscoped scan that would briefly show unrelated host history. The
adapter hardcoded `worktreesLoaded` to true and its `worktrees-loaded` action was dead, so no
golden reached that branch and the hold was unrecorded.

The list and the flag now move together, which is how the screen learns them, and `mount` takes
`worktreesLoaded: false` to start unloaded. The new scenario records the hold, then the late load.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record the goldens at the pinned baseline

Recorded from a detached worktree at c6a7216
with this branch's rpc-recording/ and pilot-scenarios.json overlaid, per the
README's migration-branch procedure. Only header fields moved; no body line in
any golden changed.

recorderSha256 moved on all 208 pre-existing goldens: this branch adds
native-mounting-substitutes.ts at the top of rpc-recording/ and routes
operation-module-loader.ts through it, and both are recorder-engine inputs.
That resolves itself when stablyai#20667 lands the same substitution on main.

adapterSha256 moved on exactly four goldens, all from the late-worktree-list
scenario's edit to adapters/agent-history-mount-adapters.ts:
aivault-history-scan-fulfilled, aivault-history-scan-unsupported,
matrix-aivault.history-aivault.listsessions-1,
matrix-aivault.history-status.get-1.

aivault-history-scan-worktrees-late.json is new.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): ask these call sites for a request port, not a whole client

The migration widened seven files from `Pick<RpcClient, 'sendRequest'>` to the
full `RpcClient` for no reason: a bound operation's `request` takes
`UnvalidatedRpcRequestPort`, which is structurally that same single member.
Name the port instead, so the signature says what each function actually needs
and a caller holding only a port still satisfies it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): make these adapters reject an action no scenario dispatches

Each of the five new mount adapters ended its `action` with an unnamed default,
so a misspelled or engine-introduced name silently ran the last branch: probing
`remount` on the history adapter ran `onRefresh()` and put `status.get` on the
wire. Twelve pre-existing adapters throw `Unknown X action` instead; these now
do too.

Deleted with it: every branch no scenario can reach. `unmount` is dispatched
only by the lifecycle derivation in derived-goldens.ts, which is restricted to
LIFECYCLE_BASES, and none of these scenarios is in it; teardown goes through
`dispose`, which already unmounts. Same for the history adapter's `retry`,
`select-scope` and refresh, the dictation start flow's `disable`, and the
viewport adapter's `reconnect`. None of them handled `remount`, which the
schedule driver always pushes after `unmount`, so the pairing was never whole.

The three single-action entries keep `_name`, matching six pre-existing
entries that do the same.

Goldens move on adapterSha256 only and are re-recorded separately.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): drop four fallback strings no catch can read

Both pointer paths wrap their whole sequence in `catch {}` with an empty body,
so the message the refusal helper builds is discarded. `interpret` already
throws on a refusal under `require-result-or-throw-message`, and the helper only
rewrote the text, so removing it keeps the same control flow: the sequence still
stops at the refused leg, the later commands still go unsent, and `setError`
still does not run. The mousemove matrix golden records `error: null` under
every refusal and transport shape either way.

The helper stays where the throw reaches a caller: dictation and agent history.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): read the viewport outcome where the reader is

`readTerminalUpdateViewportOutcome` had one caller, the reader that wraps it, so
the name bought a second file to open and nothing else. Inline the two
comparisons and keep the outcome type where it was.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record this branch's goldens after the adapter cleanup

Recorded from a detached worktree at c6a7216,
this branch's rpc-recording/ and pilot-scenarios.json overlaid, per the README's
migration-branch procedure. 266 recordings, all reproduced.

adapterSha256 moved on all 56 goldens this branch owns, because the named throws
and the deleted unreachable branches changed all five adapter modules. No other
header field moved, and no body line in any golden changed: the wire, the
effects and the state snapshots are identical, which is the claim the five
review deletions rest on.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(mobile): reach the narrow port type without naming the port module

The previous commit named `UnvalidatedRpcRequestPort` by importing the port
module, and that fails the boundary ratchet: it counts an import of
`unvalidated-rpc-request-port` as reach, so all six unlisted files became
offenders and the history panel went from 1 reference to 2.

Main's `Pick<RpcClient, 'sendRequest'>` fails it for the same reason, by a
different rule: a bare `'sendRequest'` string literal is counted too. That is
why the migration widened these signatures in the first place, so the review
finding's premise that it was done for no cause is wrong. Only the full
`RpcClient` scored zero.

Re-export the port type from `rpc-client` instead. An export declaration with no
module specifier is not counted, the seven signatures still say they need one
sender rather than a whole client, and the inventory does not move. Holding a
client already carries the same reach, so nothing new is opened.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* Revert "refactor(mobile): reach the narrow port type without naming the port module"

This reverts 92e679f and 16c9c23 as one commit, returning the seven
client signatures to the full RpcClient the migration gave them.

unvalidated-rpc-request-port-boundary.test.ts:192 pins an import of the port
type as one reach by design: naming the type is exactly what the ratchet
retires, so re-exporting it from rpc-client opened an uncounted path for every
future file. Widening to the client is the design's intended end state.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): restore this branch's native substitutes and re-record at the pin

Taking stablyai#20667's landed `native-mounting-substitutes.ts` verbatim dropped the substitutes this
branch's domains need, and five scenarios stopped recording: `speech-audio-chunk-acknowledged`,
`speech-dictation-session-transcript`, `speech-dictation-session-cancelled`,
`terminal-viewport-refit-applied` and `terminal-viewport-refit-legacy-desktop` all failed with
`Missing or completed request`, because the hook throws on the native member before it sends.

Both branches created that file independently; neither is a version of the other. Main's structure
is kept whole — `partialNativeModule`, the `__esModule` rule, the async-storage trap — and this
branch's boundaries are added inside it: `buffer`, `AppState` and `useWindowDimensions` on
`react-native`, `@orca/expo-two-way-audio`, `expo-keep-awake`, and `expo-secure-store` as a second
unusable store. Each is inert; no listener is ever fired and no audio is produced.

That is an engine edit, so every golden re-digests. Recorded at main's pin
c6a7216 from a detached worktree with this recorder overlaid, and
the product tree there was byte-identical to the pin. All 397 goldens moved on `recorderSha256`
alone and nothing else:

    git diff -U0 -- mobile/rpc-foundation/goldens | grep -E '^[+-]' \
      | grep -vE '^(\+\+\+|---)' \
      | grep -vE '^[+-]  "(recorderSha256|adapterSha256|goldenFormatVersion|baseline|scenarioSha256|lockfileSha256)":' | wc -l
    0

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): re-record at the pin after the stablyai#20685 merge

The merged recorder is a third engine: main's post-tasks-2 files plus this
branch's native-mounting-substitutes.ts, so recorderSha256 moves once and every
golden re-digests. Recorded from the pinned baseline c6a7216 in a detached
worktree with this tree's recorder laid over it, so the product source is still
main's pre-migration tree. All 509 goldens move on recorderSha256 alone; no
recorded wire byte changes.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
ivanvykopal pushed a commit to ivanvykopal/orca that referenced this pull request Sep 20, 2026
… RPC recorder (stablyai#20873)

* test(rpc-recording): record the real worktree catalog snapshot result

`worktree.catalog-snapshot`'s action returned `WorktreeCatalogSnapshotClient.fetch`'s
raw result. That value nests the live `RpcClient` under `pending.client`, so
`captureValue` threw `Unsupported observation: function`: both goldens baked an
`unhandled-rejection` effect and left the action's settlement `pending`, proving
nothing about what the fetch returns.

Project the result through `projectObservable` on the settlement path, the same
way `state()` already shows it and the same way stablyai#20667 fixed
`transport.pairing-race`. Rejections still propagate unchanged, so the two
transport-rejection matrix partitions keep their recorded errors.

The matrix golden now discriminates all eleven reply partitions: a full
admission, six invalid ones, three `request_failed` codes and two rejections.
`recorderSha256` does not move; `adapterSha256` moves on the six goldens mounted
through this adapter module, four of which have no other change.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): fail the suite when a golden records a recorder failure

A projection refusal settles as data, not as a failed run: `captureValue` throws
inside the action, the recorder captures it as an `unhandled-rejection` effect,
and `--record` writes a green golden whose action never settled. The class has
landed twice — `transport.pairing-race` in stablyai#20667 and `worktree.catalog-snapshot`
in the previous commit — and reverting either one plus a re-record would go green
with the broken golden back.

Read every golden and fail on an `unhandled-rejection` effect or on either of
`captureValue`'s refusal texts in any observation field. A positive control in the
same case asserts both detectors fire, so the absence claim is load-bearing rather
than vacuous.

Not a recording driver, so `recorderSha256` excludes it and no golden moves.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): ban only the recorder's own refusals, not product bugs

The gate also failed on any effect named `unhandled-rejection`, which bans a real
observation: detached-rejection capture exists to pin a main bug in a golden, and
`unhandled-recording.test.ts` pins the capture precisely because no golden carries
one today. Banning the name would make recording a genuine product failure a test
failure.

Drop that detector. `refusalText` alone catches all seven checkpoints of the
worktree-catalog regression, because the refusal is the message of the captured
error rather than the effect's name. The positive control now seeds that shape —
the refusal inside a recorded error under `effects` — so the surviving detector is
still proven to fire. Vacuity guards on golden and checkpoint counts are unchanged.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(rpc-recording): re-record the worktree catalog goldens over main's engine digest

stablyai#20874 re-digested `recorderSha256` on all 509 goldens, so the six goldens mounted
through the worktree catalog adapter had to be recorded again from the pin rather
than merged. Bodies and `adapterSha256` are unchanged from the pre-merge recording.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
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