Skip to content

feat(types,fields): formula and summary widgets read the spec's returnType and summaryOperations; field metadata retires the snake spellings (objectui#11070, round 3) - #11240

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11070-field-types-round3
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11070-field-types-round3

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Refs #11070
Clause-②: yes (narrowing). The form-field face declares the spec's returnType and summaryOperations (the strict accept set widens by those two keys), while a malformed value for either is now refused by the tolerant face too, and FormulaFieldMetadata / SummaryFieldMetadata / PasswordFieldMetadata retire their snake_case members (the published TypeScript types narrow), under the startup no-gradualism rule. Both directions were measured on the built face at base and head (below), and the changeset prices the break as minor.

Refs, not a closing keyword, on purpose: the card keeps its remainder after this lands (the grid field's columns, the object-chart declaration, the dashboard pairs per objectui#11228, and the reference_to stop below). This is round 3 of the card, executing the seat's answers Q2 A and Q3 A (5914385802) under claim 5915152317. Draft; the dev does not mark it ready.

What changed

  • Form-field face (@object-ui/types). FormField declares returnType?: SpecField['returnType'] and summaryOperations?: SpecField['summaryOperations']; the zod mirror FormFieldSchema declares stripImportedDefaults(SpecFieldSchema).shape.returnType / .summaryOperations. Both are the spec's FieldSchema members by reference, never a restated copy. form-field-zod-coverage gains the two keys.
  • Widgets (@object-ui/fields). FormulaField reads returnType only (number to two decimals, boolean as Yes/No, date through the shared date face, else text). SummaryField reads summaryOperations.function only (count as it arrives; sum / avg / min / max to two decimals). The snake_case reads are gone, with no alias and no dual read. FormulaField's currency branch is gone as well: the spec's returnType does not list currency.
  • Field metadata types (@object-ui/types, field-types.ts). FormulaFieldMetadata.return_type becomes returnType; SummaryFieldMetadata.summary_object / summary_field / summary_type become summaryOperations (object / field / function); PasswordFieldMetadata.min_length / max_length become minLength / maxLength. Each new member is SpecField['KEY']. SummaryFieldMetadata.summary_filter also becomes summaryOperations.filter, under the bounded in-place exemption (same defect class and same interface; a mechanical fold whose target shape the spec pins; the file is this claim's; no new verification surface). Nothing reads summary_filter, and leaving it would have kept a second spelling of filter beside summaryOperations.
  • Fixtures. fields-formula/* write the spec's expression and returnType; fields-summary/* fold summary_object / summary_field / summary_type into summaryOperations { object, field, function }. The fields-lookup and fields-password fixtures already wrote reference / minLength (round 1).
  • Tests. The five packages/fields tests that rendered FormulaField with return_type: 'date' write returnType: 'date', now as annotated literals (the as any cast is gone, so tsc -p tsconfig.test.json judges the key). A new pin, widgets/__tests__/formula-summary-spec-reads-11070.test.tsx, covers both widgets' spec reads, each against a control that changes only that key, plus the retirement (a snake spelling alone formats nothing). strict-face-read-keys-11070.test.ts accepts the two keys, refuses bad values on both faces, moves return_type / summary_type from PENDING to RETIRED (with summary_object / summary_field), and adds type-level pins: the members equal the spec's exactly, and the retired names are gone from the three metadata types.
  • Ledger rows the declarations move, and only those. zod-mirror-parity's UNNAMED_LAZY_SLOTS records form.zod.ts#FormFieldSchema.summaryOperations: its filter is the spec's recursive filter condition, a z.lazy inside an imported schema. imported-defaults-8317's comment no longer states a member count. No parity key set or total moved.
  • Docs. fields/formula.mdx, fields/summary.mdx and fields/password.mdx teach the spec members in their typed fences, their plaintext fences and their prose (including the summary page's backend sketch).
  • Changeset. .changeset/11070-field-metadata-spec-spellings.md: @object-ui/types and @object-ui/fields minor, with the break stated (AGENTS.md: the fixed group never declares major). Three pending entries this change makes false get a dated note, appended in the maintainer's shape: 6138-fields-schema-block-parity-pr2.md (the shipped return_type union), 8194-fields-date-widget-convention.md and 8809-date-carrier-unparsable.md (return_type: 'date').

Stopped: reference_to (the ruling's live-producer case)

The brief's stop rule says not to retire a spelling a live producer still writes, and to stop at that key. reference_to has live producers inside this repository, so LookupFieldMetadata.reference_to, MasterDetailFieldMetadata.reference_to, the lookup readers' reference_to limb, lookup.mdx and its fixtures are untouched. H2 holds: MetadataProvider's comment is a read (d.reference ?? d.reference_to), and the writers are these.

writer (production source) package writes reference_to onto
normalizeFieldReferenceKeys (the ingestion stamp) core every ingested object field definition with a target (MetadataProvider's list and by-name reads, ObjectStackAdapter.getObjectSchema)
paramToField app-shell an action parameter's lookup field
RecordDetailView (toField) app-shell detail section fields
FlowReferenceField app-shell the field prop of LookupField, a typed LookupFieldMetadata literal
RecordDetailPanel plugin-detail DetailViewField, whose contract declares reference_to
RecordMetaFooter plugin-detail the lookup cell renderer's field
RelatedList plugin-detail a typed FieldMetadata literal
fieldEnrichment, buildDefaultPageSchema plugin-detail enriched and synthesized field definitions
sectionFields plugin-form a section field's override
bulkParamToField plugin-grid a bulk-action parameter field
ObjectGallery plugin-list enriched field definitions

The three readers that would lose the limb are LookupField (reference_to || reference), UserField (reference || reference_to) and LookupCellRenderer (reference_to || reference). The pending .changeset/6837-reference-to-arm-deletion.md records leaving exactly those three alone for this reason. objectstack's example apps (objectstack origin/main 00a92e18d) write reference_to 0 times. Retiring it means retiring ObjectUI's own internal carrier spelling across six packages; that is the maintainer question in the report, not this PR.

Runtime probe: an object-bound formula and summary field, before and after

A scratch test (never committed) rendered ObjectForm in edit mode with a stub data source. getObjectSchema returned field definitions exactly as objectstack's example apps write them: app-todo's days_overdue (returnType: 'number'), app-crm's line_total (summaryOperations: { object, field, function: 'sum' }), plus a boolean formula, a date formula and a count roll-up. findOne returned the record. The base leg swapped in the two widget files' base blobs (hash equal to base verified) and restored them (blob equal to HEAD, git diff HEAD empty).

field, as the example apps write it value base 0c95d3d head
formula, returnType: 'number' 3 3 3.00
formula, returnType: 'boolean' true true Yes
formula, returnType: 'date' 2026-07-04 2026-07-04 Jul 4
summary, function: 'sum' 15750.5 15750.5 15750.50
summary, function: 'count' (control) 4 4 4

The type formatting did not apply on an object-bound form before, and it does now. This is the user-visible half of the round.

Census

Type-checker census of the retired members. A TypeScript LanguageService findReferences from each member's declaration, run at base 0c95d3d. The program was every tracked TS/TSX file that names one of the member names (223 files), under the root tsconfig (its paths map @object-ui/types to source), with the other packages' types resolved from built dist.

member typed references outside the declaration
FormulaFieldMetadata.return_type 0
SummaryFieldMetadata.summary_object / summary_field / summary_type / summary_filter 0 each
PasswordFieldMetadata.min_length / max_length 0 each
LookupFieldMetadata.reference_to (the live control, and the stop case) 8 writes: FlowReferenceField, RelatedList, 6 test literals
MasterDetailFieldMetadata.reference_to 1 write: RelatedList

A first run without built dists missed FlowReferenceField (its LookupField import resolved to nothing), so the reading above is the run with them. The untyped reads, found by grep because any hides them from the type checker, are these. return_type is read by FormulaField and written by the five fields tests through as any. summary_type is read by SummaryField. min_length / max_length are read only by the generic minLength ?? min_length readers: buildValidationRules, ObjectForm, EmbeddableForm, the form renderer's input / textarea / fallback branches, RichTextField and TextAreaField. Those serve every text-family type, and none is password-specific; PasswordField reads neither. So the package set is @object-ui/types and @object-ui/fields. Every package that names the changed or newly typed members was type-checked as well: core, components, plugin-designer, plugin-form, app-shell and example-schema-catalog.

M3, before and after. Round 2's method: a port of PR objectui#11069's findUndeclaredKeys plus measure-strict-authoring-face.mjs's loaders and node walk, run over the SHIPPED face from a freshly built packages/types/dist in each tree.

reading base 0c95d3d head 2a83d35
node documents 599 599
refused by the strict face 96 93
of them strict-only 60 57
refused by the tolerant face 36 36
class (ii) pairs / occurrences / strict-only 12 / 53 / 53 10 / 47 / 47

Every pair that moved is listed. form · fields[].return_type 3 → 0 and summary_type 3 → 0 leave class (ii). summary_object 3 → 0 and summary_field 3 → 0 leave class (i). form · fields[].formula 3 → 0 becomes form · fields[].expression 0 → 3, a lateral move inside class (i): the spec formula key, which nothing on the form path reads and FormField does not declare (see Acceptance notes). The 10 pairs left are the 9 dashboard pairs and the grid field's columns. H3 holds for its first half. Its second half, the doc-taught reference_to / min_length, had nothing to lose: M3 reads only JSON documents and JSON fences, and neither key occurs there at base. The password.mdx TypeScript fence is judged by check:doc-snippets instead (below).

Accept sets, measured on the built face at base and head.

document (a form field) base tolerant / strict head tolerant / strict
returnType: 'number' accept / refuse accept / accept
summaryOperations: { object, field, function: 'sum' } accept / refuse accept / accept
returnType: 'datetime' accept / refuse refuse / refuse
summaryOperations with no function, with function: 'first', or with an unknown member accept / refuse refuse / refuse
return_type: 'number', summary_type: 'sum' accept / refuse accept / refuse

Tests and gates

Final head 998fb63. It differs from 2a83d35 by one changeset sentence, and from 52a2d42 only in packages/types tests and that changeset. Heavy runs went through os-verify-lock, and exit codes were captured before any pipe.

run at result
pnpm exec vitest run packages/types/ 2a83d35 exit 0 · Test Files 291 passed (291) · Tests 7079 passed (7079)
pnpm exec vitest run packages/fields/ 52a2d42 (fields unchanged since) exit 0 · Test Files 225 passed / 1 skipped (226) · Tests 3505 passed / 7 skipped
examples/schema-catalog/ plus the tests that walk the whole catalog (app-shell widget-dom-leak sweep, cli check-validity-recogniser, core rowPredicateCanon and component-deprecation, six scripts/__tests__ censuses) 2a83d35 exit 0 · Test Files 46 passed (46) · Tests 2605 passed (2605)
type-check: types (tsc + examples + tsconfig.test.json), fields (tsc + tsconfig.test.json) 2a83d35 exit 0 · both Done
type-check: core, components, plugin-designer, plugin-form, example-schema-catalog, app-shell 2a83d35 exit 0 · all Done
closure build (check-doc-snippet-types.mjs --build-filter, turbo) 2a83d35 exit 0 · 35 successful, 35 total
pnpm check:doc-snippets 2a83d35 exit 0 · 696 of 696 block(s) judged, 0 failed
check:doc-fences · docs:check-links · check:control-bytes · check:doc-example-ids · check:test-path-roots 998fb63 all exit 0
check:changeset-claims · check:pending-changeset-literals · changeset:check · check-changeset-presence.mjs · check:new-line-citations 998fb63 all exit 0 (0 new citation(s); presence declares this change's changeset)
check:doc-examples · check:doc-types · check:spec-symbols · check:doc-example-readers 2a83d35 all exit 0
eslint JSON on the 15 changed TS files 2a83d35 0 errors; warnings equal to or below base per file (base blobs linted through --stdin). Type-aware linting is not enabled in eslint.config.js (tseslint.configs.recommended, no parser project), so the diff cannot move a verdict on an untouched file
check-governed-queue-guard.mjs --test over the 28 paths 998fb63 NOT GOVERNED

The pending changesets check:changeset-claims names for files this change edits (30 of them) were read. None states anything about the members this change moves, apart from the three with a dated note.

Reverse verification. Each ablation was committed first, predicted before the run, and applied through objectstack's scripts/ablation-replace.mjs: the anchor hit once, the blob moved, and the restore left the blob equal to HEAD with git diff HEAD empty.

ablation predicted measured
A1 FormulaField reads return_type again the new pin's 3 formula cases red; every FormulaField row of the five date files red; summary green 35 red / 190 green: 3 in the pin, 32 FormulaField rows, no other row
A2 SummaryField reads summary_type again 5 red (sum / avg / min / max and the retirement case) 5 red / 4 green
A3 zod FormFieldSchema.returnType deleted 3 red (strict-face acceptance, datetime refused on both faces, coverage exact set) plus tsc red 3 red / 83 green; tsc -p tsconfig.test.json exit 2 (TS2322 in the parity ledger)
A4 FormulaFieldMetadata keeps a return_type member tsc red at the retired-members pin only exit 2, one TS2344 at assertionRetiredMembersAreGone
A5 formula.mdx's typed fence writes return_type again check:doc-snippets 1 failed, proving the gate judged the rebuilt dist exit 1 · 696 of 696 block(s) judged, 1 failed · TS2561 naming return_type ("Did you mean to write 'returnType'?")

NOT MEASURED locally, left to CI: the repository-wide lint and test shards beyond the named sets; the Spec Main Shape Gate; type-check of the downstream packages that name none of the changed members.

Acceptance notes

  • The formula fixtures are still strict-refused, now for expression. The ruling moved them to the spec formula key. FormField does not declare expression, and nothing on the hand-authored form path reads it (the value is computed by the backend), so it is class (i), the same as formula before. The three summary fixtures now parse on the strict face. Round 1 dropped the auto-number fixtures' format for the same reason. Whether to drop expression from these three fixtures is in the report.
  • FormulaFieldMetadata.formula is the spec's expression under another name. The spec refuses formula by name, with a rename hint to expression. The type still declares formula, so formula.mdx's typed fence still teaches it. formula also has readers outside this claim (app-shell's metadata admin reads def.expression ?? def.formula, and plugin-form's sanitize reads fieldDef.formula). This is the same snake-versus-spec family, left for the card's remainder.
  • The other text-family metadata types still declare min_length / max_length (Text, Textarea, Markdown, Html, Richtext, Email and Url), and their pages teach them. They have live dual readers. Same family, outside Q3's ruling, which named PasswordFieldMetadata.
  • auto_compute / auto_update stay declared. Nothing reads them, but neither is a snake twin of a spec member, so they are noted here only.

Generated by Claude Code

…eturnType and summaryOperations (objectui#11070)

The form-field face declares `returnType` and `summaryOperations` by
reference to the spec's `FieldSchema` members, on both faces, with the
form-field-zod-coverage row. `FormulaField` reads `returnType` and
`SummaryField` reads `summaryOperations.function`; the snake_case reads are
gone. `FormulaFieldMetadata.return_type` and `SummaryFieldMetadata`'s
`summary_object` / `summary_field` / `summary_type` / `summary_filter` retire
for the spec members, and `PasswordFieldMetadata.min_length` / `max_length`
for `minLength` / `maxLength`.

Refs objectui#11070 (round 3).

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…aryOperations / minLength (objectui#11070)

The fields-formula fixtures write the spec's `expression` and `returnType`;
the fields-summary fixtures fold `summary_object` / `summary_field` /
`summary_type` into `summaryOperations { object, field, function }`. The
five packages/fields tests that wrote `return_type: 'date'` write
`returnType`, now as annotated literals. A new pin covers both widgets'
spec reads, each against a control, and the retirement of the snake reads.
formula.mdx, summary.mdx and password.mdx teach the spec members. One
changeset (types + fields minor, breaking stated), and a dated note on the
three pending changesets whose `return_type` wording this makes false.

Refs objectui#11070 (round 3).

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…azy slot (objectui#11070)

`summaryOperations` is the spec's `FieldSchema` member by reference, and its
optional `filter` is the spec's recursive filter condition, a `z.lazy` inside
an imported schema that no local const names. The blind-region ledger of the
wider direction records it, as the leg asks when a new lazy source joins.

Refs objectui#11070 (round 3).

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…larations bring (objectui#11070)

Measured on the built face at base and head: a `returnType` of `datetime`
and a malformed `summaryOperations` were stripped unjudged by the tolerant
face before, and are refused now.

Refs objectui#11070 (round 3).

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 30 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/5903-objectgantt-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743); the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair — but at that time the ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction, not that nothing was there. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger requires every registered pair's mirrored-but-undeclared key set to equal that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/5905-componentinput-inputtype-tombstone.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    The write was measured as a no-op before it was deleted, and re-measured on this branch's base rather than inherited from the card. A structural census over every inputs: array in the repository (211 regions, all tracked TS/TSX/JS sources) scores inputType at exactly ONE authoring site — the plugin-markdown registration — against name 953, type 969, label 966, description 194, enum 119, required 86 and binding 4 in the same pass over the same regions, so the instrument was not blind. The other 192 in-repo inputType hits are a DIFFERENT face: FormField.inputType (zod/form.zod.ts), the text-input renderer's prop, and SchemaBuilder.inputType, none of which sit on a ComponentInput. The publication path is unchanged and was re-confirmed: packages/sdui-parser/src/index.ts forwards exactly seven keys per input — name, type, of, required, enum, binding, description — so an authored inputType could not reach the published sdui.manifest.json even in principle.

.changeset/5928-classname-style-props-rename.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Where the non-pair is recorded now. zod-mirror-parity.test.ts keys its existing EXCLUSIONS entry — the mechanism that accounts for every exported const with no TypeScript declaration to mirror, each with its stated reason — to ClassNameStylePropsSchema. Named for its own two keys, the const leaves no like-named declaration for a name-derived pairing to reach for.

.changeset/6051-gantt-flat-config-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743) — the same retirement objectui#5903's entry carries, for the same clause and the same reason; the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair, at a time when that ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger reconciles every registered pair's mirrored-but-undeclared key set against that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/6150-undeclared-but-consumed-keys.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ AMENDED, and the amendment ships in this same release. The TreeViewSchema slice (604476d97) gave the key a zod arm after all — a NAMED REFUSAL (handlerKeyRefusal(key, 'runtime-slot', label)), never a shape — because "no mirror entry" is not neutral under BaseSchema.passthrough(): it meant an authored { "type": "tree-view", "onNodeClick": { "action": "toast" } } parsed GREEN, survived the parse, and reached a call site that expects a function. ⇒ the three clauses this bullet used to carry are no longer true of the code shipping beside it. The key is now a MEMBER of TreeViewSchema.shape and an authored value is refused BY NAME at path onNodeClick; it has LEFT zod-mirror-parity.test.ts's RuntimeOnlyDeclared for that file's KnownDrift; and it is no longer "the first pair to sit there without also sitting in UnmirroredDeclared" — draining it emptied that difference, so RuntimeOnlyDeclared is now a SUBSET of UnmirroredDeclared and the union of the two equals UnmirroredDeclared itself. ⛔ objectui#6152's ruling is untouched by any of this: what the key still does not have, and never will, is a z.function() shape — no serialized document could satisfy one.

.changeset/6175-column-state-persistence.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Nothing is retired. Both spellings remain declared on DataTableSchema; onColumnReorder stays declared and stays unwired, exactly as the RuntimeOnlyDeclared ledger in zod-mirror-parity.test.ts records it. Which of the two survives is a declared-surface ruling that stays open and is deliberately not settled here.

.changeset/6349-name-authority-batch-3.md

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    @object-ui/components — ComboboxOption now IS @object-ui/types' declaration. The component declared its own { value, label }, a strict subset of the ComboboxOption that @object-ui/types declares for ComboboxSchema.options and mirrors in form.zod.ts ({ value, label, disabled? }). The component now re-exports the types declaration (through the @object-ui/types/form subpath — the root barrel does not publish the name), so the name ComboboxOption exported from @object-ui/components gains the optional disabled?: boolean member. Every value that type-checked before still does — nothing narrows and no key changes type; the one thing that moves is keyof ComboboxOption, so a consumer that EXHAUSTS the type (a Record over its keys) will need the new key. Note that the Combobox component itself does not read option.disabled — that member was already declared on the @object-ui/types face and is now visible on this one too; it is recorded as a separate finding, not changed here.

.changeset/6396-previous-values-dom-leak.md

  • names packages/types/src/form.ts → packages/types/src/form.ts — edited by this change

    Scope is the runtime leak only. The declared key stays exactly as declared (packages/types/src/form.ts, packages/types/src/zod/form.zod.ts are untouched): it has a live consumer, so there is nothing here for the enforce-or-remove channel.

  • names packages/types/src/zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    Scope is the runtime leak only. The declared key stays exactly as declared (packages/types/src/form.ts, packages/types/src/zod/form.zod.ts are untouched): it has a live consumer, so there is nothing here for the enforce-or-remove channel.

.changeset/6397-partial-schema-collapse-pin.md

  • names packages/types/src/form.ts → packages/types/src/form.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — a generic re-spelling of this Omit does not collapse — objectui#9256. Later in this same release objectui#9256 declared a key-remapping OmitDeclared ({ [P in keyof T as P extends K ? never : P]: T[P] }, in packages/types/src/form.ts) and re-spelled the heritage of InputShorthandSchema and UiCalendarSchema with it. Such a mapped type iterates the named members and the index signature separately, so it drops exactly K, keeps every other named member with its modifiers, and keeps the index signature. The same spelling of this alias declares 28 properties at ButtonSchema and 62 at ObjectGridSchema in this release, where the shipped alias declares 1 at each; at this change's own commit, under the same TypeScript, it declares 27 and 61, the source counts in the table above. So "every generic re-spelling collapses" was not true when it was written. The alias is still left as written: re-spelling it would narrow what every instantiation accepts, a contract change and not a pin's business. The PartialSchema docblock and partial-schema-collapse-pin.test.ts carry the correction. The rest of this entry is kept as the reading of this change.

.changeset/6639-objectgrid-title-mirrored.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The gain is the typed refusal: the mirror's .passthrough() base was already admitting any title unexamined, and it now enforces the declared string. zod-mirror-parity.test.ts's UnmirroredDeclared ledger records the key as worked off — the ledger's first shrink by repair (97 + 1 mirrored + 23 reclassified is what the seeded "121" now means).

.changeset/6938-checkbox-wrapper-class.md

  • names packages/types/src/form.ts → packages/types/src/form.ts — edited by this change

    packages/components/src/renderers/form/checkbox.tsx:36 reads cn("flex items-center space-x-2", schema.wrapperClass) — classes on the wrapper div around the box and its label — and neither the TypeScript interface in packages/types/src/form.ts nor the zod mirror in zod/form.zod.ts declared the key. It compiled through BaseSchema's index signature and parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on FileUploadSchema and FilterBuilderSchema (objectui#6150); the checkbox was left out only because its doc page's schema block is a six-line summary.

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    packages/components/src/renderers/form/checkbox.tsx:36 reads cn("flex items-center space-x-2", schema.wrapperClass) — classes on the wrapper div around the box and its label — and neither the TypeScript interface in packages/types/src/form.ts nor the zod mirror in zod/form.zod.ts declared the key. It compiled through BaseSchema's index signature and parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on FileUploadSchema and FilterBuilderSchema (objectui#6150); the checkbox was left out only because its doc page's schema block is a six-line summary.

.changeset/7113-chart-data-model.md

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

.changeset/7344-handler-string-any-mirrors.md

.changeset/7530-predicate-envelope-declared.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.
  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.

.changeset/7654-floating-chatbot-trigger-icon-tombstone.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Every other tombstone in this package pairs ?: never with a retirementTombstone() refusal on the Zod twin. There is no twin here to carry one: FloatingChatbotConfig has no Zod mirror at all, and floatingConfig sits in the UnmirroredDeclared ledger (zod-mirror-parity.test.ts, complex.zod.ts#ChatbotSchema). BaseSchema is .passthrough(), so the whole floatingConfig object rides through unvalidated — before this change and after it. Minting a mirror to host a refusal would be the declared-but-UNMIRRORED axis (objectui#6152), a different defect: a key can be mirrored and inert, or unmirrored and live, and fixing one says nothing about the other. This change does not widen into it.

.changeset/7697-combobox-option-root-barrel.md

  • names src/form.ts → packages/types/src/form.ts — edited by this change

    Additive only. ComboboxOption is added to the root barrel's existing named re-export list from ./form.js, next to the sibling option types that were already there (SelectOption, RadioOption). Nothing is removed, retyped or narrowed: the declaration stays in src/form.ts, its three members (value, label, disabled?) are unchanged, and the @object-ui/types/form subpath spelling keeps working exactly as before. Both spellings now resolve to the same declaration.

.changeset/7722-wrapper-class-five-more.md

  • names form.ts → packages/types/src/form.ts — edited by this change

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

.changeset/7735-zod-mirrors-stop-authoring-defaults.md

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    What changed. All 41 .default() call sites under packages/types/src/zod/ are removed — layout.zod.ts 22, crud.zod.ts 11, form.zod.ts 5, views.zod.ts 2, app.zod.ts 1. @object-ui/components reconciles the third face a separate finding found: flex's registration defaultProps.align seeded 'center', the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds 'start'.

.changeset/7804-tree-view-handler-slot.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The pair moves from zod-mirror-parity.test.ts's RuntimeOnlyDeclared to its KnownDrift, which empties the former of the one entry the latter did not also hold — so the two unmirrored ledgers are now in a containment relation, and the cross-ledger figure that recorded their difference states the containment instead.

.changeset/8072-input-wrapper-class-mirrored.md

  • names form.ts → packages/types/src/form.ts — edited by this change

    packages/components/src/renderers/form/input.tsx reads cn("grid w-full items-center gap-1.5", schema.wrapperClass) onto the wrapper div around the input and its label. The TypeScript face has declared the key all along (form.ts, docblock "Input wrapper CSS class"); the zod mirror never did, so the value rode through .passthrough() and { type: 'input', wrapperClass: 42 } validated GREEN — while the identical document on any of the other eight schema.wrapperClass readers (checkbox, file-upload, filter-builder — objectui#6150 / b74a8598d; switch, textarea, date-picker, select, list — objectui#7722) was refused at the key.

.changeset/8338-retire-toast-action.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The parity ledgers drain with it, every figure re-derived by zod-mirror-parity.test.ts's own AST and mirror instruments rather than stepped by hand: KnownDrift 42 entries / 64 keys → 41 / 63 (the entry's whole content, so the entry went too — the ledger's first loss by RETIRING a key rather than by moving either face toward the other), and WiderThanDeclared 23 / 36 / 47 arms, split 6 / 30 / 0 / 11 → 22 / 35 / 45, split 6 / 29 / 0 / 10. The pair itself stays registered, so EXPECTED_MIRROR_PAIRS does not move.

.changeset/8478-describe-line-addresses.md

.changeset/8478-zod-pins-form-layout.md

.changeset/8499-node-slot-registered-arms.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • SemanticElementSchema (zod/layout.zod.ts) — the seven HTML sectioning tags renderers/layout/semantic.tsx registers: aside main header nav footer section article. - HtmlElementSchema (zod/layout.zod.ts) — the 37 safe flow/inline tags renderers/basic/html-elements.tsx registers (h1…h6, p, a, ul, img, …), plus the per-tag keys that module forwards to the DOM (href, target, rel, title, src, alt, width, height, dateTime, cite). ⚠️ Dated note, 2026-09-27 — that set has since gained code — objectui#10756. At this change TAGS and this arm both named 37 tags; both now name 38, and the parity pin counts 38. The rest of this entry is kept as the reading of this change. - InputShorthandSchema (zod/form.zod.ts) — email / password, the two aliases renderers/form/input.tsx registers onto the input renderer with inputType pinned. inputType is deliberately NOT declared on this arm: the wrapper spreads its own value last, so an authored one is overwritten. ⚠️ Dated note, 2026-09-28 — inputType is now declared on this arm, as a refusal — objectui#8762. Later in this same release the arm declares inputType on both faces and refuses it by name (?: never on the TypeScript face, a retirementTombstone on the zod mirror, at path inputType), with guidance pointing at { "type": "input", "inputType": "email" }. So "inputType is deliberately NOT declared on this arm" no longer holds; the reason does, since the wrapper still spreads its own value last. The rest of this entry is kept as the reading of this change. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers under exactly that key (skipFallback, because bare calendar belongs to the plugin-calendar view).

.changeset/8572-chatbot-body-retired.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Why this key and not another. It was the ONE place in this vocabulary where body did not mean "what goes inside this component": zod-mirror-parity.test.ts carried the pair under KnownDrift as "two different meanings of one key", and the same collision was the whole reason chatbot was the single arm of the component union whose output was not assignable to SchemaNode. Both ledger rows move with this change, and the two pins that recorded the old state are INVERTED rather than deleted (see below).

.changeset/9067-zod-barrel-named-arms.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • InputShorthandSchema (zod/form.zod.ts) — the email / password shorthand arm. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers, a different component from the calendar plugin view that owns the bare literal.

.changeset/9406-types-root-barrel-two-names.md

  • names form.ts → packages/types/src/form.ts — edited by this change

    form-barrel-mirror-9406.test.ts keeps it closed, and it is DERIVED rather than a pair of presence assertions: it reads form.ts's export list and the root barrel's ./form.js re-export list on every run and names whatever is in the first and not the second. A pin asserting "these two names are present" would pass on the day the next declaration lands in form.ts and is forgotten, which is this class reopening yet again. Names deliberately left off the list get a ledger row carrying the reason instead, and a row goes red once its name reaches the barrel or stops being declared.

.changeset/9511-record-id-is-a-string.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ The declaration alone would not have been enough, and this is the reusable part. A TypeScript declaration does not run at parse time. The hand-written zod mirror is the only face in this repository that can refuse an authored number, so narrowing the declaration without the mirror would have shipped declared !== enforced on a published surface — and zod-mirror-parity.test.ts would have stayed GREEN through it, because that instrument asserts a mirror accepts everything its declaration declares and a mirror left WIDER passes. Both faces moved together for that reason.

.changeset/9526-types-root-barrel-command-element-types.md

  • names form.ts → packages/types/src/form.ts — edited by this change

    The change is purely additive: the two names join the root barrel's existing named re-export list from ./form.js, beside CommandSchema. Nothing is removed, renamed or narrowed, the declarations stay in form.ts, and the /form subpath keeps working. It is the third instance of one class, repaired by the same route each time: objectui#7697 for ComboboxOption, and objectui#9406 (director decision batch 133, item 2, letter (a)) for InputShorthandSchema and UiCalendarSchema.

.changeset/object-view-unmirrored-keys-7779.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Who is NOT affected: every correctly typed document, and every document that never wrote these keys — absent stays valid on all nine. No renderer changed. The parity ledger (zod-mirror-parity.test.ts) records the move: UnmirroredDeclared 14 entries / 96 keys to 14 / 87, the ObjectViewSchema entry re-derived into the SPEC-DERIVED half because the mirror now references the spec in code.

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 cb2f6fb5b (merge-base with origin/main): 24 file(s) changed outside .changeset/, read against 1822 pending declaration(s) that publish a body (2431 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3576.7 KB 3607.4 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-B6zP4qGr.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
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.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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) 569.02KB 136.04KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.91KB 63.54KB
fields (index.js) 261.18KB 66.27KB
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.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.88KB 11.46KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.73KB 37.05KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 242.01KB 63.55KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.30KB 44.19KB
plugin-gantt (index.js) 172.43KB 42.85KB
plugin-grid (index.js) 230.79KB 63.39KB
plugin-kanban (index.js) 48.46KB 15.12KB
plugin-list (index.js) 116.28KB 28.88KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 32.26KB 9.42KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
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) 19.82KB 7.15KB
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: 998fb633fe5fdb223cc1cd5633d388c09fa08949
Local-runs: none

Inputs read: card objectui#11070 (body and all 15 comments, rulings and earlier records included); PR #11240 (body, the 28-file list, the net diff against main from the merge base 0c95d3d, and its 2 comments); the 43 check-runs on the head — 40 success, 3 skipped by condition (Test (coverage), the coverage shard, dependabot), 0 failure. The by-reference claims were checked against @objectstack/spec's FieldSchema source at objectstack origin/main. Nothing was built, run or re-run.

① Derived judgments

Every accept-set and public-surface change the diff implies, each judged:

  1. Strict authoring face (StrictAnyComponentSchema) widens by form.fields[].returnType and form.fields[].summaryOperations — RIGHT. Both are read: the form renderer hands each fields[] entry to its widget as the metadata carrier (round 1), FormulaField reads returnType, SummaryField reads summaryOperations.function. Both are the spec's FieldSchema members by reference on both faces (SpecField['returnType'] on FormField, stripImportedDefaults(SpecFieldSchema).shape.returnType on FormFieldSchema; the same for summaryOperations), and assertionSpecMembersByReference pins exact equality. The spec source confirms the shapes: returnType is z.enum(['number', 'text', 'boolean', 'date']); summaryOperations is a strictObject of object, field, function (count | sum | min | max | avg), optional relationshipField, optional filter (FilterConditionSchema).
  2. Tolerant face (AnyComponentSchema) narrows — RIGHT, and the changeset names it as the narrowing half. A malformed returnType (datetime) or summaryOperations (no function, function: 'first', an unknown member) was stripped unjudged by z.object's default and is now refused on both faces; well-formed values move accept / refuse to accept / accept on the strict face and stay accepted on the tolerant one.
  3. FormField (TypeScript) gains returnType? and summaryOperations? — RIGHT; typed members, not the index signature's any (assertionFormFieldMembersAreTyped extended).
  4. FormulaFieldMetadata.return_type ('text' | 'number' | 'boolean' | 'date' | 'datetime') becomes returnType: SpecField['returnType'] — RIGHT. datetime (typed) and currency (never typed, read by the widget) leave the vocabulary: the spec lists neither, and the widget never branched on datetime. A published-type narrowing, stated in the changeset.
  5. SummaryFieldMetadata.summary_object / summary_field / summary_type / summary_filter become summaryOperations: SpecField['summaryOperations'] — RIGHT. first / last leave the aggregation vocabulary (the widget formatted them as plain text anyway). The summary_filter fold, one member beyond the claim's three named ones, is right on the merits: the spec pins filter inside summaryOperations, the type-checker census reads 0 references, and leaving it would have kept a second spelling of one concept inside the same interface, against the no-dual-spelling rule the seat set in 5914385802; the seat's ACCEPT 5915992755 lists it.
  6. PasswordFieldMetadata.min_length / max_length become minLength / maxLength (SpecField[...]) — RIGHT; the spec declares both on the bounded-string types, password among them. Noted, not a defect of this PR: the generic readers (buildValidationRules: minLength ?? min_length, and the form branches) keep the dual read for every text-family type, so a min_length handed at runtime is still honoured while the password type no longer declares it. That is the text-family remainder (③ item 5).
  7. Widget behaviour on @object-ui/fields — RIGHT. FormulaField reads returnType only (the currency branch gone); SummaryField reads summaryOperations.function only; no alias, no dual read. A grep of the head over packages/*/src, apps, examples, content and scripts finds no remaining read or write of the five retired spellings outside comments and the retirement pins. This is the user-visible half (the object-bound probe in the PR body), and the changeset states it as the visible change.
  8. The retired spellings stay refused by name on the strict face (RETIRED block); reference_to, min_length and columns stay PENDING (strict refuses, tolerant strips) — RIGHT; the face did not open up.
  9. Corpus. The three fields-summary fixtures fold to summaryOperations and now parse on the strict face; the three fields-formula fixtures move return_type to returnType and formula to expression — RIGHT as a spec-aligned move, with one reading: expression is undeclared on FormField and unread on the hand-authored path, so those three stay strict-refused, class (i) in place of class (i) (formula). Lateral, disclosed in the PR body, seat-answered (③ item 2).
  10. Docs. formula.mdx, summary.mdx and password.mdx typed fences, plaintext fences and prose follow the types — RIGHT; Doc Snippet Type Check (green) judges the typed fences against the rebuilt dist. formula.mdx still teaches formula, which FormulaFieldMetadata still declares — consistent with the type; the card's remainder (③ item 3).
  11. Ledger rows. form-field-zod-coverage gains the two keys; zod-mirror-parity's UNNAMED_LAZY_SLOTS gains form.zod.ts#FormFieldSchema.summaryOperations — RIGHT: the spec source shows summaryOperations.filter is FilterConditionSchema, a recursive lazy inside an imported schema, so no local const names it. No parity key set or total moves. imported-defaults-8317's comment drops a member count that would have gone stale — right.
  12. Three pending changesets (6138, 8194, 8809) get an appended dated note where return_type made their text false — RIGHT: append-only, frontmatter untouched, the shape the claim re-read gate asks for; Changeset Overwrite Report green.
  13. Public API census. No export added or removed; the changed members live on @object-ui/types and are read in @object-ui/fields only; the repo-wide Type Check (green) confirms no typed consumer broke. Governed surfaces: none of the 28 paths (no .claude/**, docs/adr/**, skills/**, AGENTS.md, CLAUDE.md); Governed Surface Queue Guard green.
  14. M3 class (ii) 12 / 53 / 53 to 10 / 47 / 47 (the dev's port; not re-run here) — consistent with items 1 and 9: return_type and summary_type leave, the 9 dashboard pairs and the grid field's columns stay per the card's remainder. The card stays open (Refs), as the body says.

Nothing is judged wrong.

② Semver level

  • The diff publishes from two released packages: @object-ui/types (form.ts, field-types.ts, zod/form.zod.ts) and @object-ui/fields (the two widgets). .changeset/11070-field-metadata-spec-spellings.md declares both minor — MATCHES what the diff publishes. Changeset Declaration, Changeset Bump Policy and Changeset Fixed Group Check are green; the presence gate reads 15 source files of 2 released packages against 1 declared changeset.
  • The change is BREAKING: retired TypeScript members (an annotated literal writing one no longer compiles), a carrier holding only return_type / summary_type now renders unformatted, and the tolerant face refuses malformed values it used to strip. objectui's fixed group never declares major (AGENTS.md, enforced by check-changeset-no-major), so minor with the break stated in the body is the correct price. The body carries the FROM → TO mapping and the one-line fix (returnType; summaryOperations: { object, field, function } with filter for the former summary_filter; minLength / maxLength), names who is unaffected (spec-compliant producers: the spec's FieldSchema is a strictObject that refuses the retired spellings by name, and objectstack's example apps write only the spec spellings — 6 object files, 0 retired), and names what is not in the change (reference_to).
  • Clause-②: yes (narrowing) in the PR body — RIGHT. The strict accept set moves (yes, at least minor: held), and of the two arms the narrowing one is the breaking one, so it is the arm to declare; at most one arm is written. It matches the claim 5915152317 and the changeset.
  • objectui binds no ADR-0087 marker to a changeset (none in its AGENTS.md, no such check-run), so none is owed here.

③ Boundary flags

Every dev flag (PR body) and every open_questions entry (report 5915946736), answered or escalated:

  1. Stopped: reference_to (open_questions[0]) — the stop is RIGHT. The claim's own rule is stop-on-breach, and retiring LookupFieldMetadata.reference_to while 11 in-repo emitters plus core's ingestion stamp write it (8 typed writes, FlowReferenceField and RelatedList among them) would have reached six packages outside the claimed surface. The seat's answer A in 5915992755 (one cross-package round: emitters write reference, the three readers read reference only, ingestion folds a foreign reference_to but stops stamping it, the types and docs move, no staged window) is consistent with the maintainer ruling it quotes and with the no-dual-spelling rule. ANSWERED, deferred to its own claimed round. Consequence for this PR: lookup.mdx, the lookup fixtures and LookupFieldMetadata are untouched though the claim named them — a stated, accepted deviation, not a defect.
  2. expression in the three fields-formula fixtures (open_questions[1]) — seat answer A (drop it in the next types round: nothing on the hand-authored path reads it, ADR-0049 enforce-or-remove) is RIGHT. ANSWERED. This PR's lateral move is spec-aligned and disclosed; not blocking.
  3. FormulaFieldMetadata.formula where the spec member is expression — same family, outside Q2's ruling (which named return_type), with readers outside the claim (app-shell's metadata admin, plugin-form's sanitize). Carried on the card's remainder list in 5915992755. ANSWERED (carried).
  4. The summary_filter fold beyond the claim's named members — judged right in ① item 5; the seat accepted it. ANSWERED.
  5. Text-family min_length / max_length on seven *FieldMetadata types, six docs pages and the generic dual readers — outside Q3's ruling (PasswordFieldMetadata only); carried on the card's remainder. ANSWERED (carried). Reviewer's note for that round: the runtime dual read minLength ?? min_length in buildValidationRules and the form branches is the alias that has to retire with the types, or the password retirement landed here stays type-only at runtime.
  6. auto_compute / auto_update — declared, read by nothing, not snake twins of a spec member; the dev noted them in the PR body only, and the seat's remainder list does not carry them. ESCALATED to the seat: a declared-unread member is enforce-or-remove material and wants a row on the card's remainder or its own card. Not blocking this PR.
  7. currency dropped from FormulaField — stated in the changeset; the spec never listed it and no typed carrier could write it. ANSWERED.
  8. Not measured locally by the dev (repo-wide lint and test shards, the Spec Main Shape Gate, downstream type-check) — answered by the head's check-runs: Lint, Type Check, Test shards 1 to 8, Test (dist pins), Spec Main Shape Gate, Build & E2E, Build Docs, Doc Snippet Type Check all success.
  9. Draft, not marked ready — RIGHT; the landing is the seat's act after this record on the green head.

No flag is left unanswered, and no answer needs the maintainer before this head lands.

Implemented-by: claude/issue-11070-field-types-round3
Reviewed-by: session_01TdiauJaVCHuj45EzZGUxHh

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 17:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 615346d Sep 30, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11070-field-types-round3 branch September 30, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants