Repository navigation
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
Description
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:
- The
domain:specseat: the slot and its resolvers. The spelling below is a proposal; the spec seat owns it. - 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). - service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into an
info_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839 (domain:services), which isBlocked-bythis 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.
platformSettingsShapeinpackages/spec/src/system/translation.zod.ts(:1612onward) is strict per namespace, and its documented convention istitle,description,groups.<id>.{title,description},keys.<key>.{label,help,placeholder,options}andactions.<id>.{…}.git grep -ci banneron the file → 0.- The dev's schema probe (
PlatformTranslationDataSchema.safeParseover the spec source) refused three candidate spellings withunrecognized_keys:banners.<id>.text,groups.<id>.textandkeys.<id>.bannerText. As a control, the four shipped bundles parse.
- The dev's schema probe (
- No resolver reads one.
packages/spec/src/system/i18n-resolver.ts:git grep -ci banner→ 0, against its family ofresolveSettings*resolvers. - The console draws the banner untranslated. objectui
apps/console/src/pages/settings/SettingsField.tsx:273-281, at objectstack's.objectui-shapindaeb77fc84, rendersresolveLabel(spec.label)and the rawspec.bannerText. - The declaration says it should be translatable.
settings-manifest.zod.ts:394documentsSpecifier.idas "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}, besidegroups/keys/actions, keyed by the specifier'sid. - Resolvers in the
resolveSettings*family for a banner's title and text, falling back to the manifest's ownlabel/bannerText. Clause-②: yes (widening): the accept set ofPlatformTranslationDataSchemagrows. Nothing on theinfo_bannerspecifier itself changes.
Downstream
- objectui: the settings reader asks the new resolvers for a banner's title and text. The same card may also take the renderer gaps already reported to objectui#12610's owner:
warningdrawn in the default variant, andbannerTextdeclared as markdown but rendered as plain text. - service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into an
info_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839: moves its four warnings into banners with their translations in all four bundles, and extendssettings-translation-coverage.test.tsto banners. That also givesbeta_noticeits three missing locale copies.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 16, 2026
First grade ·
domain:spec· p1 +target:v18·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T09:54ZRead in this act: the body in full, 0 comments.
domain:spec, which owns the slot and its resolvers inpackages/spec/src/system/translation.zod.ts(platformSettingsShape). The objectui settings reader follows as a card the claimant files there, as the body orders.target:v18. It adds a slot to the translation contract's strictplatformSettingsShape, which widens the spec's accept surface (protocol-first class, 「宽窄皆算」).feature-flags.manifest.ts:19'sbeta_noticebanner reaches every non-English admin in English.pm:queue. service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into aninfo_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839 (domain:services) ispm:blockedon this card.info_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839, which read theinfo_bannerspecifier and said "no spec change". It did not check that the banner has a translation slot. service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into aninfo_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839's dev found that it has none. Recorded on R439.bugadded.⛔ Not a claim.
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 10 · 2026-10-16T10:36Z · one PR,
Fixes #23855. The platform translation contract (platformSettingsShapeinpackages/spec/src/system/translation.zod.ts) gains an optional, strict slot for a settingsinfo_bannerrow's title and text, keyed by the specifier'sid, besidegroups/keys/actions. TheresolveSettings*family inpackages/spec/src/system/i18n-resolver.tsgains the matching resolvers, falling back to the manifest's ownlabel/bannerText. The card's spellingbanners.<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 asGET /useranswers it; this card's assignee)Branch:
claude/issue-23855-settings-banner-translation-slotWorktree:
objectstack-issue-23855(in the cloud session's container)Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/main6422da58e8or later; stop on a breach and explain it in the report):packages/spec/src/system/translation.zod.ts:platformSettingsShapeand its docblocks (the convention list) only.packages/spec/src/system/i18n-resolver.ts: theresolveSettings*family, the new resolvers beside it.packages/spec/src/system/settings-manifest.zod.ts:SpecifierSchema.id's TSDoc may nameinfo_banneramong the specifiers whose text is looked up byid. TSDoc only.packages/specfor the slot and the resolvers, and the generated outputs their tooling rewrites (api-surface and export-origin baselines, JSON schema, reference docs).minorchangeset for@objectstack/spec.service-settingsmanifests or bundles, notsettings-translation-coverage.test.ts(both service-settings: the storage, knowledge and AI manifests carry their ⚠ warnings inside description and help strings; move each into aninfo_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839,domain:services), not theinfo_bannerspecifier's accept set, and not objectui.Container & model:
M,mode:cloud(the maintainer's word in this session's chat: 「batch 调到 5, 可以开云卡」),model: default tier(dispatch-gates --tier: no path-derived mandate). A widening of a published accept set: the review owed before the queue is atCONTRACT_REVIEW_TIER, facepackages/spec/src/system/translation.zod.ts.Clause-②: yes
Responsibility: n/a — a missing contract slot that blocks a downstream card, not a regression (filing gate ①; triage's first grade
6212923943)Thread-read: 6212923943
Ruling-ref: none (triage's first grade
6212923943: lanedomain:spec, p1 +target:v18)Serial constraints cleared:
Claim:of all 25 openpm:dispatchedcards were read in this act. No open PR touchestranslation.zod.ts,i18n-resolver.tsorsettings-manifest.zod.ts.domain:cliseat 2) both editpackages/platform-objects/src/apps/translations/*. This card does not.info_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839 (domain:services) ispm:blockedon this card. The claimant files the objectui settings-reader card at landing (useSettingsLabelandSettingsField.tsxread no banner slot today).objectstack-fleet commentedon Oct 16, 2026
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
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23886 comment 6215423952 ·
domain:specseat 1 · sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T12:23Z · contract review (CONTRACT_REVIEW_TIER, a widening) PASS6215400067on the same head; the head is green and goes to the queue in this act.PlatformTranslationDataSchemanow acceptssettings.NAMESPACE.banners.BANNER_ID.{title,text}, keyed by theinfo_bannerspecifier'sid. Two resolvers join theresolveSettings*family. The objectui reader card follows at landing. The cloud dev session is archived at this ACCEPT.objectstack-fleet commentedon Oct 16, 2026
Landed · PR #23886 →
dd11563f61(Fixes #23855, the settings banner translation slot) ·domain:specseat 1 ·os-elon-musk· sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T13:01Zdd11563f61origin/maingit merge-base --is-ancestor dd11563f61 origin/main→ exit 0dd11563f61equal the reviewed head1f081ab7c1(ACCEPT6215423952, contract review PASS6215400067)gh-readonly-queue/main/pr-23886-*ref remainsFixes #23855is the body's only closing keyword; this card closed by that PR, as intended, and nothing else didpm:dispatchedis cleared in this act. The cloud dev session was archived at the seat's ACCEPT.The two follow-ups the ACCEPT promised:
info_bannerrow draws its manifest English in every locale. Read the newsettings.NAMESPACE.banners.BANNER_ID.{title,text}slot (objectstack#23886) objectui#12671, filed bare for objectui triage.useSettingsLabelhas no banner helper and theinfo_bannerarm ofSettingsField.tsxdraws the manifest literals (measured at objectuimain4be76e0). It also offers that arm's two renderer gaps (warningdrawn in the default variant, markdown drawn as plain text) for triage to keep or split.info_bannerrow (bannerSeverity: warning), item 2 of objectui#12610 #23839 (domain:services): itsBlocked-by: #23855target is now closed. The seat told that card in a comment. The unlock and any re-derivation of the blocker are that lane's.Generated by Claude Code