Skip to content

feat(onboarding): shared engine for iOS + Android, TUI as render-only wrapper (PR 1)#2495

Open
WcaleNieWolny wants to merge 90 commits into
mainfrom
wolny/mcp-tui-integration
Open

feat(onboarding): shared engine for iOS + Android, TUI as render-only wrapper (PR 1)#2495
WcaleNieWolny wants to merge 90 commits into
mainfrom
wolny/mcp-tui-integration

Conversation

@WcaleNieWolny

Copy link
Copy Markdown
Contributor

What

The Builder onboarding mega-PR: both platforms' onboarding now runs on ONE shared headless engine, with the ink TUI as a render-only wrapper over the entire flow — including the post-save tail (CI secrets, env export, workflow file, build request). The MCP onboarding integration stays build-time gated OFF (__CAPGO_MCP_ONBOARDING__ = false; PR #2492 — stacked on this branch — completes and flips it).

Architecture

  • Shared engine: flow/contract.ts (PlatformFlow/StepView) + ios/flow.ts + android/flow.ts + the platform-neutral tail/flow.ts. Pure views/reducers/effects; zero ink imports.
  • TUI = thin wrapper: both wizards route every choice/input/effect through the engine; rendering stays ink. Ephemeral payloads (iOS import cert/profile/p12, CI secret entries) ride driver-held carried state and are never persisted (no secrets in progress.json).
  • Total resume: getIosResumeStep/android twin route gates (credentials-exist), the .p8 chain, verify-app, the import fork, and the marker-guarded tail.

Deliberate behavior changes (3 — everything else is parity)

  1. Apple Distribution certs (8363b26): create modern DISTRIBUTION-type certs instead of deprecated IOS_DISTRIBUTION; the cert-limit revoke picker scopes to the pool that's actually full (the old mixed list defaulted the cursor onto the user's production cert).
  2. Import save loop fix (442786e): saving-credentials builds the credential shape FIRST and self-heals only when the build fails — the persisted-resume-first order looped the iOS import fork forever.
  3. Resume data-safety gate: resuming with already-saved iOS credentials now gates on backup-or-exit instead of silently overwriting (main's behavior).

An adversarial audit verified these are the ONLY behavior changes: running the pre-change private TUI suite (which characterized main) against this branch's dist goes red on exactly those — nothing else. Test diff vs main is 100% additive (23 new files, +10.6k/-0).

Tested

  • 97-segment capgo chain green (incl. main's new suites + 23 of ours: engine units, render snapshots, frame-fit, routing parity).
  • Private TUI E2E suite: 79/79 journeys green against this branch's dist (drift guard 0 missing both platforms).
  • Release bundle leak-guard: MCP onboarding tools stripped (gate off on this branch); main's test:helper-dce preserved through the merge.

Merge notes

…ship test hooks from the bundle + CI leak guard

Proven on the real CLI build: a dev-gated branch importing a src/__dev__/ module
is fully DCE'd from dist/index.js (marker absent). Apple-API spoofing for AI tests
(PR 2) will live in src/__dev__/ behind this gate, physically absent from the NPM build.
…ndroidOnboardingProgress

Brings the platform-agnostic onboarding engine (mcp/*, flow.ts) + its additive shared
deps (keystore/oauth-google/oauth-scopes/output-record/schemas) onto main. Unifies the
data model: main's step set (incl. the 4 TUI-only ai-analysis-* + resume-prompt) plus
the 3 MCP markers (activePlatform, keystorePasswordGenerated, keystorePasswordManual);
flow.ts KIND_TABLE covers the TUI-only steps. main's android/ui/app.tsx kept verbatim
(TUI render-only rewrite is a later plan). Behavior is moved/renamed only — NOT assumed
correct; the android audit is Plan 2.

Verified: typecheck 0 errors; engine 87 unit + e2e hermetic gate PASS; main's onboarding
tests still pass. Covers plan Tasks 2-3 (one green commit; they don't split cleanly).
…r-cancel gate (credentials-exist + backing-up) to match main — closes a data-safety gap
… engine (persistAndStep derives next via getAndroidResumeStep; behavior-preserving + dev mismatch guard) + sequencing smoke test
…rough the shared runAndroidEffect (behavior-preserving; logs kept)
…source-of-truth (remove redundant state mirrors; conservative)
saving-credentials now stashes the CI-secret entries (createCiSecretEntries)
in transient before routing to ask-build, mirroring the Ink TUI's
doSaveCredentials. Adds the additive post-save tail transient fields to
AndroidStepCtx and the rebuildTailCredentials/tailCiSecretEntries helpers the
later tail steps reuse to re-derive the same entries from progress.
detecting-ci-secrets discovers CI destinations and persists the single chosen
target (GitHub → ask-github-actions-setup, GitLab → ask-ci-secrets, many →
target-select). checking-ci-secrets resolves the GitHub repo label + existing
keys and gates on confirm-secrets-push / confirm-ci-secret-overwrite.
uploading-ci-secrets pushes the entries and branches with-workflow →
pick-package-manager vs build-complete. Routing mirrors the Ink TUI tail.
…ep(s)

exporting-env writes the 0o600 .env (defaultExportPath fallback) and routes an
existing file to confirm-env-export-overwrite (persisting the resolved path);
overwrite-and-export-env re-exports with overwrite=true. writing-workflow-file
generates + writes .github/workflows/capgo-build.yml (overwrite) using the
recorded build-script choice + secret keys. All finish at build-complete,
mirroring the Ink TUI tail.
requesting-build fires capgo build request through the pre-bound
requestBuildInternal dep (driver owns apikey/logger/silent). A successful build
with pending CI entries routes to detecting-ci-secrets to offer the secret
push; with no entries it finishes at build-complete. A failed build with a
captured log surfaces the AI job id and routes to the TUI-only
ai-analysis-prompt; otherwise build-complete. Mirrors the Ink TUI tail.
… transient (parity with TUI; no rebuild, no secrets on disk)
…ient/deps surface + additive persisted fields
…ning/mobileprovision types (not placeholders)
…reserve appIdConfirmed/pendingAppIdNext; keep keyId cleared)
…ough the engine

Route the iOS IMPORT credential EFFECT steps (import-scanning,
import-validating-all-certs, import-checking-apple-cert,
import-provide-profile-path, import-create-profile-only,
import-compiling-helper, import-exporting) through the shared engine's
runIosEffect via a new engine-driven import effect driver, mirroring the
create-new effect driver. The driver wires the REAL macos-signing /
apple-api / mobileprovision-parser helpers (token-adapted via
getFreshToken; classify pre-binds the single team-wide cert fetch +
SHA-1 index), threads the ephemeral selections from iosCarriedRef, mirrors
importMatches/importProfiles/identityAvailability/profilePrefetch/
noMatchReason/certData/profileData/importedP12Password back into React
state, applies redirectIfMismatch after import-scanning, and advances.

The import CHOICE handlers (pickers, recovery hub, portal-explanation,
export-warning) stay bespoke for now but their React-state writes bridge
into the carried ref. Removes the 7 bespoke import effect bodies.

Exports parseMobileprovisionBufferDetailed so the engine can parse the
.mobileprovision bytes its readFile dep already loaded.
…e through the engine

Convert the import-pick-identity / import-pick-profile choice handlers to
pure engine resolvers: stash the resolved identity/profile into
iosCarriedRef (+ keep the React mirror), run runIosEffect(step) for the
next step. import-pick-identity's three-way routing (usable on-disk →
pick-profile; no on-disk + ASC key → checking-apple-cert; otherwise →
no-match-recovery with noMatchReason='no-profile-on-disk') and
import-pick-profile's three validations (bundle/distribution/cert-trust →
import-export-warning or error) now live in the engine; the handlers mirror
the transient noMatchReason and surface resolver 'error' via handleError.

import-distribution-mode persistence moves to applyIosInput (setupMethod +
importDistribution, or clear on __cancel__), keeping the bespoke
getImportEntryStep / import-pick-identity routing (the routing test's
DIVERGE class). __cancel__ on the identity picker also persists the
switch-to-create-new before re-driving, per the resolver contract.
…import verifying-key through the engine

Convert the remaining import CHOICE handlers to engine resolvers:
- import-no-match-recovery (5-way): stash recoveryAction + sticky
  noMatchReason into iosCarriedRef, run runIosEffect; engine routes
  browser/provide-profile-path/back/create(+/- ASC key). The no-ASC-key
  create branch persists pendingRecoveryAction via the engine; the React
  mirror stays in sync. Resets profilePickerOpened before the file picker.
- import-portal-explanation: stash portalAction; engine routes use-create/
  use-file/open-anyway (deps.openExternal + breadcrumb)/back.
- import-export-warning: stash exportWarningAction; engine routes go
  (isHelperCached ? exporting : compiling-helper)/back; 'exit' keeps the
  bespoke exitOnboarding (the routing test's DIVERGE).

import-mode verifying-key now routes through the SAME engine effect as
create-new (verify + apiKeyVerified persist preserving setupMethod/
importDistribution), then OVERRIDES the engine's create-new 'next':
pendingRecoveryAction resume → import-create-profile-only; plain import
app_store → redirectIfMismatch(matches>0 ? import-validating-all-certs :
import-pick-identity). Removes the bespoke verify body + the now-unused
ApiKeyData import.
Brings in 95 commits incl. #2397 (iOS remote App Store Connect app
verification). verify-app replaces confirm-app-id; the feature is ported
into the engine-driven TUI via the established verifying-key next-override
pattern (engine port of verify-app itself follows). Our resume/routing
tests updated to main's new verify-app invariant.
Port the PR #2397 verify-app step (remote App Store Connect verification)
from the TUI driver into the iOS engine (ios/flow.ts):

- runIosEffect('verify-app'): initial fetch (parallel listApps+listBundleIds,
  FRESH detectBundleIds re-detect, classifyAppVerification) with the four
  exits — exact-match (persists iosBundleIdOverride + iosBundleIdContextAppId
  via the returned progress, advances to carried.pendingVerifyNext ??
  creating-certificate), fetch-failed / no-release-config pass-throughs, and
  the parked picker/gate states riding transient (verifyApps / verifyPath /
  verifyResult / verifyAttempt / …).
- carried-driven gate RESOLVER (the cert-limit/duplicate pattern):
  pick / create-new / autofix (writeReleaseBundleId) / continue (fresh
  re-detect + evaluateGate escalation) / recheck (Path B re-poll +
  ask-before-reopen) / open-reopen (ensureBundleId + openExternal) / back /
  cancel (error exit sink).
- verifying-key next: create-new → verify-app (was creating-certificate);
  import app_store → verify-app + transient.pendingVerifyNext (the matches>0
  import continuation); import ad_hoc → the continuation directly;
  pendingRecoveryAction unchanged. pendingVerifyNext is EPHEMERAL
  (deps.carried) — never persisted, matching getResumeStep's fresh-mount
  fallback to creating-certificate.
- New optional deps: listApps / listBundleIds / detectBundleIds /
  writeReleaseBundleId (the engine stays IO-free; pure helpers imported).
- iosViewForStep('verify-app'): auto before classification; choice (picker /
  Path A / Path B / ask-reopen) once parked. applyIosInput: no-op (ephemeral).

Tests: new test/test-ios-verify-app.mjs (33 cases: exits, gate paths incl.
autofix + re-poll + cancel, pendingVerifyNext threading, view + reducer +
ephemeral contracts), wired into the package.json chain;
test-ios-create-new / test-ios-e2e / test-ios-import-export updated to the
new verifying-key → verify-app routing (e2e create-new now traverses
verify-app between verifying-key and creating-certificate).
…op driver overrides)

Delete the two driver-side verify-app overrides now that the engine
expresses the PR #2397 detour itself:

- the import-mode verifying-key bespoke body (the setPendingVerifyNext +
  redirectIfMismatch('verify-app') / importTarget override) — verifying-key
  now runs through the engine driver on BOTH paths, with the import
  continuation arriving via transient.pendingVerifyNext → iosCarriedRef and
  the .p8 bytes falling back to the p8ContentRef mirror in carried;
- the create-new driver's advance override (setPendingVerifyNext + forced
  'verify-app') — the driver now uses the engine's next verbatim, wrapping
  the verifying-key advance in redirectIfMismatch (the SYNC FS bundle-id
  adopt) except on the pendingRecoveryAction resume (whose React mirror is
  cleared here, as before).

verify-app itself joins IOS_ENGINE_CREATE_EFFECT_STEPS: the driver wires the
real listApps / listBundleIds / detectIosBundleIds / writeReleaseBundleId /
ensureBundleId / open deps, shows the loader during the initial fetch,
mirrors the engine's verify* transient into the React render state, mirrors
the persisted iosBundleIdOverride (setIosBundleId + setAppIdConfirmed), and
fires the Shown / Result / Passed telemetry from the returned classification.

The PARKED gate's render (loader / debug-differ warning box / picker /
Path A / Path B / ask-reopen screens) is unchanged; its Select onChange
handlers now record the pick into iosCarriedRef.verifyAction and re-drive
the engine resolver via the new runVerifyGateAction (which also fires the
Auto Fixed / Create App Opened / Gate Blocked / Passed events and clears the
consumed action). Cancel stays a bespoke driver exit (telemetry + log +
exitOnboarding). pendingVerifyNext React state, persistVerifyOverride,
verifyFetchStartedRef and the bespoke verify-app effect are deleted; the
restart reset now clears the carried verify-gate threading instead.
…y when the build fails

The self-heal guard consulted the persisted-progress resume resolver BEFORE
attempting to build the platform credentials. The iOS import payload (cert,
profile, team id, p12 password) is ephemeral by design — it rides the driver's
carried state and is never written to progress.json — so the persisted resume
always pointed back to import-scanning at save time. The guard therefore
diverted on every save, looping the import fork forever (export from keychain
-> 'required input missing' -> re-import).

Now the credential build runs first: if it succeeds we save regardless of what
the persisted resolver says; only when it throws do we consult the resolver to
route the user back to the genuinely missing step (and rethrow when the
resolver insists we are already at saving-credentials).

Tests: the two self-heal cases now inject genuinely-throwing builders, and two
new regressions pin the live loop (shared engine + iOS tail handoff with the
real persisted import shape + carried payload).
…e limit revoke picker to that pool

createCertificate now POSTs certificateType DISTRIBUTION (the cross-platform
'Apple Distribution' type Xcode 11+ uses) instead of the deprecated
IOS_DISTRIBUTION, whose separate per-team pool tends to be full of legacy
certs — hitting Apple's limit even though the modern pool has room.

When Apple still rejects on the limit, the revoke picker now lists ONLY the
DISTRIBUTION pool (CertificateLimitError payload + the driver's
listCertificates fallback): revoking a cert from another pool would not free
a slot, and the old mixed-type list put the default cursor on the user's
real Apple Distribution keychain cert — one Enter away from invalidating
their production p12 for nothing.

listDistributionCerts keeps querying BOTH types by default (the import flow
must match local Keychain identities against the full ledger); a new
optional types filter narrows it for the limit-recovery callers.

UI copy follows: 'Creating Apple Distribution certificate…' spinner, 'Apple
Distribution certificate limit reached (N existing)' header, engine view
title named after the pool.

New test/test-apple-api-cert-create.mjs (5 cases, global-fetch stub) pins the
POST type, the scoped re-list, the empty-pool rethrow, and the both-types
default; wired into the CI chain.
127 commits in, incl. the builder onboarding TUI preview CI (PR #2483:
.gitmodules + private/cli-mcp-tests submodule @ 630a8d0 + workflow),
simplified contact-support (PR #2406), streaming AI build analysis
(PR #2438), and the self-update prompt.

Conflict resolution (4 files): ours-as-base (engine-driven TUI) + main's
new features ported in —
- package.json: union of both test chains (main's 12 new segments +
  our 23) and script entries.
- oauth-google.ts: kept our startOAuthFlow split; ported main's
  appendInternalLog breadcrumb into the open-browser catch.
- ui/app.tsx + android/ui/app.tsx: discarded resurrected bespoke effect
  bodies (replaced by engine drivers on this branch); ported supaHost
  into the engine-built build request via a requestBuildInternal dep
  wrap; verified the auto-merged support flow, AI streaming and
  internal-log wiring cohere with the engine structure.

Post-merge engine follow-ups: android KIND_TABLE gained the three
support-* steps; main's internal-only appendInternalLog breadcrumbs that
lived inside bespoke effect bodies were re-homed into the engine via a
new optional onInternalLog dep (ios/flow.ts, tail/flow.ts,
android/flow.ts; wired from both wizards; forwarded by both toTailDeps
adapters).

tsc clean; ios-tui-routing 39, ios-tui-render 60, tail-engine-shared 72,
android-tail-engine 49, ios-e2e 12, ios-create-new 36, ios-verify-app 33,
update-prompt 8, support + AI streaming suites, frame-fit 8/8 — all green.
Root bun install --frozen-lockfile verified.
… (caught by the private E2E suite)

1. backing-up: the engine's success log dropped the backup destination path
   — main's bespoke logged '✔ Backup saved · <path>' (app.tsx:1473) so the
   user can find the backup. Restored the path detail.

2. verifying-key: the engine's catch echoed the multi-line verification-
   failure message into the log pane AND returned the error route — the
   error screen renders the same message, so the advice list painted twice
   on one frame. Main routes to handleError only; dropped the onLog echo
   (the onInternalLog breadcrumb stays).

Both pinned by the private e2e-tui goldens (setup-method-select.txt /
verifying-key__invalid.txt): 79/79 journeys green after this.
Suite-side counterpart of this branch: hermetic CAPGO_SKIP_UPDATE_PROMPT
(npm published 8.1.9 and the self-update prompt blocked every journey's
first frame, on main too), the documented resume-overwrite journey flip
(the credentials-exist gate this branch adds), and the pre-authorized
1-line cert-limit-prompt golden re-record ('iOS distribution' → 'Apple
Distribution', user-approved). 79/79 journeys + drift guard green against
this branch's dist.
The merged ErrorStep now delegates its options to buildHelpMenuOptions
(support first, 'Try again', 'Exit') — the old bare 'Retry' label this
assertion pinned no longer exists on either branch. Assert the new menu
shape (support entry + renamed retry) per the TUI-is-main's-reference
rule. 25/25.
Brings in 20 commits from main, most notably PR #2458 (precompiled, signed &
notarized macOS keychain helper packages) and PR #2489 (multi-channel bundle
upload), plus release chores (cli 8.3.0 / app 12.164.0) and frontend/backend
changes that do not touch onboarding.

Conflict resolutions (playbook: engine-driven structure is the base; main's
new features are ported in):

- cli/package.json: UNION. Kept our script entries (android-tail-*, ios-*,
  dev-gate-stripped, platform-flow-contract, tail-engine-shared, ...) plus
  main's `test:helper-dce` entry, and merged the `test` chain as the union of
  both sides' segments — main's `bun run test:helper-dce` is inserted right
  after `bun run build`, matching main's position. The chain otherwise keeps
  every segment from both sides. Version stays at main's 8.3.0.

- cli/build.mjs: UNION of both sides' `define` blocks (CLI + SDK builds):
  kept our `globalThis.__CAPGO_DEV__` / `globalThis.__CAPGO_MCP_ONBOARDING__`
  release gates AND main's `__CAPGO_ALLOW_HELPER_ENV_OVERRIDE__` DCE gate for
  the CAPGO_KEYCHAIN_HELPER_PATH dev override (PR #2458).

- cli/src/build/onboarding/ui/app.tsx: ours is the engine-driven thin wrapper;
  main's bespoke import-effect bodies (deleted on our branch, modified by
  PR #2458 on main) were dropped, and main's actual change — the removal of
  the import-compiling-helper step — was ported into our engine-driven
  structure instead (see below).

- bun.lock: took main's cli version stamp; `bun install` then reconciled it
  to 8.3.0.

- private/cli-mcp-tests (submodule): fast-forwarded to main's pointer
  71db228 ("test(compat): pin cert golden + resume journey to capgo main") —
  our pointer 8a615f2 is its direct ancestor, and 71db228 matches the merged
  code's helper contract. The local frozen suite reconciliation stays with
  the orchestrator.

Port of PR #2458's import-compiling-helper removal (main is the reference for
its feature; our engine consumes main's new precompiled-helper mechanism):

- macos-signing.ts took main's version wholesale (resolveHelperBinary +
  signature-verified precompiled helper; precompileSwiftHelper/isHelperCached
  deleted). types.ts / error-categories.ts / ui/steps/ios-import.tsx merged
  clean to main's contract (step union, STEP_PROGRESS, error category, and
  ImportCompilingHelperStep component all gone).

- ios/flow.ts (ours-only engine file): removed the import-compiling-helper
  effect case, the precompileSwiftHelper/isHelperCached deps, the
  helperCompiled transient + carried idempotency guard, and rerouted the
  import-export-warning resolver's 'go' branch straight to import-exporting.
  Docs updated to record the PR #2458 contract.

- ui/app.tsx: dropped the removed imports, the engine-deps wiring for the
  two deleted callbacks, the step from IOS_ENGINE_IMPORT_EFFECT_STEPS, and
  the isHelperCached arg on the export-warning runIosEffect call.

- Tests updated to main's contract (each pinned the removed step):
  - test-ios-import-export.mjs: collapsed the three 'go' resolver variants
    into one unconditional 'go' -> import-exporting case, deleted the
    import-compiling-helper effect section and the "NOT cached" driver
    journey, dropped the two deps from makeDeps.
  - test-ios-e2e.mjs: journey (b) no longer detours through the compile
    step; dropped the deps mocks, the isHelperCached overrides, and the
    step's error-route case in (e).
  - test-ios-tui-routing.mjs: replaced the cached/NOT-cached parity pair
    with a single unconditional 'go' MATCH case.
  - test-ios-tui-render.mjs: removed the ImportCompilingHelperStep render
    test + its import (component deleted by #2458).
  - test-ios-resume.mjs: dropped the step from EPHEMERAL_PICKER_STEPS.
  - test-ios-tail-handoff.mjs: comment-only update.

Gates: tsc --noEmit clean; bun run build clean; test:helper-dce green
(entry + chain segment + cli/scripts/check-helper-dce.sh all survive);
test-dev-gate-stripped green (MCP onboarding tools still ABSENT — the PR-1
gate stays off); full `bun run test` chain exit 0.

TUI E2E (frozen private suite @ 68a181d vs this dist): 79 journeys — 78 pass,
1 flaky (support flow → view logs → cancel; pass-on-retry). Runtime drift
guard: ios 40/65 covered / 25 excluded / 0 missing; android 42/65 covered /
23 excluded / 0 missing. Known follow-up (private repo, orchestrator-owned):
test:e2e-tui:harness fails at harness-tests/test-drift.mjs hygiene —
"UNCOVERED key 'import-compiling-helper' is not a STEP_PROGRESS step id" —
because the suite's restored exclusion (private PR #4) is orphaned now that
this merge ports the step's removal.
Drift-guard bookkeeping for the main merge: this branch now ports main's
import-compiling-helper step removal (PR #2458), so the private suite's
restored exclusion (#4) became orphaned — 1044e70 re-applies #3 exactly.
Harness self-tests + the 79-journey suite green against this branch's
dist (78 pass + 1 pty flake, pass-on-retry; drift 0 missing both
platforms).
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@WcaleNieWolny, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 58 minutes and 15 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0db03351-07e3-4817-8246-3e39b5326d9e

📥 Commits

Reviewing files that changed from the base of the PR and between 6096607 and 1db3bf6.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (61)
  • cli/build.mjs
  • cli/package.json
  • cli/src/__dev__/README.md
  • cli/src/build/mobileprovision-parser.ts
  • cli/src/build/onboarding/android/flow.ts
  • cli/src/build/onboarding/android/keystore.ts
  • cli/src/build/onboarding/android/oauth-google.ts
  • cli/src/build/onboarding/android/oauth-scopes.ts
  • cli/src/build/onboarding/android/progress.ts
  • cli/src/build/onboarding/android/types.ts
  • cli/src/build/onboarding/android/ui/app.tsx
  • cli/src/build/onboarding/apple-api.ts
  • cli/src/build/onboarding/env-export.ts
  • cli/src/build/onboarding/flow/android-flow.ts
  • cli/src/build/onboarding/flow/contract.ts
  • cli/src/build/onboarding/flow/ios-flow.ts
  • cli/src/build/onboarding/ios/flow.ts
  • cli/src/build/onboarding/ios/progress.ts
  • cli/src/build/onboarding/mcp/app-id-validation.ts
  • cli/src/build/onboarding/mcp/contract.ts
  • cli/src/build/onboarding/mcp/engine.ts
  • cli/src/build/onboarding/mcp/explanations.ts
  • cli/src/build/onboarding/mcp/oauth-session.ts
  • cli/src/build/onboarding/mcp/onboarding-tools.ts
  • cli/src/build/onboarding/mcp/step-input.ts
  • cli/src/build/onboarding/mcp/terminal-launch.ts
  • cli/src/build/onboarding/tail-types.ts
  • cli/src/build/onboarding/tail/flow.ts
  • cli/src/build/onboarding/types.ts
  • cli/src/build/onboarding/ui/app.tsx
  • cli/src/build/onboarding/ui/p8-error.ts
  • cli/src/build/onboarding/ui/steps/ios-credentials.tsx
  • cli/src/build/output-record.ts
  • cli/src/dev-flag.d.ts
  • cli/src/mcp/server.ts
  • cli/src/schemas/onboarding.ts
  • cli/test/dev-preload.mjs
  • cli/test/test-android-tail-engine.mjs
  • cli/test/test-android-tail-render.mjs
  • cli/test/test-android-tail-routing.mjs
  • cli/test/test-android-tui-sequencing.mjs
  • cli/test/test-apple-api-cert-create.mjs
  • cli/test/test-dev-gate-stripped.mjs
  • cli/test/test-ios-confirm-app-id.mjs
  • cli/test/test-ios-create-new.mjs
  • cli/test/test-ios-e2e.mjs
  • cli/test/test-ios-flow-contract.mjs
  • cli/test/test-ios-import-discovery.mjs
  • cli/test/test-ios-import-export.mjs
  • cli/test/test-ios-import-pickers.mjs
  • cli/test/test-ios-import-recovery.mjs
  • cli/test/test-ios-recovery.mjs
  • cli/test/test-ios-resume.mjs
  • cli/test/test-ios-tail-handoff.mjs
  • cli/test/test-ios-tui-render.mjs
  • cli/test/test-ios-tui-routing.mjs
  • cli/test/test-ios-verify-app.mjs
  • cli/test/test-p8-error.mjs
  • cli/test/test-platform-flow-contract.mjs
  • cli/test/test-tail-engine-shared.mjs
  • private/cli-mcp-tests

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

@socket-security

socket-security Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addeddeepsec@​2.0.10811001009680

View full report

@codspeed-hq

codspeed-hq Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing wolny/mcp-tui-integration (1db3bf6) with main (0b9a409)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (dc587c1) during the generation of this report, so 0b9a409 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

&& progress._keystoreBase64
&& deps.writeKeystoreFile
) {
keystoreFileWritten = true
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

🧪 Builder onboarding TUI preview — ✅ passed

▶ Open the interactive HTML report (zoomable journey tree + cast playback)

Commit: 1db3bf6 · Job summary with the result table

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73fe414168

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1384 to +1385
const recordPath = deps.buildRecordPath(buildAppId, buildPlatform)
const command = `npx @capgo/cli@latest build request ${buildAppId} --platform ${buildPlatform} --output-upload --output-record "${recordPath}"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear stale build records before launching builds

When the same app/platform has a previous successful onboarding build, this deterministic temp path is reused and the new command only overwrites --output-record on a succeeded build (request.ts writes it only when finalStatus === 'succeeded'). If the next build fails or is still running, checkBuild reads the old record and reports the prior build as the current one, incorrectly completing onboarding. Remove or uniquely namespace the record before starting a new build.

Useful? React with 👍 / 👎.

…read, bundle-id resolution, error truthfulness

BLOCKER: buildIosSavedCredentials re-reads the .p8 from the persisted
progress.p8Path via the injected deps.readFile when the carried key content
was lost (crash/restart), and REFUSES to save an app_store credential map
without the ASC key — pre-fix the resume silently wrote credentials missing
APPLE_KEY_CONTENT/APPLE_KEY_ID/APPLE_ISSUER_ID and declared success
(3-engine hostile-review consensus; regression vs main's guards).
buildSavedCredentials may now be async (tail/flow.ts awaits it).

Engine findings:
- resolveIosBundleId: override → detected Release bundle id → appId, threaded
  into the provisioning-map key, the tail rebuild, and all six runIosEffect
  call sites (bundle id ≠ Capgo appId)
- resolveP8Content wraps readFile: a stale p8Path surfaces the NeedP8-style
  re-provide error instead of a raw ENOENT
- import-portal open-anyway: success log carries the URL; openExternal
  failure logs the visit-manually fallback instead of fabricating success
- tail saving-credentials self-heal divert surfaces the underlying builder
  error (guidance line + internal log)
- requesting-build honors deps.signal (pre/post/catch abort bails; limitation
  vs request.ts internals documented)
- android backing-up: only ENOENT keeps the benign note; real copy failures
  log a truthful warning
- gcp-setup-running strips _oauthRefreshToken from persisted progress after a
  successful revoke
- createCertificate/createProfile rethrow the ORIGINAL Apple error when the
  follow-up list call also fails
- rebuildIosTailCredentials restores APPLE_KEY_ID/APPLE_ISSUER_ID from
  progress (keyId/issuerId ?? apiKeyVerified)
- preloadWorkflowScripts failures route through onInternalLog
- test-ios-import-discovery: prefetch fixture uses the RAW AscProfileSummary
  shape so the synthesizeProfileFromAscSummary mapping is actually exercised
…tale closures, tail readFile wire

- .p8 submit handlers no longer rewrite every failure to 'File not found':
  classifyP8SubmitError (new ui/p8-error.ts) keeps the exact copy for ENOENT
  only; other errors reach handleError with the real message, and every
  failure lands in the internal log
- the three [step]-keyed drivers (create-new / import / tail) call
  handleError through a ref (handleErrorRef), eliminating stale-closure
  routing on error
- NeedP8Error hoisted to module scope (stable instanceof across renders)
- tail driver carried.savedCredentials built via an explicit filtered
  conversion instead of a lying Record<string,string> cast
- tail driver deps now thread readFile (mirrors the create driver) so the
  engine's crash-recovery .p8 re-read works through the TUI — verified by the
  private e2e crash-recovery journeys (xfail pin promoted)
- error sinks: support-flow and restart deleteProgress catches log instead of
  swallowing; cert-limit/duplicate-profile Select onChange guarded against
  the @inkjs/ui re-fire with one-shot refs
assertEquals(res.next, 'import-no-match-recovery', 'still bounces back to the recovery menu')
const opened = logs.find(l => /Opened Apple Developer Portal/.test(l.msg))
assert(opened, 'the success breadcrumb still fires when openExternal succeeded')
assert(opened.msg.includes(PORTAL_PROFILES_URL), `the success line must include the URL that was opened (got: ${opened.msg})`)
assert(!logs.some(l => /Opened Apple Developer Portal/.test(l.msg)), 'must NOT claim the portal was opened when openExternal failed')
const warn = logs.find(l => /Could not open your browser/.test(l.msg))
assert(warn, 'must log the could-not-open warning (verify-app sibling pattern)')
assert(warn.msg.includes(PORTAL_PROFILES_URL), `the warning must tell the user WHERE to go (got: ${warn.msg})`)
@sonarqubecloud

Copy link
Copy Markdown

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