mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* fix(mobile): wall hosts that cannot serve the page instead of opening native screens * refactor(mobile): drop the native-route state and the shell fallback prop; unknown routes say unavailable * test(mobile): read the nullable page route refusal in its policy test * refactor(mobile): every build is the OTA shell; drop the native/OTA build switch The owner retired the native-screen fallback with no emergency native build, so EXPO_PUBLIC_MOBILE_SHELL, mobileShellBuildKind, shellSwitchDecision, the Metro cache-key suffix and both release workflows' shell input go. Every host route mounts the shell; a route naming no screen the shell could open gets the existing refusal screen instead of a native screen. Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): delete native screens the OTA shell no longer reaches With every build the OTA shell, these native siblings run nowhere: the native-executed closure (index.js plus every non-web route) does not reach them, and the page resolves their .web siblings. Each .web sibling takes the plain name, its web-overrides entry goes, and the native-only rich Markdown editor and Mermaid WebView generators and their outputs go with them. Kept: native siblings the node test suite still renders shared screens through (terminal WebView, browser frame path, clipboard, dictation, media picker, live input, snapshot budget, input styles). Claude-Session: https://claude.ai/code/session_6cea92f6 * test(mobile): drop the phone-preview comparison the deleted native sibling held Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): drop the dead web route, flag-era comments and the one-value shell kind The host route files no longer switch, so their comments say what they do now: a route naming no screen shows the unavailable screen. app/h/[hostId]/web.tsx duplicated index.tsx and nothing linked to it, so it and its stack entry go (the bundle sweep is re-measured: 77 scripts at 15 routes). Diagnostics print the bridge protocol on its own instead of a shell kind that has one value. Claude-Session: https://claude.ai/code/session_6cea92f6 * refactor(mobile): one host for the rich editor, plain Text, and drop two unused native deps MobileSelectableText was react-native's Text, so callers use Text. The rich Markdown editor's host-facing seams (messages, URL prompt, keyboard inset) are required now that the page is its only host; the WebView defaults go. Comments that described deleted native components are rewritten, and expo-image-manipulator and react-native-uitextview leave package.json and the lockfile. Claude-Session: https://claude.ai/code/session_6cea92f6 * feat(mobile): draw the host Accounts screen from the desktop's page Accounts was the last host screen the shell still drew natively, so its changes needed a phone build. It is now a page route like Tasks: the screen moves to src/accounts/MobileAccountsScreen.tsx, accounts.web.tsx renders it on the page, and accounts.tsx mounts the shell for it. What React Native Web needed: Alert.alert is a no-op there, so the reset confirmation, its outcome and a failed account switch are ConfirmModal drawers (ConfirmModal can now drop Cancel for a notice); Back goes through the route handoff, which pops the shell's stack; and the reset idempotency key is built from getRandomBytes, since randomUUID needs a secure context the iOS custom scheme is not. edit.tsx edits the phone's own pairing and never renders on the page, so the route manifest leaves it out and the page's host stack does not declare it. The script sweep is re-measured for the 14-route tree. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(accounts): answer a same-offer Codex reset retry with its recorded outcome; mobile mirrors the desktop The desktop keeps no client journal for a reset credit: the host's ledger owns the attempt. A remote client that lost a reply and retried the same offer under a new key was refused with "already attempted" (or "unknown outcome" while the provider call was unresolved), which is why the phone carried its own durable journal to replay the old key. The coordinator now answers a request for an offer an earlier key already claimed as that attempt: the settled outcome, the running attempt, or a resume of the pending one under its original key. A different offer for the account is still refused while an outcome is unknown, so nothing can spend twice. The reply shape is the one a same-key replay always had, so installed clients with their journal read it unchanged. With that, the Accounts page sends a fresh key per attempt like the desktop and keeps nothing on the phone: the AsyncStorage journal, its client singleflight and the retry-record warnings are deleted, and the journal-resume recording goes with them. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(accounts): resume an account's unresolved Codex reset when the retry names a newer offer Review follow-ups: - Host: after an unknown provider outcome the usage refresh moves the offer, so a retry names a newer one. The coordinator now answers it as the account's unresolved attempt (resumed under its own key and scope), as the desktop's consumeCurrent does, instead of refusing forever. One ledger lookup finds own key, offer claimant, then unresolved attempt. - Mobile: a reply is matched on target, account and account revision, not the offer, so the resumed attempt's scope is accepted. - The key is minted inside requestCodexResetCredit from randomV4Uuid, the session helper moved to src/platform/random-v4-uuid.ts. - RefreshControl goes; the header refresh stays. ConfirmModal's onConfirm is optional for an OK-only notice. - Stale journal comments and the mobile docs line are corrected. - The edit.tsx page exclusion is dropped from this PR; the script sweep is re-measured for 15 routes. - Root oxlint: escape the apostrophe; zod v4 strictObject. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): secure UUID fallback from expo-crypto; pin reset retry routing - randomV4Uuid never falls back to Math.random: without crypto.randomUUID it builds the v4 UUID from ExpoCrypto.getRandomBytes, as the old reset key did. Tests get Node's secure bytes for expo-crypto from the setup file, since the native module has no Node build. - Host tests: a new offer after a settled attempt spends again rather than replaying, and a retry naming a newer offer joins a resumed attempt in flight (one provider call, original key). Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * chore: restore main's bytes for files the merge's pre-commit format touched The merge commit's lint-staged run reformatted 27 files this PR does not change. Committed without the hook so they match origin/main exactly. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): stand in for expo-crypto with a Node alias, not a setup-file mock Anti-slop forbids module mocking outside test files. The vitest config now aliases expo-crypto to a faithful Node implementation of the names shared code reaches; a test file's own mock still wins. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): match the bridged-parity comments to the measured 795-golden tally Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
import { fileURLToPath } from 'node:url'
|
|
import { defineConfig } from 'vitest/config'
|
|
|
|
const vitestOxcConfig = { tsconfig: false } as never
|
|
|
|
export default defineConfig({
|
|
root: import.meta.dirname,
|
|
// Why: the app tsconfig intentionally excludes tests; Vite 8's OXC transform
|
|
// otherwise fails before Vitest can run the test modules.
|
|
oxc: vitestOxcConfig,
|
|
resolve: {
|
|
// A faithful Node stand-in for the native module, rather than a mock in every test that reaches it.
|
|
alias: {
|
|
'expo-crypto': fileURLToPath(
|
|
new URL('./src/test-support/expo-crypto-node.ts', import.meta.url)
|
|
)
|
|
}
|
|
},
|
|
test: {
|
|
pool: 'threads',
|
|
environment: 'node',
|
|
setupFiles: ['./vitest.setup.ts'],
|
|
onConsoleLog: (log) => !log.includes('react-test-renderer is deprecated'),
|
|
// .tsx too: component tests exist (react-test-renderer + mocked react-native) and were
|
|
// silently never collected, so render-level regressions shipped untested.
|
|
// scripts/ too: the CI scripts under it (rpc:diff, the tests-typecheck ratchet) need a runnable
|
|
// test home, and a test vitest never collects is not a gate.
|
|
include: ['src/**/*.test.ts', 'src/**/*.test.tsx', 'scripts/**/*.test.ts']
|
|
}
|
|
})
|