Repository navigation
console: the sign-in page renders from the built-in language packs, and the application's translations load after sign-in (the companion of objectstack-ai/objectstack#22432) #12034
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Oct 9, 2026 objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsClaim: PM loop round 16
Session:session_01MgfduSkFrfM3eorB3UGfAU
Account:os-bill
Branch:claude/issue-12034-console-i18n-after-signin
Worktree:objectui-issue-12034
Domain:domain:ui
Seat:domain:ui#2
Scope of this claim: the card's four steps, as filed. Step 1 is a measurement and sizes the rest:- Measure whether the Console re-fetches the application's translations once signed in today.
- The sign-in page renders from the built-in packs only, with no request to
/i18n. - After sign-in, the application's translations and locales load with the session's credentials, and the UI re-renders with them. The no-op re-render trap of objectui#10382 is checked explicitly.
- No signed-in user sees a raw key, including after a reload and on the first render after sign-in.
File surface (as read on
main049012b; the dev re-reads it):apps/console/src/main.tsx, theI18nProvidermount;apps/console/src/loadLanguage.tsandapps/console/src/loadLocales.ts;- the Console's session or auth state that the post-sign-in load hangs on. The dev names that file before the edit;
- the tests beside them, plus one pin test for the card's three pins;
- the docs page that describes the Console's translation loading, if one exists. The dev names it or reports that there is none;
.changeset/12034-*.md.
⛔ Not on it:
- the framework's refusal (runtime: the
/i18ndispatcher domain answers an anonymous caller —handleI18nRequestmakes noshouldDenyAnonymouscall, unlike every other dispatcher domain (ADR-0056 D2); with an objectui companion for the Console sign-in page objectstack#22432) and the framework's.objectui-shapin; - any new published prop or export on the
I18nProvideror onpackages/i18n. If the post-sign-in load cannot be built from the provider's existing API, the dev stops and reports it, and the seat routes it asClause-②; - any new
packages/i18nkey; examples/console-starter: the dev measures its loader and reports. It is not edited here, because the card names the Console.
First-load bytes:
main.tsxis in the eager closure. The dev measures the delta against the ceiling of 3,281,467 B and stops before the PR above +1,024 B gzip.Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectuiover these paths: no path-derived mandate; default tier)
Clause-②: no
Responsibility:objectui Console: the sign-in page fetches /i18n anonymously, and nothing re-loads the application's translations with the session's credentials | the Console's I18nProvider loaders and its session state already on main | every Console user at sign-in, once objectstack-ai/objectstack#22432 refuses an anonymous /i18n read
Thread-read: none
Serial constraints cleared: no open objectui PR and no in-flight claim touchesapps/console/src/main.tsx,loadLanguage.tsorloadLocales.ts(read 2026-10-09T07:29Z).
Order: this card lands first. objectstack-ai/objectstack#22432 isBlocked-by:this card, and moves the framework's Console pin past this card's merge (card body).Why
Clause-②: no: the Console is an application, not a published package. Moving its translation load from boot to after sign-in changes no contract and no published prop. New provider props are excluded above.
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 12034,
"status": "done",
"branch": "claude/issue-12034-console-i18n-after-signin",
"pr": "#12042",
"session": "session_01MgfduSkFrfM3eorB3UGfAU",
"head": "27caa7d0b",
"premise_still_valid": true,
"summary": "The console's two /i18n loaders (loadLanguage, loadLocales) now wait for this page load's session answer, published by the console AuthProvider's existing onAuthStateChange prop into a new console module (apps/console/src/i18nSession.ts). Signed out they send no request (the sign-in page renders from the built-in packs); signed in they read through withSettleSignal(createAuthenticatedFetch()), the data adapter's own request path (bearer, X-Tenant-ID, Accept-Language). No new prop, export or key on I18nProvider or packages/i18n. Premise partly corrected by measurement: the console already re-reads /i18n after sign-in, because every console sign-in exits by full-page navigation, but never with the bearer; and against the head of objectstack-ai/objectstack#22432, main shows signed-in users the untranslated authored literal, not raw keys.",
"hypotheses": {
"H1": "partly falsified. main blobs vs a fake framework refusing anonymous /i18n (bearer-only): sign-in page load made 2 reads (translations/zh, locales), no Authorization, both 401; the post-sign-in page load (LoginPage exits via window.location.assign, pinned by authExitBasename.test.tsx) made 2 more reads, no Authorization, both 401; heading stayed 'Lead'. Default credentials same-origin: on a same-origin cookie deployment the post-sign-in read would carry the cookie and be served; the bearer was never sent.",
"H2": "partly falsified. head: signed-in page load reads wait for AuthProvider's first answer (one get-session round trip), then carry Bearer, 200. 'First render already has the app labels' holds on neither main nor head: I18nProvider never holds first render for its loader; pin 2 shows Lead then the translation on one mounted heading.",
"H3": "holds with no re-run: existing loadLanguage/loadLocales props unchanged; the loader Promise settles on the session answer and the provider's own non-silent addResourceBundle (bindI18nStore 'added') re-renders. No key remount, no same-language changeLanguage (it would persist an explicit language choice). In-place sign-in is not re-run; the provider has no re-run hook (loadedAppLangs, askedForLocales guards).",
"H4": "holds by falling back, not waiting: sign-in page from the built-in zh pack; first signed-in render = built-in strings + authored literal; MutationObserver over every frame of sign-in to app: 0 raw keys (control catches a raw key). main also showed 0 raw keys: app-bundle readers fall back to the authored literal, and spec I18nLabel refuses the keyed {key, defaultValue} form (key?: never). Built-in call-site key coverage: check:i18n-keys green.",
"H5": "neutral: eagerGzipBytes base 049012b = 3,238,404 B, head 27caa7d = 3,238,537 B, delta +133 B gzip; ceiling 3,281,467 B. check:eager-closure: 'Console eager closure is 3162.6 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 41.9 KB)'."
},
"starter_measurement": "examples/console-starter (not edited): loader is a bare fetch, no loadLocales, and DefaultLoginPage signs in IN PLACE (navigate('/')). Probe with the starter's real loader vs the 401 framework: before sign-in 1 read, no Authorization, 401; after in-place sign-in 0 reads; heading stayed 'Lead' for the session. Once objectstack-ai/objectstack#22432 lands, starter users see untranslated app labels after sign-in until reload, on every deployment (the pre-sign-in read is anonymous everywhere and the provider never asks again); after reload same-origin cookie is served, bearer-only 401 again. The console's gate is not a drop-in there: in-place sign-in needs the page-load call to wait for a sign-in.",
"tests": "All at head 27caa7d (git rev-parse --short HEAD after the last commit). (1) pnpm exec vitest run over 40 files (3 touched/new test files; 31 apps/console suites mounting I18nProvider, LoginPage or App; 7 suites reading main.tsx/App.tsx source: bootSplash, insecure-origin-crypto.placement, faviconAfterNavigation, tabTitleAfterNavigation, consoleToasterAnchor.ratchet-7482, auth-namespace-3546, runtimeConfigBootDedup; column-identity.ratchet; one-authority-per-exported-name-6273): 'Test Files 40 passed (40)', 'Tests 290 passed (290)', lock VERDICT command-exit 0. (2) reverse leg: loadLanguage.ts, loadLocales.ts, App.tsx, main.tsx swapped for main's blobs (hash == 049012b blob each): pins + loader tests 'Tests 7 failed | 13 passed (20)' (red: pins 1,2,3 + 4 new loader session-rule tests; green: control + pre-existing loader tests); restore via git checkout HEAD, each hash == HEAD blob, git diff HEAD = 0 bytes. (3) wire ablation (only onAuthStateChange={publishAuthState} removed from App.tsx; anchor count 1 to 0): 'Tests 2 failed | 2 passed (4)' (pins 2,3 red; pin 1 + control green as expected); restore proven by hash, git diff HEAD = 0 bytes. All mutation scripts carried trap restore EXIT INT TERM.",
"gates": [
"pnpm --filter @object-ui/console type-check (closure built first: turbo run build --filter=@object-ui/console^... --concurrency=2, VERDICT command-exit 0): echoed 'tsc --noEmit && tsc -b tsconfig.node.json --force', VERDICT command-exit 0; --listFiles includes the 3 test files",
"pnpm exec eslint (8 touched files, --format json): files 8, errors 0, warnings 0, exit 0",
"node scripts/check-changeset-presence.mjs: '8 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)', exit 0",
"node scripts/check-changeset-no-major.mjs: 'No changeset declares a major bump', exit 0",
"pnpm check:new-line-citations: 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only', then an arrow and 'exit 0' (arrow spelled out for the body sanitizer)",
"pnpm check:control-bytes: 'check-control-bytes: OK (scanned 8189 tracked text file(s); skipped 85 binary)', exit 0",
"pnpm check:i18n-keys: 'Every in-scope call-site key resolves against the en pack (3311 keys) ...', exit 0",
"pnpm check:vi-mock-specifiers / vi-mock-inherit / vi-mock-override-shape: OK, exit 0 each",
"pnpm check:test-path-roots: OK, exit 0",
"pnpm check:changeset-claims: 'No pending changeset names a file this change touches', exit 0",
"pnpm check:pending-changeset-literals: 'No test source names a pending changeset', exit 0",
"pnpm check:unreferenced-sources: 'OK Every shipped source file in every covered package is reachable', exit 0",
"pnpm check:phantom-deps: 'Every in-scope import is declared by the package that publishes it', exit 0",
"pnpm check:eager-closure: 'Console eager closure is 3162.6 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 41.9 KB)', exit 0",
"node scripts/check-governed-queue-guard.mjs --test (9 paths): 'NOT GOVERNED'",
"NOT MEASURED locally, declared to CI: repo-wide pnpm lint and full pnpm test. docs:check-links and check:spec-symbols not applicable (content/docs untouched)."
],
"eager_closure": {
"base_049012bf0": 3238404,
"head_27caa7d0b": 3238537,
"delta_gzip_bytes": 133,
"ceiling": 3281467,
"ci_budget_bot_reading": "NOT MEASURED: reported after CI by the seat"
},
"files_changed": [
"apps/console/src/i18nSession.ts (new: the session state the post-sign-in load hangs on)",
"apps/console/src/App.tsx (AuthProvider onAuthStateChange={publishAuthState})",
"apps/console/src/loadLanguage.ts",
"apps/console/src/loadLocales.ts",
"apps/console/src/main.tsx (comment only)",
"apps/console/src/loadLanguage.test.tsx",
"apps/console/src/loadLocales.test.ts",
"apps/console/src/tests/i18nAfterSignIn-12034.test.tsx (the card's three pins + a control)",
".changeset/12034-console-i18n-after-signin.md (@object-ui/console: patch)"
],
"docs": "none: no content/docs page describes the console's translation loading; guide/console.md and guide/deployment.md only say the i18n endpoints hang off VITE_SERVER_URL, still true. No docs edit.",
"deviations": [
"The session-state file was named in the PR body and this report, not before the edit: App.tsx (the console's AuthProvider mount) plus a new console module apps/console/src/i18nSession.ts, read as the claim's third bullet.",
"Claim says the Console is 'an application, not a published package'. Measured: @object-ui/console is published (publishConfig.access public, files dist) and in the changesets fixed group; AGENTS.md section 9 Housekeeping requires a changeset for its src, so a patch changeset ships. Clause-2: no still holds (no export, prop or contract change).",
"Commit trailers follow objectui AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude') instead of the harness reminder's model-named trailer; PR footer uses the agent definition's session-URL form instead of the harness's form.",
"Two temporary probe test files (one in apps/console, one in examples/console-starter) were created, run and deleted, never committed; a detached base worktree was created for the base build and removed.",
"Zero label writes: objectui path labels (apps, tests) were the labeler's; the dispatch named none. Only the PR assignee was written."
],
"mcp_calls": "0",
"api_writes": "3 REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches): pr_create (draft PR #12042, read-back identical 11948 bytes), label-write assign os-bill on PR #12042 (read-back MATCHES), and this os-dev-report comment on #12034. Plus 4 git pushes of the branch (empty-branch probe, then 3 commits' pushes), not REST.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, the console-starter's sign-in (app-shell DefaultLoginPage signs in in place with navigate('/')) against the head of objectstack-ai/objectstack#22432: the app labels stay untranslated for the whole session after sign-in; measured by probe (1 anonymous 401 read before sign-in, 0 reads after, heading 'Lead'). Contingent on objectstack-ai/objectstack#22432 landing; the dispatch asked for this measurement, not an edit. Any host pairing app-shell's DefaultLoginPage with an I18nProvider loader has the same shape, and I18nProvider has no re-run hook after an in-place sign-in, so a fix may need a provider API (Clause-2 territory). · dedupe words: console-starter, in-place sign-in, loadLanguage, i18n 401, DefaultLoginPage",
"carrier: the seat routing objectstack-ai/objectstack#22432 · noted, not filed: if objectstack-ai/objectstack#22432 should also wait on a starter companion, that ordering is the seat's call"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsReview: ACCEPT — PR objectui#12042 (
Fixes) ·domain:uiseat 2 · 2026-10-09T08:19ZImplemented-by:
os-devsubagent ofsession_01MgfduSkFrfM3eorB3UGfAU, branchclaude/issue-12034-console-i18n-after-signin, head27caa7d.
Reviewed-by:domain:uiseat 2 (session_01MgfduSkFrfM3eorB3UGfAU), against the PR's diff on GitHub, not against the report's prose.Built:
apps/console/src/i18nSession.ts(new) holds the consoleAuthProvider's answer for the page load.App.tsxfeeds it through that provider's existingonAuthStateChangeprop.loadLanguageandloadLocaleswait for that answer:- signed out, they send no request and answer
{}/[], so the sign-in page renders from the built-in packs; - signed in, they read through
withSettleSignal(createAuthenticatedFetch()), the data adapter's own request path (bearer,X-Tenant-ID,Accept-Language). No token is lifted from storage, as the claim required.
- signed out, they send no request and answer
I18nProvideris used through its existing props. No published prop, export orpackages/i18nkey is added, soClause-②: noholds.
The card's premise, corrected by measurement (accepted):
- The Console already re-reads
/i18nafter sign-in, because every console sign-in exits by a full-page navigation (window.location.assign, pinned byauthExitBasename.test.tsx). What it never did was send the bearer. - Against the head of runtime: the
/i18ndispatcher domain answers an anonymous caller —handleI18nRequestmakes noshouldDenyAnonymouscall, unlike every other dispatcher domain (ADR-0056 D2); with an objectui companion for the Console sign-in page objectstack#22432,mainshowed a signed-in user the untranslated authored literal (Lead), not raw keys. On head, the signed-in page load reads with the bearer and gets 200. - The first render after sign-in shows built-in strings plus the authored literal, then the translation. It never shows a raw key: a
MutationObserverover every frame from sign-in to app records zero, and its control catches a key drawn raw.
Checklist:
-
PR shape: draft, first line
Fixes #12034,Clause-②: noat the start of a line. runtime: the/i18ndispatcher domain answers an anonymous caller —handleI18nRequestmakes noshouldDenyAnonymouscall, unlike every other dispatcher domain (ADR-0056 D2); with an objectui companion for the Console sign-in page objectstack#22432 is named with no closing keyword. The assignee isos-bill. Commit trailers carry no model name. -
Scope: 9 files, all inside the claim:
- the two loaders and their tests;
App.tsx(one prop);main.tsx(a comment only);- the new session module and a pin file with the card's three pins plus a control;
- one changeset,
@object-ui/console: patch.
The session module was named in the report rather than before the edit; the claim's third bullet allowed it.
-
Changeset: the claim called the Console an application. Measured,
@object-ui/consoleis published and in thefixedgroup, so AGENTS.md §9 asks for a changeset. Accepted. -
Reverse legs:
- With
main's blobs, 7 of 20 tests turn red (pins 1–3 and the four new loader-session tests). - Removing only the
onAuthStateChangewire turns pins 2 and 3 red. - Each was restored to
blob == HEAD.
- With
-
Evidence:
- 40 files and 290 tests pass, both repo-wide ratchets included.
- The Console type-check passes, and eslint shows 0 warnings.
check:i18n-keys,check:phantom-deps,check:unreferenced-sources, both changeset checks andcheck:eager-closurepass.
-
Docs: no
content/docspage describes the Console's translation loading.guide/console.mdandguide/deployment.mdonly say the endpoints hang offVITE_SERVER_URL, which stays true. -
First-load bytes: +133 B gzip (3,238,404 → 3,238,537 B), with 41.9 KB of headroom. The seat's sum of accepted, unlanded deltas is +133 B: PR objectui#12040 is +0 B, and PR objectui#12036 is not accepted.
-
CI:
27caa7dis running. It lands through the merge queue once every check is green andBundle Analysisreads success.
Order: this card lands first. objectstack-ai/objectstack#22432 is
pm:blockedwithBlocked-by:this card, and its own PR moves the framework's Console pin past this merge.Noted for objectstack-ai/objectstack#22432, not this card:
examples/console-startersigns in in place, so once #22432 lands its users see untranslated application labels until they reload. That pointer is carried on #22432 itself.
Generated by Claude Code
Filing gate: ③ a maintainer-directed task. It is the objectui companion named by ruling objectstack-ai/objectstack#22146
6074960686(batch #300 item 1, letter A, maintainer 「同意」), item 1. Filed by the triage seat (seat post objectstack-ai/objectstack#6015),session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim.Why
objectstack-ai/objectstack#22432 gives the framework's
/i18ndomain the anonymous refusal every other dispatcher domain has (401). The ruling ties the two halves together: "the Console serves its sign-in page from the built-in packs and loads the application's translations after sign-in … the two land together so no signed-in user sees raw keys."The director's reading recorded in the ruling (objectui
main8f8f760fa4)apps/console/src/main.tsx:158handsloadLanguageandloadLocalesto theI18nProviderat application boot, before sign-in.loadLanguage.ts:19andloadLocales.ts:23fetch with no Authorization header, and degrade to the built-in packs on any failure.What this card does
/i18n.Pins
/i18nrequest is made, and the page renders translated from the built-in packs./i18nreads./i18nanswers 401 to an anonymous caller (#22432's head): the whole sign-in → app path shows no raw key.Order
/i18ndispatcher domain answers an anonymous caller —handleI18nRequestmakes noshouldDenyAnonymouscall, unlike every other dispatcher domain (ADR-0056 D2); with an objectui companion for the Console sign-in page objectstack#22432 isBlocked-by:this card. Its PR moves the framework's Console pin (.objectui-sha) past this card's merge in the same PR as the refusal, so the two ship together.Dedupe: MCP
search_issueson objectui, open and closed: 「console sign-in page built-in language packs load application translations after sign-in i18n anonymous loadLanguage」 gave 7 hits, none this. The nearest are #10382 (re-render no-op, closed) and #10349 (the starter's loader, closed).