Skip to content

Instrument iOS cold-start phases - #2293

Merged
Asherlc merged 4 commits into
mainfrom
issue-2193-ios-cold-start
Jul 29, 2026
Merged

Asherlc merged 4 commits into
mainfrom
issue-2193-ios-cold-start

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • trace Expo OTA, JavaScript bootstrap, auth restore, splash dismissal, and deferred service bootstrap in one sampled mobile startup transaction
  • mark perceived app load after splash dismissal and emit correlated startup phase logs
  • document signed Release measurements and retain the current launch policy because the reported delay did not reproduce

Evidence

The signed ad-hoc Release bundle passed strict code-signature verification and rendered the login screen without a fatal runtime error. Three force-stop launches reached interactive in 712.7 ms, 968.0 ms, and 659.6 ms; a clean reinstall took 863.8 ms. OTA was 631.6–942.0 ms and all measured JS/auth/splash phases were at most 11 ms. No evidence supports changing fallbackToCacheTimeout.

Production classification remains blocked by expired Axiom connector access and the absence of historical Sentry startup spans; the issue and incident baseline record this gap.

Validation

  • pnpm exec vitest run --project mobile packages/mobile/lib/telemetry.test.ts packages/mobile/app/_layout.cleanup.test.tsx packages/mobile/lib/auth-context.test.ts packages/mobile/lib/startup-telemetry.test.ts (37 passed)
  • mobile lint
  • mobile TypeScript
  • root lint
  • root, server, and web TypeScript
  • signed Release Simulator build/install/launch with four controlled startup traces
  • full unit/mobile run emitted all discovered files green, then was terminated after an unrelated scripts/compose-env.test.ts Docker CLI child hung during teardown under shared Docker contention

Fixes #2193

Summary by Sourcery

Instrument iOS mobile cold-start with correlated Sentry startup spans and logs across OTA, JavaScript, authentication, splash dismissal, and deferred service bootstrap, and document the measurements and incident outcome.

New Features:

  • Add a Mobile Startup trace coordinator that attributes iOS cold-start time across OTA launch, JavaScript readiness, authentication restore, splash hide, and deferred service bootstrap.
  • Emit structured app-startup logs mirroring startup phase durations and outcomes for Release audits and OTLP correlation.
  • Mark app interactivity after splash dismissal via Sentry.appLoaded() so native app-start spans reflect the first interactive screen.

Enhancements:

  • Sample only App Start and Mobile Startup traces in Sentry to control startup tracing volume.
  • Wire auth bootstrap, splash dismissal, and background service initialization to startup phase telemetry without changing user-facing behavior.
  • Track service bootstrap success and failures as a distinct startup phase that can be skipped when no authenticated session exists.

Documentation:

  • Document the iOS Release startup tracing behavior, evidence, and unchanged Expo updates policy in the mobile README.
  • Record the iOS cold-start incident, measurements, root cause status, fix, and remaining risk in the production incident baseline.
  • Add a TDD plan document describing the iOS cold-start spans implementation and validation strategy.

Tests:

  • Add unit tests for the startup telemetry coordinator covering OTA attribution, phase lifecycle, interactive marking, and error handling.
  • Extend auth-context, root layout, and telemetry tests to assert correct startup phase wiring and Sentry sampling behavior.

Summary by cubic

Instrumented iOS cold start to attribute time across OTA, JavaScript, authentication, splash dismissal, and deferred service bootstrap, and to mark the app interactive at splash hide; no launch policy change was needed. Fixes #2193.

  • New Features
    • Added packages/mobile/lib/startup-telemetry.ts to start a "Mobile Startup" transaction via @sentry/react-native with child spans for each phase.
    • Captures expo-updates metadata (including Updates.launchDuration) and creates an "OTA launch" span that ends at JavaScript start.
    • Wired AuthProvider and _layout.tsx to start/finish phase spans, call Sentry.appLoaded() after splash, record outcomes (ready/authenticated/unauthenticated/deferred/error), start a new auth span after a deferred restore, and measure deferred service bootstrap.
    • Emits structured app-startup logs mirroring phase durations for Release audits and OTLP correlation.
    • Samples only "App Start" and "Mobile Startup" via tracesSampler in lib/telemetry.ts.
    • Added focused tests (startup-telemetry.test.ts) and updated docs (README.md, incident baseline); kept fallbackToCacheTimeout unchanged.

Written for commit 7059157. Summary will update on new commits.

Review in cubic

Asherlc added 3 commits July 29, 2026 08:49
Instrument native OTA, JavaScript, auth, splash, and deferred service startup before changing launch policy.

Refs #2193
Keep phase durations visible in local Release console output while retaining structured telemetry attributes.

Refs #2193
Document the controlled signed Release timings, unchanged launch policy, and unresolved production observability gap.

Refs #2193
Copilot AI review requested due to automatic review settings July 29, 2026 16:17
@Asherlc Asherlc linked an issue Jul 29, 2026 that may be closed by this pull request
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Asherlc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 524cb6e0-54b5-4cfa-afd8-e7259f9910e7

📥 Commits

Reviewing files that changed from the base of the PR and between 77e2f34 and 7059157.

📒 Files selected for processing (11)
  • docs/production-incident-baseline.md
  • docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md
  • packages/mobile/README.md
  • packages/mobile/app/_layout.cleanup.test.tsx
  • packages/mobile/app/_layout.tsx
  • packages/mobile/lib/auth-context.test.ts
  • packages/mobile/lib/auth-context.tsx
  • packages/mobile/lib/startup-telemetry.test.ts
  • packages/mobile/lib/startup-telemetry.ts
  • packages/mobile/lib/telemetry.test.ts
  • packages/mobile/lib/telemetry.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@sourcery-ai

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR introduces a coordinated iOS mobile startup telemetry pipeline that samples a single Mobile Startup transaction, breaks it into OTA, JavaScript, authentication, splash-hide, and deferred service-bootstrap phases, wires those phases into the app/auth lifecycle, and documents the incident analysis and startup tracing behavior.

Sequence diagram for coordinated iOS Mobile Startup telemetry phases

sequenceDiagram
  actor User
  participant AppProcess
  participant StartupTelemetry
  participant AuthProvider
  participant RootLayout
  participant BackgroundServices
  participant ExpoUpdates
  participant Sentry
  participant Logger

  AppProcess->>StartupTelemetry: startStartupTelemetry()
  StartupTelemetry->>ExpoUpdates: read launchDuration
  StartupTelemetry-->>StartupTelemetry: create Mobile Startup span
  StartupTelemetry-->>StartupTelemetry: create OTA launch span
  StartupTelemetry-->>Logger: info app-startup phase=ota
  StartupTelemetry-->>StartupTelemetry: startPhaseAt javascript

  RootLayout-->>StartupTelemetry: finishStartupPhase(javascript, ready)

  AuthProvider->>StartupTelemetry: startStartupPhase(authentication)
  AuthProvider-->>AuthProvider: retryBootstrap()
  AuthProvider-->>StartupTelemetry: finishStartupPhase(authentication, authenticated | unauthenticated | deferred | error)

  RootLayout->>StartupTelemetry: startStartupPhase(splash-hide)
  RootLayout->>RootLayout: SplashScreen.hideAsync()
  RootLayout->>StartupTelemetry: markAppInteractive(serviceBootstrapExpected)
  StartupTelemetry-->>Sentry: appLoaded()

  alt serviceBootstrapExpected
    RootLayout->>StartupTelemetry: startStartupPhase(service-bootstrap)
    BackgroundServices-->>BackgroundServices: initBackgroundHealthKitSync()
    BackgroundServices-->>BackgroundServices: initBackgroundAccelerometerSync()
    BackgroundServices-->>BackgroundServices: initBackgroundWatchInertialMeasurementUnitSync()
    BackgroundServices-->>StartupTelemetry: finishStartupPhase(service-bootstrap, ready | error)
  else unauthenticated path
    StartupTelemetry-->>StartupTelemetry: startStartupPhase(service-bootstrap)
    StartupTelemetry-->>StartupTelemetry: finishStartupPhase(service-bootstrap, skipped)
  end

  StartupTelemetry-->>Logger: info app-startup phase completions
  StartupTelemetry-->>Sentry: end Mobile Startup span
Loading

File-Level Changes

Change Details Files
Add a reusable startup telemetry coordinator that models the mobile cold-start lifecycle and reports structured phase spans/logs to Sentry/OTLP.
  • Create a Mobile Startup transaction with Expo Updates launch metadata and an OTA child span using Updates.launchDuration.
  • Track phase spans (JavaScript, authentication, splash-hide, service-bootstrap) with exactly-once start/finish semantics, duration and outcome attributes, and error propagation to the lifecycle outcome.
  • Mark the app interactive at splash dismissal, call Sentry.appLoaded() once, handle unauthenticated launches by skipping service-bootstrap, and close the lifecycle when service-bootstrap completes.
  • Emit app-startup logger events for each phase and for lifecycle completion to support Release audits and OTLP correlation.
  • Add unit tests that validate span structure, timings under fake timers, phase deduplication, lifecycle closure rules, and error status handling.
packages/mobile/lib/startup-telemetry.ts
packages/mobile/lib/startup-telemetry.test.ts
Wire startup phase telemetry into the root layout, splash behavior, and background service bootstrap path.
  • Initialize startup telemetry during app bootstrap and mark the JavaScript phase as ready once the AuthGate mounts.
  • Gate splash hide on isLoading while ensuring the interactive marker runs only once via a ref and wraps SplashScreen.hideAsync() in a splash-hide phase.
  • Call markAppInteractive after splash hide, passing whether service bootstrap is expected based on the presence of a user and recording errors when splash hide fails.
  • Instrument HealthKit, accelerometer, and Watch background sync initialization as a service-bootstrap phase, aggregating failures and finishing the phase based on the combined async outcomes.
  • Extend layout cleanup tests to assert startup telemetry calls (startup init, phase starts/finishes, interactive marker) are invoked in the expected order.
packages/mobile/app/_layout.tsx
packages/mobile/app/_layout.cleanup.test.tsx
Instrument authentication bootstrap to participate in startup phase tracking and expose outcome semantics.
  • Start an authentication phase when retrying bootstrap and compute a startup outcome that distinguishes authenticated, unauthenticated, deferred, and error cases.
  • Finish the authentication phase in a finally block with the computed outcome, including secure-store background deferrals and error paths.
  • Mock startup telemetry in auth-context tests and assert that each major auth scenario finishes the phase with the correct outcome label.
  • Ensure no additional blocking work is introduced in the auth provider beyond telemetry calls.
packages/mobile/lib/auth-context.tsx
packages/mobile/lib/auth-context.test.ts
Configure Sentry tracing to always sample startup-related transactions and validate the behavior via tests.
  • Add a tracesSampler function to initTelemetry that force-samples App Start and Mobile Startup transactions while delegating sampling of other traces to Sentry.
  • Update iOS telemetry tests to assert the presence and behavior of the tracesSampler, including startup trace names being sampled and unrelated traces inheriting downstream sampling.
  • Keep existing Sentry DSN and debug configuration unchanged while extending options to cover startup tracing.
packages/mobile/lib/telemetry.ts
packages/mobile/lib/telemetry.test.ts
Document the startup tracing behavior, incident analysis, and test-driven plan for iOS cold-start spans.
  • Update the mobile README to describe Release startup tracing, phase breakdown, Sentry appLoaded() usage, and measured Release startup/OTA timings, with links to Expo and Sentry docs.
  • Extend the production incident baseline with a new entry detailing the observed seven-second splash, measured phase durations, evidence that OTA dominates, telemetry gaps, and the decision not to change fallbackToCacheTimeout.
  • Add a superpowers TDD plan document outlining the cold-start spans test strategy, file structure, tasks, and evidence-based launch policy considerations.
packages/mobile/README.md
docs/production-incident-baseline.md
docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2193 Add iOS startup telemetry spans that attribute cold-start time across Expo OTA launch, JavaScript bootstrap, authentication restore, splash dismissal, and deferred service bootstrap, with correlated logs/tests/documentation.
#2193 Avoid changing the existing iOS launch policy (Expo Updates configuration) until measurements justify it, and document the measured cold-start behavior and remaining observability gaps.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-2293

Channel pr-2293
Deep Link dofek://preview/pr-2293
Commit 70a6d00

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-2293 pnpm expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-2293

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Instrument iOS cold-start phases with Sentry telemetry

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add a Mobile Startup trace coordinator attributing iOS cold-start time across OTA launch,
 JavaScript bootstrap, auth restore, splash dismissal, and deferred service bootstrap.
• Emit structured app-startup logs mirroring phase durations/outcomes and sample only `App
 Start/Mobile Startup` traces in Sentry.
• Call Sentry.appLoaded() after splash dismissal to mark perceived interactivity, and wire root
 layout/auth-context to the new telemetry.
• Document signed Release measurements in the incident baseline and README; retain current
 fallbackToCacheTimeout policy since the delay did not reproduce.
Diagram

sequenceDiagram
    participant Boot as App Bootstrap
    participant ST as startup-telemetry.ts
    participant Auth as AuthProvider
    participant Layout as RootLayout
    participant Sentry as Sentry SDK
    Boot->>ST: startStartupTelemetry()
    ST->>Sentry: startInactiveSpan(Mobile Startup + OTA)
    Layout->>ST: finishStartupPhase(javascript, ready)
    Auth->>ST: startStartupPhase(authentication)
    Auth->>ST: finishStartupPhase(authentication, outcome)
    Layout->>ST: startStartupPhase(splash-hide)
    Layout->>ST: markAppInteractive(serviceBootstrapExpected)
    ST->>Sentry: appLoaded()
    Layout->>ST: startStartupPhase(service-bootstrap)
    Layout->>ST: finishStartupPhase(service-bootstrap, outcome)
    ST->>Sentry: end Mobile Startup span
Loading
High-Level Assessment

Using Sentry's native startInactiveSpan/appLoaded API with a lightweight in-module phase map is appropriate here: it avoids pulling in a separate performance-monitoring library, integrates directly with existing Sentry tooling already used for exception capture, and keeps the coordinator small and testable with deterministic clocks. The decision to defer any launch-policy change (e.g., fallbackToCacheTimeout) until evidence justifies it is the correct conservative approach given the audit could not reproduce the reported delay.

Files changed (11) +737 / -16

Enhancement (4) +300 / -16
_layout.tsxWire root layout to startup telemetry phases +61/-16

Wire root layout to startup telemetry phases

• Initializes startup telemetry at bootstrap, marks JavaScript readiness, tracks splash-hide and service-bootstrap phases, and calls markAppInteractive once splash is dismissed while tracking service bootstrap success/failure across HealthKit, accelerometer, and Watch sync.

packages/mobile/app/_layout.tsx

auth-context.tsxTrack authentication as a startup telemetry phase +10/-0

Track authentication as a startup telemetry phase

• Wraps auth bootstrap with startStartupPhase/finishStartupPhase calls, recording outcomes (authenticated, unauthenticated, deferred, error) without changing existing auth logic.

packages/mobile/lib/auth-context.tsx

startup-telemetry.tsAdd Mobile Startup trace coordinator +227/-0

Add Mobile Startup trace coordinator

• New module creating a single correlated Sentry trace with child spans for OTA launch, JavaScript, authentication, splash-hide, and service-bootstrap phases, emitting structured logs per phase and closing the lifecycle once interactive and service bootstrap complete.

packages/mobile/lib/startup-telemetry.ts

telemetry.tsSample only App Start and Mobile Startup traces +2/-0

Sample only App Start and Mobile Startup traces

• Adds a tracesSampler to Sentry.init that forces full sampling for App Start and Mobile Startup transactions while deferring other transactions to the default sampling decision.

packages/mobile/lib/telemetry.ts

Tests (4) +257 / -0
_layout.cleanup.test.tsxAdd assertions for startup telemetry wiring in root layout +26/-0

Add assertions for startup telemetry wiring in root layout

• Mocks the new startup-telemetry module and asserts that splash hide, interactive marking, and service-bootstrap phases are invoked correctly during app lifecycle tests.

packages/mobile/app/_layout.cleanup.test.tsx

auth-context.test.tsAdd tests for auth startup phase telemetry +14/-0

Add tests for auth startup phase telemetry

• Verifies that authentication bootstrap start/finish telemetry calls fire with the correct outcome (authenticated, unauthenticated, deferred, error) in various auth-restore scenarios.

packages/mobile/lib/auth-context.test.ts

startup-telemetry.test.tsAdd unit tests for the startup telemetry coordinator +206/-0

Add unit tests for the startup telemetry coordinator

• New test suite covering trace/span creation order, phase completion exactly-once semantics, interactive marking with deferred service bootstrap, skipped service phase for unauthenticated launches, and error propagation to the top-level span.

packages/mobile/lib/startup-telemetry.test.ts

telemetry.test.tsTest tracesSampler restricts sampling to startup traces +11/-0

Test tracesSampler restricts sampling to startup traces

• Adds assertions that the new tracesSampler returns 1 for App Start and Mobile Startup transactions and defers to inheritOrSampleWith for others.

packages/mobile/lib/telemetry.test.ts

Documentation (3) +180 / -0
production-incident-baseline.mdRecord 2026-07-29 iOS cold-start audit outcome +66/-0

Record 2026-07-29 iOS cold-start audit outcome

• Adds a new incident entry documenting symptoms, evidence, root cause (unresolved), fix (added startup telemetry), and remaining risk for the reported iOS cold-start delay that did not reproduce under controlled testing.

docs/production-incident-baseline.md

2026-07-29-ios-cold-start-spans.mdAdd TDD plan for iOS cold-start span instrumentation +94/-0

Add TDD plan for iOS cold-start span instrumentation

• New planning document outlining goals, test strategy, file structure, and task checklist for adding startup telemetry, following the repo's agentic TDD workflow convention.

docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md

README.mdDocument Mobile Startup tracing and measured launch data +20/-0

Document Mobile Startup tracing and measured launch data

• Explains the new Release startup tracing lifecycle, Sentry appLoaded usage, and cites the 2026-07-29 signed Release measurements and unchanged launch policy.

packages/mobile/README.md

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 70a6d00d are ready:

This comment updates automatically on each PR push.

@qodo-code-review

qodo-code-review Bot commented Jul 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 171 rules

Grey Divider


Remediation recommended

1. Agent-only instruction in plan doc ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new startup plan doc contains a mandatory instruction for “agentic workers” and requires
specific agent skills, without providing an equivalent human/CLI-only workflow. This violates the
documentation rule that docs must not require or assume an AI agent is present.
Code

docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[3]

+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:test-driven-development before implementation. If executing this plan task-by-task, also use superpowers:executing-plans or superpowers:subagent-driven-development as appropriate. Steps use checkbox (`- [ ]`) syntax for tracking.
Relevance

●●● Strong

Team repeatedly accepted removing “For agentic workers/REQUIRED SUB-SKILL” from docs plans (PRs
#2282, #2225, #1468).

PR-#2282
PR-#2225
PR-#1468

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 773497 prohibits documentation in docs/ from requiring/assuming an AI agent is
present without an equivalent non-agent workflow. The added line explicitly requires agent skills
(REQUIRED SUB-SKILL: Use superpowers:test-driven-development...).

Rule 773497: Docs must not require or assume an AI agent is present
docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[3-3]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md` includes agent-required instructions (e.g., “REQUIRED SUB-SKILL: Use superpowers:...”), which makes the doc depend on an AI agent workflow.

## Issue Context
Compliance requires docs under `docs/` (and `README.md`) not to require or assume an AI agent is present unless an equivalent non-agent workflow is documented in the same section.

## Fix Focus Areas
- docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[3-3]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Startup span can hang 🐞 Bug ☼ Reliability
Description
finishLifecycleIfReady() refuses to end the Mobile Startup span until the service-bootstrap
phase is finished, but authenticated launches call `markAppInteractive({ serviceBootstrapExpected:
true }) without any fallback to finish or skip service-bootstrap` if it never starts. This can
leave the startup transaction un-ended (and potentially unreported) in cases where the service
bootstrap effect doesn't run or never reaches its completion path.
Code

packages/mobile/lib/startup-telemetry.ts[R82-90]

+function finishLifecycleIfReady(): void {
+  if (
+    lifecycleFinished ||
+    interactiveAtMs === undefined ||
+    !phases.get("service-bootstrap")?.finished ||
+    !startupSpan ||
+    startupStartedAtMs === undefined
+  ) {
+    return;
Relevance

●●● Strong

Team has accepted fixes preventing telemetry/lifecycle flows from getting stuck/hanging (e.g., stuck
sync flags and closure guards in PRs #2049, #1526).

PR-#2049
PR-#1526

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The coordinator blocks lifecycle closure on service-bootstrap completion, but authenticated
interactivity does not automatically create/finish that phase; it only starts later from a separate
effect. Without a fallback, the lifecycle span can remain open.

packages/mobile/lib/startup-telemetry.ts[82-90]
packages/mobile/lib/startup-telemetry.ts[204-227]
packages/mobile/app/_layout.tsx[227-243]
packages/mobile/app/_layout.tsx[262-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`finishLifecycleIfReady()` requires `service-bootstrap` to be finished before closing the startup span. When `markAppInteractive()` is called with `serviceBootstrapExpected: true`, there is no fallback path to close the lifecycle if `service-bootstrap` never starts/finishes, leaving an open startup transaction.

### Issue Context
Authenticated cold starts set `serviceBootstrapExpected` to `true` and rely on later app code to start/finish `service-bootstrap`. If that later phase never runs (or is bypassed by future gating changes), the coordinator will never call `startupSpan.end()`.

### Fix Focus Areas
- packages/mobile/lib/startup-telemetry.ts[82-90]
- packages/mobile/lib/startup-telemetry.ts[204-227]
- packages/mobile/app/_layout.tsx[227-243]
- packages/mobile/app/_layout.tsx[262-323]

### Suggested implementation direction
- Track `serviceBootstrapExpected` in the coordinator (set it in `markAppInteractive`).
- When ending the lifecycle:
 - If `service-bootstrap` span exists, wait for it to finish.
 - If it does **not** exist, create+finish it with an outcome like `skipped` (or mark lifecycle outcome `error` + add an attribute such as `app.start.service_bootstrap_missing=true`) and then close the lifecycle span.
- Optionally, add a max-duration timeout after `markAppInteractive` to force-close the lifecycle if `service-bootstrap` hasn’t finished within N seconds.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Deferred auth loses telemetry ✓ Resolved 🐞 Bug ◔ Observability
Description
startPhaseAt() returns an existing phase record even after it has finished, so later
startStartupPhase("authentication") calls cannot create a new authentication span. In the iOS
deferred-restore path (background/locked relaunch), the subsequent foreground retryBootstrap() run
will not be timed or attributed in startup telemetry.
Code

packages/mobile/lib/startup-telemetry.ts[R60-64]

+function startPhaseAt(phase: StartupPhase, startedAtMs: number): PhaseRecord {
+  const existing = phases.get(phase);
+  if (existing) {
+    return existing;
+  }
Relevance

●● Moderate

No close historical evidence on restarting finished startup telemetry phases; new file pattern,
acceptance unclear.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The coordinator never restarts finished phases, while auth bootstrap explicitly retries after
deferral; this combination makes the second (foreground) restore attempt invisible to startup phase
spans/logs.

packages/mobile/lib/startup-telemetry.ts[60-64]
packages/mobile/lib/startup-telemetry.ts[173-177]
packages/mobile/lib/auth-context.tsx[54-123]
packages/mobile/lib/auth-context.tsx[131-139]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Startup phases are stored in a map keyed by phase name, and `startPhaseAt()` always returns an existing record even if it is already `finished`. This prevents retries (notably the deferred SecureStore restore path) from being recorded, because the second attempt cannot start a new span and `finishStartupPhase()` becomes a no-op.

### Issue Context
`AuthProvider.retryBootstrap()` is invoked on mount and again when the app becomes active after a deferred background/locked restore. The second run is the one that actually gates `isLoading` and splash dismissal, but it cannot create a new `authentication` span due to the “exactly once” phase record.

### Fix Focus Areas
- packages/mobile/lib/startup-telemetry.ts[60-80]
- packages/mobile/lib/startup-telemetry.ts[173-202]
- packages/mobile/lib/auth-context.tsx[54-123]
- packages/mobile/lib/auth-context.tsx[131-139]

### Suggested implementation direction
- Update `startPhaseAt()` to treat `existing.finished === true` as restartable:
 - either delete+replace the record, or
 - store a list of attempts (e.g., `authentication#2`) and include an `attempt` attribute.
- Alternatively (if you want strict “single auth phase” semantics), do not `finishStartupPhase("authentication", "deferred")` on the background deferral path; keep the auth phase open and finish it only when the foreground retry completes (with an outcome indicating deferral occurred).
- Add/adjust tests to cover a deferred-then-foreground retry producing a second auth measurement (or a single continuous one, depending on chosen semantics).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. fallbackToCacheTimeout claim uncited ✓ Resolved 📘 Rule violation § Compliance
Description
The new TDD plan states Expo documents fallbackToCacheTimeout behavior and
Updates.launchDuration without an adjacent primary-source link. This violates the requirement to
cite third-party behavior claims in docs, making future audits harder.
Code

docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[R18-19]

+- `packages/mobile/app.json` sets `updates.checkAutomatically` to `ON_LOAD` and `updates.fallbackToCacheTimeout` to `5000`, allowing the native OTA procedure to hold launch for five seconds before using cached assets.
+- Expo documents `fallbackToCacheTimeout` as the launch-time wait before falling back to the newest local update and exposes `Updates.launchDuration` for the measured native launch time.
Relevance

●●● Strong

Docs third-party behavior claims require adjacent primary citations; similar citation-missing
findings were accepted repeatedly (PRs #2052, #2237, #2214).

PR-#2052
PR-#2237
PR-#2214

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The plan makes a behavioral claim about Expo Updates configuration and metrics in the "Current
Evidence" bullets, but does not include a primary-source link adjacent to the claim, as required by
the documentation citation rule.

Rule 1505719: Cite third-party behavior claims in docs with primary sources
docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[17-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The doc asserts third-party (Expo) behavior for `fallbackToCacheTimeout`/`Updates.launchDuration` without an adjacent primary-source citation.

## Issue Context
Compliance requires that third-party behavior claims in `docs/` and READMEs include nearby links to official sources.

## Fix Focus Areas
- docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md[17-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md Outdated
Comment thread docs/superpowers/plans/2026-07-29-ios-cold-start-spans.md Outdated
Comment thread packages/mobile/lib/startup-telemetry.ts
Comment thread packages/mobile/lib/startup-telemetry.ts
Start a fresh authentication child span when a locked-device restore defers and later retries in the foreground. Also remove agent-only plan text and place the Expo citation beside its claim.

Refs #2193
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc
Asherlc merged commit 98e4420 into main Jul 29, 2026
103 checks passed
@Asherlc
Asherlc deleted the issue-2193-ios-cold-start branch July 29, 2026 17:05
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.

[IOS-04] iOS cold start remained on splash ~7 seconds

2 participants