Skip to content

feat(mobile): gate app launch with native unlock - #5721

Merged
iscekic merged 10 commits into
mobile-context-unlock-a61f-s2from
mobile-context-unlock-a61f-s3
Sep 1, 2026
Merged

feat(mobile): gate app launch with native unlock#5721
iscekic merged 10 commits into
mobile-context-unlock-a61f-s2from
mobile-context-unlock-a61f-s3

Conversation

@iscekic

@iscekic iscekic commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preferences now includes “Unlock with biometrics” to turn app protection on or off on this device. Its help text explains unlocking at launch, after five background minutes, and with a device passcode as a fallback. The switch shows progress and prevents changes during an unlock or setting update.
  • When an unlock is required, the app hides protected screens and open sheets without discarding drafts or navigation. Hidden content does not accept taps or appear to screen readers.
  • The unlock screen shows loading progress, Retry, and feedback for unavailable device security or failures. Setting errors also appear beside the preference, with shared spoken feedback.
  • The app waits for your chosen language before requesting an unlock. The new setting label and help text support English and 86 other language options.
  • Open sheets retain working Done and Cancel controls after you unlock the app.

appUnlockScreenLayout now guards five authenticated Stacks, so nested screens and native sheets share the same unlock state. Organization and Security Agent screens retain their privacy cover, while navigation and shared draft providers stay mounted. Route registrations, sheet detents, and modal options remain unchanged.

Files
  • apps/mobile/src/app/(app)/_layout.tsx — Modified; +2/-0 lines. Applies the gate to authenticated screens, form sheets, and modals without moving connection, synchronization, cache, or chat providers.
  • apps/mobile/src/app/(app)/(tabs)/(1_kiloclaw)/_layout.tsx — Modified; +2/-1 lines. Gates the KiloClaw index and chat/instance-picker sheet.
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/organization/_layout.tsx — Modified; +5/-1 lines. Composes the unlock gate around the privacy cover for organization screens and invite-member, member-limit, and low-balance-alert sheets.
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/_layout.tsx — Modified; +5/-1 lines. Composes the unlock gate around the privacy cover for Security Agent screens and dismiss/filter sheets. The command observer stays outside the wrapper.
  • apps/mobile/src/app/(app)/pr-review/[owner]/[repo]/[number]/_layout.tsx — Modified; +2/-1 lines. Gates comment-composer, review-submit, merge, and file-navigator sheets while retaining the shared draft provider and per-user draft identity.

AppUnlockScene hides scenes in every state except unlocked, blocks touch, and excludes hidden descendants from accessibility without unmounting their drafts. Its unlocked wrapper stays layout-only to preserve native sheet headers and scroll views as siblings at indices 0 and 1. AppUnlockFeedback supplies inline messages, while AppUnlockAnnouncements centralizes outcome announcements so hidden scenes do not request iOS speech.

Files
  • apps/mobile/src/components/app-unlock-screen.tsx — Added; +135/-0 lines. Adds a scrollable accessibility-modal overlay with 24-point safe-area padding. Preference loading shows a labeled busy progressbar and skeleton; other hidden states show Retry with the shared busy state. Maps setup-required to agentChat.modelSelector.unavailable and bootstrap.couldNotLoadPrivacyDescription. Uses common.couldNotSaveSetting for save-failed and common.somethingWentWrong for failed, lockout, or preference-read errors; other outcomes show no message. Preference-read errors override outcome announcements; shared announcements also cover setting failures while the setting screen stays hidden. Per-scene status text uses a polite Android live region.

Shared languageReady now reaches AppRootProviders and RootLayoutNav; AppUnlockProvider.promptMessage accepts string | null to wait for translated prompts. AppUnlockContextValue.busy includes a null prompt, and coldStartPending survives blocked attempts so language readiness can trigger a guarded retry. String callers remain compatible, and missing preferences still mean disabled; the storage format, five-minute background threshold, and native passcode fallback stay unchanged.

Files
  • apps/mobile/src/app/_layout.tsx — Modified; +13/-7 lines. Moves language readiness into RootLayout and supplies RootLayoutNav with the flag and setter. Retains the English fallback, first-paint loading gate, and right-to-left reload handling.
  • apps/mobile/src/components/app-root-providers.tsx — Modified; +51/-36 lines. Requires languageReady and mounts one AppUnlockProvider beneath AuthProvider for children, organization state, action sheets, and overlays. Supplies the translated unlock label or null, and mounts shared announcements only after language readiness. Children remain mounted while language preparation runs.
  • apps/mobile/src/lib/app-unlock-context.tsx — Modified; +13/-7 lines. Captures the prompt for each authentication attempt and defers attempts while the prompt is null. Clears coldStartPending only after authentication guards pass; preference restoration and active returns retain pending work until then. Retries pending cold starts when the prompt changes, without bypassing mounted, active, idle, or unlock-state guards. Includes prompt readiness in busy state and context memoization.

Preferences connects an unlock switch to useAppUnlock.enabled and setEnabled; PreferenceRowProps.busy adds a spinner and busy accessibility state. preferences.biometricUnlock and preferences.biometricUnlockSubtitle explain launch protection, the five-minute return rule, device setup, and passcode fallback. The switch disables changes while busy or outside unlocked, and inline AppUnlockFeedback excludes ordinary unlock outcomes; existing rows default to busy=false.

Files
  • apps/mobile/src/components/preferences-screen.tsx — Modified; +25/-3 lines. Adds the shield-marked unlock row before existing preferences. Busy rows replace their icon with ActivityIndicator and expose disabled/busy accessibility states. Only setting outcomes reach inline feedback.
  • apps/mobile/src/i18n/locales/en.json — Modified; +2/-0 lines. Adds the authored “Unlock with biometrics” label and subtitle. The subtitle explains launch and background protection, device security setup, and passcode fallback; existing English text stays unchanged.

Tests: 3 files changed, with 773 additions and 18 removals. Added apps/mobile/src/components/app-unlock-screen.mounted.test.tsx (+312/-0) and apps/mobile/src/components/app-unlock-screen.test-helpers.tsx (+311/-0); modified apps/mobile/src/components/preferences-screen.mounted.test.tsx (+150/-18).
Generated: 86 translated catalogs updated in apps/mobile/src/i18n/locales/: 86 non-English locales, two new keys each, and 172 added entries (+258/-86 lines).


Verification

Local end-to-end verification on an iOS simulator, 2026-09-01. Seven of eleven checks passed. Four have partial coverage.

This section covers the stack's three PRs together: #5715, #5719, and #5721.

No product bug was found. No product source changed. The worktree stayed clean.

Check Result Note
4. Removed membership shows "Organization unavailable" and survives a cold launch PASS Only an explicit Personal selection clears it.
5. Picker keeps Personal available with no memberships PASS Cancel stays available.
6. A transcript shows its own context as static text PASS A label tap opens no picker; the global context survives.
7. Preferences documents launch, background, setup, and passcode fallback PASS Help text covers all four.
9. A cold launch gates content behind authentication PASS The gate hides the draft, the open sheet, and the tab bar.
10. Five background minutes require authentication again PASS Real background time was 305 seconds.
11. A successful match restores the draft and the open sheet PASS Code stayed selected; the draft text survived.
1. Context switches on Home and Agents PARTIAL Quick Chat is owner-descoped and was unreachable.
2. A saved organization restores before Personal at startup PARTIAL Local restore finished before the first visible frame, so the loading state never appeared.
3. An organization-list failure shows an error and recovers on Retry PARTIAL Backend failure verified. A local SecureStore failure has no fault seam.
8. The unlock switch guards against taps during authentication PARTIAL Verified during native authentication. The local write phase completed too fast to test.

Test-harness limits, not defects

  • Account restoration and selection saving use SecureStore. A backend lock cannot delay or fail a local keychain operation, so failed-save retention has no runtime verdict.
  • No simulator passcode was set, so the passcode fallback path was not exercised. Check 7 covers its help text only.
  • This Xcode rejects simctl biometric. Native simulator enrollment, match, and failure notifications were used instead.
  • Accessibility checks read the simulator accessibility tree. VoiceOver speech itself was not tested.

Setup

  • Real worktree stack and Metro, branch mobile-context-unlock-a61f-s3, simulator iOS 26.5.
  • Normal browser sign-in. Faults came from bounded PostgreSQL transactions on named tables.
  • Screenshots captured with xcrun simctl io <udid> screenshot. Full report and images: /Users/igor/Projects/.scratch/local-e2e-context/.

Comment thread apps/mobile/src/components/app-unlock-screen.tsx
@kilo-code-bot

kilo-code-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (3 files)
  • apps/mobile/src/components/app-unlock-screen.tsx
  • apps/mobile/src/components/app-unlock-screen.mounted.test.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
Previous Review Summaries (4 snapshots, latest commit 07105ed)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 07105ed)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (86 files)
  • apps/mobile/src/i18n/locales/af.json
  • apps/mobile/src/i18n/locales/am.json
  • apps/mobile/src/i18n/locales/ar.json
  • apps/mobile/src/i18n/locales/az.json
  • apps/mobile/src/i18n/locales/be.json
  • apps/mobile/src/i18n/locales/bg.json
  • apps/mobile/src/i18n/locales/bn.json
  • apps/mobile/src/i18n/locales/bs.json
  • apps/mobile/src/i18n/locales/ca.json
  • apps/mobile/src/i18n/locales/ckb.json
  • apps/mobile/src/i18n/locales/cs.json
  • apps/mobile/src/i18n/locales/cy.json
  • apps/mobile/src/i18n/locales/da.json
  • apps/mobile/src/i18n/locales/de.json
  • apps/mobile/src/i18n/locales/el.json
  • apps/mobile/src/i18n/locales/es.json
  • apps/mobile/src/i18n/locales/et.json
  • apps/mobile/src/i18n/locales/eu.json
  • apps/mobile/src/i18n/locales/fa.json
  • apps/mobile/src/i18n/locales/fi.json
  • apps/mobile/src/i18n/locales/fil.json
  • apps/mobile/src/i18n/locales/fr.json
  • apps/mobile/src/i18n/locales/ga.json
  • apps/mobile/src/i18n/locales/gl.json
  • apps/mobile/src/i18n/locales/gu.json
  • apps/mobile/src/i18n/locales/ha.json
  • apps/mobile/src/i18n/locales/he.json
  • apps/mobile/src/i18n/locales/hi.json
  • apps/mobile/src/i18n/locales/hr.json
  • apps/mobile/src/i18n/locales/ht.json
  • apps/mobile/src/i18n/locales/hu.json
  • apps/mobile/src/i18n/locales/hy.json
  • apps/mobile/src/i18n/locales/id.json
  • apps/mobile/src/i18n/locales/ig.json
  • apps/mobile/src/i18n/locales/is.json
  • apps/mobile/src/i18n/locales/it.json
  • apps/mobile/src/i18n/locales/ja.json
  • apps/mobile/src/i18n/locales/ka.json
  • apps/mobile/src/i18n/locales/kk.json
  • apps/mobile/src/i18n/locales/km.json
  • apps/mobile/src/i18n/locales/kn.json
  • apps/mobile/src/i18n/locales/ko.json
  • apps/mobile/src/i18n/locales/lo.json
  • apps/mobile/src/i18n/locales/lt.json
  • apps/mobile/src/i18n/locales/lv.json
  • apps/mobile/src/i18n/locales/mg.json
  • apps/mobile/src/i18n/locales/mi.json
  • apps/mobile/src/i18n/locales/mk.json
  • apps/mobile/src/i18n/locales/ml.json
  • apps/mobile/src/i18n/locales/mn.json
  • apps/mobile/src/i18n/locales/mr.json
  • apps/mobile/src/i18n/locales/ms.json
  • apps/mobile/src/i18n/locales/mt.json
  • apps/mobile/src/i18n/locales/my.json
  • apps/mobile/src/i18n/locales/nb.json
  • apps/mobile/src/i18n/locales/ne.json
  • apps/mobile/src/i18n/locales/nl.json
  • apps/mobile/src/i18n/locales/om.json
  • apps/mobile/src/i18n/locales/or.json
  • apps/mobile/src/i18n/locales/pa.json
  • apps/mobile/src/i18n/locales/pl.json
  • apps/mobile/src/i18n/locales/ps.json
  • apps/mobile/src/i18n/locales/pt-BR.json
  • apps/mobile/src/i18n/locales/pt.json
  • apps/mobile/src/i18n/locales/ro.json
  • apps/mobile/src/i18n/locales/ru.json
  • apps/mobile/src/i18n/locales/si.json
  • apps/mobile/src/i18n/locales/sk.json
  • apps/mobile/src/i18n/locales/sl.json
  • apps/mobile/src/i18n/locales/so.json
  • apps/mobile/src/i18n/locales/sq.json
  • apps/mobile/src/i18n/locales/sr.json
  • apps/mobile/src/i18n/locales/sv.json
  • apps/mobile/src/i18n/locales/sw.json
  • apps/mobile/src/i18n/locales/ta.json
  • apps/mobile/src/i18n/locales/te.json
  • apps/mobile/src/i18n/locales/th.json
  • apps/mobile/src/i18n/locales/tr.json
  • apps/mobile/src/i18n/locales/uk.json
  • apps/mobile/src/i18n/locales/ur.json
  • apps/mobile/src/i18n/locales/uz.json
  • apps/mobile/src/i18n/locales/vi.json
  • apps/mobile/src/i18n/locales/yo.json
  • apps/mobile/src/i18n/locales/zh-Hans.json
  • apps/mobile/src/i18n/locales/zh-Hant.json
  • apps/mobile/src/i18n/locales/zu.json

Previous review (commit c430162)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/components/app-unlock-screen.tsx 94 JS overlay cannot hide native Modal windows after lock
Files Reviewed (11 files)
  • apps/mobile/src/app/(app)/agent-chat/[session-id].mounted.test.tsx
  • apps/mobile/src/components/agents/session-detail-content.test.ts
  • apps/mobile/src/components/agents/session-list-screen.mounted.test.tsx
  • apps/mobile/src/components/app-unlock-screen.mounted.test.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
  • apps/mobile/src/components/context-control.mounted.test.tsx
  • apps/mobile/src/components/home/home-screen.mounted.test.tsx
  • apps/mobile/src/components/preferences-screen.mounted.test.tsx
  • apps/mobile/src/components/profile-credits-card.mounted.test.tsx
  • apps/mobile/src/components/quick-chat/quick-chat-screen.mounted.test.tsx
  • apps/mobile/src/lib/app-unlock-context.test-helpers.tsx

Fix these issues in Kilo Cloud

Previous review (commit d6e8da1)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/components/app-unlock-screen.tsx 94 JS overlay cannot hide native Modal windows after lock
Files Reviewed (14 files)
  • apps/mobile/src/app/(app)/(tabs)/(1_kiloclaw)/_layout.tsx
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/organization/_layout.tsx
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/_layout.tsx
  • apps/mobile/src/app/(app)/_layout.tsx
  • apps/mobile/src/app/(app)/pr-review/[owner]/[repo]/[number]/_layout.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/components/app-root-providers.tsx
  • apps/mobile/src/components/app-unlock-screen.mounted.test.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
  • apps/mobile/src/components/app-unlock-screen.tsx - 1 issue
  • apps/mobile/src/components/preferences-screen.mounted.test.tsx
  • apps/mobile/src/components/preferences-screen.tsx
  • apps/mobile/src/i18n/locales/en.json
  • apps/mobile/src/lib/app-unlock-context.tsx

Fix these issues in Kilo Cloud

Previous review (commit fe64126)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/components/app-unlock-screen.tsx 65 JS overlay cannot hide native Modal windows after lock
Files Reviewed (14 files)
  • apps/mobile/src/app/(app)/(tabs)/(1_kiloclaw)/_layout.tsx
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/organization/_layout.tsx
  • apps/mobile/src/app/(app)/(tabs)/(3_profile)/security-agent/[scope]/_layout.tsx
  • apps/mobile/src/app/(app)/_layout.tsx
  • apps/mobile/src/app/(app)/pr-review/[owner]/[repo]/[number]/_layout.tsx
  • apps/mobile/src/app/_layout.tsx
  • apps/mobile/src/components/app-root-providers.tsx
  • apps/mobile/src/components/app-unlock-screen.mounted.test.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
  • apps/mobile/src/components/app-unlock-screen.tsx - 1 issue
  • apps/mobile/src/components/preferences-screen.mounted.test.tsx
  • apps/mobile/src/components/preferences-screen.tsx
  • apps/mobile/src/i18n/locales/en.json
  • apps/mobile/src/lib/app-unlock-context.tsx

Fix these issues in Kilo Cloud


Reviewed by grok-4.6 · Input: 214.9K · Output: 16.8K · Cached: 876.7K

Review guidance: REVIEW.md from base branch mobile-context-unlock-a61f-s2

@iscekic iscekic self-assigned this Aug 29, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants