mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* test(mobile): record six screen-mounted call sites before migrating them Five new mount adapters and six scenarios, recorded against the pinned baseline's product code so the goldens are main's behaviour, not the refactor's. Each site is a screen the recorder could not previously mount: - `home.host-accounts` mounts `fetchMobileHomeAccounts`, whose decoder is re-exported through `AccountUsage.tsx`. That module loads under the mount loader, so the inventory's "no recording can load it" was already stale. - `notifications.display-test-screen` mounts the settings push probe and presses its button by reading the handler back off the rendered inert `Pressable`. - `aiVault.history-screen` mounts the history panel, which is where the last `worktree.ps` lives. Split in two: the base stops once the worktree list has seeded the scopes, because a reply partition there changes the scopePaths the downstream `aiVault.listSessions` carries, and a matrix variant cannot assert params it moved. The full chain is a second scenario, driven as a pilot only. - `tasks.route-repo-list` mounts the tasks screen-root hook and calls its own `ensureLoaded`, which is the only thing that fires `repo.list`. - `linear.select-workspace-picker` calls the render helper the tasks surface calls and invokes the `onSelect` on the element it returns. The picker draws inside `BottomDrawer`, whose reanimated timing driver and gesture builder the recorder would have to impersonate for a row to exist; the closure is the same either way, and the workspace a selection carries comes from the scenario. Five substitute members are added, each with the recording that reads it: `react-native-safe-area-context.useSafeAreaInsets` and `expo-router.useLocalSearchParams` for `tasks.route-repo-list`, and `react-native.TextInput`, `.SectionList` and `.RefreshControl` for `aiVault.history-screen` once its list renders. `useLocalSearchParams` answers one pinned route for the same reason the window size is pinned: a screen's own address is not a device reading, and the one screen that reads it sends `repo.list`, which takes no params. Touching the substitute table moves `recorderSha256`, so all 641 existing goldens are re-recorded. Recorded from a detached worktree at the pinned baseline with this branch's recorder laid over it: every pre-existing golden is header-only, verified by resolving both sides through the value pool — 641 header-only, 0 body, 0 deleted, one distinct `recorderSha256`, `baseline` and `lockfileSha256` across all of them. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the linear workspace picker's model fixture `mobile/tsconfig.json` covers the recorder, and the fixture's setters were written with the argument the product happens to pass rather than the `SetStateAction` the model declares. Typing them moves `adapterSha256` on the two goldens recorded through this module, so they are re-recorded here rather than in the refactor commit, which must move none. Re-recorded at the pinned baseline: `linear-select-workspace` and its reply matrix, header-only, bodies unchanged. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): send six screen-mounted call sites through typed RpcOperations Nine references off the raw request port, across six files. Every one is proven against the goldens recorded in the previous commit from the pinned baseline's product code: this commit moves no file under mobile/rpc-foundation/goldens. Reused rather than redefined: - `worktree.ps` in the history panel sends through `worktreeCatalogRead`. Same question, same acceptance — a refused list leaves the screen on what it holds. - `repo.list` in the tasks screen-root hook sends through `newTabRepoListRead`. Its policy raises the host's message and its reader takes `repos` off the payload while preserving the property-read exception a null result used to throw at the cast, which is what this call site did by hand. Its name still says new-tab; a third consumer does not make renaming it this bucket's business. Four operations are new, each because no existing reader on the method takes this consumer's input: - `files.read-directory-or-skip` and `files.legacy-explorer-list-or-skip` for the explorer. Both skip, because neither refusal is the operation's to decide: the readDir refusal code selects the legacy fallback and the list refusal supplies the message. The existing `files.list-or-skip` reads the `files` member alone, and the explorer also needs `truncated` for the "Showing first 5000" note. - `accounts.home-snapshot-or-skip` for the Home card, decoded by `decodeAccountsSnapshot` at the call site as before. - `notifications.test-push-or-skip` for the settings probe, whose `forbidden` and `method_not_found` refusals mean "try the next desktop". - `linear.select-workspace-or-skip` for the filter sheet. Two behaviours are preserved rather than repaired, both recorded: - The workspace switch never read its reply. `.then(() => loadLinearContext())` runs on a refusal exactly as on a success, so only a transport rejection reaches the error copy. Interpreting the operation here would surface a refused switch for the first time; that is a product change with its own re-record. - `app/terminal-settings.tsx` still reads `ms` off the reply envelope instead of off its result, so the value is always undefined. It did not migrate, and the inventory now carries the defect as its own note. Four mutants are added, one per new family that admits a state-only one: the Home snapshot, the push test result and the tasks repo list each decoded one level above the envelope, and the workspace switch with its context reload dropped. `aiVault.history-screen` gets none and says why in the suite: everything `worktree.ps` publishes also moves the `scopePaths` the next scripted completion asserts, so a mutant aborts the sequence instead of diverging from it. Its evidence is the reply matrix at that request. The tasks source-parity ratchet moves with the family it guards: hook, statement, declaration, render and style counts are unchanged, and the semantic source is a pure deletion of four lines — two `rpc:` call signatures and the two method literals they carried. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): matrix the six new screen families' replies One golden per scripted reply, eleven partitions each, recorded at the pinned baseline alongside the pilots. Seven sites: `accounts.list`, `notifications.testPush`, `repo.list`, `linear.selectWorkspace`, and all three of the history screen's — `worktree.ps` and the two `status.get` reads its scan chains off the worktree list. The history matrix is also that family's defect evidence in place of a mutant: every partition at `worktree.ps` changes the `scopePaths` the downstream `aiVault.listSessions` carries, and the sender args are recorded with it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): correct three operation and mutant comments Comment-only, no product behaviour and no golden movement. - `worktreeCatalogRead` says two readers; there are three. Names the third (the agent-history panel's `scopePaths` seed) and drops the stale count from the module header, which described call sites rather than the two operations. - `newTabRepoListRead`'s census counted the two operations over `repo.list`, not its own two callers, and claimed both read a workspace's connection id. The tasks route keeps the whole list for its repo pickers. The split from `nativeChatRepoListRead` stays where it belongs: acceptance. - The `aiVault.history-screen` mutant note pointed at the reply matrix as the accepted-vs-refused oracle. Decoding `matrix-aivault.history-screen-worktree.ps-1.json` through the value pool shows `normal`'s projected state is identical to all seven non-crashing partitions (spinner, two labels, zero rows). The real oracles are the next request's `scopePaths` (`["/repo/feature"]` vs `[]`) and the crash channel the three `inner-*` partitions land in. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): give the second files.list reader its real reason Comment-only, no product behaviour and no golden movement. `legacyFileListRead` claimed "the member reader rejects this consumer's input". Nothing rejects: `rpcUncheckedMemberReader` returns the member, and reusing it here would simply drop `truncated`. The reason the explorer declares its own operation is the other direction. Widening `files.list-or-skip` to a payload reader would split the `workspace-files` variant it shares with `nativeChatFileSearchRead` over `files.searchPaths`, whose only caller feeds both through one `extractPaths` in `use-mobile-native-chat-file-search.ts`, so the member read would move into that hook rather than disappear. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * style(mobile): indent the six scenario entries spliced during the merge The conflict on `pilot-scenarios.json` was resolved by id rather than by hunk, splicing this branch's six entries into main's text at the array's close. The splice started at the entry's `{` instead of at its line, so those six lines lost their indentation. oxfmt's only change is those six lines; the parsed document is identical, and the recording suite still matches all 667 goldens, so no scenario digest depends on the raw text. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the merged goldens once at the pin One record for the whole merged tree, at the unchanged baselinee7206f62a8, through a detached worktree reset to that pin with this branch's rpc-recording tree, scenarios and recorder script overlaid. Product source in that worktree was proven identical to the baseline before the run, so the recordings describe the pre-refactor product. 13 goldens move, all of them the ones #20915 added. They arrived carrying the recorder digest from before this branch edited `screen-native-substitutes.ts`, and `recorderSha256` is the only key that moves on any of them; every recording body is identical after decoding through the value pool. The other 654 were re-recorded byte-for-byte and are not in this commit. All 667 goldens now carry one `recorderSha256`, one `baseline` and one `lockfileSha256`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the real gates on two screen holdouts Comment-only, no product behaviour and no golden movement. The accounts route said "the screen now mounts". It does not, at this commit: it reads `expo-router.useFocusEffect` and `react-native.ScrollView`, neither is a substituted member, and the trap refuses before any effect runs. The note now names that as the first gate and the `accounts.subscribe` effect as the second, and says why the two members are not added here. The host-screen overlay note blamed a "reanimated timing driver" for deciding when the drawer's children exist. Nothing gates them: `resolveBottomDrawerMounted` returns `visible || mounted`, `BottomDrawer` renders `MountedBottomDrawer` on that, and that component renders its children unconditionally inside its `Modal`. The blocker is the module's own imports of reanimated and gesture-handler, neither substituted. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): drop the tasks route adapter's unreachable reload action No scenario names `reload-repos`, and no schedule driver can generate it: the drivers emit only disconnect, cutover, reset, unmount, blur and remount. Every other action on this adapter is reached by a scenario. Deleting the branch leaves the remount and unmount branches, which are driven. Re-recorded once at the pine7206f62a8with the product source in that worktree proven identical to the baseline first. Two goldens move, both in the `tasks.route-repo-list` family, with `adapterSha256` the only moved key and both recording bodies identical after decoding through the value pool. The other 665 re-recorded byte-for-byte. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb