Skip to content

Inbox mobile: TestFlight and App Review preparation (M8) - #1798

Merged
backnotprop merged 1 commit into
mainfrom
feat/mobile-m8-testflight
Oct 8, 2026
Merged

backnotprop merged 1 commit into
mainfrom
feat/mobile-m8-testflight

Conversation

@backnotprop

Copy link
Copy Markdown
Owner

The story

The iPhone app gets what App Store Connect needs before the first upload: a privacy manifest truthful to the code, the notes App Review reads, and a release runbook the owner runs himself. Nothing in the app's behaviour changes: the manifest is a resource, and the only project change is XcodeGen adding it. The Workspaces build setting this step names already exists on M7's branch (WORKSPACES_ORIGIN), so this PR references it and does not add a second one.

What changed

  • apps/ios/Plannotator/PrivacyInfo.xcprivacy (new): no tracking, no tracking domains, no collected data types, one required-reason API: NSPrivacyAccessedAPICategoryUserDefaults with CA92.1 (app-only defaults: the paired sources, appearance, haptics, Send's idempotency keys).
  • apps/ios/Plannotator.xcodeproj/project.pbxproj: xcodegen generate adds the manifest to the app's Resources (4 lines).
  • apps/ios/APP-REVIEW.md (new): what the app is (local-only App Store build; Workspaces in TestFlight builds only); Guideline 2.5.2 (the bundled surface is the app's own code, agent HTML is sandboxed content with no network, by CSP and content rule list); the reviewer's path without a computer (what exists today: none in the app; what the owner provides: a device screen recording, optionally a demo Inbox shared over Tailscale) with the notes text to paste; account deletion (5.1.1(v)): none needed for the account-less App Store build, DELETE /v1/me required before any Workspaces build leaves internal TestFlight, OWNER-ITEMS item 30; Sign in with Apple (4.8, item 31, TestFlight builds only); export compliance (CryptoKit only, Apple's "limited to that within the Apple operating system" row, his declaration); App Privacy answers for both builds.
  • apps/ios/RELEASE.md (new): the owner's runbook: Team ID and Xcode account, the Team ID for the staging association file, two APNs keys into the relay's and Workspaces' secret stores, the App Store Connect record under ai.plannotator.app, the internal TestFlight group, the TestFlight archive with WORKSPACES_ORIGIN set and exported testFlightInternalTestingOnly, the App Store archive with it empty, the privacy report, export compliance, the listing and the submission. Each step one line with its exact command or screen.
  • apps/ios/README.md: "What you need to run it" and "Release and App Review" pointing at the three documents.
  • AGENTS.md: the apps/ios structure line names the three documents.

Record sections it makes real

PLAN.md step M8 (privacy manifest, review notes with bundled code and sandboxed agent HTML, account deletion, the release runbook; upload and submission are the owner's). OWNER-ITEMS 1, 3, 4, 7, 8, 9, 10, 30, 31 are each placed in the runbook or the notes as the owner's step. No screen of the iPhone record changes.

Proofs and the commands that run them

  • Release build for the simulator, no signing, the manifest in the bundle: xcodebuild -project apps/ios/Plannotator.xcodeproj -scheme Plannotator -configuration Release -destination 'generic/platform=iOS Simulator' -derivedDataPath /tmp/mobile-m8/derived CODE_SIGNING_ALLOWED=NO build (BUILD SUCCEEDED; Plannotator.app/PrivacyInfo.xcprivacy present and byte-identical to the source).
  • Shape: plutil -lint apps/ios/Plannotator/PrivacyInfo.xcprivacy (OK), and a check of every key and reason code against the lists in Apple's current "Describing use of required reason API" and NSPrivacyAccessedAPIType documentation (read from developer.apple.com on 2026-10-08).
  • The required-reason APIs, found by reading the code on main and on every in-flight app branch (M2, M3, M4, M5, M7, P2): only UserDefaults; no file timestamp, boot time, disk space or active keyboard API.
  • The runbook's names: xcodebuild -list (scheme Plannotator, configurations Debug and Release) and xcodebuild -showBuildSettings -scheme Plannotator -configuration Release -destination 'generic/platform=iOS' DEVELOPMENT_TEAM=... (bundle id ai.plannotator.app, the team override taken); on M7's branch the same with WORKSPACES_ORIGIN=https://staging.workspaces.plannotator.ai shows the origin, and without it Release leaves it empty. The export options keys (method app-store-connect, destination upload, signingStyle, testFlightInternalTestingOnly) checked against xcodebuild -help on Xcode 26.6.
  • Waits on the owner: Xcode's privacy report and a real archive need his signing (RELEASE.md 5.3).

Gates

bun run typecheck (green), bun run check:release-version (green), gitleaks git . --log-opts="<merge-base>..HEAD" (no leaks). No TypeScript, server or UI file changes, so no bun or DOM suites.

What it leaves out, and what M8's rebase must add

  • M8 rebases last. When M5 is on main: the app's manifest adds 1C8F.1 (App Group defaults, Notifications.swift), and PlannotatorNotifications/PrivacyInfo.xcprivacy is added with the same entry, because Apple wants a manifest in every bundle whose executable uses a required-reason API.
  • Found, for M7: no associated-domains entitlement (applinks: and webcredentials: for the staging origin) on M7's branch. A signed build's ASWebAuthenticationSession https callback cannot return without it. RELEASE.md names it as a precondition; it belongs in M7 or a follow-up, beside M5's entitlements file.
  • No ITSAppUsesNonExemptEncryption key: the export answer is the owner's declaration. RELEASE.md 6.2 offers the one-line PR once he gives it.
  • No CI upload: plannotator-ops' rule keeps signing and TestFlight secrets out of CI until the owner decides (item 4). The runbook uploads from his Mac.
  • No demo mode in the app: the reviewer path is a recording, named in APP-REVIEW.md section 3.

Deviations

  • The brief calls the Team ID for the association file a "staging secret". In Workspaces it is a plain var, APPLE_TEAM_ID in apps/rooms/wrangler.api.jsonc (value TEAMIDTODO), since Apple publishes the Team ID in every association file. RELEASE.md 1.4 says so. The Team ID itself is never committed here (plannotator-ops: no account ids in the repo); the runbook uses <TEAM_ID>.
  • Collected data types are empty, judged against Apple's definition of "collect" (sent off the device and kept readable by the developer): the App Store build sends content only to the person's own computer, or through the relay as ciphertext it deletes on acknowledgment. The relay's readable APNs token is named as the owner's App Privacy call, with the alternative written out. A public Workspaces build adds its entries in that release.

Limits

None added.

@backnotprop
backnotprop merged commit 5d39c98 into main Oct 8, 2026
35 of 36 checks passed
@backnotprop
backnotprop deleted the feat/mobile-m8-testflight branch October 8, 2026 17:24
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