Skip to content

Mobile app + vendored protocol (core-loop UI, EAS, runbooks)#2

Merged
Quantumlyy merged 19 commits into
mainfrom
pr/mobile-app
May 17, 2026
Merged

Mobile app + vendored protocol (core-loop UI, EAS, runbooks)#2
Quantumlyy merged 19 commits into
mainfrom
pr/mobile-app

Conversation

@Quantumlyy
Copy link
Copy Markdown
Member

Scope

Expo SDK 55 RN app (the mobile deliverable) + the vendored protocol it depends on. Full signed commit history of the app+protocol work; gateway Terraform is a separate PR.

Contents

  • packages/protocol/ vendored from background-agents @ a7b968f (pure Hermes-safe subset) + scripts/vendor-protocol.sh + PIN
  • apps/mobile/: core-loop screens on a typed SessionGateway mock seam (TanStack Query + ported pure stream transforms); native UIKit headers; expo-sqlite/kv-store connection profiles; mock auth gate
  • EAS: TestFlight-ready (eas.json, bundle id, runtimeVersion appVersion), EAS Update channels, EAS Insights
  • Docs: core-loop design spec + dev/EAS runbook; repo-wide secret .gitignore hardening; pnpm packageManager pin

Verification

pnpm tsc --noEmit clean; expo export (iOS Metro/Hermes) bundles; every commit GPG-signed; no secrets committed.

Notes

Data layer is the mock gateway until ColeMurray/background-agents@a7b968f is deployed; the real HTTP/WS impl drops into the same seam with no screen changes.

Quantumlyy added 19 commits May 17, 2026 07:00
Approach A mock seam; parallel UI track on build/mobile-ui; backend (M0, PIN url) deferred until ColeMurray/background-agents@a7b968f is deployed.
…safe subset)

6-file type/model closure (types, integrations, triggers-{types,conditions}, models, git) + reproducible scripts/vendor-protocol.sh. PIN url=pending-deploy. Closure typechecks strict; no zod/node/cloudflare/crypto in closure.
…nav shell

Approach-A seam: SessionGateway + MockSessionGateway + TanStack Query hooks; pure stream transforms (collapseTokenEvents/foldEvent) ported @ a7b968f. @expo/ui wrapper with Expo-Go-safe RN primitives. Expo Router shell + runnable placeholder screens. tsc clean; Metro/Hermes iOS bundle succeeds (punycode shim for markdown-it). Removed Expo template starter components.
…in, settings

Five slices built in parallel against the frozen ui/data seam (exports intact, in-dir only, no new deps). Integrated: pnpm exec tsc --noEmit clean; Metro/Hermes iOS bundle succeeds. Profiles use an in-memory store seam (AsyncStorage not installed; reversible).
…auth gate + sheets

1) profile-store backed by expo-sqlite/kv-store (sync hydrate+persist, same API). 2) expo-updates + runtimeVersion=fingerprint in app.json + eas.json channels (dev/preview/production). 3) mock AuthProvider + Stack.Protected gate + formSheet detents for new/sign-in; sign-in flips auth. 4) docs/runbooks/dev-and-eas.md. tsc clean; Metro iOS bundle OK.
… separate PR)

Removes terraform/ + gateway TF spec and reverts apps/gateway/package.json to main; the rest is the build/mobile-ui app+protocol work with its full signed history.
Copilot AI review requested due to automatic review settings May 17, 2026 12:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Bootstraps an Expo SDK 55 React Native mobile app along with a vendored, Hermes-safe subset of the Open-Inspect protocol that it depends on. The mobile app implements the core-loop screens against a typed SessionGateway mock seam (TanStack Query + pure stream transforms) so the real HTTP/WS implementation can drop in later without screen changes, and ships EAS/TestFlight config and runbooks.

Changes:

  • Vendor packages/protocol from background-agents@a7b968f via a reproducible scripts/vendor-protocol.sh (with PIN provenance and flattened/rewritten imports).
  • Add apps/mobile core-loop UI: list/detail/connect screens, mock SessionGateway + event emitter + fixtures, TanStack Query data layer, stream transforms (live fold + replay coalesce), expo-sqlite/kv-store, mock auth gate, native UIKit headers.
  • EAS + ops: eas.json, bundle id / runtimeVersion: appVersion, expo-updates/expo-insights, dev/EAS runbook, core-loop design spec, repo-wide secret .gitignore hardening, pnpm packageManager pin.

Reviewed changes

Copilot reviewed 65 out of 67 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/vendor-protocol.sh Reproducible vendor script: clones upstream, copies fixed file set with provenance header, rewrites cross-dir imports, writes PIN.
packages/protocol/src/*.ts, PIN, package.json Vendored Hermes-safe protocol subset (types, integrations, triggers, models, git) + barrel + pinning metadata.
apps/mobile/src/features/sessions/stream/transforms.ts foldEvent (live) and collapseTokenEvents (replay) — live path does not progressively expose tokens (see comment).
apps/mobile/src/features/sessions/{list,detail,connect}/* Core-loop screens consuming the gateway via TanStack Query and useSessionStream.
apps/mobile/src/data/{gateway.ts,queries.ts,mock/*} SessionGateway interface, query hooks, mock gateway + scenario fixtures + event emitter.
apps/mobile/src/features/auth/*, native UIKit header components Mock auth gate and iOS-flavored chrome.
apps/mobile/{app.json,eas.json,package.json,babel.config.js,...} Expo/EAS config: bundle id, runtimeVersion: appVersion, channels, expo-updates/expo-insights, expo-sqlite.
pnpm-lock.yaml, root package.json New deps (expo-updates, expo-insights, expo-sqlite, punycode, @constructor/protocol workspace link) and pnpm pin.
docs/superpowers/specs/2026-05-17-mobile-ui-core-loop-design.md, docs/runbooks/dev-and-eas.md Core-loop design spec (source of truth for live/replay semantics) and dev/EAS runbook.
.gitignore Repo-wide hardening of secret/env patterns.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +82
export function foldEvent(prev: SandboxEvent[], event: SandboxEvent, pending: PendingRef): SandboxEvent[] {
if (event.type === 'token' && event.content && event.messageId) {
pending.current = {
content: event.content,
messageId: event.messageId,
sandboxId: event.sandboxId,
timestamp: event.timestamp,
};
return prev;
}
if (event.type === 'execution_complete') {
const out = [...prev];
if (pending.current) {
const p = pending.current;
pending.current = null;
out.push({
type: 'token',
content: p.content,
messageId: p.messageId,
sandboxId: p.sandboxId,
timestamp: p.timestamp,
});
}
out.push(event);
return out;
}
return [...prev, event];
}
Comment on lines +49 to +59
subscribe(id: string, on: StreamListeners): StreamHandle {
const state = mockSessionState(id);
const snapshot: SubscribeSnapshot = {
state,
artifacts: [],
replay: { events: [], hasMore: false, cursor: null },
};
const script = state.status === 'failed' ? scenarioError() : scenarioHappy();
const cancel = startScriptedStream(snapshot, script, on);
return { unsubscribe: cancel };
}
Comment on lines +69 to +102
export function scenarioHappy(): SandboxEvent[] {
const t = () => Date.now() / 1000;
const mid = 'm_1';
const partials = [
'Looking at the settings screen…',
'Looking at the settings screen… adding a `Toggle`',
'Looking at the settings screen… adding a `Toggle` bound to the theme store.',
'Looking at the settings screen… adding a `Toggle` bound to the theme store.\n\n```tsx\n<Toggle value={dark} onValueChange={setDark} />\n```\n\nDone.',
];
const evts: SandboxEvent[] = [
{ type: 'user_message', content: 'Add a dark-mode toggle to the settings screen', messageId: mid, timestamp: t() },
{ type: 'step_start', messageId: mid, sandboxId: SBX, timestamp: t() },
{ type: 'tool_call', tool: 'read_file', args: { path: 'src/app/settings.tsx' }, callId: 'c1', messageId: mid, sandboxId: SBX, timestamp: t() },
{ type: 'tool_result', callId: 'c1', result: 'export default function Settings() { … }', messageId: mid, sandboxId: SBX, timestamp: t() },
];
for (const p of partials) {
evts.push({ type: 'token', content: p, messageId: mid, sandboxId: SBX, timestamp: t() });
}
evts.push({ type: 'step_finish', cost: 0.0123, tokens: 1840, messageId: mid, sandboxId: SBX, timestamp: t() });
evts.push({ type: 'execution_complete', messageId: mid, success: true, sandboxId: SBX, timestamp: t() });
return evts;
}

/** Scenario B — error path. */
export function scenarioError(): SandboxEvent[] {
const t = () => Date.now() / 1000;
const mid = 'm_err';
return [
{ type: 'user_message', content: 'Refactor the auth module', messageId: mid, timestamp: t() },
{ type: 'step_start', messageId: mid, sandboxId: SBX, timestamp: t() },
{ type: 'tool_call', tool: 'run_tests', args: {}, callId: 'c9', messageId: mid, sandboxId: SBX, timestamp: t() },
{ type: 'error', error: 'Test suite failed: 3 failing in auth.test.ts', messageId: mid, sandboxId: SBX, timestamp: t() },
{ type: 'execution_complete', messageId: mid, success: false, error: 'aborted', sandboxId: SBX, timestamp: t() },
];
@Quantumlyy Quantumlyy merged commit f6b93d2 into main May 17, 2026
1 check passed
@Quantumlyy Quantumlyy deleted the pr/mobile-app branch May 17, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants