Repository navigation
refactor(mobile): send the transport pairing and status domain through typed RpcOperations - #20667
Conversation
…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
|
Run failed. View the logs →
|
|
Too many files changed for review (361 files, 100 file limit). Bypass the limit by tagging |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe 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 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)
✅ Passed checks (3 passed)
Full details: Docstring CoverageExplanation 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 checkExplanation 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
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 1ede4e34-6cc2-4128-a4e7-32ed7b1626a4
📒 Files selected for processing (261)
mobile/rpc-foundation/goldens/b1.jsonmobile/rpc-foundation/goldens/b2.jsonmobile/rpc-foundation/goldens/b3.jsonmobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/inventory-lifecycle.jsonmobile/rpc-foundation/goldens/inventory-repeat-query.jsonmobile/rpc-foundation/goldens/lifecycle-b3.jsonmobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.jsonmobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.jsonmobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.jsonmobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.jsonmobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.jsonmobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.jsonmobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-times-out.jsonmobile/rpc-foundation/goldens/probe-new-tab-both-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.jsonmobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-commits.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.jsonmobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.jsonmobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.jsonmobile/rpc-foundation/goldens/sc-base-ref-default.jsonmobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.jsonmobile/rpc-foundation/goldens/sc-base-ref-unavailable.jsonmobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.jsonmobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.jsonmobile/rpc-foundation/goldens/sc-commit-message-canceled.jsonmobile/rpc-foundation/goldens/sc-commit-message-generated.jsonmobile/rpc-foundation/goldens/sc-create-existing-review.jsonmobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.jsonmobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.jsonmobile/rpc-foundation/goldens/sc-create-pushes-then-creates.jsonmobile/rpc-foundation/goldens/sc-create-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-create-rejected-empty-message.jsonmobile/rpc-foundation/goldens/sc-eligibility-fetched.jsonmobile/rpc-foundation/goldens/sc-history-loaded.jsonmobile/rpc-foundation/goldens/sc-pr-link-hosted-review.jsonmobile/rpc-foundation/goldens/sc-pr-link-read.jsonmobile/rpc-foundation/goldens/sc-pr-link-set.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.jsonmobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.jsonmobile/rpc-foundation/goldens/sc-prerequisite-publish.jsonmobile/rpc-foundation/goldens/sc-prerequisite-push.jsonmobile/rpc-foundation/goldens/sc-prerequisite-skipped.jsonmobile/rpc-foundation/goldens/sc-reveal-first-poll.jsonmobile/rpc-foundation/goldens/sc-reveal-timeout.jsonmobile/rpc-foundation/goldens/sc-review-commit-inner-failure.jsonmobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-review-commit-rejected.jsonmobile/rpc-foundation/goldens/sc-review-commit.jsonmobile/rpc-foundation/goldens/sc-review-status-entries-not-array.jsonmobile/rpc-foundation/goldens/sc-review-status-normalized.jsonmobile/rpc-foundation/goldens/schedules-b3.jsonmobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.jsonmobile/rpc-foundation/goldens/settings-home-coalesced.jsonmobile/rpc-foundation/goldens/settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-home-providers-refused.jsonmobile/rpc-foundation/goldens/settings-home-providers-transport-error.jsonmobile/rpc-foundation/goldens/settings-new-tab-refused.jsonmobile/rpc-foundation/goldens/settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/settings-new-tab-transport-error.jsonmobile/rpc-foundation/goldens/settings-repo-cache-expiry.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-single-host.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refused.jsonmobile/rpc-foundation/goldens/settings-task-hydration-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-linear.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.jsonmobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-workspace-refused.jsonmobile/rpc-foundation/goldens/settings-task-workspace-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-write.jsonmobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-context-transport-error.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.jsonmobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.jsonmobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.jsonmobile/rpc-foundation/goldens/transport-capability-probe-publishes.jsonmobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-ready.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.jsonmobile/rpc-foundation/goldens/transport-pairing-race-both-refused.jsonmobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.jsonmobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.jsonmobile/rpc-foundation/goldens/tw-capabilities-advertised.jsonmobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.jsonmobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-created.jsonmobile/rpc-foundation/goldens/tw-create-retry-name-collision.jsonmobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.jsonmobile/rpc-foundation/goldens/tw-create-retry-warning-kept.jsonmobile/rpc-foundation/goldens/tw-hosted-base-resolved.jsonmobile/rpc-foundation/goldens/tw-hosted-base-soft-error.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-resolved.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-always.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.jsonmobile/rpc-foundation/goldens/tw-smart-search-all-providers.jsonmobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.jsonmobile/rpc-foundation/goldens/tw-smart-search-linear-listed.jsonmobile/rpc-foundation/goldens/tw-task-preferences-resume-write.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-saved.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connected.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.jsonmobile/rpc-foundation/pilot-scenarios.jsonmobile/src/test-support/rpc-recording/native-mounting-substitutes.tsmobile/src/test-support/rpc-recording/operation-module-loader.tsmobile/src/test-support/rpc-recording/pairing-journal-mount-adapters.tsmobile/src/test-support/rpc-recording/pilot-mount-adapters.tsmobile/src/test-support/rpc-recording/relay-credential-mount-adapters.tsmobile/src/test-support/rpc-recording/relay-pairing-fixtures.tsmobile/src/test-support/rpc-recording/transport-status-mount-adapters.tsmobile/src/transport/host-status-gates.tsmobile/src/transport/host-status-probe-operations.tsmobile/src/transport/mobile-relay-credential-rotation.tsmobile/src/transport/mobile-relay-direct-upgrade.tsmobile/src/transport/mobile-relay-pairing-operations.tsmobile/src/transport/mobile-relay-pairing-recovery.tsmobile/src/transport/pairing-candidate-race.tsmobile/src/transport/pairing-relay-candidate.tsmobile/src/transport/pre-profile-pairing-coordinator.tsmobile/src/transport/runtime-capability-probe.tsmobile/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.
| "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"] |
There was a problem hiding this comment.
🎯 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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" |
There was a problem hiding this comment.
🩺 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-supportRepository: 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 | sortRepository: 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' | sortRepository: 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.tsRepository: 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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
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
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
There was a problem hiding this comment.
ℹ️ 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 newrecorderSha256; novaluesline changed, so the recorded wire behavior is unchanged. - Made the native substitutes strict —
partialNativeModulenow throws on any unlisted member read forreact-native,expo-cryptoand@noble/hashes/sha256, so an unmounted native API fails loudly instead of recordingundefined. - Dropped the unused
expo-secure-storesubstitute — only@react-native-async-storage/async-storageremains as a call-time-throwing store. - Deduped the credential-hash fixture loader —
credentialHashmoves intorelay-pairing-fixtures.ts, both adapters call it, and the unread fixture exports are de-exported.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 4
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 5e825368-b2c9-4c80-924e-34bf539da441
📒 Files selected for processing (250)
mobile/rpc-foundation/goldens/b1.jsonmobile/rpc-foundation/goldens/b2.jsonmobile/rpc-foundation/goldens/b3.jsonmobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/inventory-lifecycle.jsonmobile/rpc-foundation/goldens/inventory-repeat-query.jsonmobile/rpc-foundation/goldens/lifecycle-b3.jsonmobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.jsonmobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.jsonmobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.jsonmobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.jsonmobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.jsonmobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.jsonmobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-times-out.jsonmobile/rpc-foundation/goldens/probe-new-tab-both-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.jsonmobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-commits.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.jsonmobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.jsonmobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.jsonmobile/rpc-foundation/goldens/sc-base-ref-default.jsonmobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.jsonmobile/rpc-foundation/goldens/sc-base-ref-unavailable.jsonmobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.jsonmobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.jsonmobile/rpc-foundation/goldens/sc-commit-message-canceled.jsonmobile/rpc-foundation/goldens/sc-commit-message-generated.jsonmobile/rpc-foundation/goldens/sc-create-existing-review.jsonmobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.jsonmobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.jsonmobile/rpc-foundation/goldens/sc-create-pushes-then-creates.jsonmobile/rpc-foundation/goldens/sc-create-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-create-rejected-empty-message.jsonmobile/rpc-foundation/goldens/sc-eligibility-fetched.jsonmobile/rpc-foundation/goldens/sc-history-loaded.jsonmobile/rpc-foundation/goldens/sc-pr-link-hosted-review.jsonmobile/rpc-foundation/goldens/sc-pr-link-read.jsonmobile/rpc-foundation/goldens/sc-pr-link-set.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.jsonmobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.jsonmobile/rpc-foundation/goldens/sc-prerequisite-publish.jsonmobile/rpc-foundation/goldens/sc-prerequisite-push.jsonmobile/rpc-foundation/goldens/sc-prerequisite-skipped.jsonmobile/rpc-foundation/goldens/sc-reveal-first-poll.jsonmobile/rpc-foundation/goldens/sc-reveal-timeout.jsonmobile/rpc-foundation/goldens/sc-review-commit-inner-failure.jsonmobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-review-commit-rejected.jsonmobile/rpc-foundation/goldens/sc-review-commit.jsonmobile/rpc-foundation/goldens/sc-review-status-entries-not-array.jsonmobile/rpc-foundation/goldens/sc-review-status-normalized.jsonmobile/rpc-foundation/goldens/schedules-b3.jsonmobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.jsonmobile/rpc-foundation/goldens/settings-home-coalesced.jsonmobile/rpc-foundation/goldens/settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-home-providers-refused.jsonmobile/rpc-foundation/goldens/settings-home-providers-transport-error.jsonmobile/rpc-foundation/goldens/settings-new-tab-refused.jsonmobile/rpc-foundation/goldens/settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/settings-new-tab-transport-error.jsonmobile/rpc-foundation/goldens/settings-repo-cache-expiry.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-single-host.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refused.jsonmobile/rpc-foundation/goldens/settings-task-hydration-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-linear.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.jsonmobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-workspace-refused.jsonmobile/rpc-foundation/goldens/settings-task-workspace-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-write.jsonmobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-context-transport-error.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.jsonmobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.jsonmobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.jsonmobile/rpc-foundation/goldens/transport-capability-probe-publishes.jsonmobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-ready.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.jsonmobile/rpc-foundation/goldens/transport-pairing-race-both-refused.jsonmobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.jsonmobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.jsonmobile/rpc-foundation/goldens/tw-capabilities-advertised.jsonmobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.jsonmobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-created.jsonmobile/rpc-foundation/goldens/tw-create-retry-name-collision.jsonmobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.jsonmobile/rpc-foundation/goldens/tw-create-retry-warning-kept.jsonmobile/rpc-foundation/goldens/tw-hosted-base-resolved.jsonmobile/rpc-foundation/goldens/tw-hosted-base-soft-error.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-resolved.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-always.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.jsonmobile/rpc-foundation/goldens/tw-smart-search-all-providers.jsonmobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.jsonmobile/rpc-foundation/goldens/tw-smart-search-linear-listed.jsonmobile/rpc-foundation/goldens/tw-task-preferences-resume-write.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-saved.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connected.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.jsonmobile/rpc-foundation/pilot-scenarios.jsonmobile/src/test-support/rpc-recording/adapters/mounted-operation-modules.tsmobile/src/test-support/rpc-recording/adapters/pairing-journal-mount-adapters.tsmobile/src/test-support/rpc-recording/adapters/relay-credential-mount-adapters.tsmobile/src/test-support/rpc-recording/adapters/transport-status-mount-adapters.tsmobile/src/test-support/rpc-recording/native-mounting-substitutes.tsmobile/src/test-support/rpc-recording/operation-module-loader.tsmobile/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.
| "2d40d1d38104": { | ||
| "name": "unhandled-rejection", | ||
| "value": { | ||
| "category": "Error", | ||
| "isRpcDeliveryUnknown": false, | ||
| "message": "Unsupported observation: function" | ||
| } | ||
| }, |
There was a problem hiding this comment.
🗄️ 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 settledThere was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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
There was a problem hiding this comment.
ℹ️ No new issues in this delta. One prior doc/comment nit (
native-mounting-substitutes.tsstill 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
__esModuletrap —unusableNativeStorenow answers the module system's interop probe withundefined, so a transpiled default import binds the module object instead of the trap's own throwing function. PreviouslyimportDefault(...).defaultbound a function andAsyncStorage.getItemread asundefined, producing a TypeError instead of the named "Native store reached during recording" error;partialNativeModulealready handled the marker correctly. - Added
native-mounting-substitutes.test.ts— pins the named store error under a default import,__esModule === undefinedfor both trap types, and the unlisted-member throw. Confirmed falsifiable: reverting theunusableNativeStorefix fails 2 of the 3 tests. - Deleted the redundant
recordingRandomBytes— both credential callers already defaultrandomBytestoExpoCrypto.getRandomBytes, which theexpo-cryptosubstitute 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:
recorderSha256on all 242,adapterSha256only on the 10 relay-credential goldens, zerovalueschanges. The recording suite replays green on this tree (311 passed / 3 skipped).
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟠 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 winReturn a serializable result from the pairing-race adapter.
racePairingCandidatesresolves to a rawPairingCandidate, whosecandidateClient(...)value contains functions. Whenactionreturnssettled, recording recursively projects those functions and raisesUnsupported observation: function. Race recordings can therefore remain pending instead of recording the winner.Return the chained promise. Return
winner.pathon 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 winExpose the verification state in the gate recording.
The adapter serializes capabilities and
pending, butstatusPendingbecomesfalsewhenconnStatechanges toconnecting. 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 winSchedule both status completions before one flush.
runRecordingflushes after every step, so the currentrelay-statuscompletion selects relay beforedirect-statuscompletes. The direct-tie golden and its matrixdirect-statusrecordings 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
📒 Files selected for processing (246)
mobile/rpc-foundation/goldens/b1.jsonmobile/rpc-foundation/goldens/b2.jsonmobile/rpc-foundation/goldens/b3.jsonmobile/rpc-foundation/goldens/interruptions-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/interruptions-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/inventory-lifecycle.jsonmobile/rpc-foundation/goldens/inventory-repeat-query.jsonmobile/rpc-foundation/goldens/lifecycle-b3.jsonmobile/rpc-foundation/goldens/lifecycle-inventory-lifecycle.jsonmobile/rpc-foundation/goldens/lifecycle-settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/lifecycle-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.baserefdefault-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-git.base-ref-chain-worktree.show-1.jsonmobile/rpc-foundation/goldens/matrix-git.commit-message-ai-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-git.history-read-git.history-1.jsonmobile/rpc-foundation/goldens/matrix-git.remote-prerequisite-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-git.review-preparation-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-chain-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.bulkstage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.commit-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.generatecommitmessage-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.push-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-3.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-git.status-4.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.create-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-hostedreview.getcreationeligibility-2.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.create-intent-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-hostedreview.eligibility-hostedreview.getcreationeligibility-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-1.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-files.searchpaths-2.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-fresh-inventory.jsonmobile/rpc-foundation/goldens/matrix-legacy-inventory-old-inventory.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.getissue-1.jsonmobile/rpc-foundation/goldens/matrix-linear-detail-barrier-linear.issuecomments-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-direct-status.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-pairing.pre-profile-relay-status.jsonmobile/rpc-foundation/goldens/matrix-project-explicit-false-github.project.updateissuebyslug-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.credential-rotation-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.getendpoints-2.jsonmobile/rpc-foundation/goldens/matrix-relay.direct-upgrade-pairing.provisionrelay-1.jsonmobile/rpc-foundation/goldens/matrix-relay.pairing-recovery-pairing.getendpoints-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.activate-1.jsonmobile/rpc-foundation/goldens/matrix-session.tab-reveal-session.tabs.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings-agent-read-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings-best-effort-settings.update-1.jsonmobile/rpc-foundation/goldens/matrix-settings.bot-overrides-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.home-providers-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-folderworkspace.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-projectgroup.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-repo.list-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.resume-metadata-worktree.ps-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-hydration-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-create-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-settings.task-workspace-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-linear.status-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-preflight.check-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-context-ui.get-1.jsonmobile/rpc-foundation/goldens/matrix-settings.workspace-submit-settings.get-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.reposlug-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitem-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-github.workitembyownerrepo-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.paste-lookup-gitlab.workitembypath-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-github.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-gitlab.listworkitems-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.listissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-linear.searchissues-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.smart-source-search-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.searchrefs-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-source-repo.sparsepresets-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-repo.savesparsepreset-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-sparse-ssh.getstate-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-local-preflight.detectagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-preflight.detectremoteagents-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-repo.hooks-1.jsonmobile/rpc-foundation/goldens/matrix-tasks.workspace-ssh-ssh.connect-1.jsonmobile/rpc-foundation/goldens/matrix-transport.capability-probe-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.host-status-gates-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-direct-status.jsonmobile/rpc-foundation/goldens/matrix-transport.pairing-race-relay-status.jsonmobile/rpc-foundation/goldens/matrix-worktree.create-retry-worktree.create-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolvemrbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.hosted-base-worktree.resolveprbase-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.review-link-worktree.set-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.runtime-capabilities-status.get-1.jsonmobile/rpc-foundation/goldens/matrix-worktree.setup-hook-trust-ui.set-1.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-direct-wins-and-provisions.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-provision-unsupported-saves-direct-host.jsonmobile/rpc-foundation/goldens/pairing-pre-profile-times-out.jsonmobile/rpc-foundation/goldens/probe-new-tab-both-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-null-sibling-refused.jsonmobile/rpc-foundation/goldens/probe-new-tab-refused-sibling-rejects.jsonmobile/rpc-foundation/goldens/probe-new-tab-rejects-sibling-refused.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-commits.jsonmobile/rpc-foundation/goldens/relay-direct-upgrade-unsupported-host-declines.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-invite-authorizes.jsonmobile/rpc-foundation/goldens/relay-pairing-recovery-resume-committed.jsonmobile/rpc-foundation/goldens/relay-rotation-installs-and-commits.jsonmobile/rpc-foundation/goldens/relay-rotation-resumes-committed-pending.jsonmobile/rpc-foundation/goldens/sc-base-ref-default.jsonmobile/rpc-foundation/goldens/sc-base-ref-repo-fallback.jsonmobile/rpc-foundation/goldens/sc-base-ref-unavailable.jsonmobile/rpc-foundation/goldens/sc-base-ref-worktree-hit.jsonmobile/rpc-foundation/goldens/sc-commit-message-cancel-rejected.jsonmobile/rpc-foundation/goldens/sc-commit-message-canceled.jsonmobile/rpc-foundation/goldens/sc-commit-message-generated.jsonmobile/rpc-foundation/goldens/sc-create-existing-review.jsonmobile/rpc-foundation/goldens/sc-create-intent-stage-commit-push-create.jsonmobile/rpc-foundation/goldens/sc-create-link-failure-is-non-fatal.jsonmobile/rpc-foundation/goldens/sc-create-pushes-then-creates.jsonmobile/rpc-foundation/goldens/sc-create-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-create-rejected-empty-message.jsonmobile/rpc-foundation/goldens/sc-eligibility-fetched.jsonmobile/rpc-foundation/goldens/sc-history-loaded.jsonmobile/rpc-foundation/goldens/sc-pr-link-hosted-review.jsonmobile/rpc-foundation/goldens/sc-pr-link-read.jsonmobile/rpc-foundation/goldens/sc-pr-link-set.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-refusal.jsonmobile/rpc-foundation/goldens/sc-prefill-unavailable-on-rejection.jsonmobile/rpc-foundation/goldens/sc-prerequisite-force-with-lease.jsonmobile/rpc-foundation/goldens/sc-prerequisite-publish.jsonmobile/rpc-foundation/goldens/sc-prerequisite-push.jsonmobile/rpc-foundation/goldens/sc-prerequisite-skipped.jsonmobile/rpc-foundation/goldens/sc-reveal-first-poll.jsonmobile/rpc-foundation/goldens/sc-reveal-timeout.jsonmobile/rpc-foundation/goldens/sc-review-commit-inner-failure.jsonmobile/rpc-foundation/goldens/sc-review-commit-refused-empty-message.jsonmobile/rpc-foundation/goldens/sc-review-commit-rejected.jsonmobile/rpc-foundation/goldens/sc-review-commit.jsonmobile/rpc-foundation/goldens/sc-review-status-entries-not-array.jsonmobile/rpc-foundation/goldens/sc-review-status-normalized.jsonmobile/rpc-foundation/goldens/schedules-b3.jsonmobile/rpc-foundation/goldens/schedules-settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/schedules-settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-fulfilled.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refresh-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-refused.jsonmobile/rpc-foundation/goldens/settings-bot-overrides-transport-error.jsonmobile/rpc-foundation/goldens/settings-home-coalesced.jsonmobile/rpc-foundation/goldens/settings-home-providers-fulfilled.jsonmobile/rpc-foundation/goldens/settings-home-providers-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-home-providers-refused.jsonmobile/rpc-foundation/goldens/settings-home-providers-transport-error.jsonmobile/rpc-foundation/goldens/settings-new-tab-refused.jsonmobile/rpc-foundation/goldens/settings-new-tab-ssh.jsonmobile/rpc-foundation/goldens/settings-new-tab-transport-error.jsonmobile/rpc-foundation/goldens/settings-repo-cache-expiry.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-single-host.jsonmobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-fulfilled.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-refused.jsonmobile/rpc-foundation/goldens/settings-resume-metadata-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-hydration-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-task-hydration-refused.jsonmobile/rpc-foundation/goldens/settings-task-hydration-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-linear.jsonmobile/rpc-foundation/goldens/settings-task-workspace-create-pr-start-point.jsonmobile/rpc-foundation/goldens/settings-task-workspace-fulfilled.jsonmobile/rpc-foundation/goldens/settings-task-workspace-refused.jsonmobile/rpc-foundation/goldens/settings-task-workspace-transport-error.jsonmobile/rpc-foundation/goldens/settings-task-write.jsonmobile/rpc-foundation/goldens/settings-workspace-context-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refuse-after-data.jsonmobile/rpc-foundation/goldens/settings-workspace-context-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-context-transport-error.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-fulfilled.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-refused.jsonmobile/rpc-foundation/goldens/settings-workspace-submit-transport-error.jsonmobile/rpc-foundation/goldens/transport-capability-probe-cutover-reasks-fast.jsonmobile/rpc-foundation/goldens/transport-capability-probe-non-string-capabilities-drop.jsonmobile/rpc-foundation/goldens/transport-capability-probe-publishes.jsonmobile/rpc-foundation/goldens/transport-capability-probe-refused-backs-off.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-drop-keeps-capabilities.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-ready.jsonmobile/rpc-foundation/goldens/transport-host-status-gates-refused-degrades.jsonmobile/rpc-foundation/goldens/transport-pairing-race-both-refused.jsonmobile/rpc-foundation/goldens/transport-pairing-race-direct-wins.jsonmobile/rpc-foundation/goldens/transport-pairing-race-relay-wins-when-direct-refused.jsonmobile/rpc-foundation/goldens/tw-capabilities-advertised.jsonmobile/rpc-foundation/goldens/tw-capabilities-cutover-retried.jsonmobile/rpc-foundation/goldens/tw-capabilities-legacy-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-after-drop.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-while-connected.jsonmobile/rpc-foundation/goldens/tw-create-retry-ambiguous-without-idempotency.jsonmobile/rpc-foundation/goldens/tw-create-retry-created.jsonmobile/rpc-foundation/goldens/tw-create-retry-name-collision.jsonmobile/rpc-foundation/goldens/tw-create-retry-unretryable-refusal.jsonmobile/rpc-foundation/goldens/tw-create-retry-warning-kept.jsonmobile/rpc-foundation/goldens/tw-hosted-base-resolved.jsonmobile/rpc-foundation/goldens/tw-hosted-base-soft-error.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-resolved.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-refused.jsonmobile/rpc-foundation/goldens/tw-paste-lookup-slug-unsupported.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-always.jsonmobile/rpc-foundation/goldens/tw-setup-hook-trust-approved.jsonmobile/rpc-foundation/goldens/tw-smart-search-all-providers.jsonmobile/rpc-foundation/goldens/tw-smart-search-gitlab-provider-error.jsonmobile/rpc-foundation/goldens/tw-smart-search-linear-listed.jsonmobile/rpc-foundation/goldens/tw-task-preferences-resume-write.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-source-presets.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-missing-preset.jsonmobile/rpc-foundation/goldens/tw-workspace-sparse-saved.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connect-refused.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-connected.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-local-agents.jsonmobile/rpc-foundation/goldens/tw-workspace-ssh-not-ready.jsonmobile/src/test-support/rpc-recording/adapters/relay-credential-mount-adapters.tsmobile/src/test-support/rpc-recording/native-mounting-substitutes.test.tsmobile/src/test-support/rpc-recording/native-mounting-substitutes.tsmobile/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
There was a problem hiding this comment.
ℹ️ No new issues in this delta. The merge and re-digest both check out; the one prior doc/comment nit (
native-mounting-substitutes.tsstill 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
mainand resolved three structural conflicts —adapters/mounted-operation-modules.tsunions both registers (this branch'stransport-status/relay-credential/pairing-journalentries join main's five new adapters, alphabetized);pilot-scenarios.jsonis an additive union (149 scenarios, no duplicate ids);unvalidated-rpc-request-port-inventory.tskeeps the branch'ssrc/transport/holdouts and drops main's now-migratedsrc/worktree/entries. - Re-digested main's 47 new goldens on
recorderSha256only — this branch adds two files to the recorder engine, so the header digest moves;baseline,adapterSha256,scenarioSha256and everyvaluesline 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 ofrpc-operation-contract.ts),relay-dial-stage.tsand 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.
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
#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
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
…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
…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
…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
… 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
…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
…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
… 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

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.
recorderSha256header line moved; zero body lines changed (verified with the inert-proof diff in the sections below)pilot-scenarios.json.test.tsfiles andmobile/src/test-support/rpc-recording/mobile/srcnon-test filesWhat
Step 4,
transportdomain: the pairing / probing / capability call sites inmobile/src/transport/move onto typedRpcOperations. Three operations over three methods in two new modules, zero new acceptance policies, one reader per method:host-status-probe-operations.ts—hostStatusProbeonstatus.get,success-result-or-skip. All three transport callers treat a refusal as an absent answer. It is a third named policy onstatus.get, separate from the two intasks, because an operation name is what a decode failure reports and transport must not import tasks.mobile-relay-pairing-operations.ts—relayCredentialProvisionandrelayPairingEndpointsRead, bothrequire-result-or-throw, which reuses the exactcode: messagetext the four call sites each spelled by hand. Three sites still read the raw envelope formethod_not_foundbefore 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
rpcUncheckedPayloadReaderand 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.ts2 → 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 aPairingCandidateClientwith director recovery and forwards whatever method it is handed; it implements the port. Its one chosen method string now comes fromhostStatusProbe.operation.method.mobile-runtime-capability-negotiation.ts(2) sends through the physical clients' authenticated-but-not-yet-connected path, which is not anRpcClient(it deadlocks onwaitForConnectedif 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 throughhostStatusProbe, butpush-registration.tsholds only a sender, so widening toRpcClientbreaks another domain's file.Evidence
Oracle parity. Commit 1 records the scenarios against main's product code (
git worktree add --detachat 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 anddiff -rqagainst the tree reported no differences, and the recording suite replays 296 green on the migrated tree. Note for repeaters:git archivecannot serve as the overlay tree because the record fence shells out togit diff; usegit worktree add --detach.Mutation census, each applied / run / reverted:
status.getrepointed toui.getpairing.provisionRelayrepointedpairing.getEndpointsrepointedhostStatusProbepolicy swappedrelayCredentialProvisionpolicy swappedrelayPairingEndpointsReadpolicy swappedNo 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.tsneeds aSAFETYcast because the reader returnsunknownand 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
PushClientinsrc/notifications/push-registration.tsnot widened (another domain; would takeruntime-capability-probe.tsto zero). Ticket.mobile-relay-rpc-session.ts's ownpairing.getEndpointsacceptance throws onerror.codealone and disagrees with the four migrated sites. Behaviour change; ticket.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 typecheck0 ·pnpm --dir mobile test581 files, 5057 passed / 6 skipped ·pnpm tc0 ·check:code-quality:changed0 over 18 files ·check:react-doctor:changedclean ·oxfmt --check .frommobile/clean ·oxlintclean on changed files and the wholemobile/tree.After #20662 landed (2026-09-15)
Merged main at
6a11a0b8e6; adapters moved intoadapters/and registered. Round-1 items applied: the three partial native substitutes are now throwing proxies (an unlisted member read fails instead of recordingundefined; the ES-module interop probe for__esModuleis the one exempt key, becauseimport * asreads it before copying members), the unreadexpo-secure-storesubstitute is deleted, nine dead fixture exports dropped,credentialHashdeduped. Re-recorded at main's pinc6a72169843e(an interim repin to6a11a0b8e6was reverted in55c7d2916a; 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 (
recorderSha256against main;baseline/recorderSha256/adapterSha256against the pre-merge head). The engine re-digest is unavoidable for this PR: it addsnative-mounting-substitutes.tsandrelay-pairing-fixtures.tsand 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
59cfcc64b1and re-recorded in6fda563942. The store substitute's throwing proxy answered the module system's__esModuleprobe with a throwing function, so a default import boundproxy.defaultandAsyncStorage.getItemread asundefined(a TypeError instead of the named "native store reached" error); both traps now leave that marker undefined, andnative-mounting-substitutes.test.tspins the named error under a default import, the marker, and an unlisted member.recordingRandomByteswas theexpo-cryptosubstitute's own default handed back to it, so it is deleted along with its stranded comment. Re-record movedrecorderSha256on all 242 goldens andadapterSha256on the 10 relay credential goldens, zero non-header lines. Against main the branch remains 208 modified (recorderSha256only) and 34 added.Bot-comment triage before merge (2026-09-15)
Fixed on this branch (
8d4b6e06c2,d6baaf3a7b,4df87db5a4,6ca467c9ce):PairingCandidate, whose client has function members, so the recorder threwUnsupported observation: functionand every pairing-race golden baked an unhandled-rejection effect with the race settlement leftpending. The adapter now returnswinner.path; all four race goldens record a fulfilled (direct/relay) or rejected (both-refused) settlement with the loser closed.direct-wins-the-tierecordedrelay. 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 totransport-pairing-race-relay-completes-firstand added the mirrortransport-pairing-race-direct-completes-first; the two matrix goldens inherit the checkpoint name.d6baaf3a7bstays 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);
__esModuleinterop (already fixed with a test).Proofs vs main
44268d9616: 306 M allrecorderSha256-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.