Repository navigation
spec(i18n): the one authority for TranslateFn — the narrowed-t shape — lands in @object-ui/i18n; app-shell and fields re-export it #8261
Description
Sub-card of objectui#8165, filed by the director seat on the maintainer's ruling (decision batch #72, 2026-09-07 — option A). Blocks #8165.
The ruling this card executes
(key: string, options?: Record<string, unknown>) => string — the i18next t-narrowing that app-shell and fields both declare locally as TranslateFn — gets one authority, exported from @object-ui/i18n. The two remaining local declarations become type re-exports of it.
Why @object-ui/i18n and not @object-ui/types (B, also dependency-legal): semantic fit — this is a translation-function shape, not a schema type. ⛔ Not option D (rename the fields copy): a renamed copy makes the gate green while making the real duplication less visible, which is precisely the defect objectui#8203 records.
What has to happen
- Declare and export
TranslateFnfrom@object-ui/i18n's public face (measure the entry file rather than assume its path — finding(app-shell,fields):TranslateFnis declared three times, character-for-character identical — the cheapest available shrink of the one-authority KNOWN_COLLISIONS baseline #8165's dispatch named a staleindex.tsonce already). packages/app-shell/src/providers/writeWarningToast.ts(today's de facto authority) andpackages/fields/src/widgets/file-size-guard.tsre-export it:export type { TranslateFn } from '@object-ui/i18n'.saveAdvisoryToast.tsalready re-exports fromwriteWarningToast(PR fix(app-shell): re-point saveAdvisoryToast's TranslateFn at its one authority #8205) and can keep doing so or point at the new home.- The
KNOWN_COLLISIONSentry forTranslateFninscripts/__tests__/one-authority-per-exported-name-6273.test.tsshrinks to zero sites / is removed — the gate itself will tell you which, as it did in fix(app-shell): re-point saveAdvisoryToast's TranslateFn at its one authority #8205's ablation leg B ("lists collisions that no longer exist"). ⚠️ Re-measure that the two remaining declarations are still byte-identical before re-pointing (they were: sha25629f2f334…, 86 bytes, on158d75bb3). A re-export of a drifted type silently changes a call site's contract and no gate notices.
Clause-② — YES
This publishes a new name from a shared package (@object-ui/i18n). ⇒ the landing PR carries needs:contract-review. The #7741 landing-PR reading that TranslateFn was on no published face was true then and stops being true here.
Not in this card
- The other nine names / eleven declarations of the same shape across six packages (objectui#8203). This card creates the home; finding(app-shell,fields,plugin-*): one narrowed-
tshape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 is the mechanical re-point of everything else onto it, and should be dispatched after this lands.
Verification the dispatch will expect
Gate before/after; ablation putting a local declaration back and showing the gate reddens; the published .d.ts of @object-ui/i18n carrying the name (count against a control); type-check of app-shell and fields after the dependency closure is built.
Refs: objectui#8165 (parent) · PR #8205 (the landed half) · objectui#8203 (the shape population) · objectui#6172 (the one-authority ruling) · ruling comment on #8165, batch #72.
Activity
objectstack-fleet commentedon Sep 25, 2026
Claim: PM loop round 6 —
domain:uiexecution seat 2Session:
session_014mXUNuFomfj24w7s1pZzhNBranch:
claude/issue-8261-translatefn-authorityWorktree:
objectui-issue-8261Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/i18n/src/(declare and exportTranslateFnfrom the package's public entry),packages/app-shell/src/providers/writeWarningToast.tsandpackages/fields/src/widgets/file-size-guard.ts(type re-exports of it),scripts/__tests__/one-authority-per-exported-name-6273.test.ts(theKNOWN_COLLISIONSrow forTranslateFnshrinks to zero sites / is removed), tests, one changeset ('@object-ui/i18n': minorfor the newly published name;'@object-ui/app-shell': patch,'@object-ui/fields': patch). Stop on breach and explain it in the reportContainer & model:
S,mode:subagent,model: opus—dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes the maintainer's ruling on objectui#8165 (5565867673, decision batch #72, option A), which this card carriesClause-②: yes —
@object-ui/i18npublishes a new name,TranslateFnThread-read: none
Serial constraints cleared: open-PR file lists read 2026-09-25T16:05Z: no open PR touches
packages/i18n/src/index.ts,writeWarningToast.ts,file-size-guard.tsor the one-authority gate.check-governed-queue-guard.mjs --test: NOT GOVERNED. objectui#8203 (the other nine names) is NOT in this card; it follows after this lands, as the card says.Clause-②: yes— a shared package publishes the one authority for a type two packages declare locally today; the two local declarations become re-exports of it, and no call site's contract moves (re-measured byte-identical before re-pointing).Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 8261, "status": "done", "branch": "claude/issue-8261-translatefn-authority", "pr": "https://github.com/objectstack-ai/objectui/pull/10703", "session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, the dispatching PM's id; identity is the branch named in claim 5835312654", "premise_still_valid": true, "summary": "Option A is executed. packages/i18n/src/translateFn.ts declares TranslateFn once, and the i18n entry (src/index.ts, whose built output is exports['.'].types ./dist/index.d.ts) publishes it with export type. The two remaining declarations, app-shell providers/writeWarningToast.ts and fields widgets/file-size-guard.ts, now import type it and re-export it from @object-ui/i18n. saveAdvisoryToast.ts and metadataReadWarningToast.ts point at @object-ui/i18n directly: their docblocks named writeWarningToast as the owner and had to be rewritten anyway. The local, unexported TranslateFn in preview/usePublishAllDrafts.ts is also re-pointed; it differs only in its parameter NAME (opts), which TypeScript does not compare. The TranslateFn row leaves KNOWN_COLLISIONS. H2 byte identity held on base c659439b4 (sha256 29f2f334..., 86 bytes with the newline, cmp 0), so no call site's contract moves. H3: app-shell and fields already depend on @object-ui/i18n (workspace:*) and i18n depends only on core, which depends only on types, so there is no cycle and no package.json changed. Draft PR #10703 is open with a closing first line naming this card and a Clause-② line copied from the claim. The PR assignee os-elon-musk was set and read back. The card's assignee was never written.", "tests": "All at head 367e586e4. Heavy runs went through os-verify-lock.sh; verdicts are its VERDICT lines and timings are shared-box seconds. (1) Closure build: turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 gave 'Tasks: 28 successful, 28 total', VERDICT command-exit 0. (2) type-check (tsc --noEmit plus tsc -p tsconfig.test.json, script echo confirmed) exited 0 with zero 'error TS' lines for @object-ui/i18n, @object-ui/fields and @object-ui/app-shell; pnpm type-check:scripts also exited 0. (3) Built .d.ts: packages/i18n/dist/index.d.ts has TranslateFn 1 ('export type { TranslateFn } from ./translateFn.js') against control SafeTranslate 2; dist/translateFn.d.ts carries the declaration; packages/fields/dist/index.d.ts has TranslateFn 0 against control FileField 2. (4) vitest run over the gate, packages/i18n/, app-shell providers/ + preview/ + console/home/__tests__/ + MetadataService.saveAdvisories + useMetadataClient.advisorySink, and the 28 fields tests naming a file-size-guard consumer: 'Test Files 149 passed (149), Tests 2616 passed (2616)', VERDICT command-exit 0. (5) Gate before, on a detached base worktree at c659439b4 with 1 TranslateFn row: 'Test Files 1 passed (1), Tests 11 passed (11)'. Gate after, at head with 0 rows: 'Test Files 1 passed (1), Tests 11 passed (11)'. (6) Ablation through objectstack scripts/ablation-replace.mjs in wrap mode, anchors hit x1 to x0 and blobs moved on disk. LEG A put the declaration back in file-size-guard.ts (blob ca873659cf07 to 6e625184bee3): gate exit 1, 'TranslateFn — a NEW colliding name', sites packages/fields/src/widgets/file-size-guard.ts and packages/i18n/src/translateFn.ts, then the SHRINK-ONLY refusal. LEG B put the row back with the re-points kept (blob 600bed9be040 to 38991d28dc31): gate exit 1, 'KNOWN_COLLISIONS lists collisions that no longer exist ... TranslateFn — no longer collides at: writeWarningToast.ts, file-size-guard.ts', which proves the row removal was owed. LEG C was the type reverse-verification: a probe 'export const typeProbe8261: TranslateFn = (key: number) => String(key);' made fields tsc --noEmit exit 1 with exactly one error, TS2322 'Type (key: number) => string is not assignable to type TranslateFn'. Restore was proven by state: disk blob equals HEAD blob for both paths (ca873659cf07, 600bed9be040), git diff HEAD is empty, and afterwards declarations 0, re-exports 1, rows 0. (7) README snippet compiled with tsc --strict against the built i18n dist: exit 0; the negative control t(42) gave exit 2 with TS2345. (8) Gates: check-changeset-presence exit 0 ('7 source file(s) of 3 released package(s) changed ... declares 1 changeset'); changeset-no-major 0; changeset-fixed 0; changeset-overwrite 0 (report-only; lists the 8165 prose correction as case 2, both sides declaring nothing); check:new-line-citations 'VERDICT new-cross-file-line-citations: 0 new citation(s)'; check:control-bytes OK (8784 files); doc-fences 0; check-doc-links 0; check:readme-exports 0 (TranslateFn judged real from @object-ui/i18n; 8 other packages unbuilt and unjudged locally); phantom-deps 0; unused-deps 0; unreferenced-sources 0; pending-changeset-literals 0; i18n-keys 0; esm-specifiers 0; governed-queue-guard --test on the 11 paths reads NOT GOVERNED. eslint --no-inline-config --format json on the 8 changed source files: 8 files, 0 errors, 3 warnings, all pre-existing no-explicit-any in usePublishAllDrafts.ts (the base file has the same 3). DECLARED NARROWINGS: tests are targeted because the change is type-only and erases at build; the full app-shell and fields suites are CI's. Lint ran on changed files only; eslint.config.js has no projectService or parserOptions.project (a control grep for typescript-eslint fires), so untouched files cannot move. NOT MEASURED: check:doc-snippets, which needs a near-whole-workspace build and is CI's; the snippet was compiled standalone instead, as in (7). Main moved to 69a6fc1ce: merge-tree --write-tree is clean, 0 of main's 139 moved paths overlap this branch's 11, and main's diff has 0 TranslateFn lines, so there was no merge.", "mcp_calls": "0", "api_writes": "3 — all through the fleet-write relay as objectstack-fleet[bot], each one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft, PR #10703; body read back byte-identical, 14676 bytes, no differing byte); (2) label-write --assign, POST /repos/objectstack-ai/objectui/issues/10703/assignees (os-elon-musk; read back MATCHES, labels untouched); (3) post-stamped os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/8261/comments. Plus 2 git pushes, which are not REST.", "files_changed": [ "packages/i18n/src/translateFn.ts (new)", "packages/i18n/src/index.ts", "packages/i18n/README.md", "packages/app-shell/src/providers/writeWarningToast.ts", "packages/app-shell/src/providers/saveAdvisoryToast.ts", "packages/app-shell/src/providers/metadataReadWarningToast.ts", "packages/app-shell/src/preview/usePublishAllDrafts.ts", "packages/fields/src/widgets/file-size-guard.ts", "scripts/__tests__/one-authority-per-exported-name-6273.test.ts", ".changeset/8261-translatefn-i18n-authority.md (new: i18n minor, app-shell patch, fields patch)", ".changeset/8165-translatefn-one-authority.md (prose only, scoped 'at this change' plus a dated note; frontmatter lines 1-2 md5 2ee28f1e1b449ccc2dd97fdcc6c2bc1f before and after)" ], "deviations": [ "Interrupted by an API session limit, roughly 16:10Z to 18:25Z. Resumed from disk: the tree was clean and commit 367e586e4 was complete, then pushed; every gate was re-run afterwards.", "At resume, git fetch origin main failed with 'cannot lock ref refs/remotes/origin/main' because a sibling was fetching concurrently. I fetched into refs/issue-8261/main instead, and deleted that ref at the end.", "Three verify-lock calls ended VERDICT queue-timeout (exit 99). They are NOT MEASURED, not red. Each was re-queued under the same OS_VERIFY_LOCK_SLOT, and every run cited above is a measured VERDICT command-exit 0 run.", "H1: preview/usePublishAllDrafts.ts was re-pointed. The order allowed it, and it is identical in type, not bytes: only the parameter name opts differs. utils/managedByEmptyState.ts also has a local, unexported, byte-identical TranslateFn, which the order's H1 did not name. I left it untouched because it is outside the claim's and the order's file surface; see out_of_scope_findings.", "H4: saveAdvisoryToast.ts and metadataReadWarningToast.ts point at @object-ui/i18n directly rather than through writeWarningToast. Their docblocks asserted writeWarningToast was the owner, so they needed rewriting either way.", "Conflict, order followed: the card says the landing PR carries needs:contract-review, but the order allows no label write beyond the PR assignee. The label is NOT applied; the seat applies it.", "Conflict, order followed: the triage comment 5807061952 on objectui#8203 (closed not_planned) invited the #8261 taker to re-point the other names in the same PR. The order fences #8203's names out, so only TranslateFn-named sites were touched.", "The order's worktree rule was followed: the shared checkout was never edited, and all fetches ran in the worktree. After the PR opened, the worktree /home/user/objectui-issue-8261 and the scratch base worktree were removed without --force, having node_modules deleted first.", "Beyond the role-file template, the report adds files_changed and deviations keys, which the order asks for." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none (承接者:无; objectui#8203 is closed not_planned) · noted, not filed. packages/app-shell/src/utils/managedByEmptyState.ts keeps a local, unexported type TranslateFn that is byte-identical to the new authority. The gate cannot see it because it needs the export keyword. The one-line remedy is import type { TranslateFn } from '@object-ui/i18n'. It is an observation, not class a/b/c: it has no runtime reach. Recorded in the PR's Acceptance notes.", "carrier: none · noted, not filed. For objectui#8203's population: packages/core/src/actions/recordDelete.ts declares the same shape as 'Translate' and can never re-point at @object-ui/i18n, because @object-ui/i18n depends on @object-ui/core. Recorded in the PR's Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
✅ ACCEPT: objectui#10703 at
b235ae5; body, label removal, ready and auto-merge follow this ACCEPTdomain:uiseat #2,session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim5835312654. It executes the maintainer's ruling on objectui#8165 (5565867673, decision batch #72, option A).@object-ui/i18npublishesTranslateFn(newtranslateFn.ts,export typefrom the entry), declared once.writeWarningToast.tsandfile-size-guard.tsre-export it. The two sibling toast modules point at@object-ui/i18ndirectly. The local copies inusePublishAllDrafts.tsand (round 2)managedByEmptyState.tsimport it. The row leaves the one-authority gate'sKNOWN_COLLISIONSgit grepoverpackages/*/src/**: 4 declarations on basec659439b4, 1 at head (packages/i18n/src/translateFn.ts). Every re-point is byte-identical in shape (sha256 re-derived by the reviewer). No runtime line moved: the built JS gains no@object-ui/i18nimport, and there is no dependency cycle@object-ui/i18ndist/index.d.tsexports the name. The addressable faces of@object-ui/app-shelland@object-ui/fieldsare unchanged (the published 17.6.0index.d.tscarries 0TranslateFn), so no consumer import breaksClause-②: yes/ semver@object-ui/i18nminor(a newly published name),@object-ui/app-shelland@object-ui/fieldspatchmanagedByEmptyState.ts, and reworded the README to "the one authority". Round 2 found one body clause false. The seat applied the reviewer's replacement, and round 3 confirmed it. The pending8165changeset is scoped "at this change" with a dated note, and its frontmatter md5 is identical367e586(prose), round 2 FAIL atb235ae5(one body clause), round 3 PASS atb235ae5(body-only), all atCONTRACT_REVIEW_TIER. The round-3 record is posted with this landing. The seat read every transcript: every model turn ran at tier. It dischargesneeds:contract-review, which this landing removesgit merge-treeagainst mainf9d772b16: clean. No open PR touches the 12 pathsb235ae5: 40 green, 3 skipped, 0 redFixes #8261onlyFindings, each with its disposition
core'srecordDelete.tsTranslatecan never re-point, because@object-ui/i18ndepends on@object-ui/core⇒ Acceptance notes for objectui#8203's population. objectui#8203 is closednot_planned, so there is no carrier.KNOWN_COLLISIONSentry. The row is gone at this head, so the seat closes objectui#8165 after this merges and is verified on main.Deviations recorded
managedByEmptyState.tsin round 2, on the round-1 review's finding.usePublishAllDrafts.ts, the sibling unexported copy, was already re-pointed in round 1.State
In one stroke: the PR body,
needs:contract-reviewremoved,readyand auto-merge (squash), through the relay opsissue_patch+labels_remove+pr_ready+automerge_enable.Generated by Claude Code
feat(i18n): publish TranslateFn as the one authority; app-shell and f…