Skip to content

feat(mobile): show the active organization where sessions start - #8083

Merged
iscekic merged 11 commits into
mainfrom
drain/org-context-screens
Oct 21, 2026
Merged

iscekic merged 11 commits into
mainfrom
drain/org-context-screens

Conversation

@iscekic

@iscekic iscekic commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

bot:

Change

Members of several organizations could not see which account a new session would belong to: only Profile > Credits named it. This PR surfaces the active account (organization or Personal) at both places sessions start:

  • Home: the switchable account control (ContextControl) now sits in the header's context slot under the greeting. It opens the existing account picker sheet and updates in place.
  • New session: a labeled "Organization" section above "Run on" shows the active account. Picking another account switches the global account (the same persistence Profile uses) and reopens the form with the new organizationId through the stack-safe replace, so org-scoped data (repositories, models, profiles) and the create attribution follow, and the entry's own route params are carried through. The durable text draft is flushed before the replace, so a switch inside the debounce window keeps the typed prompt. The Continue/clone entry renders the same section as a read-only row locked to the source session's organization, so a clone names the account it belongs to without offering a switch.
  • One account for the whole form: the section, the model catalog, the repositories, the profiles, the attachments and the create read a single resolved account — an explicit organizationId in the route, otherwise the active account (useNewSessionOrganization). Entries that carry no organization (the tour hand-off, the launcher "New Agent" shortcut) therefore start in the account Home shows, and the section cannot name an account the create does not use. A Continue/clone entry keeps its own source scope, so a personal clone never inherits the active organization.

Reuse over new code: ContextControl/useContextPicker gain an optional onSelectionChange callback; no new copy keys (reuses common.organization, common.personal, profile.selectAccount).

Verification

  • pnpm --filter kilo-app run typecheck, lint and format:check clean at HEAD; the full mobile suite passes (1192 files / 17,888 tests), including new tests for the account resolver, for a control that shows an account other than the active one, and for the route gate.
  • The form mounts only once its account is known. A failed account restore, or an account-list failure where the active account is what decides the scope, shows the error with a retry instead of mounting a form that would fetch and create in a fallback Personal scope; entries settled by their own route (an explicit organization, a clone) mount through the same failure.
  • Android end-to-end on the emulator against the local development stack, fake account in two organizations:
    • Home header names the active account; the picker lists Personal and both organizations and switches the header in place.
    • New session shows the Organization section above Run on. Typing a prompt, switching organization and returning to the form keeps the prompt, while the section and the form data follow the new account.
    • The launcher "New Agent" entry, which carries no organization, opens scoped to the active account and names it in the section; switching that entry to Personal moves the whole form to Personal.
    • Continue/clone entry shows the Organization section as a read-only row naming the source organization; a source without one stays Personal while the active account is an organization, and tapping the row opens no picker.
    • Arabic (RTL): the header control, picker rows, the new-session section and the clone row all mirror to the trailing edge and stay readable.
  • Visual proof: feat(mobile): show the active organization where sessions start #8083 (comment) and feat(mobile): show the active organization where sessions start #8083 (comment).

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: Where should the organization control live on Home?
    Answer: ScreenHeader context slot (below title)
    Confidence: 0.65 · Precedent: Speed and least work: 'do the least amount of work possible, with no drop in quality' (7f3470e0ba6f); UX: 'CLS is a massive enemy' (00eb2a1d3e4b). The documented slot avoids width plumbing next to the title.
  2. Question: On the new-session screen, how should switching behave?
    Answer: Switch globally + replace route (Recommended)
    Confidence: 0.75 · Precedent: UX: keep similar elements consistent across surfaces, 'the thought row match the tool call row' (a247f3c19b1e). Use the same global semantics as Profile so the Home attribution stays correct.
  3. Question: Which row style on the new-session screen?
    Answer: Section like Environment (Recommended)
    Confidence: 0.75 · Precedent: UX: keep similar elements consistent across surfaces (a247f3c19b1e), so it matches the form's other pickers.
  4. Question: Should the org row be read-only on the Continue/clone entry?
    Answer: Lock (Recommended)
    Confidence: 0.65 · Precedent: Autonomy: make executive decisions with the 'is this reasonable?' test (7f3470e0ba6f). Locking keeps clone attribution correct.
  5. Question: Scope: On the tour hand-off and the widget "New Agent" shortcut (no organizationId in the route), which way should the new-session screen resolve the account?
    Answer: Inherit the active account (Recommended)
    Confidence: 0.72 · Precedent: Fix the root cause and the class on every affected surface: session 75f8833fe8c1, "fix this for all shared services model pickers on all surfaces (web, app)"; keep similar elements consistent across surfaces: session a247f3c19b1e

Home gets the switchable account control in the header's context slot,
and the new-session screen gets an Organization section above Run on.
Switching updates the global account (as Profile does) and reopens the
form with the new organization id through the stack-safe replace, so the
org-scoped picks and the create attribution follow; the durable text
draft survives. The clone entry stays locked to its source organization.
Reuse: ContextControl and the context picker sheet, no new copy keys.
@iscekic iscekic added drain Work item from the findings drain explorer:android Finding surfaced by the Android explorer labels Oct 19, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change closes the previous gap: a failed account list now keeps the active-account new-session form gated behind the retry, while route-scoped and clone entries still mount from their own scope. No new issues found.

Files Reviewed (3 files)
  • apps/mobile/src/app/(app)/agent-chat/new.tsx
  • apps/mobile/src/components/agents/use-new-session-organization.ts
  • apps/mobile/src/app/(app)/agent-chat/new.mounted.test.tsx
Previous Review Summaries (9 snapshots, latest commit ae3a2d1)

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

Previous review (commit ae3a2d1)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The retry now covers a failed secure-store restore, but a failed organization-list fetch still mounts the form scoped to Personal with no error or retry on this screen.

Overview

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

WARNING

File Line Issue
apps/mobile/src/app/(app)/agent-chat/new.tsx 26 The retry renders only while isResolved is false, but a failed organizations.list publishes isLoaded: true with error: 'restore'. The form then mounts as Personal with the error hidden (the section passes value, which nulls ContextControl's error) and the account picker disabled because the list is unavailable.
Files Reviewed (2 files)
  • apps/mobile/src/app/(app)/agent-chat/new.tsx - 1 issue
  • apps/mobile/src/app/(app)/agent-chat/new.mounted.test.tsx - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit a856f28)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The new account gate closes the previous Personal-scope window, but a failed organization restore leaves the new-session screen on a spinner with no retry or fallback.

Overview

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

WARNING

File Line Issue
apps/mobile/src/app/(app)/agent-chat/new.tsx 17 The gate never exits when the organization restore read fails (isLoaded: false, error: 'restore'), so the screen is a spinner with no retry; it also waits on the organizations list fetch when no stored choice exists.
Files Reviewed (3 files)
  • apps/mobile/src/app/(app)/agent-chat/new.tsx - 1 issue
  • apps/mobile/src/app/(app)/agent-chat/new.mounted.test.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-screen-body.tsx - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit 4240120)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The new account resolver makes the active account load-bearing for the new-session screen, but the unresolved state is silently treated as Personal by every org-scoped read and by session creation, which can latch a Personal model or create in the wrong account on a cold start.

Overview

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

WARNING

File Line Issue
apps/mobile/src/components/agents/new-session-screen-body.tsx 150 organizationResolved only gates the Organization section; the model provider, org-scoped queries and the create treat the unresolved organizationId: undefined as Personal, and Start is not blocked while it is false.
Files Reviewed (7 files)
  • apps/mobile/src/app/(app)/agent-chat/new.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-organization.ts - 0 issues
  • apps/mobile/src/components/agents/new-session-organization.test.ts - 0 issues
  • apps/mobile/src/components/agents/use-new-session-organization.ts - 0 issues
  • apps/mobile/src/components/agents/new-session-screen-body.tsx - 1 issue
  • apps/mobile/src/components/context-control.tsx - 0 issues
  • apps/mobile/src/components/context-control.mounted.test.tsx - 0 issues

Previous review (commit 6fe6289)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only change since the previous review is a documentation comment in useContextPicker describing onSelectionChange; it accurately matches the guarded select logic at HEAD, and the previously reported account-switch defects remain fixed.

Files Reviewed (1 file)
  • apps/mobile/src/components/context-control.tsx - incremental doc comment; accurate, no issue

Previous review (commit e7c14d8)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit only removes the dev-server-generated Next.js rules block from apps/ai-gateway/AGENTS.md, restoring the base version so the net PR diff is unchanged; the previously reported account-switch defects and the prop-doc issue remain fixed at HEAD.

Files Reviewed (4 files)
  • apps/ai-gateway/AGENTS.md - incremental change; content matches base, no issue
  • apps/mobile/src/components/agents/new-session-screen-body.tsx - targeted re-check; params carried through, still fixed
  • apps/mobile/src/components/context-control.tsx - targeted re-check; no-op re-selection guarded, still fixed
  • apps/mobile/src/components/agents/new-session-configure-form.tsx - targeted re-check; prop doc current, still fixed

Previous review (commit 033a4c9)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit only corrects the organizationSection prop doc to match the clone-entry read-only row; the described behavior matches the current body, and the previously reported account-switch defects remain fixed at HEAD.

Files Reviewed (1 file)
  • apps/mobile/src/components/agents/new-session-configure-form.tsx

Previous review (commit 9f3b19b)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1

Fix these issues in Kilo Cloud

Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/mobile/src/components/agents/new-session-configure-form.tsx 58 The prop doc still says undefined hides the section for the clone entry, but the clone entry now passes a read-only section.
Files Reviewed (3 files)
  • apps/mobile/src/components/agents/new-session-screen-body.tsx
  • apps/mobile/src/components/agents/new-session-configure-form.tsx - 1 issue
  • apps/mobile/src/components/context-control.tsx

Previous review (commit 63bd6ee)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Both previously reported account-switch defects are fixed: the entry's existing search params are carried through the remount and re-picking the active account is now a no-op. The incremental changes introduce no new issues.

Files Reviewed (3 files)
  • apps/ai-gateway/AGENTS.md
  • apps/mobile/src/components/agents/new-session-screen-body.tsx
  • apps/mobile/src/components/context-control.tsx

Previous review (commit eb0a0c5)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

The new account switch on the new-session screen rewrites the route with only organizationId, dropping share, prefill, to-do and tour params and silently remounting on a no-op re-selection.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0

Fix Link

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/components/agents/new-session-screen-body.tsx 213 Account switch href drops shareId, prefill params, todoId, and preselectRunOn, so staged share files, to-do linking, prefills and the tour's run-on choice are lost.
apps/mobile/src/components/context-control.tsx 64 Picking the already-active account still invokes onSelectionChange, causing the new-session form to remount and discard session state for a no-op selection.
Files Reviewed (6 files)
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts - 0 issues
  • apps/mobile/src/components/agents/new-session-configure-form.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-screen-body.tsx - 1 issue
  • apps/mobile/src/components/context-control.tsx - 1 issue
  • apps/mobile/src/components/home/home-screen.mounted.test.tsx - 0 issues
  • apps/mobile/src/components/home/home-screen.tsx - 0 issues

Reviewed by deepseek-v4.1-flash-1017 · Input: 55.7K · Output: 8.1K · Cached: 405.4K

Review guidance: REVIEW.md from base branch main

@iscekic

iscekic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Android end-to-end run on the emulator against the local development stack, using owned non-production test data.

Home header shows the active workspace and switches it in place:

  1. Home with the test organization active: the workspace control sits under the greeting. # 1. Home: active organization in the header
  2. The header picker sheet listing both options, check on the active row. # 2. Home: picker sheet
  3. After choosing the other option the header label updates immediately. # 3. Home: switched option

New session names the workspace the create will be attributed to:

  1. Organization section above Run on, showing the current option. # 4. New session: Organization section
  2. After picking the test organization the form remounts scoped to it. # 5. New session: switched organization

Arabic RTL pass:

  1. Header control mirrors to the trailing edge. # 6. RTL Home header
  2. New session Organization section mirrored. # 7. RTL new session
  3. Picker rows mirrored, check on the active row. # 8. RTL picker sheet

cap-01

cap-02

cap-03

cap-04

cap-05

cap-06

cap-07

cap-08

Switching the account from the new-session Organization section now
carries the entry's own route params (share prefill, clone/fork ids,
run-on preselect) into the replaced route, so the remount reopens the
same entry instead of a bare one. Re-picking the already-active account
in the picker no longer fires the selection callback, so the screen
does not remount for a no-op.
…ush the draft before an account switch

The Continue/clone entry now renders the account as a read-only
ContextControl scope, so a clone still names the organization it
belongs to. Switching the account on the ordinary entry flushes the
pending debounced draft write before the stack-safe replace, so the
remount's draft load reads the newest prompt text inside the debounce
window.
@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 19, 2026
The branch picked up the agent-rules block that `next dev` writes into apps/ai-gateway/AGENTS.md. Restore the file so the change stays limited to the mobile screens that show the active account.
@iscekic

iscekic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Android emulator run against the local development stack, with an owned fake account in two test organizations (test fixtures only).

What these captures show, in order:

  1. Home names the active account in the header, with the picker one tap away.
  2. The picker lists Personal and both test organizations, checked on the active row.
  3. New session shows the Organization section above Run on.
  4. After typing a prompt and switching account, the form is scoped to the new account and the prompt is still in the composer.
  5. Continue/clone entry shows the source organization as a read-only row: no chevron, and tapping it opens no picker.
  6. Arabic RTL: the Home header control mirrors to the trailing edge.
  7. Arabic RTL: the new-session Organization section mirrors.
  8. Arabic RTL: the clone row mirrors and stays read-only.

The captured rows name the test organizations "Northwind Labs" and "Cedar Works", and the clone entry uses the fixture title "Fixture source".

Home header names the active test account
Account picker listing Personal and two test organizations
New session Organization section above Run on
Prompt kept after switching account in the new-session form
Clone entry showing the source organization as a read-only row
RTL Home header control mirrored
RTL new-session Organization section mirrored
RTL clone entry read-only organization row

Home header with the active account control

Account picker sheet with the account options

New session form with the organization section

New session form after switching the account

Continue entry with the read-only organization row

RTL Home header control

RTL new session organization section

RTL Continue entry organization row

The picker hook calls its change callback only for a picked account that differs from the active one. Say so, so a caller that reopens its screen can rely on a re-pick of the current account being a no-op.
The form, the model catalog, the repositories, the profiles, the attachments and the create read one resolved account: an explicit route organization first, then the active account. Entries that carry no organization (the tour hand-off, the launcher New Agent shortcut) therefore start in the account Home shows instead of a personal scope. A clone entry keeps its own source scope, and an explicit personal clone never inherits the active account. The Organization section shows that same value, so the account it names is the account the create uses.
@iscekic

iscekic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Follow-up verification of the account resolution, on the Android emulator against the local development stack, fake account in two test organizations.

  1. The launcher "New Agent" entry carries no organization in its route. The form now opens scoped to the active account and the Organization section names it, instead of showing a personal scope.
  2. A Continue entry for a personal source, opened while the active account is an organization, keeps its own scope: the read-only row says Personal, so a clone is never moved into the organization.

The section, the model catalog, the repositories, the profiles, the attachments and the create read the same resolved account, so the row cannot name an account the session is not created in.

Launcher new-session entry scoped to the active organization
Personal clone entry keeping its own read-only scope

Launcher new-session entry scoped to the active organization

Personal clone entry keeping its own read-only scope

The form, the model catalog, the repositories, the profiles and the create all take the resolved account, and an unresolved value reads as Personal. Mount them only after the account settles, so a launcher or tour entry cannot fetch a personal catalog, latch a personal model, or start a personal session before the active account arrives. Resolving is a stored-choice read, so the wait is a frame in the common case; an offline first run settles to Personal through the same fallbacks the account context already uses.
…tored

A failed restore leaves the account unresolved, so the gate would otherwise show a spinner with no way forward. Show the failure with a retry that re-runs the account restore, and say in the gate comment that a stored choice resolves from storage while an absent one waits on the account list.
A failed account list publishes Personal for the whole app so it stays usable. When the active account is what decides this screen, that fallback is not the user's choice, so keep the form gated behind the retry instead of creating in it. A route-scoped entry and a clone entry know their account without the list, so they still mount.
@iscekic
iscekic added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 0d18f25 Oct 21, 2026
105 checks passed
@iscekic
iscekic deleted the drain/org-context-screens branch October 21, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain explorer:android Finding surfaced by the Android explorer merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants