Repository navigation
Fixes from real-app trials: ask once, doc-backed decisions, clearer iOS proof - #32
Merged
Merged
Conversation
A urlHandler that returns false re-opens the URL: Android fires ACTION_VIEW pinned to the app itself, so a link on the app's own scheme or tracking domain loops, offline too, because a failed redirect passes the tracking URL on. The allowed-protocols pitfall claimed http was implicit; only https is, and the match is case-sensitive on the text before "://". The routing rows sent allowed protocols to the UUA article and deep links to the Branch/AppsFlyer stub. The skill now has a compiled App Links snippet with the 3.7.0 names, autoVerify, and the Iterable prerequisites, and new pitfalls 24-25. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A React Native app now finishes Android (provisioned, integrated, and proved by iterable-android-verify) before iOS starts. The iOS phase reuses the Android setup and the scope confirmed in Step 0, which the skill passes as --features. The app is integrated when every platform in scope has passed its verifier. For bare iOS the skill owns the native push code: the AppDelegate token and notification callbacks, a native notification-permission request at launch, and universal links in the 0.88 template's SceneDelegate. It gives the exact Release Simulator build and install commands, with signing on, because a live trial installed a stale build found with find and built with code signing off, which strips aps-environment. It loads the Mobile key the way provisioning delivers it: an optional require on bare React Native and EXPO_PUBLIC_ITERABLE_API_KEY on Expo, where push sets requestPermissionsForPushNotifications. Install resolves the latest version with one npm view that survives a scoped private registry, and never prints an npmrc. The New Architecture is always on from 0.82, so the installing article's opt-out is skipped. Deep links no longer loop through Linking.openURL. The Xcode 27 notes live in pitfall 12, and the skill and its regenerated corpus are pinned to SDK 3.2.1. New pitfalls 16-22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bin/ios-project.sh answers where the app is, where its Xcode project is, and which kind of app it is: native, react-native, or expo. Every iOS actor resolves through it. Source scans prune node_modules, Pods and build output, so the SDK's own sources no longer pass for the app's integration, and inspect reads React Native signals where they live. The proof artifact for React Native is a Release build: a Debug Simulator build has no main.jsbundle and runs only while Metro serves it. Before giving any push advice, I6 and ios-prove now check the app actually installed on the Simulator. It must contain the Iterable SDK (and, for React Native, a bundle that references RNIterableAPI), carry aps-environment, match the recorded build, and not be older than the source. A live trial reinstalled a pre-integration build four times, and ios-prove's first answer sent it hunting for a permission problem. inspect warns when an Expo app never asks for notification permission. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The iOS provisioning path writes the Mobile key where a JavaScript app reads it: .iterable/rn/iterable.local.ts on bare React Native, and EXPO_PUBLIC_ITERABLE_API_KEY in a gitignored .env.local on Expo. Native apps keep the xcconfig. When the Android program already ran, iOS offers import-from-android. One Mobile and one Server-side key serve both platforms, with the Android region, No JWT and test email, validated again for iOS. The search walks up to the git toplevel, so a monorepo app finds the workspace kept at the root. The scope the React Native skill confirmed arrives as --features. It is shown on the import and confirmation screens before anything is written, and recorded with its source. restart --app-path now binds a fresh build to an existing proof run, and a missing .app is refused before anything is archived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live trial's agent never printed context on any Android screen, so the developer saw "Which key is on your clipboard?" with no steps. The Firebase approval also asked them to agree to "the changes listed above" with no list on screen. The steps, links, values, and the list of changes being approved now go in the prompt, with the question last, as on iOS. Steps 1 and 2 link to app.eu.iterable.com for an EU base, and "show again" re-asks the question instead of printing into a tool result nobody sees. The package screens recommend the applicationId read from the app's app.json or build.gradle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the Mastodon iOS trial, iterable-ios Step 0 asked features and identity with the app in view. Then ios-provision asked both again. The second Features answer dropped Mobile Inbox and Embedded, so the recorded scope contradicted Step 0. The generic identity help steered an app whose signed-in session has no email toward Email. The skill said provisioning "can conduct" the conversation, and native iOS had no way to hand Step 0's answers to the tool; only the React Native import path could. status and question now take --features and --identity as read-only proposals. Values the tool's own screens would not record are refused before anything is written. With no Android setup to import, one carry_scope screen names exactly what Step 0 confirmed. Its recommended option runs carry-scope, which records them through the same writer import-from-android now uses. Each carried field keeps its own source, so confirm_scope marks each line's origin, scope.json keeps it, and a later answer drops only that field's attribution. Change the answers records the decline and falls back to the normal screens. The source follows the app's framework, ios-step0 or react-native-step0. A native app next to an Android setup is therefore not labeled as the React Native skill, and its carried identity wins over the Android test email. Without the flags the flow and scope.json are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skills recommended identifiers without the rules behind them: nothing shipped defined project types, the userId rules (no PII, no handles, not guessable, 128 characters) or the EU data center. Add those articles to all three platform corpora at each config's existing pin, plus the identifying-the-user article React Native was missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live trial recommended setUserId("<accountID>@<instance>"), a handle,
because nothing pointed the agent at Iterable's userId rules when it chose.
Each skill's Step 0 now asks the project type first (with a "help me" path
that inspects the app and recommends), has existing clients reuse the
identifier their other systems send, and holds a decisions table: before
recommending, read the named section and cite it, or offer no
recommendation.
The Android per-install UUID default is now the account-less case only,
with the reinstall caveat and the doc it follows. Routing rows the table
replaces are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/decision-docs.sh parses each skill's decisions table and checks that every slug has a file in that platform's reference/ (or the sibling path React Native uses), every named section is a heading in it, and every row names at least one doc. A mutated copy of the iOS table proves it can fail. run-all.sh picks it up with the other suites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live trial integrating Iterable into the Mastodon iOS app never printed context, so the developer confirmed the integration scope seeing only "Use this Iterable integration scope?": the Features, Identity, Region and JWT lines lived only in context. The proof summary, the Android-reuse consent, the App Store Connect plan's side effects, every rejected-key reason, the detected Bundle ID and Team ID, and the missing-tool steps had the same gap. As on Android, they now sit in the prompt with the question last, context keeps background only, and no option points "above" at text it does not show. The proof start defaulted to the installed app, which was the pre-Iterable baseline build. When the workspace knows a build of the app that carries the SDK, recorded by an earlier run or under the project's -derivedDataPath, a new App build screen recommends binding that exact .app unless the installed copy was built later; the installed app stays an option. An existing run offers a fresh run bound to a newer build. The developer also had to type a 36-character user ID with no way to find it. No device or Iterable source yields it reliably: the SDK keeps it in the Keychain and never logs it, and the public API has no lookup by device token. So it stays typed, and the screen says where to read it, down to a Simulator debugger read of the running app's IterableAPI.userId. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live trial integrated Iterable into the Mastodon iOS app, which already had its own push relay and Notification Service Extension. Two verification gaps showed up. The agent read the Unified Log, saw the APNs token and no registerToken line, and decided the device was not registering. SDK 6.7.5 reports a successful registration only at debug level, and the default log delegate drops debug, so that line never appears. I12's next step also told agents to inspect "registration logs". Both iOS skills now say registration is read from Iterable through ios-verify status. Pitfall 29 records the SDK facts, and the I12 step points at the device record and says to re-run after a rebuild. Nothing proved Mastodon's own pushes still worked after its shared callbacks and extension were edited, and done did not list that as unproved. inspect now detects an existing non-Iterable push path: callbacks that do more than forward to Iterable, an extension that hands back content itself, or another push SDK. It warns about it. With push in scope, status keeps existing-push-coexistence in feature_evidence_required, so integration_complete stays false. Rule 8 gains the closing step. A probe on the iOS 26.2 and 27.0 Simulators showed simctl push never runs the extension, so it cannot stand in for that proof. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…irst Franco's rule: once Iterable's push is proved, the integration counts as done. The app's own push must not hold up `done`. ab9d5cc listed existing-push-coexistence in feature_evidence_required, which kept integration_complete false. On iOS it now goes in verification_limitations instead, and done offers an optional after-integration check that the app's own notifications still show. It runs only when the developer asks for it. The real decision belongs earlier, so Step 0 now asks it on iOS, Android, and React Native whenever existing push is found: keep the app's own notifications alongside Iterable's, or use Iterable notifications only. Keep both uses rule 8's forwarding. Iterable only means proposing, in the one diff the developer confirms, how the old provider stops handling pushes. Android's and React Native's rule 8 "never replace" now names that confirmed diff as its one exception. Android gets the same optional line. bin/existing-push.js now holds the detection for both platforms, so they share one list of React Native push libraries. On Android it flags an app FirebaseMessagingService that does more than forward to Iterable, a MESSAGING_EVENT service that is not Iterable's, and push SDKs in Gradle. Only next_action's done summary reads it, so no gate changes and config.sh grows by five lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he Simulator In the Mastodon trial the integration agent found the signed-in user ID in the Simulator's log: the SDK's default logger echoes Iterable's "No user exists with userId <id>" for a request sent before the user existed. The proof screen still made Franco type the 36-character ID, so he copied it from the agent's message. Typing added friction and no safety. The identity screen now gathers candidates from the device and the agent: the integration's temporary ITBL_PROOF marker, the SDK's own no-user lines, and --identity-candidate. It offers one only when Iterable holds this Simulator's current APNs token on that user, the same read and token match the verifier's I12 gate makes, and records it only when the developer taps it. With no candidates it makes no request. A relaunch option restarts the app with -iterableProofIdentity YES so the marker logs the identity it signs in with. The iOS and React Native skills add that marker behind the launch argument, not #if DEBUG, because the proved build can be Release. They remove it after the proof, and inspect and the done summary warn proof_identity_marker_present until it is gone. The verify skill now describes the App build screen and the offered identity instead of a hand-appended --app-path and a typed answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fixtures and scenarios named the test apps the skill was trialled on. They now use same-length neutral names, so length-sensitive fixtures and digests are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
These fixes come from running the skill as a client would on real open-source apps: the official Mastodon iOS app (UIKit, with its own push relay and Notification Service Extension) and a bare React Native app. The Mastodon run ended green, with a push proved on the iOS Simulator. Along the way it showed where the skill asked twice, recommended from guesswork, or hid what the developer was agreeing to.
ios-provision status --features … --identity ….carry_scopescreen records them, so provisioning no longer asks again.identifying-the-user.tests/decision-docs.shfails CI when a table cites a doc or heading that doesn't ship.prompt, not only incontext, matching the Android fix in [SDK-800] Provision and prove React Native apps on Android and iOS #31. In the trial, the scope confirmation showed only "Use this Iterable integration scope?".ITBL_PROOF identity=log line, which prints only when the app is launched with-iterableProofIdentity YES. The proof reads it, and offers it only after Iterable confirms this Simulator's current device token is on that user.inspectwarns if it's left in.ios-verify status(new pitfall 29).inspect, and the Androiddoneline, detect an existing push path: the app's own callbacks or extension, aFirebaseMessagingService, or OneSignal, Braze and similar.tests/andeval/included.Stacked on #31.
Reviewing it
reference/files.bin/existing-push.js.ITBL_PROOFmarker,--identity-candidate, and the token-verified offer.tools/screens.How it was tested
donewith a nonce-matched delivery. The fixes here address every friction point that run surfaced.simctl pushand Notification Service Extensions: a probe app on the iOS 26.2 and 27.0 Simulators showedsimctl pushnever runs an extension. That's why the app's own push check is developer-triggered.Known limitations
:::v-preblocks and list-indented callouts raw, and doesn't strip "Want to learn more?" sections, in a few articles, old and new.🤖 Generated with Claude Code