Uh oh!
There was an error while loading. Please reload this page.
docs(types): ui-action.ts no longer claims spec 17 narrowed I18nLabel to a plain string - #5617
Conversation
…bel is a plain string
The `ActionParam` doc block justified deleting the local `label` /
`options[].label` override with a claim about `@objectstack/spec` that was never
true: "in spec 17 `I18nLabelSchema` is `z.ZodString` -- inline per-locale
objects were dropped in favour of translation files". Measured against the
installed GA pin, `@objectstack/spec@17.0.0`, `dist/ui/index.d.ts:614`:
declare const I18nLabelSchema: z.ZodUnion(readonly (z.ZodString,
z.ZodRecord(z.ZodString, z.ZodString))) // real syntax at that line
Executed against `dist/ui/index.mjs`: plain string ACCEPTED, inline locale map
ACCEPTED, `{ key, defaultValue }` REJECTED. `ActionParamSchema.label` and
`options[].label` both carry that union, so the keys this paragraph is about
admit the map form today.
The correction describes what `I18nLabel` admits and quotes the schema's own doc
block (two authorized forms, "Both are real; neither is deprecated by this
schema") rather than restating a zod expression a spec bump can falsify. Where
today's spelling is named it is scoped as a measurement against 17.0.0 with its
file and line, which is the distinction the replaced sentence failed to make.
The decision the paragraph records is unchanged and was never structurally
dependent on the false premise: `label` flows in by reference through
`Omit(z.input(typeof ActionParamSchema), 'type')`, and deleting the override was
right for a reason the union's contents do not affect -- a plain string is one
of the admitted forms, so a local `string | I18nLabel` collapses to
`I18nLabel`.
Two things found while reading, filed rather than fixed here: the parity test's
self-described "INVERTED PIN" on this decision cannot detect a re-widening (it
assigns a plain string, which type-checks under either form -- so the widening
above went through it green), objectui#5612; and this file's now-unused
`I18nLabel` type import, objectui#5613.
Fixes#4611
Co-Authored-By: Claude <noreply@anthropic.com>`dist/ui-action.d.ts` is the release-visible surface that moves: the corrected paragraph is JSDoc on the exported `ActionParam` interface, so it ships in the declaration file and in consumers' hover text. Measured with the package's real `tsc` build (`removeComments: false`, per `tsconfig.base.json`), dist cleared and tsbuildinfo removed between runs: dist/ui-action.d.ts 29,176 -> 31,026 bytes, sha256 7c438361 -> 5a4513cc dist/ui-action.js 3,480 bytes, sha256 1b24e8f5, BYTE-IDENTICAL 108 emitted files both sides; ui-action.d.ts is the only one that moves The JavaScript does not move here -- not because comments are stripped (they are not), but because the comment documents an `interface`, which tsc erases together with its leading comment. Confirmed: no fragment of the block appears in `dist/ui-action.js` on either side. Co-Authored-By: Claude <noreply@anthropic.com>
✅ Console Performance Budget
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
Size Limits
|
os-sales
commented
Aug 21, 2026
PM review — ACCEPT (card #4611)Gates. 22 named check runs read individually for The observation that makes this more than a text swapThe old comment reached a correct conclusion through a false premise. The recorded decision — inherit, do not override — was right, and right for a reason the union's contents cannot affect: Keeping the conclusion and replacing only the reasoning is the right call and the harder one. The tempting move when you find a false premise is to assume the decision resting on it is also wrong; checking whether the conclusion survives on its own is what separates a repair from a regression. Two of my dispatch's claims were falsified, both correctlyThe emit premise. I told you a comment edit here moves The lane note is amended accordingly: comments on value declarations and inside function bodies move The
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#4611
One paragraph of
packages/types/src/ui-action.ts'sActionParamdoc block asserted something about@objectstack/specthat was never true, sitting on the exact symbol the label vocabulary turns on. A seat reading it while implementing thelabel/descriptionwidening would have taken that work for a no-op.The measurement, taken fresh against the installed GA
The card was filed against
17.0.0-rc.6; the pin has since moved to GA. Resolved frompackages/types:@objectstack/spec@17.0.0,node_modules/.pnpm/@objectstack+spec@17.0.0_.../dist/ui/index.d.ts:614, quoted with the generic brackets flattened so GitHub's body sanitizer cannot eat them (the file holds real syntax):Still a union; the comment is false against GA as it was against rc.6. Corroborated three ways:
:571-613) says it in as many words: a display label in one of two authorized forms (objectstack#5728, maintainer ruling 2026-08-06), a plain default-language string or an inline locale map, closing with "Both are real; neither is deprecated by this schema."dist/ui/index.mjs: plain string ACCEPTED, inline locale map ACCEPTED,{ key, defaultValue }REJECTED.ActionParamSchema.labelandoptions( ).labelare bothstring | Record( string, string )in the shipped declaration.Live rather than theoretical, measured in this tree: 26 files under
packages/contain apickLocalized(call site (19 of them non-test), e.g.packages/components/src/renderers/layout/containers.tsx:739resolvingschema.title.What the replacement says, and why it should not rot the same way
It leads with what
I18nLabeladmits and cites the spec's own doc block, rather than restating a zod expression a spec bump can falsify. Today's spelling is still recorded — a reader needs it — but scoped as "against@objectstack/spec@17.0.0it is ... (dist/ui/index.d.ts:614)", a reading of one named version with its file and line, and it says so: that is the distinction the replaced sentence failed to make.The decision is unchanged, and never structurally depended on the false premise.
labelflows in by reference throughOmit( z.input( typeof ActionParamSchema ), 'type' ), so it is whatever the spec declares. Deleting the local override was right for a reason the union's contents do not affect: a plain string is one of the admitted forms, so a localstring | I18nLabelcollapses toI18nLabeleither way — an exact restatement claiming to be wider than it is. The old text reached the right conclusion through a false premise; the correction keeps the conclusion and replaces the reasoning.Diff shape: comment-only, proven rather than asserted
@@ -315,9 +315,38 @@, 47 changed lines, every one matching^[+-] \*( |$)— JSDoc continuation lines, all inside the block spanning 226-355 that documentsexport interface ActionParam.tscbuild (removeComments: falsepertsconfig.base.json, so comments are emitted), dist cleared and tsbuildinfo removed between legs, restore leg guarded by an EXIT trap: 108 files both sides, exactly one differs —dist/ui-action.d.ts, 29,176 to 31,026 bytes, and its diff is likewise one hunk of 47 lines all matching^[+-] \*( |$).dist/ui-action.jsis byte-identical (3,480 bytes, sha2561b24e8f5). Not because comments were stripped — they were not — but because this block documents aninterface, which tsc erases along with its leading comment. No fragment of the block appears in the emitted JS on either side. Flagging this because the dispatch expected shipped JS to move: it does not here, and the reason is structural rather than a measurement artifact.Changeset form follows that measurement:
patchfor@object-ui/types, not empty frontmatter. The JSDoc is on an exported declaration, so it ships indist/*.d.tsand in consumers' hover text — consumer-visible API documentation, which is the criterion. No behaviour changes.No test and no ablation is possible on this change — there is no executable behaviour to pin and nothing to ablate; a mutation of comment text cannot turn any assertion red. Stating that plainly rather than manufacturing a test that would only pin prose.
Gates — exit code captured before any pipe, each gate's own verdict quoted
All run at
ec10aa217(the final commit) with a clean tree, from the repo root:pnpm --filter @object-ui/types type-checktsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json(script name echoed, so not a zero-match)pnpm --filter @object-ui/types lint✖ 255 problems (0 errors, 255 warnings)pnpm exec vitest run packages/types/Test Files 40 passed (40)·Tests 460 passed (460)check-control-bytescheck-control-bytes: OK (scanned 4666 tracked text file(s); skipped 85 binary)check-changeset-presence1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)check-changeset-fixedAll workspace packages are in the changeset fixed group.check-changeset-no-majorNo changeset declares a major bump.check-type-check-coverage45/46 via type-check, 0 known-broken (0 errors outstanding)check-lint-coverage46/46 packages linted, 0 with outstanding errors (0 total)check-spec-symbol-derivation1290 files scanned against 4912 spec export namescheck-action-forward-parity5 surfaces checked against 40 runtime-read keys from 4 consumersvitestwas run from the repo root with a path filter, neverpnpm --filter ... test(objectui#3378). Heavy runs went through the container's shared verify lock.Declared narrowing. Repo-wide
pnpm lint/pnpm type-check(turbo, 46 packages), the four test shards, and the import-shape gates (check:phantom-deps,check:self-import,check:esm-specifiers,check:i18n-keys,check:i18n-drift) were left to CI. Why the narrowing cannot hide a failure: ESLint here is not type-aware —eslint.config.jsdeclares noparserOptions.projectand noprojectService— so every file's lint verdict is a function of that file's own source, and the only source file this PR touches was linted green. For everything type- or behaviour-shaped, the emitted-artifact measurement above is the argument: the sole changed byte range in the whole build output is JSDoc text inside one.d.ts, so no declaration, no type, and no runtime value that any other package compiles or executes against has moved. Imports are untouched, no locale strings ort()call sites are involved.Two findings filed rather than fixed here
INVERTED PINon this very decision cannot detect a re-widening.packages/types/src/__tests__/page-nav-misc-spec-parity.test.ts:350-366assertsconst label: SpecI18nLabel = 'Priority', which type-checks under both the narrow and the wide shape. The spec has already re-widened and the pin stayed green — the event it promised to catch happened and it observed nothing. Out of this card's scope (different file), so it is filed, not touched. Itsit(...)title is now the last copy of the false belief in this package.ui-action.ts:27importsI18nLabeland never uses it (residue of the removed override). Confirmed by this PR's own lint run:27:3 warning 'I18nLabel' is defined but never used. Nothing fails on it:packages/types/tsconfig.jsonextends the roottsconfig.json, which setsnoUnusedLocals: false(tsconfig.base.json'struenever reaches the package), and the ESLint rule is a warning with no--max-warningscap. Left in place to keep this diff prose-only.skills/objectui/guides/i18n.mdcarries the same false belief as a rule — that is #5081,needs-user-decision, on a governed human-merge-only surface. Not touched here. Notes for whoever picks it up are in the report to the dispatching seat.Generated by Claude Code