Skip to content

feat(i18n): publish TranslateFn as the one authority; app-shell and fields re-export it (objectui#8261) - #10703

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-8261-translatefn-authority
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-8261-translatefn-authority

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #8261
Clause-②: yes — @object-ui/i18n publishes a new name, TranslateFn

Executes the maintainer's ruling on objectui#8165 (comment 5565867673, decision batch #72, option A): the narrowed i18next t gets ONE authority, exported from @object-ui/i18n, and the two remaining declarations become type re-exports of it. Round 1 was verified at 367e586e4. Round 2 (b235ae53d) applies the contract review's prose ruling and re-points the last local copy; its checks are listed separately below.

Spelling note: GitHub strips tag-shaped fragments from bodies, so the options parameter's type is written Record(string, unknown) here; the source spells it with angle brackets.

What changed

  • packages/i18n/src/translateFn.ts (new): export type TranslateFn = (key: string, options?: Record(string, unknown)) => string; with a docblock naming it the one authority.
  • packages/i18n/src/index.ts: export type { TranslateFn } from './translateFn.js';, next to the existing SafeTranslate export.
  • packages/app-shell/src/providers/writeWarningToast.ts and packages/fields/src/widgets/file-size-guard.ts: the local declaration is gone. Each now does import type { TranslateFn } from '@object-ui/i18n' for its own uses and export type { TranslateFn } from '@object-ui/i18n', so every existing import of the name from these two modules still resolves. From writeWarningToast that is AdapterProvider; from file-size-guard it is FileField, ImageField, LocationField and numberBadInput. None of their import paths change.
  • packages/app-shell/src/providers/saveAdvisoryToast.ts and metadataReadWarningToast.ts now re-export from @object-ui/i18n directly, not through writeWarningToast. Their docblocks said writeWarningToast was the owner, which this change makes false, so they had to be rewritten anyway. Pointing them at the real owner means an import resolves in one hop and no module in the chain claims ownership it lacks.
  • packages/app-shell/src/preview/usePublishAllDrafts.ts and packages/app-shell/src/utils/managedByEmptyState.ts: each local, unexported type TranslateFn is replaced by import type { TranslateFn } from '@object-ui/i18n' (see H1). managedByEmptyState.ts was added in round 2, when the seat widened the file surface by this one file.
  • scripts/__tests__/one-authority-per-exported-name-6273.test.ts: the TranslateFn row is removed from KNOWN_COLLISIONS. Its comment block is replaced by a "sat here" note in the form the other retired rows use. No line was added.
  • .changeset/8261-translatefn-i18n-authority.md: '@object-ui/i18n': minor (a newly published name), '@object-ui/app-shell': patch, '@object-ui/fields': patch. No major.
  • .changeset/8165-translatefn-one-authority.md: prose reconciled with a dated note (H6). The frontmatter is byte-identical.
  • packages/i18n/README.md: a short TranslateFn subsection (H6).

After this change, git grep over packages/*/src finds exactly 1 declaration of TranslateFn (exported or local, type or interface): packages/i18n/src/translateFn.ts. The same pattern finds 4 on the base c659439b4, which is the control.

H1: census on the base (c659439b4)

TranslateFn declarations:

site exported? seen by the one-authority gate?
packages/app-shell/src/providers/writeWarningToast.ts yes yes (the gate's row)
packages/fields/src/widgets/file-size-guard.ts yes yes (the gate's row)
packages/app-shell/src/preview/usePublishAllDrafts.ts no no
packages/app-shell/src/utils/managedByEmptyState.ts no no

saveAdvisoryToast.ts and metadataReadWarningToast.ts re-exported the name from writeWarningToast, as the order stated.

Neither local declaration is visible to the gate. Its DECLARATION pattern needs a leading export, and it counts exported type-level names only (bound 1 in its header). Both local declarations are rows in objectui#8203's table.

  • usePublishAllDrafts.ts is re-pointed. It is in this order's file surface. The order also asked whether it is identical in shape. It is identical in type, but not byte-identical: the parameter is spelled opts. TypeScript does not compare parameter names when it checks a function type, so the signature of usePublishAllDrafts(t: TranslateFn) is unchanged. packages/i18n/src/__tests__/perm-home-namespace-3546.test.tsx pins the text export function usePublishAllDrafts(t: TranslateFn), which is kept.
  • managedByEmptyState.ts is re-pointed (round 2). Round 1 left it alone because it was outside the file surface. The contract review then found that it made two published sentences false, and the seat widened the surface by this file. Before re-pointing, its declaration was checked against the authority with the export prefix set aside. Both are type TranslateFn = (key: string, options?: Record(string, unknown)) => string;, 79 bytes with the newline, sha256 083c3a40d29b6f256a603bb907f3e4de032a4dfe5abcca6c199220172d9dea24. It is byte-identical, so no signature moves.

The same shape under other names on the base: TranslationFn (AiChatPage), TFn (CloudAiModelStatus), OrgTranslate (exported), Translate (packages/core/src/actions/recordDelete.ts), InboxTranslate (exported), ActivityTranslate, CommentsTranslate, SummaryTranslate and TimelineTranslate (exported). Some spell the second parameter vars or params. These are objectui#8203's population. objectui#8203 is closed not_planned and is not addressed here.

H2: byte-identity, re-measured before re-pointing

On c659439b4, the two exported declarations are the same line:

export type TranslateFn = (key: string, options?: Record(string, unknown)) => string;
sha256 29f2f334f47b8ac55bd05fc5dcf7f0d769c303f0e2deb1c79aff6a9cd46e3762  (both lines, trailing newline included: 86 bytes)
sha256 1a7e8e9dd0c69e6cfb4e6ccff472df17d878277ef4eafb907fbb018d6600e2b2  (both, without the newline: 85 bytes)
cmp exit 0; identical od -c dumps

This matches the card's 29f2f334…, 86 bytes on 158d75bb3, so nothing has drifted. The new declaration in @object-ui/i18n is the same line, so re-pointing changes no call site's contract.

H3: the home

  • @object-ui/i18n's package.json declares types and exports['.'].types as ./dist/index.d.ts. tsconfig.json sets rootDir: src, so the entry file is packages/i18n/src/index.ts. The package has no stale second entry.
  • @object-ui/app-shell and @object-ui/fields both already list @object-ui/i18n: workspace:* in dependencies. @object-ui/i18n's only workspace dependency is @object-ui/core, which depends only on @object-ui/types, so there is no cycle. No package.json changed.
  • Built declaration, after turbo run build --filter='@object-ui/app-shell^...' (28/28 successful):
file TranslateFn control
packages/i18n/dist/index.d.ts 1: export type { TranslateFn } from './translateFn.js'; SafeTranslate 2
packages/i18n/dist/translateFn.d.ts 3 (the declaration plus two docblock mentions) n/a
packages/fields/dist/index.d.ts 0: the name is not on @object-ui/fields' face FileField 2

H5: the gate, before and after, plus ablation

run tree result
gate BEFORE detached worktree at c659439b4 (TranslateFn rows: 1) Test Files 1 passed (1), Tests 11 passed (11)
gate AFTER head 367e586e4 (TranslateFn rows: 0) Test Files 1 passed (1), Tests 11 passed (11)

Ablation was run at 367e586e4 after the commit. Every leg went through scripts/ablation-replace.mjs (objectstack) in wrap mode: the anchor had to hit exactly once, the blob change was checked on disk, and the file was restored from HEAD. The gate reads source from disk, so no build or dist is involved in legs A and B.

  • Leg A: a local declaration put back in file-size-guard.ts. The re-export line was replaced by the old declaration: anchor x1 to x0, replacement x0 to x1, blob ca873659cf07 to 6e625184bee3. Gate exit 1 (Tests 1 failed, 10 passed). The collision is named:
    TranslateFn — a NEW colliding name
        packages/fields/src/widgets/file-size-guard.ts — type declaration
        packages/i18n/src/translateFn.ts — type declaration
    ⛔ KNOWN_COLLISIONS is SHRINK-ONLY: adding a line is not a supported way …
    
  • Leg B: the removed row put back, re-points kept. This is the stale-row direction, modelled on fix(app-shell): re-point saveAdvisoryToast's TranslateFn at its one authority #8205's leg B. The mutation was 0 rows to 1, blob 600bed9be040 to 38991d28dc31. Gate exit 1 (Tests 1 failed, 10 passed), which proves the row removal was owed:
    KNOWN_COLLISIONS lists collisions that no longer exist …
      TranslateFn — no longer collides at:
          packages/app-shell/src/providers/writeWarningToast.ts
          packages/fields/src/widgets/file-size-guard.ts
    
  • Leg C: type reverse-verification of the rebuilt .d.ts. A probe export const typeProbe8261: TranslateFn = (key: number) => String(key); was planted in file-size-guard.ts, then pnpm --filter @object-ui/fields exec tsc --noEmit was run. Exit 1 with exactly one error, error TS2322: Type '(key: number) => string' is not assignable to type 'TranslateFn'. This shows that the type fields resolves from @object-ui/i18n is the real narrowed shape, not an any from an unresolved or stale declaration.
  • Restore, proven by state. Both paths ended with disk blob equal to the HEAD blob: ca873659cf07… for file-size-guard.ts and 600bed9be040… for the gate. git diff HEAD was empty. After the run: declarations 0, re-export 1, rows 0.

H6: published prose

  • packages/i18n/README.md did not list the package's exported types; its API section documents functions. Per AGENTS.md commandment Add automated testing infrastructure and CI/CD workflows #2, it gains a short ### TranslateFn — typing a t you are handed subsection with one ts snippet.
    • The section says the package is "the one authority for that type". It deliberately does not say "the one place that type is declared", because the same shape still exists under other names, such as core's Translate.
    • check:readme-exports judges TranslateFn as real from @object-ui/i18n.
    • The snippet was also compiled standalone with tsc --strict against the built packages/i18n/dist/index.d.ts: exit 0. A negative control changing t('common.save') to t(42) gave exit 2 with TS2345.
  • Pending changesets. git grep -l TranslateFn c659439b4 -- .changeset finds one file, .changeset/8165-translatefn-one-authority.md. This change makes two of its sentences false: that writeWarningToast is the one authority, and that the name is on no package's published face. Following the objectui#10533 form:
    • both sentences are now scoped "at this change";
    • a ⚠️ **Dated note, 2026-09-25 — the one authority has since moved to @object-ui/i18n — objectui#8261.** paragraph follows them;
    • the frontmatter is unchanged: lines 1–2 have md5 2ee28f1e1b449ccc2dd97fdcc6c2bc1f before and after.
  • This change's own changeset says "writeWarningToast, which its sibling toast modules re-export", because on the base, among app-shell's toast modules, only writeWarningToast declared the name; saveAdvisoryToast and metadataReadWarningToast re-exported it from there. It also keeps "the name now has one declaration", which the count above re-derives. Its frontmatter (lines 1–5) has md5 014eca5f59a0b36c1901fefa5467f47c at both 367e586e4 and b235ae53d.
  • check:changeset-claims lists three other pending bodies that name a file this change touches: 6349-types-internal-name-collisions-batch-1, 8632-malformed-picklist-option-loud and 9615-locale-count-ten. I read all three and none is falsified. Each describes its own change, and the README heading 8632 cites still exists.

Round 2 checks (at b235ae53d)

run result
turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 28 successful, 28 total
pnpm --filter @object-ui/fields type-check (tsc --noEmit && tsc -p tsconfig.test.json) exit 0, zero error TS
pnpm --filter @object-ui/app-shell type-check (same pair) exit 0, zero error TS
vitest run over the one-authority gate and packages/app-shell/src/utils/ the gate file has 11 of 11 tests passed, including declares no name twice outside the named, shrinking baseline. The whole run shows Test Files 35 passed, 1 skipped (36) and Tests 559 passed, 8 skipped (567); the lock run printed VERDICT command-exit 0. The skipped file is auditHistoryDisplay.dateOnlyZone-10183.test.ts, which is timezone-conditional and unrelated to this change
node scripts/check-control-bytes.mjs OK (8784 tracked text files)
pnpm check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s)
node scripts/check-changeset-presence.mjs exit 0: 8 source files of 3 released packages changed, 1 changeset added
git merge-tree --write-tree HEAD 56b7fb4d5 (fresh main) clean. None of main's 151 moved paths is among this branch's 12, and main's diff has 0 TranslateFn lines, so no merge was made

Round 1 tests and gates (all at 367e586e4)

Heavy runs went through os-verify-lock.sh, and the verdicts are its own VERDICT lines. Timings are shared-box seconds.

run result
turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 28 successful, 28 total; VERDICT command-exit 0
pnpm --filter @object-ui/i18n type-check exit 0, zero error TS
pnpm --filter @object-ui/fields type-check exit 0, zero error TS
pnpm --filter @object-ui/app-shell type-check exit 0, zero error TS
pnpm type-check:scripts (covers the gate file) exit 0, zero error TS
pnpm exec vitest run over the gate, packages/i18n/, app-shell providers/, preview/, console/home/__tests__/, MetadataService.saveAdvisories, useMetadataClient.advisorySink, and the 28 fields test files naming a file-size-guard consumer Test Files 149 passed (149), Tests 2616 passed (2616); VERDICT command-exit 0
node scripts/check-changeset-presence.mjs exit 0: 7 source files of 3 released packages changed, 1 changeset added
check-changeset-no-major / check-changeset-fixed exit 0 / exit 0
check-changeset-overwrite exit 0. Report-only; it reports the 8165 edit as its case 2 ("correcting … on purpose"), and both sides declare nothing
pnpm check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s)
pnpm check:control-bytes OK (8784 tracked text files)
check:doc-fences / docs:check-links / check:readme-exports exit 0 / exit 0 / exit 0
check:phantom-deps / check:unused-deps / check:unreferenced-sources exit 0 / exit 0 / exit 0
check:pending-changeset-literals / check:i18n-keys / check:esm-specifiers exit 0 / exit 0 / exit 0
check-governed-queue-guard --test on the 11 changed paths NOT GOVERNED
eslint --no-inline-config --format json on the 8 changed source files 8 files, 0 errors, 3 warnings. All 3 are pre-existing no-explicit-any in usePublishAllDrafts.ts; the base file has the same 3

Declared narrowings, so what was left out is visible:

  • Tests are targeted, not whole packages. The change is type-only (import type / export type erase at build). The full app-shell and fields suites are CI's.
  • Lint ran on the 8 changed files, not the repository. The file count comes from eslint's own JSON. eslint.config.js has no type-aware linting: no projectService, no parserOptions.project, and a control grep for typescript-eslint in the same file fires. So this diff cannot move the verdict on any untouched file. Repo-wide pnpm lint is CI's.
  • check:doc-snippets was NOT MEASURED locally. It needs almost the whole workspace built. The README snippet was instead compiled standalone against the built .d.ts, with a negative control, as shown under H6.
  • check:readme-exports saw 8 packages unbuilt. Their rows are unjudged locally. The @object-ui/i18n rows, including TranslateFn, were judged.
  • Main at round 1 was 69a6fc1ce. The merge-tree was clean with zero overlapping paths. Round 2's fresh-main reading is in the table above.

Acceptance notes

  • For whoever takes up objectui#8203's population: packages/core/src/actions/recordDelete.ts Translate can never point at @object-ui/i18n, because @object-ui/i18n depends on @object-ui/core.
  • Labels: the card says the landing PR carries needs:contract-review. This dispatch allowed no label write beyond the PR assignee, so the seat applied it; the seat removes it at landing, after a contract review PASS.
  • objectui#8165: its fields half is done by construction here. The ruling comment says that card closes on the gate's reading of the KNOWN_COLLISIONS entry, so objectui#8165 remains open for the seat to close.

⛔ Left as draft on purpose: the seat flips it after review.


Generated by Claude Code

…ields re-export it (objectui#8261)

The narrowed i18next `t` shape `(key: string, options?: Record<string,
unknown>) => string` was declared byte-identically in app-shell's
writeWarningToast and fields' file-size-guard. Per the maintainer's ruling
on objectui#8165 (option A) the one declaration now lives in
@object-ui/i18n and is published from its entry; both former declarations
re-export it, the sibling toast modules and usePublishAllDrafts import it
from there, and the TranslateFn row leaves the one-authority gate's
KNOWN_COLLISIONS baseline.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 3 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6349-types-internal-name-collisions-batch-1.md

  • names scripts/__tests__/one-authority-per-exported-name-6273.test.ts → scripts/__tests__/one-authority-per-exported-name-6273.test.ts — edited by this change

    The three KNOWN_COLLISIONS lines came down in the same change; that baseline (scripts/__tests__/one-authority-per-exported-name-6273.test.ts) is shrink-only and fails in both directions, so converging without deleting them would have been red too. 43 entries → 40.

.changeset/8632-malformed-picklist-option-loud.md

  • names packages/i18n/README.md → packages/i18n/README.md — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

.changeset/9615-locale-count-ten.md

  • names packages/i18n/README.md → packages/i18n/README.md — edited by this change

    Correcting the numeral to ten was weighed and refused. A restated count is the construct that permitted the error, and this repository has now watched it recur three times over the same fact — objectui#3351 in a changeset file, objectui#7989 in packages/i18n/README.md, and this one in published source. So the number is gone rather than corrected: the sentence now claims only what it needs (the key is translated in every locale the package ships) and names all-locales-key-parity.test.ts as the instrument that re-derives it. That test asserts the stronger invariant — every pack defines every en key — over whatever the pack set is on the day it runs, so an eleventh pack makes the sentence more true rather than false, and there is no figure left to rot (AGENTS.md commandment 完善设计器的每一个细节 #9).

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with c61099087 (merge-base with origin/main): 10 file(s) changed outside .changeset/, read against 1538 pending declaration(s) that publish a body (2123 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.5 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-_5r5-xaZ.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 547.33KB 130.98KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.18KB 65.61KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.52KB 14.64KB
plugin-charts (index.js) 80.42KB 22.22KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 133.85KB 35.54KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.55KB 39.16KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.70KB 59.29KB
plugin-kanban (index.js) 48.36KB 15.09KB
plugin-list (index.js) 115.82KB 28.76KB
plugin-map (index.js) 22.85KB 7.58KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.84KB 9.01KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.83KB 22.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 367e586e4e70ea08bd53336440f390f901955b79

① Derived judgments

  • One authority — for exported names yes; for the name, no. git grep over packages/*/src at head finds two declarations of TranslateFn: packages/i18n/src/translateFn.ts (exported, the authority) and packages/app-shell/src/utils/managedByEmptyState.ts (type TranslateFn = …, unexported, untouched). At merge-base c659439b4 there were four (exported: writeWarningToast.ts, file-size-guard.ts; unexported: usePublishAllDrafts.ts spelled opts, managedByEmptyState.ts). Every exported site is now export type { TranslateFn } from '@object-ui/i18n' (writeWarningToast, saveAdvisoryToast, metadataReadWarningToast, file-size-guard); no aliasing re-export as TranslateFn exists anywhere in the tree.
  • Byte identity holds. The declaration line with its newline is 86 bytes, sha256 29f2f334f47b8ac5…, at base in both writeWarningToast.ts and file-size-guard.ts, at 158d75bb3 in all three (incl. saveAdvisoryToast.ts), and at head in packages/i18n/src/translateFn.ts. managedByEmptyState.ts's line hashes identically once export is prefixed. usePublishAllDrafts differed only in the parameter name; parameter names are not part of a function type; the pin usePublishAllDrafts(t: TranslateFn) in perm-home-namespace-3546.test.tsx is kept.
  • Published face. Built at head (turbo 29 tasks, 28 content-hash cache hits, app-shell executed): packages/i18n/dist/index.d.ts carries export type { TranslateFn } from './translateFn.js' (1; control SafeTranslate 1); dist/translateFn.d.ts carries the declaration. fields/dist/index.d.ts 0 (control FileField 2); app-shell/dist/index.d.ts 0 (control AdapterProvider 1). Published 17.6.0 tarballs (npm pack): dist/index.d.ts of @object-ui/i18n, @object-ui/app-shell, @object-ui/fields each carry 0 TranslateFn; their exports maps expose only . plus a css subpath, so the deep dist/providers/writeWarningToast.d.ts and dist/widgets/file-size-guard.d.ts that did declare it are shipped bytes, not an addressable face. So no consumer could import TranslateFn from @object-ui/app-shell or @object-ui/fields before, and nothing breaks now. Root tsconfig: isolatedModules: true, no verbatimModuleSyntax; export type { X } from is the legal spelling under both, and every in-repo importer carries the inline type marker.
  • Runtime erases. Built app-shell/dist/providers/{writeWarningToast,saveAdvisoryToast,metadataReadWarningToast}.js have zero import lines; usePublishAllDrafts.js has 6, none from @object-ui/i18n; a single-file isolatedModules transpile of file-size-guard.ts emits no import; the fields bundle's one @object-ui/i18n import statement names only pre-existing runtime symbols. Same as 17.6.0. Arrows: i18n depends on core, core on types; app-shell and fields on i18n; no package.json changed; no cycle.
  • Toast modules. Comment-stripped diff base..head of saveAdvisoryToast.ts and metadataReadWarningToast.ts is exactly two lines each: the import type and export type { … } from specifiers, './writeWarningToast.js' to '@object-ui/i18n'. No runtime line moved.
  • Gate re-run in a detached worktree at head under os-verify-lock.sh (VERDICT command-exit 0, held 161s): 11 passed. Leg A, the declaration put back in file-size-guard.ts (decl 0 to 1, re-export 1 to 0, blob ca873659 to 6e625184): 1 failed, "TranslateFn — a NEW colliding name" naming file-size-guard.ts and i18n/src/translateFn.ts, then the SHRINK-ONLY refusal. Leg B, the row put back with re-points kept (rows 0 to 1, blob 600bed9b to 49a2b024): 1 failed, "KNOWN_COLLISIONS lists collisions that no longer exist … TranslateFn — no longer collides at writeWarningToast.ts, file-size-guard.ts", so the row's removal was owed. Both restores: disk blob equals the HEAD blob, git status --porcelain empty. Leg C, a probe typed TranslateFn but taking key: number in file-size-guard.ts: exactly one TS2322 from fields tsc, restored to the HEAD blob. Type-check (tsc --noEmit plus tsconfig.test.json) exit 0, zero error TS, for i18n, fields and app-shell.
  • Declared leftovers. managedByEmptyState.ts is inside the gate's population (published package, src/, not a test) but invisible to it: its DECLARATION regex requires a leading export. The ruling's sentence is about the shape ("the one authority for the narrowed-t shape lives in @object-ui/i18n") and explicitly sequences the rest of the population into finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 "once spec(i18n): the one authority for TranslateFn — the narrowed-t shape — lands in @object-ui/i18n; app-shell and fields re-export it #8261 lands", so leaving it is consistent with the ruling's ordering. But finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 was closed not_planned on 2026-09-24 (no carrier remains; the PR says "Carrier: none"), and the PR re-pointed the other unexported TranslateFn (usePublishAllDrafts.ts, an finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 row) while leaving the byte-identical one. core's Translate cannot re-point because i18n depends on core: correct.

② Semver level

  • '@object-ui/i18n': minor is right: a new name on the exports-reachable face is Clause-② yes, at least minor, and major is refused by the fixed group (check-changeset-no-major; CI Changeset Bump Policy green). '@object-ui/app-shell': patch and '@object-ui/fields': patch are right: their addressable faces are unchanged (built index.d.ts 0 before and after); only shipped deep .d.ts bytes move. The fixed group lifts all 39 packages to the same minor regardless.
  • Clause-②: yes — @object-ui/i18n publishes a new name, TranslateFn on the PR body's second line: correctly stated (value first after the colon; no arm is needed for a non-breaking addition), identical to the claim's line, consistent with minor.
  • New changeset, sentence by sentence. Publishes-as-authority sentence: true. Snippet: compiles. "each declared their own copy … byte-identical … both modules now re-export … no call site's contract moves": true for writeWarningToast and file-size-guard; the parenthetical "(writeWarningToast and its sibling toast modules)" is inexact, at base both siblings re-exported (decl 0, re-export 1 each) and only writeWarningToast declared. "Neither package publishes the name from its own entry": true. ⛔ "the name now has one declaration": false on the tree, managedByEmptyState.ts declares type TranslateFn; the sentence ships into three CHANGELOGs.
  • README section. First sentence true. The snippet compiles against the built packages/i18n/dist/index.d.ts (exit 0; a t(42) control gives TS2345; a control pointed at a declaration file without the export gives TS2305, so the pass is a real resolution). ⛔ "This package is the one place that type is declared": false on the tree for the same reason (and ten further declarations of the shape under other names per finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203); it ships in the tarball.
  • .changeset/8165-translatefn-one-authority.md. Frontmatter lines 1–2 md5 2ee28f1e1b449ccc2dd97fdcc6c2bc1f at main and at head; the file is unchanged between merge-base and main. The two rewritten sentences are scoped "At this change" / "at this change"; the ⚠️ **Dated note, 2026-09-25 — … — objectui#8261.** paragraph matches the form 28 pending changesets on main already carry. Every sentence is true after this change; "later in this same release" holds as long as the file exists to be edited. Empty frontmatter, so this prose reaches no CHANGELOG.
  • Other pending changesets matching TranslateFn|writeWarningToast|file-size-guard on main: only 8165 and 9615-locale-count-ten.md; 9615's claims (the docblock names all-locales-key-parity.test.ts and restates no count) still hold in file-size-guard.ts at head. CI Changeset Claim Re-read green.

③ Boundary flags

  • CI on head 367e586e4: 43 check runs, 40 success, 3 skipped (matrix placeholders), 0 failure, 0 in progress at the final read; commit status success. Type Check, Lint, Test shards 1–8, Build & E2E, the four changeset checks, README Export Check, Doc Snippet Type Check and Governed Surface Queue Guard all success.
  • Mergeability: git merge-tree --write-tree clean against main at bc97f9247 and again at 1e946c96a; 0 of 148 paths main moved since merge-base overlap the PR's 11; main's diff carries 0 TranslateFn lines. PR is draft, no auto-merge, head unchanged through this review.
  • Open PRs: 11 others; none touches any of the 11 paths, packages/i18n/, app-shell/src/providers/, file-size-guard or the gate.
  • Governed surfaces: none of the 11 paths is under docs/adr, .claude, skills, AGENTS.md or CLAUDE.md.
  • File surface vs. claim: the Claim comment names i18n src/, writeWarningToast.ts, file-size-guard.ts, the gate, tests, one changeset. The PR also touches saveAdvisoryToast.ts (card-sanctioned: "can … point at the new home"), metadataReadWarningToast.ts (same pattern), usePublishAllDrafts.ts (an finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 row; the body's "in this order's file surface" is not verifiable from the claim), i18n/README.md (commandment Add automated testing infrastructure and CI/CD workflows #2) and the 8165 changeset (truth maintenance). Not a breach I can show; the usePublishAllDrafts-yes, managedByEmptyState-no split is the inconsistency noted in ①.
  • Residue carrier: finding(app-shell,fields,plugin-*): one narrowed-t shape is declared 11 times under 9 names — 8 of them invisible to the one-authority gate, which matches on NAME #8203 closed not_planned; the PR's acceptance note names none. Nothing in this PR owes that work, but the published prose may not claim it done.
  • Discharge: this record is FAIL, so needs:contract-review stays on the PR. The wording fix is one commit; that commit is a new head and needs its own same-shape record; every ① and ③ reading above carries over while the source diff is unchanged.

Implemented-by: claude/issue-8261-translatefn-authority
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

What stops it: two false sentences in published prose (CHANGELOG and README bytes); nothing in code.

  1. .changeset/8261-translatefn-i18n-authority.md, last sentence: replace "the name now has one declaration." with "the gate now derives one authority for the name: one exported declaration, in @object-ui/i18n."
  2. packages/i18n/README.md, ### TranslateFn section: replace "This package is the one place that type is declared — import it rather than declaring a local copy:" with "This package is the one authority for that type — import it rather than declaring a local copy:".
    Optional in the same commit: "(writeWarningToast and its sibling toast modules)" to "(writeWarningToast, which its sibling toast modules re-export)". The alternative that makes sentence 1 literally true for the name is a one-line, type-only re-point of managedByEmptyState.ts to import type { TranslateFn } from '@object-ui/i18n' (byte-identical shape verified); sentence 2 still needs the wording change because the shape stays declared under other names.

Generated by Claude Code

…bject-ui/i18n; make the published prose true (objectui#8261)

managedByEmptyState.ts kept a local, unexported, byte-identical copy of
the narrowed `t`, which left two published sentences false: the changeset's
"the name now has one declaration" and the README's "the one place that
type is declared". The copy now imports the one authority; the README
says "the one authority for that type" (the same shape still exists under
other names), and the changeset names writeWarningToast as the only
app-shell module that declared it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.8 KB 3104.5 KB
Main entry chunk (gzip) 148.2 KB 350 KB
Entry file index-DiPrIq3Q.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 547.33KB 130.98KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.18KB 65.61KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.52KB 14.64KB
plugin-charts (index.js) 80.42KB 22.22KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 133.85KB 35.54KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 154.22KB 39.52KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.70KB 59.29KB
plugin-kanban (index.js) 48.36KB 15.09KB
plugin-list (index.js) 115.91KB 28.80KB
plugin-map (index.js) 22.85KB 7.58KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.84KB 9.01KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.83KB 22.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b235ae53d2b8a6c591953d5dda4a34467319f3ce

① Derived judgments

  • Round-2 diff (367e586e4..b235ae53d): one commit, three files. managedByEmptyState.ts loses type TranslateFn = … plus its docblock and gains import type { TranslateFn } from '@object-ui/i18n' plus a three-line comment; the non-comment diff is exactly those two lines, no runtime line, and @object-ui/app-shell already lists @object-ui/i18n: workspace:*. The changeset changes only the parenthetical; the README only the one sentence.
  • Byte identity, re-derived. The removed line (79 bytes with newline) hashes 083c3a40d29b6f256a603bb907f3e4de032a4dfe5abcca6c199220172d9dea24 at base and at 367e586e4; the authority line with export stripped hashes the same; with it, the 86-byte 29f2f334….
  • Census. git grep -P '^\s*(export\s+)?(declare\s+)?(type|interface)\s+TranslateFn\b' over :(glob)packages/*/src/**: base 4 (writeWarningToast, file-size-guard, usePublishAllDrafts, managedByEmptyState), 367e586e4 2, head 1 (packages/i18n/src/translateFn.ts:30). Whole tree at head also 1; the only other type TranslateFn text is a string fixture in scripts/__tests__/check-i18n-call-site-keys.test.ts:527. Note for the next reader: a literal pathspec 'packages/*/src' returns 0 at every ref; the glob needs /**.
  • Type-check and suites, at head, in a detached worktree with the app-shell dependency closure restored from turbo's content-hash cache (28 of 28 cached, not executed; built i18n face carries export type { TranslateFn } once, dist/translateFn.d.ts present) and a self-contained pnpm install --offline --frozen-lockfile --ignore-scripts, under the lock (VERDICT command-exit 0, held 129s): app-shell tsc --noEmit && tsc -p tsconfig.test.json exit 0, fields the same, zero error TS. Gate file 11 of 11, including declares no name twice outside the named, shrinking baseline. Gate plus packages/app-shell/src/utils/: Test Files 35 passed, 1 skipped (36); Tests 559 passed, 8 skipped (567); the skipped file is auditHistoryDisplay.dateOnlyZone-10183.test.ts (8 skipped tests), as the body says. Legs A, B, C not re-run: the gate file, file-size-guard.ts and translateFn.ts are byte-identical between 367e586e4 and head, so round 1's readings carry.
  • Unlocked gates at head: eslint on the 9 changed source files 0 errors, 3 pre-existing no-explicit-any warnings in usePublishAllDrafts.ts; check-changeset-presence 8 source files of 3 released packages, 1 changeset; check-control-bytes OK (8784); check:new-line-citations 0 new; check-changeset-claims names the same three pending bodies.
  • Round 1's two sentences. Changeset: "the name now has one declaration" is now true on the tree. README: "the one authority for that type" is the round-1 wording; "one place" is gone. Parenthetical: reads as round 1 proposed; true at head under the reading that the siblings re-export the name (they do, from @object-ui/i18n). Frontmatter lines 1–5 md5 014eca5f59a0b36c1901fefa5467f47c at both heads. No earlier-true sentence regressed: "Neither package publishes the name from its own entry", "both modules now re-export it from @object-ui/i18n", the README snippet, and the 8165 changeset (unchanged since round 1, lines 1–2 md5 2ee28f1e… equal to main; its dated note "declared once in @object-ui/i18n" is now literally true) all hold.
  • Landing body file (md5 ddbab486…, 160 lines), what changed since round 1: no angle brackets; the only closing keyword is Fixes #8261 on line 1; no model identifier. Round-2 paragraph and table: every figure re-derived (type-checks 0/0, vitest totals and skipped file, control bytes 8784, citations 0, presence 8/3/1; at 56b7fb4d5 main had moved 151 paths, the branch has 12, overlap 0). H1 table: the 4 base rows match the census; the gate-visibility column matches its DECLARATION regex (leading export required); the siblings' base re-exports from writeWarningToast and both locals as objectui#8203 rows (closed not_planned 2026-09-24) confirmed. managedByEmptyState.ts bullet: sha and 79 bytes correct. "exactly 1 declaration … 4 on the base": correct. H6 README bullet: correct, and CI README Export Check is green. Three pending bodies: 6349 (its claims are about its own change; the gate is still shrink-only, rows 30 to 29 here), 8632 (README heading "Scope — the engine.* carve-out" exists, line 273), 9615 (file-size-guard.ts unchanged since round 1, still names all-locales-key-parity.test.ts, restates no count): none falsified. Labels sentence: card spec(i18n): the one authority for TranslateFn — the narrowed-t shape — lands in @object-ui/i18n; app-shell and fields re-export it #8261 says the landing PR carries needs:contract-review; the timeline shows objectstack-fleet[bot] applied it at 19:23:58Z; it is still on the draft PR; 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 is open. Minor: H3's SafeTranslate 2 is a grep -c line count (useSafeTranslate matches as a substring); round 1 counted the name, 1; not wrong as stated.
  • ⛔ One body clause is false against the body's own H1 table. H6, the "This change's own changeset" bullet: "because on the base only writeWarningToast declared the name in app-shell." On the base app-shell declared the name three times (writeWarningToast exported, usePublishAllDrafts and managedByEmptyState local), as H1 lists. Replacement: "because on the base, among app-shell's toast modules, only writeWarningToast declared the name; saveAdvisoryToast and metadataReadWarningToast re-exported it from there."

② Semver level

Unchanged from round 1 and still right: '@object-ui/i18n': minor (new name on the exports-reachable face), '@object-ui/app-shell': patch, '@object-ui/fields': patch; round 2 adds only a type-only import inside app-shell, no face or runtime change; no major; frontmatter byte-identical; CI Changeset Bump Policy and Fixed Group Check green. Body line 2 Clause-②: yes — … still correctly stated.

③ Boundary flags

  • CI on head: 43 check runs, 40 success, 3 skipped matrix placeholders, 0 failure, 0 in progress at 20:13:25Z; commit status success; PR draft, mergeable_state clean, no auto-merge, head unchanged through this review.
  • Mergeability: git merge-tree --write-tree clean against main 383a34f52; 0 of main's 169 moved paths overlap the PR's 12; main's diff carries 0 TranslateFn lines.
  • Measurement conditions: shared box; two borrowed sibling installs vanished mid-run (their checkouts were removed by other agents), so the final readings come from the worktree's own offline install; the turbo build was a full cache restore, not an execution. check:doc-snippets and check:readme-exports not run locally; both are green in CI at head.
  • File surface: 12 paths; the one added in round 2 is under packages/app-shell/src/utils/, not governed (CI Governed Surface Queue Guard green).
  • Discharge: not as the body file stands. The stop is one clause in the landing body, not in the tree: apply the replacement above to the body file and every reading in this record stands at the same head, with no new commit or record needed; on that edit the seat may remove needs:contract-review at landing.

Implemented-by: claude/issue-8261-translatefn-authority
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

What stops it: one false clause in the landing body file (H6 changeset bullet), exact replacement given in ①; nothing in code, changeset, README or CI.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b235ae53d2b8a6c591953d5dda4a34467319f3ce

① Derived judgments

  • The replaced clause is true at base c659439b4. writeWarningToast.ts:26 declares export type TranslateFn = …; saveAdvisoryToast.ts:73 and metadataReadWarningToast.ts:96 each carry export type { TranslateFn } from './writeWarningToast.js' (plus an import type of it at lines 41 and 63). Of the 14 toast-named files under packages/app-shell/src at base, only those three sources and two tests mention the name; no other toast module declares it. "Among app-shell's toast modules, only writeWarningToast declared the name; the two siblings re-exported it from there" is exact.
  • Consistent with H1. The H1 table's three app-shell declarations are writeWarningToast (toast module, exported) plus usePublishAllDrafts.ts and managedByEmptyState.ts (preview/ and utils/, not toast modules); the clause's "among app-shell's toast modules" scope excludes exactly those two. H1's own line "saveAdvisoryToast.ts and metadataReadWarningToast.ts re-exported the name from writeWarningToast" says the same thing. The census with :(glob)packages/*/src/** re-derives base 4, 367e586e4 2, head 1 (packages/i18n/src/translateFn.ts:30, also the whole-tree count), so the bullet's "the name now has one declaration" is still true on the tree.
  • The body file differs from round 2's judged version by exactly this one clause. Reverting only the H6 clause in a copy reproduces round 2's recorded md5 ddbab486af09e986adfb198864982393; reverting the label sentence as well does not. So the Labels sentence was already in the file round 2 judged, and no sentence exists that round 2 did not judge. Live PR body is still round 1's (all differences are the round-2 edits round 2 enumerated, plus this clause).
  • Labels sentence is true. Timeline: the dispatch's one allowed write is the assignee (objectstack-fleet[bot], 19:19:08Z); needs:contract-review was applied by objectstack-fleet[bot] at 19:23:58Z, after the round-1 body had been posted saying the label was "left for the seat", and the seat's round-1 review prompt (19:24:39Z) states "The label is on the PR". It is still on the draft PR. "The seat removes it at landing, after a contract review PASS" matches round 2's discharge sentence.
  • No regression; re-derived at head: intro ("re-points the last local copy": 2 declarations at 367e586e4, 1 at head; round-2 diff is 3 files); What-changed bullet (managedByEmptyState.ts:28 import type { TranslateFn } from '@object-ui/i18n', non-comment diff is that line plus the removed declaration); line 38 (79 bytes, sha256 083c3a40…, equal to the authority line with export stripped); line 103 (changeset frontmatter md5 014eca5f… at both heads); README says "one authority for that type", "one place" absent; Round-2 table's main figures (151 moved paths at 56b7fb4d5, 12 branch paths, overlap 0, 0 TranslateFn lines; 56b7fb4d5 and 69a6fc1ce both on origin/main).
  • Hygiene. The file has no < character at all; every > is inside a code span's =>. Closing keywords: only Fixes #8261 on line 1; the other keyword words (resolves, closed, closes, fixed, close) are followed by prose, and objectui#NNNN is not an owner/repo closing form. No CR bytes, no model identifier.

② Semver level

Body-only round; the tree is unchanged since round 2. '@object-ui/i18n': minor, '@object-ui/app-shell': patch, '@object-ui/fields': patch stand; no major; body line 2 Clause-②: yes — … correctly stated; CI Changeset Bump Policy and Fixed Group Check green at head.

③ Boundary flags

  • Head unchanged: branch claude/issue-8261-translatefn-authority and the PR head both read b235ae53d2b8a6c591953d5dda4a34467319f3ce; merge-base with origin/main is c659439b4.
  • CI on head: 43 check runs, 40 success, 3 skipped (two coverage matrix placeholders, dependabot), 0 failure, 0 in progress; commit status success. Type Check, Lint, Test shards 1–8, Build & E2E, the changeset checks, README Export Check, Doc Snippet Type Check, Governed Surface Queue Guard all success.
  • Mergeability: git merge-tree --write-tree clean against origin/main at f9d772b16; PR draft, mergeable_state clean, no auto-merge.
  • Tree readings carry: with the tree byte-identical to round 2's head, every ① and ③ reading in rounds 1 and 2 stands.
  • Discharge: yes, with this body. The live PR body is still round 1's and carries sentences now false at head ("managedByEmptyState.ts is NOT touched", "All gates below were run at head 367e586e4", the managedByEmptyState acceptance note), so the seat must apply the body file verbatim before landing; on that, needs:contract-review is discharged and the seat removes it at landing. No new commit or record is needed.

Implemented-by: claude/issue-8261-translatefn-authority
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 20:37
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit a695f50 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-8261-translatefn-authority branch September 25, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(i18n): the one authority for TranslateFn — the narrowed-t shape — lands in @object-ui/i18n; app-shell and fields re-export it

2 participants