Skip to content

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

@os-zhuang

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

  1. Declare and export TranslateFn from @object-ui/i18n's public face (measure the entry file rather than assume its path — finding(app-shell,fields): TranslateFn is declared three times, character-for-character identical — the cheapest available shrink of the one-authority KNOWN_COLLISIONS baseline #8165's dispatch named a stale index.ts once already).
  2. packages/app-shell/src/providers/writeWarningToast.ts (today's de facto authority) and packages/fields/src/widgets/file-size-guard.ts re-export it: export type { TranslateFn } from '@object-ui/i18n'. saveAdvisoryToast.ts already re-exports from writeWarningToast (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.
  3. The KNOWN_COLLISIONS entry for TranslateFn in scripts/__tests__/one-authority-per-exported-name-6273.test.ts shrinks 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").
  4. ⚠️ Re-measure that the two remaining declarations are still byte-identical before re-pointing (they were: sha256 29f2f334…, 86 bytes, on 158d75bb3). 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

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

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 7, 2026
  2. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 6 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-8261-translatefn-authority
    Worktree: objectui-issue-8261
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/i18n/src/ (declare and export TranslateFn from the package's public entry), packages/app-shell/src/providers/writeWarningToast.ts and packages/fields/src/widgets/file-size-guard.ts (type re-exports of it), scripts/__tests__/one-authority-per-exported-name-6273.test.ts (the KNOWN_COLLISIONS row for TranslateFn shrinks to zero sites / is removed), tests, one changeset ('@object-ui/i18n': minor for the newly published name; '@object-ui/app-shell': patch, '@object-ui/fields': patch). Stop on breach and explain it in the report
    Container & 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 carries
    Clause-②: yes — @object-ui/i18n publishes a new name, TranslateFn
    Thread-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.ts or 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

  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    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

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT: objectui#10703 at b235ae5; body, label removal, ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim 5835312654. It executes the maintainer's ruling on objectui#8165 (5565867673, decision batch #72, option A).

    Implemented-by:  claude/issue-8261-translatefn-authority
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change @object-ui/i18n publishes TranslateFn (new translateFn.ts, export type from the entry), declared once. writeWarningToast.ts and file-size-guard.ts re-export it. The two sibling toast modules point at @object-ui/i18n directly. The local copies in usePublishAllDrafts.ts and (round 2) managedByEmptyState.ts import it. The row leaves the one-authority gate's KNOWN_COLLISIONS
    one authority git grep over packages/*/src/**: 4 declarations on base c659439b4, 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/i18n import, and there is no dependency cycle
    gate the one-authority gate: 11 of 11. Leg A (a declaration put back gives a NEW colliding name), leg B (the stale row gives "no longer collides") and leg C (a wrong signature gives TS2322) were each measured and restored
    published face @object-ui/i18n dist/index.d.ts exports the name. The addressable faces of @object-ui/app-shell and @object-ui/fields are unchanged (the published 17.6.0 index.d.ts carries 0 TranslateFn), so no consumer import breaks
    Clause-②: yes / semver @object-ui/i18n minor (a newly published name), @object-ui/app-shell and @object-ui/fields patch
    published text round 1 found two false sentences ("the name now has one declaration", "the one place that type is declared"). Round 2 made the first true by re-pointing managedByEmptyState.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 pending 8165 changeset is scoped "at this change" with a dated note, and its frontmatter md5 is identical
    reviews round 1 FAIL at 367e586 (prose), round 2 FAIL at b235ae5 (one body clause), round 3 PASS at b235ae5 (body-only), all at CONTRACT_REVIEW_TIER. The round-3 record is posted with this landing. The seat read every transcript: every model turn ran at tier. It discharges needs:contract-review, which this landing removes
    serial git merge-tree against main f9d772b16: clean. No open PR touches the 12 paths
    governed NOT GOVERNED (real paths)
    CI head b235ae5: 40 green, 3 skipped, 0 red
    closing keywords Fixes #8261 only

    Findings, each with its disposition

    • Dev: core's recordDelete.ts Translate can never re-point, because @object-ui/i18n depends on @object-ui/core ⇒ Acceptance notes for objectui#8203's population. objectui#8203 is closed not_planned, so there is no carrier.
    • The ruling says objectui#8165 closes on the gate's reading of the KNOWN_COLLISIONS entry. The row is gone at this head, so the seat closes objectui#8165 after this merges and is verified on main.

    Deviations recorded

    • The seat widened the file surface by managedByEmptyState.ts in round 2, on the round-1 review's finding. usePublishAllDrafts.ts, the sibling unexported copy, was already re-pointed in round 1.
    • Seat: the seat edited one Acceptance-notes sentence about the label, and the round-2 reviewer's clause, in the landing body; round 3 judged both.

    State

    In one stroke: the PR body, needs:contract-review removed, ready and auto-merge (squash), through the relay ops issue_patch + labels_remove + pr_ready + automerge_enable.


    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

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions