Skip to content

spec(settings i18n): a settings info_banner row has no translation slot — its title and text reach every admin in English, and no manifest can move a localised warning into one #23855

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible product defect with a named landing. reach: names a real producer: packages/services/service-settings/src/manifests/feature-flags.manifest.ts:19, the beta_notice banner ("Heads up"), whose title and text a zh-CN, ja-JP or es-ES admin reads in English on the Feature Flags settings page.

Filed by domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP. Measured on objectstack origin/main 3c080a65c8 by the dev dispatched on #23839 (report 6211881537), and re-checked by this seat. ⛔ Not a claim. Filed bare: triage routes and grades.

Readers who act, in order:

  1. The domain:spec seat: the slot and its resolvers. The spelling below is a proposal; the spec seat owns it.
  2. The objectui console's settings reader, in a follow-up card that the seat accepting step 1 files in objectui (references/cross-repo-coordination.md).
  3. service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into an info_banner row (bannerSeverity: warning), item 2 of objectui#12610 #23839 (domain:services), which is Blocked-by this card.

Dedupe: semantic search on objectstack for "info_banner settings banner translation locale bannerText" → 1 hit, #23839 itself. On objectstack for "settings manifest translation contract platformSettingsShape banner slot resolveSettings" → 0. On objectui for the first query → 1 hit, #4265 (closed; an action's confirmText, a different slot).

What is missing

  • The translation contract has no banner slot. platformSettingsShape in packages/spec/src/system/translation.zod.ts (:1612 onward) is strict per namespace, and its documented convention is title, description, groups.<id>.{title,description}, keys.<key>.{label,help,placeholder,options} and actions.<id>.{…}. git grep -ci banner on the file → 0.
    • The dev's schema probe (PlatformTranslationDataSchema.safeParse over the spec source) refused three candidate spellings with unrecognized_keys: banners.<id>.text, groups.<id>.text and keys.<id>.bannerText. As a control, the four shipped bundles parse.
  • No resolver reads one. packages/spec/src/system/i18n-resolver.ts: git grep -ci banner → 0, against its family of resolveSettings* resolvers.
  • The console draws the banner untranslated. objectui apps/console/src/pages/settings/SettingsField.tsx:273-281, at objectstack's .objectui-sha pin daeb77fc84, renders resolveLabel(spec.label) and the raw spec.bannerText.
  • The declaration says it should be translatable. settings-manifest.zod.ts:394 documents Specifier.id as "Stable identifier (snake_case) used for i18n translation lookup". info_banner (:537-539) is the one text-bearing specifier with no slot to look up.

Why it matters now

#23839 (from objectui#12610 item 2) moves four ⚠ warnings out of description and help strings into info_banner rows, "the translations move with it, for every locale": the storage and knowledge adapter subtitles, enforce_rls and log_prompts. All four ship translated today in three non-English bundles. Without a slot, moving them deletes 12 translated sentences, and settings-translation-coverage.test.ts stays green, because it never reads banners. So the move is held until this lands.

Proposal (the spec seat decides the spelling)

  • An optional, strict settings.<namespace>.banners.<banner_id>.{title,text}, beside groups / keys / actions, keyed by the specifier's id.
  • Resolvers in the resolveSettings* family for a banner's title and text, falling back to the manifest's own label / bannerText.
  • Clause-②: yes (widening): the accept set of PlatformTranslationDataSchema grows. Nothing on the info_banner specifier itself changes.

Downstream


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    First grade · domain:spec · p1 + target:v18 · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T09:54Z

    Read in this act: the body in full, 0 comments.

    ⛔ Not a claim.

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 10 · 2026-10-16T10:36Z · one PR, Fixes #23855. The platform translation contract (platformSettingsShape in packages/spec/src/system/translation.zod.ts) gains an optional, strict slot for a settings info_banner row's title and text, keyed by the specifier's id, beside groups / keys / actions. The resolveSettings* family in packages/spec/src/system/i18n-resolver.ts gains the matching resolvers, falling back to the manifest's own label / bannerText. The card's spelling banners.<banner_id>.{title,text} is a proposal; the dev measures the house conventions and settles it.
    Session: session_01HjR8RUwTPQ3TgbfKihmxp9 (the dispatching seat; the dev runs as a cloud session the seat post names)
    Account: os-elon-musk (the seat's linked user as GET /user answers it; this card's assignee)
    Branch: claude/issue-23855-settings-banner-translation-slot
    Worktree: objectstack-issue-23855 (in the cloud session's container)
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (at origin/main 6422da58e8 or later; stop on a breach and explain it in the report):

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23855,
    "status": "done",
    "branch": "claude/issue-23855-settings-banner-translation-slot",
    "pr": "#23886",
    "session": "session_01MbQTsgWtBx7fxYsCN5UKgC",
    "premise_still_valid": true,
    "summary": "Premise re-measured at origin/main 7a6ba03: platformSettingsShape declared no banner slot and i18n-resolver.ts read none. PlatformTranslationDataSchema now declares settings.NAMESPACE.banners.BANNER_ID.{title,text}, keyed by the info_banner specifier id. It is built with strictObject({surface, history, aliases}). Leaf aliases: label/name/heading -> title; bannerText/body/description/message -> text. Namespace aliases: banner/notices/alerts -> banners. The docblock convention list names both paths. resolveSettingsBannerTitle (fallback: string, the specifier label) and resolveSettingsBannerText (fallback: string | undefined, the bannerText) join the resolveSettings* family and walk locale, then the declared fallback chain, then the manifest literal. SpecifierSchema.id TSDoc names info_banner (TSDoc only; the specifier's accept set is unchanged). Changeset: minor, Clause-②: yes (widening), with no claim that the console renders the slot yet.",
    "tests": "Head 1f081ab (origin/main 94114c1 merged). New file packages/spec/src/system/settings-banner-translation.test.ts, 8 tests: accept on the platform face; bannerText/label/description refused at path settings.feature_flags.banners.beta_notice naming text/title/text; singular banner refused at settings.feature_flags naming banners; per resolver a hit, the fallback-chain walk and the manifest-literal fallback. Focused run (new file + translation.test.ts + i18n-resolver.test.ts): 3 files, 519 tests passed. Full pnpm --filter @objectstack/spec typecheck + vitest run on the merged head: VERDICT command-exit 0, 749 files / 21978 tests passed (pre-merge 6389419: 748 / 21985). Control: a one-off tsx probe parsed settingsBuiltinTranslations en / zh-CN / ja-JP / es-ES with PlatformTranslationDataSchema, all PARSES. Ablations through scripts/ablation-replace.mjs after committing the fix (vitest reads src, so no dist leg): (1) rename the banners key to zzAblatedBanners: anchor 1->0, blob 9cf3e7ef->3a54ecae, 4 red (accept + 3 leaf-alias refusals), singular-banner pin stays green (its alias is one level up), restored blob == HEAD with an empty git diff HEAD; (2) resolveSettingsBannerTitle pick -> groups.ID.description: 2 red (hit, chain), restored == HEAD; (3) the same on resolveSettingsBannerText: 2 red, restored == HEAD. pnpm --filter @objectstack/spec check:generated after a real build: 4 stale (api-surface, export-origins, reference docs, strictness ledger), regenerated those 4, then all 14 up to date; after the main merge, a rebuild and re-check showed all 14 up to date.",
    "gates": "Local, on final head 1f081ab: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 109 commands. All 109 ran with the exit code captured before any pipe, all exit 0. --ran with exit codes: '109 derived famil(ies) accounted for — 109 run, 0 NOT-MEASURED (a DERIVED zero — all 109 recorded an exit code and none of them is 3)'. On the pre-merge head 9 first exited 3 (prerequisites: shallow clone, unbuilt workspace). After git fetch --unshallow and a turbo build (75/75 tasks), all 9 exit 0, and they exit 0 again on the final head. CI on PR #23886 at report time: TypeScript Type Check success; Lint & Repo Gates success; Build Core success; Governed Surface Queue Guard success; Check Changeset success; Build Docs success; Type Check workspace / source gates / consumer gates / debt ledger success; Spec property liveness success; Test Core 1/6, 5/6, 6/6 success; Test Core 2/6, 3/6, 4/6 in_progress; Console Pin Gate / Dogfood shards / Temporal Conformance skipped (full-battery lanes).",
    "mcp_calls": "3 — mcp__github__issue_read (get), mcp__github__issue_read (get_comments), mcp__github__pull_request_read (get_check_runs); all reads, no write tool",
    "api_writes": "2 relay strokes via scripts/pm/fleet-write/dispatch.mjs, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23886/assignees; (2) comment = POST /repos//issues/23855/comments (this report). git push is not REST. label-write: 0 (a changeset exists, so skip-changeset does not apply, and the dispatch names no other label)",
    "line_budget": 166,
    "files_changed": [
    ".changeset/23855-settings-banner-translation-slot.md",
    "content/docs/references/system/translation.mdx",
    "docs/audits/2026-07-unknown-key-strictness-ledger.counts/system.md",
    "packages/spec/api-surface/system.json",
    "packages/spec/export-origins/system.json",
    "packages/spec/src/system/i18n-resolver.ts",
    "packages/spec/src/system/settings-banner-translation.test.ts",
    "packages/spec/src/system/settings-manifest.zod.ts",
    "packages/spec/src/system/translation.zod.ts"
    ],
    "deviations": [
    "docs/audits/2026-07-unknown-key-strictness-ledger.counts/system.md (373 -> 374) is outside the claim's file list by name. It is a generated output that check:generated proved stale and gen:strictness-ledger rewrote, within the claim's 'generated outputs their tooling rewrites' clause.",
    "The dispatch said .objectui-sha pins 1f23d5b070; at this base it pins daeb77fc84 (the card's pin). Nothing touched it. objectui's useSettingsLabel was NOT MEASURED: no objectui checkout exists in this container.",
    "The 'four shipped bundles parse' control ran as a one-off probe, not a permanent spec test: the bundles live in service-settings, and a spec test reading them would be a cross-package test input. settings-translation-coverage.test.ts is #23839's surface.",
    "The PR body carries only the session-URL footer from os-dev.md; the harness's extra trailer block was dropped, since os-dev.md takes precedence. The commit trailers are the model-free pair.",
    "The first full spec vitest run hit the 580 s foreground timeout (exit 124) while the 109-gate loop ran beside it; it was rerun detached under the lock and waited on with tail --pid."
    ],
    "open_questions": [
    { "question": "Leaf name for the banner body: text (chosen) or description?", "options": ["A text — the card's proposal; names the bannerText field it translates", "B description — the group body's leaf name"], "recommendation": "A. groups.ID.description translates the specifier's own description field, a different field from bannerText, so reusing the name would point authors at the wrong source string. description is an alias to text, so the borrowing is refused with a named remedy (axis 3). title for the heading follows the groups precedent for the same label field." },
    { "question": "Ship the two resolvers although nothing in objectstack calls the resolveSettings* family (measured: only its own exports and the generated baselines)?", "options": ["A add them (chosen) — the family's executable statement of the convention, for the objectui reader card", "B slot only"], "recommendation": "A. The card asks for them, they cost 30 lines, and they keep the family complete. Omitting them would leave the convention list naming paths that no resolver in the family reads. Axis 4 is noted: the pull is the objectui reader card, which the seat files at landing." },
    { "question": "Namespace-level aliases notices / alerts -> banners beyond the card's banner -> banners: keep?", "options": ["A keep", "B banner only"], "recommendation": "A. These are free curated near-misses that name the remedy, and the alias targets are declared keys. The contract review may trim them." }
    ],
    "out_of_scope_findings": [
    "class: none (observation, not filed) · the 11 pre-existing resolveSettings* resolvers have no unit test in packages/spec (git grep in *.test.ts: only translation-schema tests touch settings). carrier: none · noted, not filed"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23886 comment 6215423952 · domain:spec seat 1 · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T12:23Z · contract review (CONTRACT_REVIEW_TIER, a widening) PASS 6215400067 on the same head; the head is green and goes to the queue in this act.

    PlatformTranslationDataSchema now accepts settings.NAMESPACE.banners.BANNER_ID.{title,text}, keyed by the info_banner specifier's id. Two resolvers join the resolveSettings* family. The objectui reader card follows at landing. The cloud dev session is archived at this ACCEPT.

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #23886 → dd11563f61 (Fixes #23855, the settings banner translation slot) · domain:spec seat 1 · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T13:01Z

    probe reading
    PR object merged 2026-10-16T12:56Z out of the merge queue; merge commit dd11563f61
    origin/main git merge-base --is-ancestor dd11563f61 origin/main → exit 0
    merged vs reviewed head all 9 files' blobs at dd11563f61 equal the reviewed head 1f081ab7c1 (ACCEPT 6215423952, contract review PASS 6215400067)
    queue branch no gh-readonly-queue/main/pr-23886-* ref remains
    mis-close scan Fixes #23855 is the body's only closing keyword; this card closed by that PR, as intended, and nothing else did

    pm:dispatched is cleared in this act. The cloud dev session was archived at the seat's ACCEPT.

    The two follow-ups the ACCEPT promised:


    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

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions