Skip to content

fix(mobile): explain an expired organization plan instead of a server error - #8790

Merged
iscekic merged 7 commits into
mainfrom
drain/mobile-organization-error-copy
Oct 24, 2026
Merged

iscekic merged 7 commits into
mainfrom
drain/mobile-organization-error-copy

Conversation

@iscekic

@iscekic iscekic commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Evidence

Evidence: demonstrated — on the local dev stack with a fixture organization whose trial is hard-expired (no seat purchase, require_seats on), app language German, the Android dev build showed the defect and then the fix: starting a session with a repository rendered the generic internal line before the change and the localized plan-ended copy after it, and saving a member daily limit rendered the raw English server sentence before and the German copy after. The server answered 403 FORBIDDEN for organizations.cloudAgentNext.prepareSession and organizations.members.update in both runs (dev/logs/nextjs.log). Both scenarios were re-run unchanged on the head after merging main.

What changed

An organization whose trial passed its enforcement window is refused by requireActiveSubscriptionOrTrial (packages/web-shared/src/lib/organizations/trial-middleware.ts) on every organization mutation. The app did not recognize that rejection:

  • session start classified it as an unknown server error (cloudPrepareFailureMessage returned queryError.serverDescription);
  • the member daily limit sheet rendered mutations.updateMember.error.message as is, so a German reader saw Organization trial has expired. — an untranslated server sentence.

Both are the same class: an organization surface showing server text instead of localized copy.

  • New apps/mobile/src/lib/server-error-copy.ts owns the exact server sentences the app can explain and the localized copy for each, and moves the two constants cloud-prepare-error-display.ts already held. knownServerErrorMessage maps those sentences; organizationErrorMessage adds the localized generic line for anything else.
  • cloudPrepareFailureMessage consults the map first, so a lapsed plan keeps its actionable copy. isCloudPrepareRetryableError already treats FORBIDDEN as terminal, so the composer shows no retry control for it (covered by mobile-session-manager.test.ts).
  • Every organization surface that showed a mutation rejection now reads the map: member limit sheet, invite sheet, the shared useOrganizationMutations toast, the invitation resend toast, the low balance alert save, and the organization rename dialog (RenameModal gained an optional errorCopy prop; the org hub passes the organization mapper, the session/chat callers keep the current behaviour).
  • The member limit and invite sheets now own a per-code copy helper (saveErrorCopy, inviteErrorCopy); each consults the shared sentence map first, so a rejection the app can name keeps its own line and only an unnamed one falls back to the sheet's generic line. The resend toast does the same, with the missing-invitation sentence resolving to the existing not-found copy.
  • The map covers every user-facing sentence these procedures throw, not only the trial gate: owner-only authority (organization.members.ownerAuthority), the three invitation resend refusals (inviteExpired, inviteAccepted, inviteNotResendable), a missing invitation (the existing not-found copy), an address that is already a member, an outstanding pending invitation, and a self role change.
  • withUiDeadline now marks its own rejection, so the rename dialog keeps the app's localized deadline line instead of running it through the caller's server-error mapper.
  • New English copy: common.organizationTrialEnded ("The organization's trial has ended and it has no active plan. An organization owner can update billing at kilo.ai, then try again."), organization.inviteMember.alreadyMember, organization.inviteMember.pendingInvitation, organization.members.ownRoleChange, organization.members.ownerAuthority, organization.members.inviteExpired, organization.members.inviteAccepted, organization.members.inviteNotResendable. All 86 non-English catalogs carry the same keys translated (one batch per locale, sibling strings passed as context), verified by check:i18n and check:plurals.

The message is text only: the app has no in-app organization billing or plan screen, and the organization hub already links out to kilo.ai for billing.

Verification

  • pnpm --filter kilo-app typecheck, lint, check:i18n, check:plurals — clean on the merged head.
  • Vitest: affected suites (server-error-copy, cloud-prepare-error-display, invite-member-validators, member-limit-validators, member-limit-sheet.mounted, invited-member-row-state, use-organization-mutations, low-balance-alert-validators, ui-deadline) — 83 tests, all passing. server-error-copy.test.ts asserts the trial gate, the three resend refusals, owner authority, the invite sentences and the missing-invitation fallback; ui-deadline.test.ts asserts the deadline rejection is marked.
  • E2E on the Android emulator (omp36), local dev stack on this VM, app language German, dev build from the native-build artifact, fixture organization with free_trial_end_at 10 days in the past:
    • Session form: prompt plus the seeded repository, Start session. Before: Die Sitzung konnte nicht erstellt werden. / Bei uns ist ein Fehler aufgetreten. Bitte versuche es erneut. After: Die Testphase der Organisation ist beendet und sie hat keinen aktiven Plan. Ein Inhaber der Organisation kann die Abrechnung unter kilo.ai aktualisieren und es dann erneut versuchen. No retry control for that error in either run.
    • Member daily limit sheet, value 12,5, Speichern. Before: Organization trial has expired. After: the same German sentence.
    • The before state came from staging only the two pre-change source files for one capture and restoring them; git status was clean for those paths afterwards.
  • Re-ran both device scenarios on the merged head ae8c4d2f2d: same German copy on both surfaces, both requests answered 403 from the local stack, and the screenshots above were captured together with the uiautomator dumps that assert them.
  • CI on the merged head: kilo-app CI, CI, and the remaining workflows green; mobile-native-build re-ran after its concurrency cancellation.

Notes

  • An organization sentence the app has no copy for renders the localized generic line instead of English server text.
  • organization.members.ownRoleChange is mapped even though the member list offers role changes only for other members, so the app cannot currently produce it.

Proxy answers

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

  1. Question: Trial copy: The app has no in-app organization billing/plan screen; the org hub links out to kilo.ai (WEB_BASE_URL/organizations//payment-details). One shared message must serve the owner (who can fix billing) and a member (who cannot). Which copy/action?
    Answer: Text only, names owner + where to fix
    Confidence: 0.55 · Precedent: Speed and least work: do the least work with no drop in quality (session 7f3470e0ba6f, "do the least amount of work possible, with no drop in quality"). The app has no in-app billing screen, and one message works for both owners and members without new action plumbing.
  2. Question: Unknown errors: The brief asks for a localized generic fallback for unknown organization mutation errors. Evidence: the same procedures return deliberately user-facing sentences (invite: "This user is already a member of this organization", "This email already has a pending invitation"; update: "You cannot change your own role"). Routing all of them to the generic line would lose those reasons. Which fallback?
    Answer: Map every known user-facing server sentence to its own localized key: trial expired, already a member, pending invitation, and cannot change own role. Keep each server sentence as a shared constant next to ORGANIZATION_ACCESS_MESSAGE. Any unknown error gets the localized generic fallback. The class fix must not leave English text on an organization surface, and members keep the specific reason.
    Confidence: 0.50 · Precedent: UX and design quality: localize every string in the mobile app (session dcb3f9170164, "the mobile app always must respect i18n"). Autonomy: fix the class (session 00eb2a1d3e4b).

… error

An organization whose trial passed its enforcement window is refused by every
organization mutation. The app turned that rejection into the generic internal
line when a session started, and rendered the raw English sentence on the
member daily limit sheet, so a German reader saw either an internal error or
untranslated text.

One shared module now holds the server sentences the app can explain and the
localized copy for each. The cloud-prepare classification and every organization
surface that showed a rejection (member limits, invite, org rename, member and
invitation mutations) read it, and a rejection the app cannot explain falls back
to the localized generic line, so no server sentence reaches a screen.

Adds the expired-plan, invite-conflict and own-role sentences with translations
in every catalog.
@iscekic iscekic added drain Work item from the findings drain explorer:android Finding surfaced by the Android explorer labels Oct 23, 2026
@iscekic

iscekic commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

bot: German-language run on the Android dev build against the local stack, with a fixture organization whose trial is hard-expired. Each capture was taken together with a uiautomator dump, which is what the wording below quotes.

Starting a session with a repository:

  • before: the generic internal line under "Die Sitzung konnte nicht erstellt werden." (screenshot 2)
  • after: "Die Testphase der Organisation ist beendet und sie hat keinen aktiven Plan. Ein Inhaber der Organisation kann die Abrechnung unter kilo.ai aktualisieren und es dann erneut versuchen." (screenshot 1), with no retry control

Saving a member daily limit (value 12,5):

  • before: the raw English sentence "Organization trial has expired." under the field (screenshot 4)
  • after: the same German sentence (screenshot 3)

The server logged 403 FORBIDDEN for the organization session prepare and for the member update in both runs. Screenshots show fixture data only (an owned fake organization and fake members on a local stack).

Session form in German showing the localized plan-ended message

Session form in German showing the previous generic internal line

Member daily limit sheet in German with the localized plan-ended message under the field

Member daily limit sheet in German showing the previous untranslated server sentence

Comment thread apps/mobile/src/i18n/locales/hi.json Resolved
@kilo-code-bot

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

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only change since the previous review at 138f36ba2 is a one-line comment correction in use-organization-mutations.ts (plus a main merge that kept the localized-error mapping intact), and all four earlier findings remain fixed at ae8c4d2f2: owner-authority and the three invitation-resend refusals now have localized keys, the Hindi string is corrected, and the rename dialog preserves the UI-deadline message.

Assumptions: [INFERENCE] typecheck, lint, check:i18n, and the mobile test suites were not executed in this read-only review; catalog validity and key presence were checked directly (87/87 catalogs contain each new key exactly once, JSON parses). Organization sentences still without a mapping (for example the two SSO-only invite refusals or "You cannot remove yourself from the organization") intentionally fall back to the localized generic line rather than server text, which the PR documents and the mapper comments state; these are not raised as findings.

Files Reviewed (105 files)
  • apps/mobile/src/lib/server-error-copy.ts / .test.ts
  • apps/mobile/src/lib/ui-deadline.ts / .test.ts
  • apps/mobile/src/lib/hooks/use-organization-mutations.ts / .test.ts
  • apps/mobile/src/components/agents/cloud-prepare-error-display.ts / .test.ts
  • apps/mobile/src/components/organization/hub-screen.tsx
  • apps/mobile/src/components/organization/invite-member-validators.ts / .test.ts
  • apps/mobile/src/components/organization/invited-member-row-state.ts / .test.ts
  • apps/mobile/src/components/organization/low-balance-alert-validators.ts / .test.ts
  • apps/mobile/src/components/organization/member-limit-validators.ts
  • apps/mobile/src/components/organization/member-limit-sheet.mounted.test.tsx
  • apps/mobile/src/components/rename-modal.tsx
  • apps/mobile/src/i18n/locales/*.json (all 87 catalogs)
Previous Review Summaries (4 snapshots, latest commit 138f36b)

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

Previous review (commit 138f36b)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The new head 138f36ba2 removes six stale translation keys from the 87 catalogs (none are referenced anywhere in the repo), moves a JSX comment in the organization hub, and changes one test fixture; the merge from main kept the PR's localized-error mapping intact on the three conflicted files, and all four previously raised findings are fixed in current code.

Assumptions: [INFERENCE] typecheck, check:i18n and the mobile test suites were not executed in this read-only review. The SSO-only invite refusals (This user must join through your organization SSO provider, This organization has an invalid SSO configuration) still fall back to the localized generic invite line; the PR documents this as intentional, and the copy is still localized, so it is not raised as a finding.

Files Reviewed (15 files)
  • apps/mobile/src/components/organization/hub-screen.tsx
  • apps/mobile/src/components/organization/invite-member-sheet.tsx
  • apps/mobile/src/components/organization/invited-member-row-state.ts
  • apps/mobile/src/components/organization/member-limit-sheet.tsx
  • apps/mobile/src/components/organization/invite-member-validators.ts
  • apps/mobile/src/components/organization/invite-member-validators.test.ts
  • apps/mobile/src/components/organization/member-limit-validators.ts
  • apps/mobile/src/components/organization/low-balance-alert-validators.ts
  • apps/mobile/src/components/agents/cloud-prepare-error-display.ts
  • apps/mobile/src/components/rename-modal.tsx
  • apps/mobile/src/lib/server-error-copy.ts
  • apps/mobile/src/lib/ui-deadline.ts
  • apps/mobile/src/lib/hooks/use-organization-mutations.ts
  • apps/mobile/src/i18n/locales/*.json (all 87 catalogs; new keys present exactly once in each)

Previous review (commit 7de6917)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit 7de6917 only restructures the rename-dialog error branch into an early if and rewrites the deadline test to assert the rejection via rejects.toSatisfy; both are behavior-preserving, and the previously resolved deadline finding stays fixed.

Assumptions: [INFERENCE] typecheck and the mobile test suites were not executed in this read-only review; the restructured branch is equivalent to the prior ternary in every case (mapper only for a non-deadline save rejection, otherwise the error message or the localized generic line), and withUiDeadline still clears its timer on both settle paths so the test change introduces no leaked timer.

Files Reviewed (2 files)
  • apps/mobile/src/components/rename-modal.tsx - 0 issues
  • apps/mobile/src/lib/ui-deadline.test.ts - 0 issues

Previous review (commit 19f7131)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up commit resolves all four prior findings: the owner-authority and resend-invite refusals now map to localized copy, the rename dialog preserves the localized 15s deadline message, and the Hindi ownRoleChange typo is fixed, with the five new server sentences matching the router literals exactly.

Assumptions: [INFERENCE] pnpm check:i18n/check:plurals, typecheck, and the mobile test suites were not executed in this read-only review; catalog coverage was verified structurally (each of the four new keys appears exactly once in all 87 catalogs, and no non-English catalog left the English copy untranslated). The invite sheet still falls back to the generic localized line for the two SSO refusals (This user must join through your organization SSO provider, This organization has an invalid SSO configuration), which the PR body documents as intentional; the admin-inviting-owner case that previously dead-ended is now mapped to organization.members.ownerAuthority.

Files Reviewed (92 files)
  • apps/mobile/src/components/rename-modal.tsx - 0 issues
  • apps/mobile/src/lib/server-error-copy.ts - 0 issues
  • apps/mobile/src/lib/server-error-copy.test.ts - 0 issues
  • apps/mobile/src/lib/ui-deadline.ts - 0 issues
  • apps/mobile/src/lib/ui-deadline.test.ts - 0 issues
  • 87 locale catalogs under apps/mobile/src/i18n/locales/ - 0 issues

Previous review (commit 01f2ed2)

Status: 4 Issues Found | Recommendation: Address before merge

Executive Summary

The expired-plan copy fix is correct and well covered, but routing every organization rejection through the exact-sentence map drops specific, reachable server reasons on the invite and resend-invite surfaces, most notably an admin inviting an owner.

Assumptions: [INFERENCE] pnpm check:i18n/check:plurals and the mobile test suites were not executed in this read-only review; catalog parity was verified structurally (each of the four new keys appears exactly once in all 87 catalogs) and translations spot-checked. Server sentences and their tRPC codes were verified against the routers in this checkout.

Overview

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

WARNING

File Line Issue
apps/mobile/src/components/organization/invite-member-sheet.tsx 170 An admin sees the owner option (isMoneyRole includes admin) and the server's Only an organization owner can manage owners rejection now renders as "Something went wrong"; the SSO invitation refusals collapse the same way.

SUGGESTION

File Line Issue
apps/mobile/src/components/rename-modal.tsx 99 The org rename errorCopy also replaces the 15s common.takingLonger deadline message with the generic line.
apps/mobile/src/components/organization/invited-member-row-state.ts 91 Resend-invite conflicts (expired, already accepted, no longer resendable, owner authority) now render the generic line instead of the reason.
apps/mobile/src/i18n/locales/hi.json 3147 Hindi ownRoleChange contains the fused word अपनीस्वयं; should be अपनी स्वयं.

Verified clean

  • server-error-copy.ts constants match the server literals exactly, including the trailing period on Organization trial has expired., and every mapped i18n key resolves in en.json.
  • cloudPrepareFailureMessage ordering does not shadow the NOT_FOUND/PAYMENT_REQUIRED branches; trial expiry is FORBIDDEN and terminal, so the composer correctly shows no retry control.
  • low-balance-alert-validators.ts keeps the BAD_REQUEST recipient branch first; trial expiry is FORBIDDEN, so it reaches the new form-level mapping.
  • No double announcement was introduced: invite and member-limit surfaces keep a single AccessibleStatus owner, and the org rename has no toast.
Files Reviewed (99 files)
  • apps/mobile/src/lib/server-error-copy.ts - 0 issues
  • apps/mobile/src/lib/server-error-copy.test.ts - 0 issues
  • apps/mobile/src/components/agents/cloud-prepare-error-display.ts - 0 issues
  • apps/mobile/src/components/agents/cloud-prepare-error-display.test.ts - 0 issues
  • apps/mobile/src/components/organization/hub-screen.tsx - 0 issues
  • apps/mobile/src/components/organization/invite-member-sheet.tsx - 1 issue
  • apps/mobile/src/components/organization/invited-member-row-state.ts - 1 issue
  • apps/mobile/src/components/organization/low-balance-alert-validators.ts - 0 issues
  • apps/mobile/src/components/organization/low-balance-alert-validators.test.ts - 0 issues
  • apps/mobile/src/components/organization/member-limit-sheet.tsx - 0 issues
  • apps/mobile/src/components/organization/member-limit-sheet.mounted.test.tsx - 0 issues
  • apps/mobile/src/components/rename-modal.tsx - 1 issue
  • apps/mobile/src/lib/hooks/use-organization-mutations.ts - 0 issues
  • apps/mobile/src/lib/hooks/use-organization-mutations.test.ts - 0 issues
  • apps/mobile/src/i18n/locales/hi.json - 1 issue
  • 85 other locale catalogs - 0 issues

Reviewed by deepseek-flash-latest · Input: 102.1K · Output: 13.3K · Cached: 1.9M

Review guidance: REVIEW.md from base branch main

Review follow-up. Organization surfaces still lost three kinds of detail after
they stopped echoing server text: an admin touching an owner, and an invitation
that expired, was already accepted, or can no longer be resent, all fell to the
generic line. Those sentences now have their own keys and translations, and a
missing invitation reads as the existing not-found copy.

The rename dialog's deadline is the app's own localized message, so it is marked
and kept instead of being rewritten by a caller's mapper. Corrects one Hindi
entry that was missing a word space.
@iscekic

iscekic commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Re-ran both German device scenarios on the follow-up head, same fixture organization, each capture taken together with a uiautomator dump:

  • session start with a repository: "Die Sitzung konnte nicht erstellt werden." with "Die Testphase der Organisation ist beendet und sie hat keinen aktiven Plan. Ein Inhaber der Organisation kann die Abrechnung unter kilo.ai aktualisieren und es dann erneut versuchen.", no retry control
  • member daily limit sheet, value 12,5, Speichern: the same German sentence under the field

Both requests answered 403 from the local stack. Fixture data only.

Session form in German with the localized plan-ended message

Member daily limit sheet in German with the localized plan-ended message

The review of the previous commit flagged the shape of the new conflict test and
a nested ternary in the rename dialog's rejection branch; both now follow the
repository style rules without changing behaviour.
@iscekic

iscekic commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Final head run of both German device scenarios, captures taken together with a uiautomator dump:

  • session start with a repository: the plan-ended sentence under "Die Sitzung konnte nicht erstellt werden.", no retry control
  • member daily limit sheet, value 12,5, Speichern: the same sentence under the field

Requests answered 403 from the local stack; fixture data only.

Session form in German showing the localized plan-ended message

Member daily limit sheet in German showing the localized plan-ended message

@iscekic iscekic added the merge:bot Bot-owned review; merge when ready once readiness passes and a review approves. label Oct 23, 2026
@iscekic
iscekic enabled auto-merge October 23, 2026 09:50
@iscekic
iscekic added this pull request to the merge queue Oct 23, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 23, 2026
…tion-error-copy

# Conflicts:
#	apps/mobile/src/components/organization/invite-member-sheet.tsx
#	apps/mobile/src/components/organization/invited-member-row-state.ts
#	apps/mobile/src/components/organization/member-limit-sheet.tsx
#	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/en.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
@iscekic iscekic removed the merge:bot Bot-owned review; merge when ready once readiness passes and a review approves. label Oct 23, 2026
…tion-error-copy

# Conflicts:
#	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/en.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
@iscekic

iscekic commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

bot: German-language run at the merged head on the Android dev build against the local dev stack, with a fixture organization whose trial is expired. Each capture was taken in the same command as the uiautomator dump quoted here.

  • New session form, with a seeded repository chosen and the start button pressed: the form shows the session line and, below it, the plan-ended sentence in German. That error offers no retry control; the separate branch-list row is a different, unrelated control. dev/logs/nextjs.log recorded organizations.cloudAgentNext.prepareSession failed: FORBIDDEN with HTTP 403.
  • Member daily limit sheet, a value entered with the locale's decimal separator, then save: the same plan-ended sentence renders below the field. The log recorded organizations.members.update failed: FORBIDDEN with HTTP 403.

Both surfaces resolve the rejection through one shared map, so their copy cannot drift apart.

Session form with the plan-ended copy in German

Member limit sheet with the plan-ended copy in German

@iscekic iscekic added the merge:bot Bot-owned review; merge when ready once readiness passes and a review approves. label Oct 23, 2026
@iscekic
iscekic added this pull request to the merge queue Oct 23, 2026
@iscekic
iscekic removed this pull request from the merge queue due to a manual request Oct 23, 2026
@iscekic iscekic added merge:blocked:judge-unavailable Risk assessment unavailable and removed merge:blocked:judge-unavailable Risk assessment unavailable labels Oct 23, 2026
@iscekic
iscekic added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit 0b04c32 Oct 24, 2026
53 of 54 checks passed
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:bot Bot-owned review; merge when ready once readiness passes and a review approves.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants