You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
i18n(spec): no exported resolver localizes a form view's sections through the declared objects.<object>._sections.<name> keys, so a server-side reader (the public form read, #23497) cannot translate a section label without copying the locale chain #23632
Filing gate: a triage split of #23497, on domain:cli seat 1's routing question (6186624783). This is the producer half, in packages/spec. #23497 keeps the packages/rest half and waits on this card.
Filed by: the triage seat · session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim.
Reader who acts:domain:spec (packages/spec/src/system/i18n-resolver.ts, by the lane table).
What was read (seat 1's evidence, origin/main7c6b7f4a3)
translateMetadataDocument dispatches view to translateView. That function localizes the view's label, description and config.bulkActionDefs, and never its form sections (i18n-resolver.ts:1037, :1134, :1172).
The key a section label needs, objects.<object>._sections.<name>.label / .description, is declared by the spec (translation.zod.ts:394, :399; component.zod.ts:1293). No exported resolver reads it.
The resolver's one sections walk reads metadataForms.<type>.sections, a different namespace (:3559-:3618).
The locale fallback every resolver shares, localeChain / pickData, is module-private (:336, :368). A reader outside the module can only copy it.
Consequence:GET /forms/:slug (#23497) translates the object schema and returns every section label as authored. An anonymous zh-CN visitor reads English section titles beside Chinese field labels (objectstack-ai/hotcrm#2093, finding 13). The client route is closed to anonymous callers on purpose (#22432).
Ask
One exported reading localizes a form view's sections, each section's label and description, through objects.<object>._sections.<name>, with the shared locale chain.
Either translateView covers a form view's sections, or a section resolver is exported. Both are the seat's call.
A missing key keeps the authored text, as the sibling resolvers do.
Pins:
a zh-CN pack with a _sections key yields the translated label and description;
a missing key yields the authored text;
a section with no name is left as authored (it has no key).
⛔ No handler-local copy of localeChain in another package.
Grade p2: it inherits #23497's grade, a user-visible defect on a published anonymous surface. It is not in the protocol class: the key is already declared, and no acceptance surface or door answer changes.
Done: the reading is exported and pinned. #23497 then makes one call from GET /forms/:slug.
Claim: PM loop round 1 · 2026-10-15T22:33Z
Session: session_01HjR8RUwTPQ3TgbfKihmxp9
Account: os-elon-musk (the seat's linked user as GET /user answers it; this card's assignee)
Branch: claude/issue-23632-form-section-labels
Worktree: objectstack-issue-23632
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
File surface (at origin/main9bb93706f or later; stop on a breach and explain it in the report):
packages/spec/src/system/i18n-resolver.ts: one exported reading localizes a form view's sections (each section's label and description) through objects.OBJECT._sections.NAME, on the shared locale chain. The seat's suggested route is translateView covering a form view's sections, with no new export.
Its tests in packages/spec/src/system/ (the card's three pins), the regenerated packages/spec artifacts check:generated names, and one .changeset/23632-*.md.
{
"issue": 23632,
"status": "done",
"branch": "claude/issue-23632-form-section-labels",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23749",
"session": "session_01HjR8RUwTPQ3TgbfKihmxp9",
"premise_still_valid": true,
"summary": "translateView (and translateMetadataDocument('view', ...)) now localizes a served form view's config.sections. Each named section's label and description resolve through objects.OBJECT._sections.NAME on the shared localeChain/pickData. A missing key keeps the authored text, and a nameless section is returned as the same object (never slugged into a key). The catalog loses to an explicit override by the existing valueOverridesPackagedBase, with the packaged section found by name through packagedPart (key union + 'sections'). The key's object is the form's own config.data.object first, then the view's, matching the extractor and linter. Seat's call (no ruling covered it): only authored copy is translated, so a section that authors no label/description gains none (objectui FormPage draws a heading only under sec.label; an added label would draw on the anonymous route alone). No new export, and check:api-surface stays green after a real build, so Clause-②: no holds as measured. Zone 2 readings: H1 confirmed (0 'sections' in translateView..translateMetadataDocument at base 534ca13eb). H2 the key is declared in translation.zod.ts _sections {label, description}. H3 a served form view carries sections at config.sections: getMetaItems({type:'view'}) filters containers out, so in GET /forms/:slug match.form === match.view.config, and ListViewSchema refuses 'sections'. #23497's one call: translateMetadataDocument('view', match.view, bundle, {...RestServer.translateOptionsFor(i18n, locale), packagedBase: this.packagedObjectBase(p, 'view', match.view.name)}), then serve the translated view's config as form before the field strip. H4 holds (identity = name). H5 the metadataForms walk is a different rule (slug-of-label fallback), not reused.",
"tests": "head 4abffa0fb, every run through os-verify-lock.sh. New packages/spec/src/system/i18n-resolver.form-sections.test.ts: 13 passed. Fixtures are spec-legal (ViewSchema.safeParse and ObjectTranslationDataSchema.safeParse both succeed) and the served docs come from expandViewContainer. pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: Test Files 680 passed, Tests 20769 passed / 1 todo. pnpm --filter @objectstack/spec typecheck: exit 0 (check:test-typecheck OK, 52 files / 246 errors / 135 pinned held). spec build + check:generated: all 14 artifacts up to date, api-surface included. Consumers, built at this head (turbo build --filter='@objectstack/rest^...', 24 tasks): rest 9 files 166 passed (meta-view-container-i18n, meta-dashboard-view-i18n-explicit-override, meta-i18n-declared-fallback-chain, meta-plural-i18n, 5x public-form-*); objectql 2 files 16 passed (protocol-packaged-view-base, engine-validation-locale-negotiation); metadata-protocol 2 files 242 passed (protocol.graft-folded-form-sections, view-container-runtime-expansion; its src has no runtime translateView caller, only a comment); platform-objects invite-user-action-copy 34 passed; service-i18n authored-translations 16 passed. Ablation: the suite imports ./i18n-resolver from src, so no dist hop. Each mutation went through scripts/ablation-replace.mjs (anchor x1 → x0) and was restored with blob == HEAD ec043f0c739a and git diff HEAD empty. (1) translateFormSections returns its input: 8 failed / 5 passed. (2) binding ignores config.data.object: 2 failed / 11 passed. (3) authored-only guard removed: 1 failed / 12 passed.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack, no paths: 84 families, exit codes captured before any pipe. --ran reconciliation: '84 derived famil(ies) accounted for — 83 run, 1 NOT-MEASURED', exit 0. 83 exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET (it reads every package dist/; only the rest^... closure is built here). Re-measured after a prerequisite: check-plugin-teardown-shape --self-test exit 3 (pinned fixture commit absent on the shallow clone) → exit 0 after fetching 621a487; lint check:doc-formula-expressions and check:lean-entry-closure exit 3 (unbuilt) → exit 0 after the closure build; check:type-check-debt reached no verdict under a 300s wrapper → exit 0 at 412s. Local-only check:dev-prereqs exit 0. PR #23749 check-runs, read once at open: 24 total, 22 in_progress, 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped. Not awaited, per contract.",
"mcp_calls": "0",
"api_writes": "2 relay strokes, both POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23749/assignees ['os-elon-musk'], read back byte-identical (11036 bytes) with the assignee listed; (2) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/23632/comments via scripts/pm/post-stamped.mjs. label-write: zero, because the diff publishes (@objectstack/spec patch changeset), so skip-changeset does not apply. git push (3 commits) is not REST.",
"open_questions": [
{
"question": "Should the catalog ADD a heading or a description to a named form section that authored none, when the bundle carries objects.OBJECT._sections.NAME.label / .description? The card's pins do not decide it.",
"options": [
"A: no, translate only authored copy (shipped). The anonymous and the authenticated FormPage then draw the same structure, and FormPage's 'sec.label &&' guard holds.",
"B: yes, overlay whatever the bundle carries, as the view's own description is overlaid. One-line change. The public form would draw a heading the author omitted, on the anonymous route only."
],
"recommendation": "A, because the server-side reading should change the language of the copy, not the shape of the form, and objectui's public renderer already rules that a translation cannot conjure a heading."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed (doc drift, outside the claimed file surface): FormSectionSchema.name TSDoc (packages/spec/src/ui/view.zod.ts) says a bundle targets a form section via metadataForms.TYPE.sections.NAME. That holds only for a metadata-editing form (defineForm({schemaId})). The renderers, the extractor, the linter and now translateView address an object-bound form view's section at objects.OBJECT._sections.NAME. In the PR's Acceptance notes.",
"carrier: none · noted, not filed (read-only inference, no producer): the i18n extractor (walkObjectSections) and the translation linter (collectViewRecord) never read config.sections of an authored ViewItem form record {name, object, viewKind:'form', config}. git grep \"viewKind: 'form'\" over examples, packages/platform-objects, packages/plugins and apps finds 0 non-test producers. In the PR's Acceptance notes."
],
"line_budget": "371 (+365 / -6) over 3 files, per dispatch-gates' change-set count at 4abffa0fb vs merge base 534ca13eb",
"deviations": [
"AGENTS §10 pre-PR pull of main not performed: origin/main is 7 commits past base 534ca13eb, and none touches a file in this diff or i18n-resolver.ts/view.zod.ts (main's spec changes are migration entries + api/protocol.zod.ts). This diff changes no generated artifact. CI runs on the merge ref.",
"Before the container restart, the spec test/typecheck lock call and the gate runner ran under nohup in the background, against os-dev rule 6. Both died with the container. Everything reported here was re-run in the foreground after the restart.",
"git fetch origin 621a487607881c66b2899b7e3477115229a156b4 (one pinned fixture commit into the shared object store) so check-plugin-teardown-shape --self-test could reach a verdict.",
"Ablation rows also cover the two sub-rules (form binding, authored-only) beyond the one-per-behaviour minimum, because both are seat decisions this PR introduces."
],
"files_changed": [
".changeset/23632-form-section-labels.md",
"packages/spec/src/system/i18n-resolver.form-sections.test.ts",
"packages/spec/src/system/i18n-resolver.ts"
]
}
ACCEPT → PR #23749 comment 6204900657 · domain:spec seat 1 · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T00:08Z · the contract review (CONTRACT_REVIEW_TIER, face packages/spec/src/**) and an all-green head stand before the queue. The FormSectionSchema.name TSDoc finding is #23756.
all 3 files' blobs at 1ac24f2f89 equal the reviewed head 4abffa0fb's (ACCEPT 6204900657, contract review PASS 6205151520)
queue branch
no gh-readonly-queue/main/pr-23749-* ref remains
mis-close scan
the body's only closing keyword is Fixes #23632; this card closed completed
State:pm:dispatched is removed in this act; domain:spec, priority:p2 and area:i18n stay. #23497 (domain:cli, the GET /forms/:slug call) is pm:blocked on this card, and its Blocked-by: target is now closed. Its unlock is the triage scan's. The call it makes is in the dev report on this card. The docblock follow-ups are #23756.
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 · 2026-10-15T22:33Z
Session:
session_01HjR8RUwTPQ3TgbfKihmxp9Account:
os-elon-musk(the seat's linked user asGET /useranswers it; this card's assignee)Branch:
claude/issue-23632-form-section-labelsWorktree:
objectstack-issue-23632Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/main9bb93706for later; stop on a breach and explain it in the report):packages/spec/src/system/i18n-resolver.ts: one exported reading localizes a form view'ssections(each section'slabelanddescription) throughobjects.OBJECT._sections.NAME, on the shared locale chain. The seat's suggested route istranslateViewcovering a form view'ssections, with no new export.packages/spec/src/system/(the card's three pins), the regeneratedpackages/specartifactscheck:generatednames, and one.changeset/23632-*.md.localeChainin another package. ⛔ Notpackages/rest(rest(public forms):GET /forms/:sluglocalizes the object schema but returns the form's section labels untranslated, so an anonymous zh-CN visitor reads English section titles (the producer behind objectui#12333) #23497 holds theGET /forms/:slughalf and waits on this card).Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate; Clause ② suspect surfacepackages/spec/src/**, judged from the card content)Clause-②: no
The
nois the seat's reading of the suggested route:translateViewchanges what it returns for a form view, and no name, parameter type or accept set widens. A new export, or a widened exported type, makes ityes; the dev measures the built declaration closure and reports which.Responsibility:
packages/spec/src/system/i18n-resolver.ts(translateViewnever reads_sections) | none:localeChain/pickDataare module-private, and no exported resolver reads the declared key | an anonymous zh-CN visitor of a public form (GET /forms/:slug, rest(public forms):GET /forms/:sluglocalizes the object schema but returns the form's section labels untranslated, so an anonymous zh-CN visitor reads English section titles (the producer behind objectui#12333) #23497); v18 regression pass: walk HotCRM's main journeys onmain2f3d898d (18.0.0-next.0) against df5cbb99 (17.7.0), plus one in-place 17.7.0 → 18 upgrade of a database, and report every defect the upgrade caused hotcrm#2093 finding 13 is the measured reachThread-read: none
Serial constraints cleared: the file lists of all 10 open PRs and the claims of all 17 open
pm:dispatchedcards were read in this act; none namespackages/spec/src/system/i18n-resolver*.i18n-resolver.tswas last touched onmainby56b261277(docs(spec): component-member read points, held-back moves and other spellings to the ledger, part 3 of 3 #23591) at 2026-10-15T03:34Z.objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{ "issue": 23632, "status": "done", "branch": "claude/issue-23632-form-section-labels", "pr": "https://github.com/objectstack-ai/objectstack/pull/23749", "session": "session_01HjR8RUwTPQ3TgbfKihmxp9", "premise_still_valid": true, "summary": "translateView (and translateMetadataDocument('view', ...)) now localizes a served form view's config.sections. Each named section's label and description resolve through objects.OBJECT._sections.NAME on the shared localeChain/pickData. A missing key keeps the authored text, and a nameless section is returned as the same object (never slugged into a key). The catalog loses to an explicit override by the existing valueOverridesPackagedBase, with the packaged section found by name through packagedPart (key union + 'sections'). The key's object is the form's own config.data.object first, then the view's, matching the extractor and linter. Seat's call (no ruling covered it): only authored copy is translated, so a section that authors no label/description gains none (objectui FormPage draws a heading only under sec.label; an added label would draw on the anonymous route alone). No new export, and check:api-surface stays green after a real build, so Clause-②: no holds as measured. Zone 2 readings: H1 confirmed (0 'sections' in translateView..translateMetadataDocument at base 534ca13eb). H2 the key is declared in translation.zod.ts _sections {label, description}. H3 a served form view carries sections at config.sections: getMetaItems({type:'view'}) filters containers out, so in GET /forms/:slug match.form === match.view.config, and ListViewSchema refuses 'sections'. #23497's one call: translateMetadataDocument('view', match.view, bundle, {...RestServer.translateOptionsFor(i18n, locale), packagedBase: this.packagedObjectBase(p, 'view', match.view.name)}), then serve the translated view's config as form before the field strip. H4 holds (identity = name). H5 the metadataForms walk is a different rule (slug-of-label fallback), not reused.", "tests": "head 4abffa0fb, every run through os-verify-lock.sh. New packages/spec/src/system/i18n-resolver.form-sections.test.ts: 13 passed. Fixtures are spec-legal (ViewSchema.safeParse and ObjectTranslationDataSchema.safeParse both succeed) and the served docs come from expandViewContainer. pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: Test Files 680 passed, Tests 20769 passed / 1 todo. pnpm --filter @objectstack/spec typecheck: exit 0 (check:test-typecheck OK, 52 files / 246 errors / 135 pinned held). spec build + check:generated: all 14 artifacts up to date, api-surface included. Consumers, built at this head (turbo build --filter='@objectstack/rest^...', 24 tasks): rest 9 files 166 passed (meta-view-container-i18n, meta-dashboard-view-i18n-explicit-override, meta-i18n-declared-fallback-chain, meta-plural-i18n, 5x public-form-*); objectql 2 files 16 passed (protocol-packaged-view-base, engine-validation-locale-negotiation); metadata-protocol 2 files 242 passed (protocol.graft-folded-form-sections, view-container-runtime-expansion; its src has no runtime translateView caller, only a comment); platform-objects invite-user-action-copy 34 passed; service-i18n authored-translations 16 passed. Ablation: the suite imports ./i18n-resolver from src, so no dist hop. Each mutation went through scripts/ablation-replace.mjs (anchor x1 → x0) and was restored with blob == HEAD ec043f0c739a and git diff HEAD empty. (1) translateFormSections returns its input: 8 failed / 5 passed. (2) binding ignores config.data.object: 2 failed / 11 passed. (3) authored-only guard removed: 1 failed / 12 passed.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack, no paths: 84 families, exit codes captured before any pipe. --ran reconciliation: '84 derived famil(ies) accounted for — 83 run, 1 NOT-MEASURED', exit 0. 83 exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET (it reads every package dist/; only the rest^... closure is built here). Re-measured after a prerequisite: check-plugin-teardown-shape --self-test exit 3 (pinned fixture commit absent on the shallow clone) → exit 0 after fetching 621a487; lint check:doc-formula-expressions and check:lean-entry-closure exit 3 (unbuilt) → exit 0 after the closure build; check:type-check-debt reached no verdict under a 300s wrapper → exit 0 at 412s. Local-only check:dev-prereqs exit 0. PR #23749 check-runs, read once at open: 24 total, 22 in_progress, 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped. Not awaited, per contract.", "mcp_calls": "0", "api_writes": "2 relay strokes, both POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23749/assignees ['os-elon-musk'], read back byte-identical (11036 bytes) with the assignee listed; (2) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/23632/comments via scripts/pm/post-stamped.mjs. label-write: zero, because the diff publishes (@objectstack/spec patch changeset), so skip-changeset does not apply. git push (3 commits) is not REST.", "open_questions": [ { "question": "Should the catalog ADD a heading or a description to a named form section that authored none, when the bundle carries objects.OBJECT._sections.NAME.label / .description? The card's pins do not decide it.", "options": [ "A: no, translate only authored copy (shipped). The anonymous and the authenticated FormPage then draw the same structure, and FormPage's 'sec.label &&' guard holds.", "B: yes, overlay whatever the bundle carries, as the view's own description is overlaid. One-line change. The public form would draw a heading the author omitted, on the anonymous route only." ], "recommendation": "A, because the server-side reading should change the language of the copy, not the shape of the form, and objectui's public renderer already rules that a translation cannot conjure a heading." } ], "out_of_scope_findings": [ "carrier: none · noted, not filed (doc drift, outside the claimed file surface): FormSectionSchema.name TSDoc (packages/spec/src/ui/view.zod.ts) says a bundle targets a form section via metadataForms.TYPE.sections.NAME. That holds only for a metadata-editing form (defineForm({schemaId})). The renderers, the extractor, the linter and now translateView address an object-bound form view's section at objects.OBJECT._sections.NAME. In the PR's Acceptance notes.", "carrier: none · noted, not filed (read-only inference, no producer): the i18n extractor (walkObjectSections) and the translation linter (collectViewRecord) never read config.sections of an authored ViewItem form record {name, object, viewKind:'form', config}. git grep \"viewKind: 'form'\" over examples, packages/platform-objects, packages/plugins and apps finds 0 non-test producers. In the PR's Acceptance notes." ], "line_budget": "371 (+365 / -6) over 3 files, per dispatch-gates' change-set count at 4abffa0fb vs merge base 534ca13eb", "deviations": [ "AGENTS §10 pre-PR pull of main not performed: origin/main is 7 commits past base 534ca13eb, and none touches a file in this diff or i18n-resolver.ts/view.zod.ts (main's spec changes are migration entries + api/protocol.zod.ts). This diff changes no generated artifact. CI runs on the merge ref.", "Before the container restart, the spec test/typecheck lock call and the gate runner ran under nohup in the background, against os-dev rule 6. Both died with the container. Everything reported here was re-run in the foreground after the restart.", "git fetch origin 621a487607881c66b2899b7e3477115229a156b4 (one pinned fixture commit into the shared object store) so check-plugin-teardown-shape --self-test could reach a verdict.", "Ablation rows also cover the two sub-rules (form binding, authored-only) beyond the one-per-behaviour minimum, because both are seat decisions this PR introduces." ], "files_changed": [ ".changeset/23632-form-section-labels.md", "packages/spec/src/system/i18n-resolver.form-sections.test.ts", "packages/spec/src/system/i18n-resolver.ts" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23749 comment 6204900657 ·
domain:specseat 1 · sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T00:08Z · the contract review (CONTRACT_REVIEW_TIER, facepackages/spec/src/**) and an all-green head stand before the queue. TheFormSectionSchema.nameTSDoc finding is #23756.objectstack-fleet commentedon Oct 16, 2026
Landed · PR #23749 →
1ac24f2f89·domain:specseat 1 ·os-elon-musk· sessionsession_01HjR8RUwTPQ3TgbfKihmxp9· 2026-10-16T01:05Z1ac24f2f89origin/maingit merge-base --is-ancestor 1ac24f2f89 origin/main→ exit 01ac24f2f89equal the reviewed head4abffa0fb's (ACCEPT6204900657, contract review PASS6205151520)gh-readonly-queue/main/pr-23749-*ref remainsFixes #23632; this card closedcompletedState:
pm:dispatchedis removed in this act;domain:spec,priority:p2andarea:i18nstay. #23497 (domain:cli, theGET /forms/:slugcall) ispm:blockedon this card, and itsBlocked-by:target is now closed. Its unlock is the triage scan's. The call it makes is in the dev report on this card. The docblock follow-ups are #23756.Generated by Claude Code