fix(mobile): bind restored context and drafts to account scope - #5651
fix(mobile): bind restored context and drafts to account scope#5651iscekic wants to merge 1 commit into
Conversation
| }; | ||
| void load(); | ||
| }, | ||
| [token, isSigningOut, userId, owner, ownerKey] |
There was a problem hiding this comment.
WARNING: Token refresh recreates resolve and drops context readiness
token is in resolve's dependency list, so setToken on refresh (401 recovery in auth-context.tsx or foreground rotation) rebuilds resolve. The restore effect cleanup then increments generation, which aborts an in-flight select (and overwrites lastRequest with restore) and publishes unresolved. That drops isReady/isLoaded, calls setLocalAccessContextReady(false), and bumps selectionGeneration so admitted draft writes lose their grant.
Refresh is not account replacement; ownerKey already covers sign-in/out. trpcClient reads the rotated token from the token owner, so the bearer string does not need to be an effect input.
| [token, isSigningOut, userId, owner, ownerKey] | |
| [isSigningOut, userId, owner, ownerKey] |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 1 Issue Found | Recommendation: Address before merge Executive SummaryToken refresh rebuilds organization-context Overview
Issue Details (click to expand)WARNING
Files Reviewed (24 files)
Fix these issues in Kilo Cloud Reviewed by grok-4.6 · Input: 130.1K · Output: 21.8K · Cached: 935.7K Review guidance: REVIEW.md from base branch |
Summary
AuthenticatedOwnerbinds a proved account to an immutable authentication epoch and generation, whichuseCurrentUserIdreturns alongside matching account fields.confirmAuthenticatedOwneraccepts only a currentuser.getMeresponse; cached identities and canceled or superseded requests cannot authorize content.beginAuthenticatedOwnerrevokes that proof during sign-in and sign-out, whilecaptureAccountGenerationguards queued work without changing token-refresh behavior.Files
apps/mobile/src/lib/context-scope.ts— Source; A (added), 130 changed lines. Adds observable ownership, generation checks, strict Personal/organization tags, context comparisons, and version-one selection records. Encodes account keys without collisions and separates missing, malformed, and wrong-owner records. Classifies FORBIDDEN, NOT_FOUND, and UNAUTHORIZED as unavailable access.apps/mobile/src/lib/hooks/use-current-user-id.ts— Source; M (modified), 46 changed lines. Restricts queries to active authentication, forwards cancellation, and preserves the existing query key. Fetches proof when another observer supplies unproved data, exposes the owner, and withholds mismatched identity fields.apps/mobile/src/lib/context-scope.test.ts— Test; A (added), 83 changed lines. Adds the context-scope test suite.AuthProvidercloses content access and removes account-bound state before publishing replacement credentials; sign-out captures identity before revoking ownership.writeAccountMetadataadds generation and optionalisCurrentchecks without changing existing calls, whilerunLogoutCleanup(userId)requires the captured identity or null.initializeLocalAccessbinds secure storage, the authentication prompt, owner changes, and app lifecycle events without exposing biometric controls.Files
apps/mobile/src/lib/auth/auth-context.tsx— Source; M (modified), 109 changed lines. Closes sign-in admission, cancels queries, and clears metadata, preferences, session media, review history, consent outcomes, and telemetry identity before credential publication. Deletes the previous session's read cache during direct sign-in and rejects stale legacy exchange results. Keeps token-derived identity limited to navigation, captures the logout owner, retains selection records, and attempts independent cleanup despite individual failures. Connects local access to secure storage, ownership, lifecycle events, and a translated prompt.apps/mobile/src/lib/auth/account-metadata-write.ts— Source; M (modified), 14 changed lines. Checks the captured generation and optional caller guard inside each serialized write. Preserves unconditional, ordered deletion and the existing convenience setter.apps/mobile/src/lib/auth/logout-cleanup.ts— Source; M (modified), 11 changed lines. Takes the proved logout identity from the caller instead of querying cached identity. Uses it for failed push-unregister records and preserves the nonthrowing cleanup contract.apps/mobile/src/lib/auth/auth-context.test.tsx— Test; M (modified), 285 changed lines. Updates tests for authentication transitions.apps/mobile/src/lib/auth/logout-cleanup.test.ts— Test; M (modified), 60 changed lines. Adapts logout tests to the explicit identity argument.contextScopeSchemaandselectedContextSchemadefine account-ownedContextScoperecords underSELECTED_CONTEXT_KEY_PREFIX;SelectedContextResultdistinguishes absent, malformed, and wrong-owner data.ContextStateandOrganizationContextValueseparate readiness from failure;isLoadedequalsisReady, so nullorganizationIdidentifies Personal only after readiness.organizations.listverifies access andpermissionFailureSchemaclassifies denial; sign-out preserves choices, butORGANIZATION_STORAGE_KEYnow requires explicit selection.Files
apps/mobile/src/lib/organization-context.tsx— Source; M (modified), 245 changed lines. Orders selection reads and writes, rejects superseded results, and waits for live membership checks and successful persistence before declaring readiness. Exposes the owner, selection generation, legacy candidate, deliberate selection, and retry actions. Distinguishes identity, storage, membership, and write failures without discarding saved choices. Selects Personal by default only when account-owned and legacy selections are absent. Updates local-access readiness and retries identity or the last failed request.apps/mobile/src/lib/organization-context.mounted.test.tsx— Test; A (added), 393 changed lines. Adds the mounted organization-provider test suite.restorePersistedCacheForOwnerandcreateReadCachePersisterrequire current server-proved ownership and matching snapshot identity, then exclude stored identity, membership, and mutations from restoration.CachePersistenceMountrestores before subscription, whilerestorePersistedCacheOnColdStart,takeOverColdStartRestore, andACTIVE_USER_ID_KEYretain hints without hydrating or clearing current queries.readCachedUserIdrejects unproved identities, andclearCacheScopeForSignOutchecks the generation while restricting known-account deletion to numeric schema versions.Files
apps/mobile/src/lib/persist/read-cache.ts— Source; M (modified), 184 changed lines. Filters restored snapshots to successful allowed content and guards reads, writes, and removals against owner changes. Accepts serialized default session-list keys with omitted undefined fields without relaxing the live allowlist. Retains schema version 1, the 2,097,152-byte limit, and 24-hour expiry. Contains restore failures so live queries continue; unknown-owner cleanup clears cache scopes but leaves drafts intact.apps/mobile/src/lib/persist/cache-persistence-mount.tsx— Source; M (modified), 132 changed lines. Writes the identity hint, completes owner-checked restoration, and then starts persistence. Stops stale work after unmount or ownership changes and prevents conflicting hints from clearing the current query client. Removes the previous account's read cache when the account changes.apps/mobile/src/lib/persist/read-cache.test.ts— Test; M (modified), 338 changed lines. Updates the read-cache test suite.apps/mobile/src/lib/persist/cache-persistence-mount.test.ts— Test; M (modified), 614 changed lines. Updates the cache-persistence mount tests.EncryptedStoreRecoveryandencryptedStoreRecoveryclassify open failures without deleting stored data or replacing its key, retiring automatic reset recovery.retryEncryptedKvOpenretries a failed open explicitly;setItem,removeItem, andclearScopepreserve old calls and add optional checks immediately before mutation.clearScopePrefixuses literal prefixes, an optionalisCurrentguard, andnumericSuffixOnlyto restrict cleanup to one account's numeric cache versions.Files
apps/mobile/src/lib/persist/encrypted-kv.ts— Source; M (modified), 267 changed lines. Returns retry guidance for SecureStore and input/output failures, and protected outcomes for malformed keys, corruption, missing SQLCipher, or unknown errors. Supplies key restoration, database repair, native rebuild, and non-reset retry guidance without deleting data. Preserves empty or malformed keys and closes failed handles without resetting the database. Reuses a pending generated key after storage failure; concurrent opens share one attempt, and successful handles stay open. Keeps failures until explicit retry, reports sanitized errors instead of native SQL text, and checks mutation guards before and after opening. Replaces wildcard deletion with literal prefix matching and optional numeric suffix validation.apps/mobile/src/lib/persist/encrypted-kv.test.ts— Test; M (modified), 224 changed lines. Updates the encrypted-store test suite.DraftLoadResultandloadDraftResultdistinguish absence, malformed data, and read failure; legacyloadDraftstill supplies nullable results.DraftWriteOptionscontrolssaveDraftConfirmed, which returnsDraftWriteResult;saveDraft,flushDraft,clearDraft, andclearDraftIfStillreject work from superseded account generations.parseStoredDraftandserializeStoredDraftcentralize bounded formats, preserving old imports and stored values; tagged writes require owner guards and cannot evict legacy candidates.Files
apps/mobile/src/lib/persist/drafts.ts— Source; M (modified), 621 changed lines. Uses collision-free tuple queues and captures each write's owner, key, generation, and guard. Adds committed, failed, stale, and conflict outcomes with expected-byte comparisons for confirmed saves. Protects pending edits and unchanged source bytes during conditional removal, preserves malformed records, and contains storage errors. Retains account scopes, sign-out retention, the 500-millisecond debounce, the 65,536-byte limit, and the 100-entry cap. Tagged writes count only tagged entries for eviction; legacy exports remain available.apps/mobile/src/lib/storage-keys.ts— Source; M (modified), 131 changed lines. Moves composer, review, merge, reply, comment, share, search, security, and prefill contracts without changing their serialized forms. Adds bounded parsing and serialization with content-free parse errors, plus the account selection prefix and the legacy recovery marker.apps/mobile/src/lib/persist/drafts.test.ts— Test; M (modified), 103 changed lines. Updates the draft persistence tests.DraftTarget,draftTargetSchema, andscopedKeySchemaletscopedDraftKeyandparseScopedDraftKeydistinguish contexts and targets underSCOPED_DRAFT_KEY_PREFIX; existing producers retain their keys.listLegacyDraftCandidatesreturnsLegacyDraftCandidatemetadata only;migrateLegacyDraftrequiresMigrationInput.selection: 'explicit', checks session ownership throughcliSessionsV2.get, and returnsDraftMigrationResult.Migration preserves conflicting or unreadable destinations and removes unchanged source bytes only after a confirmed destination write or a matching existing value.
Files
apps/mobile/src/lib/persist/scoped-draft-keys.ts— Source; A (added), 185 changed lines. Adds strict new-session, session, search, and quick-chat targets within the existing account scopes. Discovers legacy names and timestamps without displaying draft content. Validates exact candidates and remote session account, session, and organization matches. Resolves ambiguous Personal and new-session names through explicit selection, protects destination content, and conditionally removes the source after destination confirmation. Adds migration helpers without moving existing draft producers to the new keys.apps/mobile/src/lib/persist/scoped-draft-keys.test.ts— Test; A (added), 244 changed lines. Adds the scoped-key and migration test suite.useScopedDraftLoadconsumesScopedDraftInputand reportsScopedDraftState; only a ready selection with a present or absent result permits new persistence actions.useFencedDraftLoadadds epoch checks;UseRemoteSpawnDraftCleanupInputextendsuseRemoteSpawnDraftCleanupwith optional keys and guards, retaining clear-after-attempt and flush-without-attempt behavior.Concurrent retries share work; prefills cannot authorize writes, and accepted saves remain bound to their original owner and key after selection changes.
Files
apps/mobile/src/lib/persist/use-draft-load.ts— Source; M (modified), 337 changed lines. Adds unresolved, present, absent, malformed, and failed restoration states with recovery, retry, save, flush, clear, and explicit import actions. Invalidates late loads and new actions after owner, selection, retry, or unmount changes while preserving already accepted saves at their captured destination. Keeps nullable legacy restoration and the default new-session key. Cleanup retains the captured identity and still clears after any spawn attempt, including failure; leaving without an attempt flushes the draft.apps/mobile/src/lib/persist/use-draft-load.test.ts— Test; M (modified), 82 changed lines. Updates the legacy draft-hook tests.apps/mobile/src/lib/persist/scoped-draft-load.mounted.test.tsx— Test; A (added), 471 changed lines. Adds the mounted scoped-draft hook tests.Tests: 11 changed files (4 added, 7 modified), totaling 2,897 changed lines:
auth-context.test.tsx,logout-cleanup.test.ts,context-scope.test.ts,organization-context.mounted.test.tsx,read-cache.test.ts,cache-persistence-mount.test.ts,encrypted-kv.test.ts,drafts.test.ts,scoped-draft-keys.test.ts,use-draft-load.test.ts,scoped-draft-load.mounted.test.tsx.Generated: none (0 files).
Verification
bot-e2e; the requesting human has no device-testing task.Visual Changes
Visual Changes: N/A
Reviewer Notes
Human steps
Supplied check results
Scope
Kilo-Org/cloud./Users/igor/Projects/.worktrees/mobile-context-lock-758a.mobile-context-lock-758a-s2; base:mobile-context-lock-758a.47c4d521001589b56a81c106e0125e16d08918ca.Notes
This level adds ownership and persistence boundaries without exposing biometric controls. Full iOS and Android verification runs on the final stack level.
Stacked PRs — merge bottom to top. Each level shows only its own diff.
Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.
mobile-context-lock-758a— feat(mobile): add account-owned local access state #5642mobile-context-lock-758a-s2— fix(mobile): bind restored context and drafts to account scope #5651 ← this PRmobile-context-lock-758a-s3— fix(mobile): enforce local access at final transport dispatch #5658mobile-context-lock-758a-s4— fix(mobile): bind Kilo Chat effects to local access #5664mobile-context-lock-758a-s5— feat(mobile): protect native privacy and accessibility boundaries #5683 (tip)