Skip to content

ios-native parity pass 1: SDK contracts, chat behavior, and account copy - #1153

Merged
AnthonyRonning merged 17 commits into
codex-ios-native-swiftfrom
codex-ios-native-parity-1
Oct 13, 2026
Merged

AnthonyRonning merged 17 commits into
codex-ios-native-swiftfrom
codex-ios-native-parity-1

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Brings the 18 bounded Research parity items to Mark's native Swift iOS branch, using its existing components and presentation conventions.

An existing saved model remains sendable when catalog loading fails; the server decides model, plan and usage authorization. Selecting a new non-Free catalog model still requires a paid plan. Web search starts enabled for new preferences and older records without a stored choice, preserves an explicit off choice, and follows the saved preference. Read-aloud blocks only a known Free plan, and images/dictation retain their paid-plan checks. Stop updates active output only, completed content and unrelated errors survive settlement, and each restored context rejection owns one alert acknowledgment that survives screen remounts. The composer shows the privacy line in empty New Chat and the mistakes disclaimer in conversations. Compact chat navigation also regains its native Menu button after model/project options, queue, media and paste sheets dismiss, and after shared camera/image covers. Native dismissal completion reconciles the retained controller without releasing another presentation’s lease.

Base: codex-ios-native-swift at 4232814f7de039f3762526d1c20f732e2ea2f91d (Mark's draft #1152). Research reference: read-only master at 70cd8aa607c04c894d2a1cc0f1d2bdda673077f1. Scope follows the parity audit and pass-1 handoff. This PR targets Mark's branch, with no master rebase.

R/ means apps/maple-research/frontend/src/ at the reference commit.

Item Status and implementation Research/reference source
1 Done — anonymous requests omit the cache root; cache-only persistence failures do not discard verified sessions; streams retain events for stalled consumers; corrupt authority recovers with durable-cleanup fencing; shared refresh survives caller cancellation. Credential writes remain fail-closed. Cleanup stays within the existing SDK owners. Five scoped findings in the 2026-10-11 Swift SDK review; sdk/src/lib/encryptedApi.ts:592-605, transportV2/auth.ts:654-674, transportV2/runtime.ts:284-298, transportV2/session.ts:446-482, sdk/rust/src/client.rs:1235.
2 Done — exactly current + rollback PCR0 values per environment with approval dates; signed-history verification retained. sdk/src/lib/pcr.ts:1-19.
3 Done — optional Stripe ID, credit balance, iOS switches, conflict and pending-plan-change decoding. Nested fields also tolerate omitted/null values. No new billing UI. R/billing/billingApi.ts:38-63.
4 Done — supplied error codes must agree; unknown/conflicting codes never use the legacy fallback. Exact legacy messages still classify when no code is supplied, including plan denial. Separate issues and product-name-aware usage copy; draft restoration precedes Message Too Large. Acknowledgment belongs to the runtime issue occurrence, so revisiting the screen does not reopen that alert; a new rejection does. R/services/chatResponseErrors.ts:26-93,115-164, R/services/chatAccessError.ts:23-54, R/components/ContextLimitDialog.tsx:26-59.
5 Done — deprecated plan references removed; exact Free/paid substring mapping; only a known Free guest blocks account chat. A valid saved model reaches the server even when catalog data is unknown/loading/failed. Every non-Free picker tier requires a paid plan. Web search has no client plan/capability gate and defaults on for new or missing/null preferences, retaining explicit off choices; speech blocks only known Free; images/dictation retain paid checks without selected-model tool-use/vision gates. README matches these rules. R/billing/billingAccess.ts:3-14, R/components/ModelSelector.tsx:245-253, R/components/UnifiedChat.tsx:3842-3851,5010,5241, R/services/tts/TTSContext.tsx:98.
6 Done — ordinary failed responses show the exact failure notice unless stopped; partial text remains. Server incomplete and recovered failed responses settle as stopped. Settlement preserves current history, storage, composer and queue issues rather than replacing them with run feedback. R/components/UnifiedChat.tsx:2668-2690,2714-2782,4937.
7 Done — Stop marks only this run's active output incomplete and displays Chat Canceled; completed reasoning/tool/text blocks retain completed status. Streaming text and tool events carry their current status. R/components/UnifiedChat.tsx:832-850,1427-1432,2618-2639.
8 Done — native ProgressView before first assistant output and inside empty in-progress assistant messages, with Research's responding accessibility label. R/components/UnifiedChat.tsx:1420-1442,1532-1533, R/components/chat/ChatTurn.tsx:98-114.
9 Done — Thinking while reasoning streams; completed word-count duration at 26 words/second, rounded with minimum 1. Disclosure remains collapsed initially. R/components/markdown.tsx:103-186.
10 Done — title-only 0.5-second exponential polling capped at 10 seconds while New Conversation; stops on title change, deselection, background, account retirement or deletion. Injected-clock tests cover delay and ownership changes. Unchanged polls do not invalidate library reads. R/components/UnifiedChat.tsx:3308-3382.
11 Done — exact Stop/recovery/image-capacity/dictation/composer strings and two-second Copied feedback for code and whole-message copy. R/components/UnifiedChat.tsx:2641-2818,4485,5485-5494,5936-5938, R/services/chatResponseErrors.ts:204-217, R/components/chat/ChatCopyButton.tsx:86-117.
12 Done — empty New Chat shows the existing lock + Encrypted and private at every step; conversations show AI can make mistakes. Check important info. R/components/UnifiedChat.tsx:6128-6133,6384-6386.
13 Done — badges omit plain access tier; locked models use the existing Done-only notice with model-name payload; trigger displays selected short name. Options dismissal restores the compact native navigation bar. R/components/ModelSelector.tsx:283-362, R/components/UpgradePromptDialog.tsx:200-217.
14 Done — active bold/pin rows, pull refresh, ten-project cap/hint, exact empty copy, selection exit after successful bulk actions, pinned project children and persisted iPad sidebar. R/components/ChatHistoryList.tsx:344-360,760-809,932-953,1235-1329,1442-1611; server cap services/opensecret/src/models/responses.rs:41.
15 Done — raw product_name Plan and provider-aware Renews on / Expires on date. R/components/settings/AccountSettings.tsx:76-81,127-142.
16 Done — no Name field; ANON and empty invite code; existing password minimum enforced before signup. R/routes/signup.tsx:115-121.
17 Done — current-year legal footer and Terms/Privacy through existing link confirmation; About tagline. R/components/AuthMain.tsx:31-41, R/components/settings/AboutSettings.tsx:59.
18 Done — exact font descriptions, voice groups/help, new-record 1.2× default and chooser option. Stored rates and legacy preference records retain their values. R/services/chatTypographyPreferences.ts:21-37, R/services/tts/ttsPreferences.ts:1-5,40-64, R/components/settings/PreferencesSettings.tsx:321-322.

Follow-up validation at 09fcb3359894db3ad1fa1dd60b6fa142240bb9bc

  • App package: 475 tests in 60 suites passed, warnings as errors, excluding only the user-deferred Vision screenshot OCR test. Includes missing/null web-search preference migration, restart persistence, retained explicit-off choices and rejection of malformed values without replacing stored bytes.
  • Strict app formatting, full unsigned PR simulator build, arm64 iOS simulator/device package cross-builds and Local simulator build-for-testing passed with Xcode 26.5/17F42. No SDK source changed after its 120-test/release-build pass below.
  • Independent source review found no P0–P2 issue in the final preferences and native-dismissal hooks.
  • Four selected native iPhone UI methods passed: options scrim/swipe dismissal restores direct Menu; queue Done/swipe dismissal restores direct Menu/Close with draft, queue and storage owners unchanged; shared image cover dismissal restores direct Menu/Close without replaying covered completion; and existing image cover completion eligibility remains correct.
  • The user manually smoke tested the existing Local iPhone build at 6425bbb7 and reported it looked good. That phone and its saved preference were left untouched during follow-up testing. The task-owned Local stack remains running for manual tests.

Follow-up runtime artifact: apps/maple-ios/.build/xcode-local/Build/Products/Local-iphonesimulator/Maple.app, cloud.opensecret.maple.native.dev, version 0.1.0/build 8, profile Local. Public endpoints are the same as below. The follow-up native tests used a separate task-owned iPhone 17 Pro, iOS 26.5/23F77, 0C7E271E-EFE3-4D02-A317-09E3CD57F814, with ad-hoc simulator identity. Result bundle: /tmp/ios-parity-shared-presentations.xcresult.

Camera hardware, actual paste input, native Photo/file picker completion and OCR remain unverified. The new hooks cover the named sheets and shared full-screen presentation path; they are not a claim that every system picker has been runtime-tested.

Earlier validation at 6425bbb7f3a7e246c47704b41d760aa51c37787f

  • Swift SDK: 120 tests in 17 suites passed, warnings as errors; release build passed.
  • App package: 474 tests in 60 suites passed, warnings as errors. The single Vision screenshot OCR test was explicitly excluded at the user's request. Strict formatting passed.
  • Full unsigned PR simulator build, arm64 iOS simulator/device package cross-builds, and Local simulator build-for-testing passed with Xcode 26.5/17F42.
  • Six selected iPad synthetic UI methods passed: all five EntitlementUITests plus context-alert acknowledgment across remount and a new rejection.
  • Native options regression: untouched Mark base failed the new direct Menu test; current iPhone passed after both scrim and swipe dismissal, without an edge gesture or reset first. UIKit inspection on the actual shipping screen confirms restored interaction and accessibility. Edge navigation could recover the old issue, so the earlier "until relaunch" report was too strong.
  • Real Local Pro password sign-in passed on iPhone. Actual shipping model/web preference and direct Menu/Close/New control tests passed on Pro iPhone and Pro iPad. New chat preserves the current unsent New Chat draft under Mark's existing behavior; these checks prove control admission and retention, not a conversation switch.
  • The supplemental Free iPad run reached and verified model/web persistence, native options dismissal, Menu/New control admission and the dictation notice, then failed at the image-menu query. XCTest returned an Application-only hierarchy with no windows/children during that native menu. Direct CUA interaction confirmed Attach image → Photo library → the paid-plan Images notice works. The whole Free method remains failed; these reached steps are not counted as a passed test. The speculative menu-query workaround was reverted.
  • Live Local billing/catalog test passed both Pro and Free cases, including the actual Gemma catalog entry: Pro selection succeeds, Free selection returns paidPlanRequired, and the previous selection is restored. Both permit web-search preference dispatch. No inference was requested.
  • Compared current Research source with its running Local Vite frontend at a 390×844 viewport: Pro can select Gemma, Free gets its paid-model notice, and both can toggle web search. This comparison did not launch a Research Tauri/WKWebView artifact.

Earlier supplementary attempts remain separate: an obsolete Done-button expectation was corrected to the existing outside-dismissal control; the Free image-menu attempt exposed an empty XCTest accessibility tree; its speculative lookup workaround was reverted. A concurrent phone/iPad credential attempt failed before navigation and showed a clipboard change during entry; the same phone test passed unchanged when run serially. Desktop-coordinate misses do not override the passing native control tests.

Runtime artifact: apps/maple-ios/.build/xcode-local/Build/Products/Local-iphonesimulator/Maple.app, cloud.opensecret.maple.native.dev, version 0.1.0/build 8, profile Local. Public origins: API http://127.0.0.1:31714, billing :36972, flags :41248, auth :35694; client UUID ba5a14b5-d915-47b1-b7b1-afda52bc5fc6. Tests ran on the task-owned iOS 26.5 iPhone 8EF68006-89EB-4225-938C-8D207B4506DF and iPad B2096BF9-D6BC-47D0-98DB-9FA56A45587C, with ad-hoc simulator identity and no development team or distribution credentials.

Current result bundles: /tmp/ios-parity-review-ui-ipad.xcresult, /tmp/ios-options-final-directmenu.xcresult, /tmp/ios-options-phone-signin-pro.xcresult, /tmp/ios-parity-review-local-pro-ipad-repeat.xcresult, /tmp/ios-options-phone-reference-pro-serial.xcresult, /tmp/ios-parity-review-local-free-ipad-repeat.xcresult (failed supplemental image-menu automation). Expected baseline failure: /tmp/ios-options-base-directmenu.xcresult.

All changes stay inside apps/maple-ios/ and sdk/swift/; deprecated-plan grep and diff checks pass. The configured identity and OpenPGP signing remain in place. Commits use the documented MAPLE_HOOK_SKIP=1 after explicit gates: the previous wrapper chose the newer Command Line Tools SDK, and this pass deliberately excludes OCR. The hook, workflows and dependencies are unchanged. Xcode's stale Textual lockfile cleanup was inspected and restored.

Before merging, GitHub reported a clean, mergeable PR with no status checks on this branch. Local package, artifact and runtime evidence does not establish provider inference, hosted OAuth, physical-device behavior, deployment or store distribution.

Earlier command tails at 6425bbb7

nix develop --no-update-lock-file .#apple -c scripts/ci/sdk-swift.sh — passed

✔ Suite CredentialObservationTests passed after 0.076 seconds.
✔ Test stalledResponseConsumerPreservesEveryEventAndTerminal() passed after 0.078 seconds.
✔ Test cancelledRefreshCallerKeepsSharedRotationAliveForOtherCallers() passed after 0.074 seconds.
✔ Suite CredentialAuthorityTests passed after 0.079 seconds.
✔ Test sseHandlesEveryByteSplitAndWithholdsTerminalUntilEOF() passed after 0.111 seconds.
✔ Test untrustedHTTPFailuresAreSanitized(_:) with 5 test cases passed after 0.236 seconds.
✔ Suite PCRHistoryHTTPTests passed after 0.238 seconds.
✔ Test run with 120 tests in 17 suites passed after 0.239 seconds.

nix develop --no-update-lock-file .#apple -c scripts/ci/sdk-swift.sh build — passed

[0/1] Planning build
Building for production...
[0/2] Write sources
[1/2] Write swift-version-5975E236E64FF690.txt
[3/3] Compiling MapleSDK MapleTransport.swift
Build complete! (10.51s)

nix develop --no-update-lock-file .#apple -c env -u MAPLE_RUN_LIVE_ATTESTATION -u MAPLE_SWIFT_INTEGRATION_URL -u MAPLE_SWIFT_DEV_INTEGRATION -u MAPLE_IOS_ACCOUNT_LIVE -u MAPLE_IOS_AUTH_LIVE -u RUN_LIVE_AI -u LD -u LDPLUSPLUS -u CC -u CXX xcrun swift test --package-path apps/maple-ios --sdk /Applications/Xcode_26.5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -Xswiftc -warnings-as-errors --skip denseScreenshotActuallyDownscalesToTheBoundedLosslessWireImage — passed

✔ Test latePreparationCannotCrossGenerationOrCancellation() passed after 2.118 seconds.
✔ Test completedHistoryRenderingSurvivesRepeatedOwnedScrollSuspension() passed after 2.126 seconds.
✔ Test suspensionRejectsLatePreparationAndRestartsTheSameIncompleteRequest() passed after 2.154 seconds.
✔ Test replacingARequestCannotReuseItsOldCompletionAfterReturningToTheSameText() passed after 2.175 seconds.
✔ Suite MessageRenderModelTests passed after 2.179 seconds.
✔ Test run with 474 tests in 60 suites passed after 2.179 seconds.

nix develop --no-update-lock-file .#apple -c scripts/ci/ios-native.sh simulator-build — passed

Ignoring --strip-bitcode because --sign was not passed

** BUILD SUCCEEDED **

MAPLE_IOS_SWIFT_SCRATCH_PATH=/tmp/ios-parity-review-cross-build nix develop --no-update-lock-file .#apple -c scripts/ci/ios-native.sh cross-build — passed

[0/1] Planning build
Building for production...
[0/2] Write sources
[1/2] Write swift-version-5975E236E64FF690.txt
[3/3] Compiling MapleAppUI EntitlementFixtureView.swift
Build complete! (49.01s)
[0/1] Planning build
Building for production...
[0/2] Write sources
[1/2] Write swift-version-5975E236E64FF690.txt
[3/3] Compiling MapleAppUI EntitlementFixtureView.swift
Build complete! (28.30s)

MAPLE_IOS_SIMULATOR_ID=8EF68006-89EB-4225-938C-8D207B4506DF nix develop --no-update-lock-file .#apple -c apps/maple-ios/scripts/build.sh runtime-build local — passed


** TEST BUILD SUCCEEDED **

Historical OCR evidence and later scope

The earlier unfiltered app run executed 460 tests and failed only ImageScreenshotTests/denseScreenshotActuallyDownscalesToTheBoundedLosslessWireImage with Vision Foundation._GenericObjCError.nilError. That failure was also reproduced on untouched base 4232814f on this host. OCR was not rerun or modified in this review-fix pass; the user will validate it on a phone.

Identity/URL schemes/xcconfig/entitlements, privacy-manifest and release-lane changes, account-activation cleanup concerns, broader conversation/recents polling, thinking-level controls, documents, guests, team management, verification and IAP remain outside this pass. The only new polling is the scoped title loop. The task-owned Local stack remains running for the user’s manual simulator tests; workspace and test evidence remain available.

@AnthonyRonning
AnthonyRonning marked this pull request as ready for review October 13, 2026 07:38
@AnthonyRonning
AnthonyRonning merged commit 6baf139 into codex-ios-native-swift Oct 13, 2026
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.

1 participant