Skip to content

QA run · i18n (FULL area) · 92f26f75 · 2026-08-11 · 1 PASS / 3 PARTIAL / 1 FAIL #7640

Description

@baozhoutao

Full i18n area run of the checklist-test skill — all 5 items driven against a live showcase in a genuine Chinese session (opus subagent, isolated port + file DB). Text-only per RUNNER.md.

Result: 1 PASS · 3 PARTIAL · 1 FAIL. The resolver itself is in good shape — zero raw translation keys rendered on any of 12 surfaces. The findings are in what the platform advertises and in two items whose clauses no longer match the shipped contract.

Environment — framework 92f26f75 (branch claude/platform-test-checklist-ocwugl) · console bundle 09987b680 (packages/console/dist/.objectui-sha; the server itself prints a drift warning against the repo-root pin 6314e87f2) · showcase app · isolated port + file DB.

✅ PASS — strict-translation-key-rejection (6/6 + both negatives)

The strongest result of the run. All 10 declared bad-key variants were rejected at both doors — the runtime PUT /api/v1/meta/translation/<n> and the built defineTranslation / defineTranslationBundle — for 20/20 rejections and zero 2xx. No #3778/#4522 asymmetry between the two doors.

Every message names the surface, echoes the key in backticks, and carries either a did-you-mean or a located prescription:

Nothing half-persisted: the item 404s before the first attempt and after every one of the 10 rejections, so the valid sibling key in the same document never landed. The corrected document then saves 200 and the new label renders live in the browser.

🔴 FAIL — surface-matrix: /i18n/locales advertises locales the app never opted into

11 of the 13 declared surfaces render fully Chinese, each expectation read from the served bundle first: nav, list headers, option labels (three places), form labels, view tabs, dashboard widget titles, SDUI pages, settings, Studio metadataForms, sys_* objects, and activity-feed verbs (localized at write time per ADR-0053 — a PATCH wrote sys_activity.summary='更新了 任务…'). A regex scan for all nine declared group prefixes across 12 surfaces returned zero raw dotted keys. /i18n/labels/<obj>/<locale> agrees with the rendered header row string-for-string. An unknown locale returns an honest empty bundle — no 500, no key dump, no synthesized default.

Clause 6 fails.

Reproduction rule — boot the showcase on a fresh file DB, sign in, then GET /api/v1/i18n/locales.
Expected descriptors for the artifact's declared i18n.supportedLocales = ['en','zh-CN'].
Actual four descriptors — en, zh-CN, ja-JP, es-ES. Reproduced on two boots.

The envelope half is fine ({success,data:{locales}} with isDefault — #3636 shape intact); the set is a superset. ja-JP and es-ES are real and servable here (200 / 200,501 bytes and 200 / 188,750 bytes) but the app never opted into them.

Root cause (located). Nothing narrows the i18n service's locale set to the app's declared supportedLocales. runtime/src/app-plugin.ts:1409 loadTranslations() reads i18nConfig.defaultLocale only, then loads every locale key of every bundle — and each platform plugin independently pushes its own 4-locale bundle at kernel:ready (platform-objects/src/plugin.ts:206-230, plus service-settings, service-storage, service-messaging, service-realtime, plugin-security, plugin-sharing, plugin-webhooks — each shipping en/zh-CN/ja-JP/es-ES). getLocales() then returns the loaded key set (core/src/fallbacks/memory-i18n.ts; same in service-i18n/src/file-i18n-adapter.ts:193), and both the dispatcher domain and the service-i18n route map that set straight to the response.

Consequence: any client building a locale picker from this route — including the platform's own Settings → Localization select, which already offers all four — offers locales in which only sys_* objects are translated. Picking ja-JP or es-ES guarantees a mixed-language session for app-owned metadata.

Honest gaps in this item's run: the object-action confirm/success clause is not-run (the declared action's menu could not be surfaced, and no confirmText is translated anywhere in the bundle to test against), and the list-filter third place for option labels was not proven (the filter panel didn't open under the driver).

🟡 PARTIAL — 3 items

notification-localized-and-clears — the clear works; the item's premise is a capability the spec retired

The mechanical half passes: clicking an entry fired POST /api/v1/notifications/read → 200 and the panel's mark-all fired POST /api/v1/notifications/read/all → 200 against the running Hono server, so the #3362 dispatcher-only regression is absent; a server re-read confirms read:true, and the clear survives a full reload (the residual badge was new mail from the digest flow firing every minute, verified by createdAt, not a failed clear).

Clauses 0 and 4 are blocked(fixture) — and this needs a checklist decision, not a code fix. The notification title renders New task assigned: Build homepage because that is a literal English string authored in the showcase flow's notify node (src/automation/flows/index.ts:160-166), not a translation key. There is no translation group for notification copy at all: notifications is a retired top-level key whose own spec guidance reads "notifications is the retired object-first dialect — notifications have no translation group, omit them", and platform-side assignment notifications were deliberately moved to app-authored flows (#3403). sys_user carries no locale field, and the notify path never consults a recipient locale.

As written, clauses 0 and 4 are unprovable on any stock app. The item assumes a per-recipient localized notification title — a capability the spec retired.

studio-follows-app-locale — Studio does follow; the clause wording collides with declared coverage debt

Studio flips with the app: designer chrome, object list, column headers and the metadataForms object form all render Chinese; the choice is stored and survives both a reload and a fresh sign-in in a new browser context; the return trip to English is symmetric (cjk=0 latin=1147), not a ratchet. No Studio surface stayed English — which is the failure this item exists to catch.

Two reasons it is not a clean pass:

  1. Residual English strings (Announcement, Business Unit, Owning Business Unit, Sales Region, …) are the showcase's frozen untranslated debt — they render their EN source label, never a key — which the sibling item's knownGaps declares legitimate. The clause's wording ("no untranslated declared string next to translated ones") should be reconciled with the coverage ratchet.
  2. The date formatter is half-localized. Past relative forms localize (逾期 6 天, 创建于10天前, 刚刚) while future relative forms stay English on the very same Chinese page (In 3 days, In 2 days), and absolute timestamps render en-US (8/11/2026 9:25 am). This is a genuine product gap, just not one this item's clauses name.

build-gates-hold — the gates behave correctly; packages/spec/dist is stale in this checkout

What was proven: both --self-tests pass with their classifiers staying distinct; drift detection was driven red-then-green for real (the actual gate run from a /tmp symlink farm — every workspace entry symlinked with one real copy of the package under test — going in sync → DRIFTED (1) → in sync on revert, without touching a repo file); and the unbuilt-CLI prerequisite is named correctly by both gates with "Nothing was checked/measured", while --write prints regenerat* zero times.

Clauses 0 and 3 are blocked(environment) on one shared cause: packages/spec/dist predates commit 06be54ec (#7285, which added authorisesIrreversibleAction) by 13 minutes — dist built 2026-08-10T06:29Z, commit 06:42Z. So platform-objects' extract fails with "The requested module '@objectstack/spec/system' does not provide an export named 'authorisesIrreversibleAction'", and the clean-tree green cannot be observed. The runner deliberately did not rebuild, since the workspace build is shared with concurrent batches.

Worth acting on regardless of this run: the coverage gate handles this cause well — it refuses to judge the ratchet on a partial round ("Nothing was compared… the baseline was left exactly as committed"), which is the designed #6033/#5862 honesty. The bundles gate handles the same cause poorly: it reports a stale-dist prerequisite failure as "1 bundle problem(s)", a content verdict. It should name the prerequisite the way its sibling does.

Checklist maintenance falling out of this run

  1. build-gates-hold step 4 is a no-op as written. "Perturb one committed bundle value" leaves the gate green by design — merge mode re-uses the committed file as its own baseline, so drift is structural, not textual. It should read "remove or rename a committed bundle key". (The runner discovered this by trying the literal step first, watching it stay green, and then finding the structural perturbation that does bite.)
  2. notification-localized-and-clears clauses 0 and 4 need rewriting or waiving — see above.
  3. surface-matrix _sections has no addressable surface on this fixture. The record page renders the showcase's record:details block whose sections are authored with label and no name (task-detail.page.ts:74-76), and component.zod.ts:491 documents that "a nameless section renders its authored label in every locale". The bundle does carry 概览/排期/详细信息 — they are simply unreachable. Fixture authoring gap, not a resolver defect.
  4. surface-matrix view emptyState — the showcase authors none on any view, so no translated key exists to test.
  5. studio-follows-app-locale clause 1 should allow for the frozen untranslated debt the sibling item already declares legitimate.

Runner-environment facts worth keeping

  • The console LocaleSwitcher offers language-only codes and persists localStorage['objectui-locale']. Writing zh-CN there is rejected by isKnownLanguage() and silently boots en — the working Chinese session is zh, which the server resolves to zh-CN for both /i18n/* and Accept-Language-driven metadata localization.
  • The app shell bell does issue read/read-all requests; only the console home header bell is inert (confirming the earlier finding).
  • I18nServicePlugin/FileI18nAdapter is not installed on this app — the i18n routes are served by the dispatcher /i18n domain over core's in-memory fallback. A language-only or script tag resolves by prefix match (/translations/zh and /translations/zh-Hans both return the zh-CN bundle).

Activity

  1. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    Extraction ledger — COMPLETE. Closing.

    Every FAIL, PARTIAL and checklist-maintenance item extracted; all four premises re-verified live on origin/main, zero already-fixed, zero duplicates.

    Run item Finding Card Lane
    surface-matrix c6 (FAIL) /i18n/locales advertises 4 locales when the app declared 2 — loadTranslations() reads defaultLocale only and never narrows to supportedLocales, while each platform plugin pushes its own 4-locale bundle #7679 domain:cli
    studio-follows-app-locale (PARTIAL) date formatter half-localized — past relative forms localize, future forms and absolute timestamps stay en-US objectstack-ai/objectui#4272 objectui
    build-gates-hold (PARTIAL) the bundles gate reports a stale-dist prerequisite failure as a content verdict ("1 bundle problem(s)"), where its sibling coverage gate correctly refuses to judge #7681 domain:devx
    notification-localized-and-clears c0/c4 (PARTIAL) the clauses assume a per-recipient localized notification title — a capability the spec retired (notifications is a retired translation group; #3403 moved them to app-authored flows) #7684 domain:devx
    Checklist maintenance ×3 step-4 is a no-op as written (perturb a bundle key, not a value); _sections has no addressable surface; emptyState unauthored; c1 should allow the frozen untranslated debt #7686 domain:devx

    The PASS item (strict-translation-key-rejection, 20/20 rejections across both doors) produced no cards.

    Two notes worth carrying

    #4272's root cause was located beyond what the run stated. The run observed the symptom; the extraction traced it to objectui packages/fields/src/index.tsx — formatDateTime (L551-563) takes no locale and calls toLocaleDateString(undefined, …), which is why absolute timestamps render en-US, and formatRelativeDays's future path never threads the app locale.

    #7681 is a gate-honesty defect, not the stale dist. The packages/spec/dist staleness in this checkout is a build artifact, not the bug. The bug is that looksLikeMissingCliCommand only matches "command not found", so a stale-dist export mismatch falls through to broken.push('extract failed') and is reported as a content verdict — while the coverage gate handles the identical cause correctly ("Nothing was compared… the baseline was left exactly as committed", the #6033/#5862 honesty). #5217 added the CLI-case classifier only; this is its uncovered sibling.

    Provenance: maintainer standing rule that every QA problem becomes an issue; run records close at extraction, same exit as #7514/#7463/#7627.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingqa-run

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions