Skip to content

feat(types,core,fields,app-shell,plugins)!: reference is the only relational-target spelling objectui writes or reads; reference_to retires (objectui#11070, round 4) - #11264

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-11070-reference-to-round4
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-11070-reference-to-round4

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Refs #11070
Clause-②: yes (narrowing). LookupFieldMetadata, MasterDetailFieldMetadata, and DetailViewField retire reference_to and declare reference, and the ingestion pass no longer stamps reference_to onto served defs, under the startup no-gradualism rule. It is priced in the changeset.

Round 4 of objectui#11070, the reference_to round: the seat's answer A to Q1 (5915992755), under the maintainer ruling on objectui#6837 (5475017957, 2026-08-31, verbatim: 「objectui不是前端的项目吗?后端的元数据只要对,前端按协议执行就行了呀」, with the earlier question 「按 spec 拒绝面直接删不是更合理吗」). Claim 5916640555.

reference is now the only spelling ObjectUI writes or reads for a relational field's target object. The one fold that remains is the ruling's choke point: normalizeFieldReferenceKeys folds a foreign reference_to / referenceTo onto reference when reference is absent, keeps its dev warning, and never drops a key.

What changed

  • Types (@object-ui/types). LookupFieldMetadata and MasterDetailFieldMetadata declare reference typed by reference to @objectstack/spec's FieldSchema.reference. DetailViewField and its zod mirror DetailViewFieldSchema move from reference_to to reference in the same commit, so the two faces keep equal key sets. The form.ts comments and the form-field mirror's comment now say that the lookup and user widgets read reference only. strict-face-read-keys-11070.test.ts moves reference_to from the pending list to the retired list, and gains a type-level pin: both field metadata types carry the spec member, and none of the three contracts has a reference_to key.
  • Ingestion (@object-ui/core, reference-keys.ts). The pass folds onto reference and no longer stamps reference_to. The header prose that described a stamp of "BOTH snake_case keys" now describes the fold, and it records why the stamp protected nothing once both ends moved. The leave-arm rule stands.
  • Readers read reference only: LookupField, UserField, LookupCellRenderer and UserCellRenderer (only those two reads in fields/src/index.tsx), InlineFieldInput's reference fallback, schemaDefaults' current_user seed, sectionFields' view override, and MetadataProvider's inline-subform parent.
  • Emitters write reference: paramToField, RecordDetailView, FlowReferenceField (its one target line), RecordDetailPanel, RecordMetaFooter, RelatedList, fieldEnrichment, buildDefaultPageSchema, sectionFields, bulkParamToField and ObjectGallery.
  • Copy sets and ledgers. plugin-grid's RELATIONAL_META_READ_SET loses its reference_to row and the adapter-stamped verdict that row alone held. plugin-dashboard's CELL_RELATIONAL_META_KEYS and FieldMeta drop reference_to. resolveActionParams' unread RuntimeField.reference_to member goes. FieldDefaultsSchemaLike pins three members.
  • Docs and fixtures. lookup.mdx (typed fences, plaintext fences, prose, plus one paragraph on the retirement), record-edit-modes.md, plugin-form.mdx's field-slot row and plugin-form's README. The tests that fed reference_to to a widget, a cell or a served-def stub now feed reference. The pins whose job is the fold, or a refusal, keep reference_to as their input and assert the fold to reference with no stamp back, or the refusal.
  • One changeset, .changeset/11070-reference-to-round4.md, minor with the break stated (AGENTS.md §9: never major). Fifteen pending changesets that this round made false get an appended dated note, in the append-only shape: 6837-reference-to-arm-deletion, 6837-gantt-tree-referenceto-arms, 6837-recorddrawer-invented-target-arms, 6694-dashboard-lookup-reference-meta, 6597-retire-fieldmeta-referenceto, 7166-retire-inert-fieldmeta-copies, paramtofield-reference-rule-derives-from-core-5312, 6711-retire-reference-to-field, 6874-retire-titleformat, 7155-converge-lookup-dialect-camelcase, 6528-resolve-reference-to-census, 7324-plugin-form-nameable-parameter-types, 11070-field-metadata-spec-spellings, 10535-person-name-read-gate and 6875-grid-relational-meta-derive.

Pins that flipped, and why

These pins asserted the stamp. Each now asserts that no stamp happens, and that the fold still does:

  • reference-keys.test.ts: a reference def gains no reference_to; referenceTo folds to reference only; idempotence; the map and array containers.
  • reference-keys.legacyWarning-6837.test.ts, case 5: "the STAMP is unchanged" becomes "the FOLD still runs". The referenceTo case asserts no reference_to; the reference_to case keeps the input key, because the pass never drops one.
  • MetadataProvider.itemReferenceKeys-7650.test.tsx: the first pin asserted the stamp and now asserts its absence. The rest feed a legacy spelling, because a reference-only def comes back unchanged and cannot show that the pass ran.
  • getObjectSchema.test.ts (data-objectstack).
  • The emit-side pins that read reference_to off an emitted bag: referenceArms-6837.divergent.test.ts (plugin-detail), RecordDetailDrawer.referenceArms-6837.test.tsx, HeaderHighlight.editable.test.tsx, relationalMetaCopySet-6711 / -6874, lookupRelationalMeta-6694, sectionFields*, paramToField.test.ts, resolveActionParams.test.ts, bulkParamToField.test.ts.
  • New refusal pins: the lookup cell, the user cell and LookupField each read no target from a reference_to-only def.

Pins that assert something else were not "fixed green". Three copy-set floors, which required more than two keys in RELATIONAL_META_KEYS, now require more than one, because the set is now reference and displayField, and each floor now also names reference.

Beyond the claim's file surface, stated

  • ObjectDataTable.tsx gains ObjectDataTableRetiredReferenceToSnakeTombstone, and both ObjectDataTable test files gain a directive and a counter-control for it. Retiring FieldMeta.reference_to removed the key from the derived refusal bands' pool, and recordFields.tsx's own FieldMeta docblock requires that a retired member be re-refused by hand at that seam. This is the third tombstone, beside decimals and referenceTo.
  • Comment-only corrections to statements this round made false: auditHistoryDisplay.ts, deriveRelatedLists.ts, useDatasetFields.ts, object-fields-io.ts, components/custom/field.tsx, core ActionRunner.ts / chart-series.ts / expand-fields.ts, data-objectstack/src/index.ts, RecordPickerDialog.tsx, lookupColumnDisplay.tsx, HeaderHighlight.tsx and ObjectGantt.tsx, plus the stamp prose in the gantt / tree / drawer 6837 pins.
  • The named tail is folded. PR fix(plugin-list,plugin-grid): a grouped list view under a toolbar search groups on the server, with the searched counts (objectui#11021) #11243 landed, so the three ObjectGrid.tsx relational-metadata comments now name reference and displayField (seat message, 18:53Z). ListView.tsx's two lines were left as they are, because both are true after this round: one says FieldSchema refuses reference_to, the other that a list column declares neither spelling. PR fix(types,components,plugin-form,console,core): a faulted or blank visibleWhen refuses the submit, naming the field and the rule; a blank field rule is refused at authoring; blank gates are diagnosed (objectui#8069) #11233 landed, so the form.zod.ts comment, the FormPage.tsx comment, the plugin-form.mdx row and the FormPage.sharedFieldResolver-10179 pin were folded as well (seat message, 19:54Z; the comment only in form.zod.ts, with no new declaration). FormPage.sharedWidgets-10179's lookup fixture moved first, because that test went red on the change.

Census, before and after

Base: merge base 615346d for the round's own diff. Code lines are non-comment lines in packages/*/src, apps/console/src and examples/*/src, excluding tests. The typed half comes from a TypeScript LanguageService findReferences over 5568 source files, with workspace packages resolved to source.

kind before after
emitter sites writing reference_to onto a def or widget field 11 0
ingestion stamp of reference_to 1 0
copy sets carrying reference_to (grid ledger row, dashboard cell keys) 2 0
reader lines reading reference_to (widgets, cells, inline editor, seeding, section override, subform parent) 10 0
the ingestion fold's read of the legacy key (the ruling's choke point) 1 1
declared reference_to members (field-types ×2, views, views.zod, RuntimeField, FieldMeta, schemaDefaults ×2) 8 0
reference_to?: never refusal (tombstone) 0 1
typed references to a reference_to member: production writes / test writes 2 / 7 no member left to reference

The eleven emitters are the PM census's eleven. The type-checker census found two of them typed (FlowReferenceField, RelatedList); the other nine write through any or Record bags. It found no typed reader: every reader was an untyped read.

Runtime probe — the user-visible proof

A scratch vitest file, never committed, ran against the source at merge base 615346d and at head (1972d01, re-run at 338b19d with identical readings). ObjectForm in create mode opens its lookup picker; ObjectGrid renders a lookup column with a primitive id; the referenced record is { id: 'a1', name: 'Acme Corp' }.

case base head
(a) served def with reference only, through ObjectStackAdapter picker queries probe_account; cell shows Acme Corp; served keys label, reference, reference_to, type picker queries probe_account; cell shows Acme Corp; served keys label, reference, type
(a) the same def from a BYO DataSource works works
(b) action param referenceTo through paramToField into LookupField field carries reference_to; picker queries probe_account field carries reference; picker queries probe_account
(c1) reference_to-only def served through ObjectStackAdapter folded (reference added); dev warning fires; picker and cell work the same: folded, warned, picker and cell work
(c2) reference_to-only def from a BYO DataSource picker queries probe_account; cell shows Acme Corp no find call at all; the cell shows the raw id a1

(c2) is the break, and the changeset's BREAKING paragraph names that host.

Hypotheses

  • H1 holds. Read at objectstack origin/main 165c1d49e. The example apps write reference_to zero times, against a control of 30 Field.lookup / Field.masterDetail calls in 19 files; both helpers write reference. FieldSchema refuses reference_to by name (installed 17.5.0: unrecognized_keys, "Did you mean reference_to → reference?"), and so does the spec's form-field schema. The field-reference-to-alias conversion (packages/spec/src/conversions/registry.ts) rewrites stored rows on rehydration and in os migrate meta. The only other hits are refusals (the SQL and Mongo doors, verify, lint), a reader of three spellings in plugin-security, and a comment in plugin-approvals that describes this repo's widget key (noted below).
  • H2 holds. Every production write is one of the eleven emitters, the ingestion stamp, or one of the two copy sets the brief listed as readers. No twelfth writer turned up, typed or untyped.
  • H3 holds. On the ObjectStack path the cells receive folded defs, as probe cases (a) and (c1) show. RELATIONAL_META_KEYS is derived to reference, displayField, and the grid's derivation gate re-extracts the cell's read set with reference in it.
  • H4: false as posed. The reference_to pair was not in M3's class (ii) at this base. Round 1 (PR feat(types): declare the read keys the strict authoring face refused (objectui#11070) #11115) moved both fields-lookup fixtures to reference, and round 3 reported class (ii) without it. Measured with scripts/measure-strict-authoring-face.mjs --json at merge base af9e957 and at 338b19d: every refusal figure is identical (2224 nodes, 105 strict-refused, 66 strict-only, 39 red today, 599 documents, 98 refused whole-tree), and reference_to occurs in zero refusals at both. The only differences are the renderer-source word-mention counts, which the comment edits moved, and the error text of one authored module that fails to load under either build state. So this round moves M3 by zero in every class. The absolute class (ii) figure at this base is NOT MEASURED: round 3's shipped-face port of PR feat(cli): objectui validate and objectui check judge through the strict authoring face (objectui#5250, slice A) #11069's findUndeclaredKeys was a scratch script and was never committed.

Checks

Heavy runs went through os-verify-lock (slot issue-11070). Exit codes were captured before any pipe.

  • Build closure (check-doc-snippet-types --build-filter, turbo --concurrency=2): 35 successful, 35 total, before and after the first merge.
  • type-check of 21 packages, the ones this diff touches plus the console and the schema catalog (the type-checker census found no other typed reader): Tasks: 56 successful, 56 total, before and after the first merge. After the second merge, @object-ui/types was rebuilt and type-checked again, exit 0.
  • Tests, full affected set (the 21 packages' paths, 3536 files), before the first merge: 4 failed | 3527 passed | 5 skipped (3536). The four were pins that asserted the old shape: three copy-set floors of more than two keys, and the console shared-widget lookup fixture. They were fixed in c684de4 and re-run: Test Files 4 passed (4), Tests 30 passed (30).
  • Tests after the first merge (338b19d), narrowed to the files both sides touched plus this round's relational surface (types, fields tests and widgets, the Flow inspectors, console components, plugin-grid, plugin-detail and plugin-dashboard tests, plugin-form, core reference-keys, the adapter, and app-shell providers and utils): Test Files 1137 passed | 3 skipped (1140), Tests 16884 passed | 31 skipped (16915). After the second merge (16b43a7, no file overlap): packages/types/ 295 passed (295), 7375 passed (7375), including zod-mirror-parity.test.ts, whose totals did not move.
  • Doc gates at 338b19d: check:doc-snippets 696 of 696 block(s) judged, 0 failed; check:doc-examples, check:doc-types, check:spec-symbols, check:doc-example-readers and check:doc-example-ids all exit 0.
  • At 16b43a7, all exit 0: check:doc-fences, docs:check-links, check:control-bytes, check:pending-changeset-literals, check-changeset-presence (174 source file(s) of 20 released package(s) changed, and this change declares 1 changeset(s)), changeset:check (no major), check:new-line-citations (0 new citation(s)), check:test-path-roots, check:changeset-claims (report-only; every named changeset was read), and check-governed-queue-guard --test over the 194 paths (NOT GOVERNED).
  • Lint, a measured narrowing. The population is eslint.config.js's **/*.{ts,tsx} files entry; there are no parserOptions.project or projectService, so type-aware linting is off and this diff cannot move an untouched file's verdict. The 174 changed .ts / .tsx files were linted at 16b43a7 and at merge base 54a7830: 0 errors at both, 1909 warnings at both, and no file is worse than base.
  • Ablations, each run on a committed, clean tree through objectstack's ablation-replace.mjs. In every case the anchor hit once, the blob moved, the blob was restored equal to HEAD, and git diff HEAD was empty afterwards.
    • A1: lookup.mdx's fence writes reference_to. check:doc-snippets exited 1 with 696 of 696 block(s) judged, 1 failed, TS2561 "'reference_to' does not exist in type 'LookupFieldMetadata'. Did you mean to write 'reference'?", which proves the gate read the rebuilt .d.ts.
    • A2: the fold stamps reference_to again. 6 red / 83 green, against 5 predicted: the three core stamp pins, the adapter pin and the provider pin, plus the empty-target pin, because an empty reference also gained a stamp.
    • A3: LookupCellRenderer reads reference || reference_to again. 2 red / 14 green, as predicted: the new cell refusal pin, and the grid derivation gate's unclassified-spelling check.
  • Type-checker census at 338b19d: the seven contracts carry no reference_to member. The control run of the same instrument on reference finds 7 declarations, the same two typed production writes that moved, 1 production read, 3 test reads and 10 test writes.
  • Diff size: 194 files, +1022 / −647 against merge base 54a7830. That is under the 5000-line human-merge threshold: 62 non-test files (16 of them changesets) and 132 test files.

NOT MEASURED:

  • The repository-wide lint and the test shards outside the sets above. CI runs those.
  • Tests after the second merge outside packages/types/. Its three incoming commits share no file with this branch, and none contains reference_to.
  • A browser dogfood against a live objectstack app. The probe used ObjectForm / ObjectGrid with a stub or adapter-wrapped DataSource.
  • The absolute M3 class (ii) figure (see H4).
  • CI convergence. It was not waited on, per the dispatch contract.

Acceptance notes

  • docs/adr/0059-action-params-shared-field-widgets.md's decision section still records referenceTo → reference_to as paramToField's mapping. It was left as written: it is the accepted decision's record (2026-07-19), and the ruling kept the historical record. Editing docs/adr/** would also make this PR governed. The seat may want a dated amendment line in a docs-only PR.
  • ROADMAP.md's two hits are completed items and changelog history, not current behaviour, so they were not changed.
  • .changeset/audit-field-def-reference-narrow.md says the choke point "stamps both snake_case spellings". It has empty front matter, so nothing publishes it, and no note was appended.
  • objectstack plugin-approvals' sys-approval-request.object.ts has a comment saying the console resolves a lookup config as reference_to: sys_user. That spelling is now reference. It is a comment in the sister repo, not a producer; noted, not filed.

Generated by Claude Code

…lling objectui writes or reads

Round 4 of objectui#11070 (the reference_to round), under the objectui#6837
ruling. The ingestion pass folds a foreign reference_to / referenceTo onto
reference and no longer stamps reference_to; the in-repo emitters write
reference; LookupField, UserField and the lookup / user read cells read
reference only; LookupFieldMetadata, MasterDetailFieldMetadata and
DetailViewField (with its zod mirror) declare reference.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…ted notes on the pending entries this round made false

The three relational copy-set controls asserted more than two keys; with
reference_to retired the set is reference and displayField, so each floor
now also names reference. The console shared-widget pin feeds its lookup
the spec spelling. One changeset declares the round (minor, break stated),
and fifteen pending changesets get an appended dated note.

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

PR #11243 has landed, so the three column-building comments in ObjectGrid are
in scope; they named reference_to and display_field, neither of which the
copy set carries any more.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
…age and two console pins name reference

PR #11233 has landed, so the remaining tail is in scope: the form-field zod
comment, FormPage's metadata comment, plugin-form.mdx's field-slot row and the
shared-field-resolver pin now spell the target reference. deriveRelatedLists'
docblock drops the reference_to it has not read since objectui#6837 half 2.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
Keeps each touched test file at its base count of no-explicit-any warnings.

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

⚠️ 55 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/10106-settled-schema-equal-payload.md

  • names packages/react/src/hooks/__tests__/useSettledSchema.equalPayload-10106.test.tsx → packages/react/src/hooks/__tests__/useSettledSchema.equalPayload-10106.test.tsx — edited by this change

    The hook refetches getObjectSchema whenever the adapter identity changes, and every consumer its doc comment instructs — ObjectGantt, ObjectCalendar, ObjectView, ObjectTimeline, ObjectGallery — keys its record fetch on the returned def, which is what AGENTS.md §5 commandment [WIP] Enhance every detail of the designer #10 tells a consumer to do (key on the payload, never on a memo identity). The producer half of that same sentence is what this fixes: a byte-identical answer arriving as a fresh object re-fired every one of those fetches, and the duplicate query carried the same $expand set as the one before it. Measured first-hand on ObjectGantt with an instrumented adapter, with a control swap in the same command; the counts, and the control that makes them a discrimination rather than a counting artefact, are asserted by packages/react/src/hooks/__tests__/useSettledSchema.equalPayload-10106.test.tsx (objectui#10106).

.changeset/10456-objectmap-description-lookup.md

  • names ObjectMap.descriptionDisplay-10456.test.tsx → packages/plugin-map/src/ObjectMap.descriptionDisplay-10456.test.tsx — edited by this change

    No schema or spec key changes. ObjectMap.descriptionDisplay-10456.test.tsx pins each case above.

.changeset/10530-timeline-lookup-display.md

  • names ObjectTimeline.lookupDisplay-10530.test.tsx → packages/plugin-timeline/src/ObjectTimeline.lookupDisplay-10530.test.tsx — edited by this change

    No schema or spec key changes. ObjectTimeline.lookupDisplay-10530.test.tsx pins each case above.

.changeset/4730-retire-dead-locale-key-batch.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • Superseded twin vocabularies. cellRender.* and rowAction.* duplicated a grid.* vocabulary that won. RowActionMenu.tsx is fully i18n-wired and reads grid.openMenu / grid.edit / grid.delete; ObjectGrid.tsx reads grid.empty / grid.yes / grid.no / grid.systemFields. The twins had no reader on either side. - Labels that outlived their control. calendar.agenda labelled a view mode b55a34647 retired from CalendarViewMode (now 'month' | 'week' | 'day'). home.quickActions.createApp*, layout.systemNav.createApp, actionDialog.defaultActionTitle / .ok and grid.bulk.selectPlaceholder sit in namespaces whose consumers are live and wired but demonstrably read other siblings. - Surfaces that left the product. map.* is the strongest form: @object-ui/plugin-map declares no @object-ui/i18n dependency and contains no t() call at all, so it cannot consume a locale string. home.stats.* and recordDetail.viewersTooltip name surfaces nothing renders.

.changeset/5876-one-lookup-predicate.md

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — edited by this change

    computeLookupExpand in ObjectDataTable.tsx carried its own isLookup, byte-identical to the exported isLookupType in recordFields.tsx after objectui#5692 pointed both at @object-ui/core's EXPANDABLE_FIELD_TYPES. Nothing kept them aligned: a future edit to either — a member added, the retirement gate moved — would have re-forked the $expand decision from the predicate whose docblock claims to drive it. computeLookupExpand now calls isLookupType, which gains its first production consumer, and the module no longer imports the shared family or the retirement gate at all.

  • names recordFields.tsx → packages/plugin-dashboard/src/recordFields.tsx — edited by this change

    computeLookupExpand in ObjectDataTable.tsx carried its own isLookup, byte-identical to the exported isLookupType in recordFields.tsx after objectui#5692 pointed both at @object-ui/core's EXPANDABLE_FIELD_TYPES. Nothing kept them aligned: a future edit to either — a member added, the retirement gate moved — would have re-forked the $expand decision from the predicate whose docblock claims to drive it. computeLookupExpand now calls isLookupType, which gains its first production consumer, and the module no longer imports the shared family or the retirement gate at all.

.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/6059-publish-omit-server-resolved-defaults.md

  • names schemaDefaults.ts → packages/plugin-form/src/schemaDefaults.ts — edited by this change

    Both are pure functions over plain data (no React, no registry). The rest of schemaDefaults.ts — seedCreateValues, schemaDefaultValues, isSeedableDefault, isCreateFormMode, SeedContext — stays module-private, and isRuntimeDefault stays @object-ui/core's to publish.

.changeset/6237-wizard-step-config-split.md

  • names content/docs/plugins/plugin-form.mdx → content/docs/plugins/plugin-form.mdx — edited by this change

    Documentation repair in the same stroke: the support table in the README and in content/docs/plugins/plugin-form.mdx still said formType: 'tabbed' sections drop the predicate. That stopped being true when the tabbed arm landed — the row now reads Yes, the surrounding prose no longer claims two inert arms or a diagnostic that fires for tabbed, and the wizard row stays No, which is still exactly true.

.changeset/6329-actionparam-one-authority.md

  • names utils/resolveActionParams.test.ts → packages/app-shell/src/utils/resolveActionParams.test.ts — edited by this change

    ActionParam has one authority again (objectui#6329). The name was declared three times, not twice as the card counted: @object-ui/types publishes it — derived from the spec's ActionParamSchema input, with its own parity suite — and ActionDefaultInspector.tsx and ActionPreview.tsx each carried a module-local interface of the same name. app-shell already read the published one elsewhere (utils/resolveActionParams.test.ts), so both locals were shadows. They are deleted, not reconciled against each other, under the 2026-08-25 family ruling 甲A1.

.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/6475-gantt-block-face-declared.md

  • names plugin-gantt/src/ObjectGantt.tsx → packages/plugin-gantt/src/ObjectGantt.tsx — edited by this change

    The gantt nested-block spelling of a gantt config ({ type: 'object-gantt', gantt: { … } }) had no mirror entry at all: it rode through BaseSchema's .passthrough() entirely unvalidated, and the published TypeScript never taught the shape either — an author writing gantt: { … } got no completion, no type checking, no error on a misspelt member. It was the 28th and last of the keys getGanttConfig (plugin-gantt/src/ObjectGantt.tsx) reads off the schema that objectui#6051 (PR feat(types,plugin-gantt): declare the flattened GanttConfig face ObjectGantt reads (#6051) #6472) did not declare — severed into its own card because, unlike the other 27, declaring it changes what a published tool refuses.

.changeset/6519-retired-field-key-strip.md

  • names previews/object-fields-io.ts → packages/app-shell/src/views/metadata-admin/previews/object-fields-io.ts — edited by this change

    previews/object-fields-io.ts is the single read door for draft.fields across the whole object designer — inspector, form designer, design surface, settings / validations / API panels — and writeFields writes each def back verbatim. Its strip set named one key while FieldSchema refuses five by name, so a stored field carrying any of the others round-tripped straight back out to PUT /api/v1/meta/object/:name. Measured on the installed @objectstack/spec 17.2.0, through the whole document that endpoint validates:

.changeset/6527-retired-field-key-tombstone-registry.md

  • names object-fields-io.ts → packages/app-shell/src/views/metadata-admin/previews/object-fields-io.ts — edited by this change

    One tombstone registry for the designer seam's retired field keys (objectui#6527). Three independently maintained RETIRED_FIELD_KEYS literals — the metadata-admin read door (object-fields-io.ts), MetadataService's carry-over and MetadataFieldsPage's carry-over — become derivations from a single registry in @object-ui/types (RETIRED_FIELD_KEY_TOMBSTONES + retiredFieldKeysFor(site)), naming each retired key, the card that retired it, and its PER-SITE applicability.

.changeset/6594-headercolor-mirror-enum.md

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

    The nearest precedent is fc62bb490 (.changeset/5853-tablecolumn-type-canonical-union.md), which narrowed TableColumn.type on the same three-ends pattern and exported a TABLE_COLUMN_TYPES tuple for the zod mirror to build its enum from. That shape is not available here and the difference is structural, not a preference: packages/types/src/views.ts is a type-only module, so a tuple there would add a runtime export to the package barrel (a value export cannot ride the barrel's export type block) and a runtime import edge from the zod entry into views.js. fc62bb490 had a second reason to export — producers needed its normalizeTableColumnType() at their emit seam — and headerColor has no producer that needs a runtime value. The literals are therefore written on each half and the anti-drift guarantee is carried by the pin above, which also covers the third end a shared tuple could not reach: the renderer, in a package @object-ui/types must not depend on.

.changeset/6625-retire-fieldmeta-decimals.md

  • names fields/src/index.tsx → packages/fields/src/index.tsx — edited by this change

    buildFieldMeta computed decimals: overrides.decimals ?? meta?.decimals ?? meta?.scale on every call and the value reached nothing. Re-measured on this branch's base (efdc6c62): zero .decimals member reads across @object-ui/fields, @object-ui/i18n, @object-ui/components, @object-ui/core and plugin-dashboard itself — the only non-comment occurrence was the write being removed here. The positive control in the same query shape fires: .scale member reads hit NumberField.tsx, GridField.tsx and fields/src/index.tsx. So the zero is a finding, not a broken query. The overrides.decimals ?? head of that chain had already lost its only feeder when objectui#6425's ruling removed the authored read from ObjectDataTable.enrich(); RecordDetailDrawer, the only other buildFieldMeta caller, passes no overrides at all. Both halves retire together, so the key leaves in one move.

  • names ObjectDataTable.overrideSource-6425.test.tsx → packages/plugin-dashboard/src/__tests__/ObjectDataTable.overrideSource-6425.test.tsx — edited by this change

    Behaviour is unchanged — no reader existed to notice, pinned by the unchanged runtime assertion in ObjectDataTable.overrideSource-6425.test.tsx that an authored decimals renders byte-identical to its absence.

.changeset/6648-relationship-target-carriers.md

  • names chart-series.ts → packages/core/src/utils/chart-series.ts — edited by this change

    objectui#6528 narrowed both relationship-target resolvers to the single spec SPELLING reference and left the CARRIER — the shape the value may take — explicitly for its own census. That census is done, and it says the same thing: resolveReferenceTo (dataset designer) and its sibling resolveRelationshipTarget (chart-series.ts) each accepted three carriers on the canonical key, two of which FieldSchema never declared. Both are removed in BOTH files in one pass (they must not diverge — a fix leaving them disagreeing recreates the defect one file over).

.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/6939-objectql-record-source-refinement.md

  • names plugin-gantt/src/ObjectGantt.tsx → packages/plugin-gantt/src/ObjectGantt.tsx — edited by this change

    Both renderers resolve their records from one of three keys, in this order — getDataConfig in plugin-map/src/ObjectMap.tsx and plugin-gantt/src/ObjectGantt.tsx: data, then staticData, then objectName. Both mirrors required objectName alone, so a document authored on staticData drew correctly and was refused by safeValidateSchema — six catalog entries, three per component.

.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/7125-dashboard-empty-state-keys-retired.md

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/7287-resolve-title-field-shared-ladder.md

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

    def.primaryField — a DetailViewSchema key (@object-ui/types views.ts), read off an OBJECT def and ranked ABOVE the canonical nameField ADR-0079 Phase 2 made the pointer (AGENTS.md Commandment #0.1). No producer can put it there: @objectstack/spec's object schema is a strictObject that answers unrecognized_keys: ['primaryField'], and ObjectSchema.create() throws — which is why objectstack#6326 deleted the identical read from two lint rules. A census across both repos found zero object payloads carrying it (the only writers are three test fixtures), and primaryField appears in zero files of the shipped @objectstack/spec@17.2.0 dist against 68 for nameField. Same shape as the undeclared objectDef.titleField read objectui#6531 measured and Five more consumer-side reads of the undeclared object-level titleField survive #6531 — in ObjectView view-config fallbacks and a useRecordSearch memo key #6557 removed. DetailViewSchema.primaryField is untouched and still honoured by DetailView's own header — it is a view key, and on a view it is legitimate.

.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/7586-primary-field-consumer-reads.md

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

    primaryField is a DetailViewSchema key (@object-ui/types views.ts) — a view key, which DetailView.resolveDisplayTitle reads off schema and is welcome to. Read off an object def it is undeclared: @objectstack/spec's object schema is a strictObject answering unrecognized_keys: ['primaryField'], and ObjectSchema.create() throws. primaryField appears in zero files of the shipped @objectstack/spec@17.2.0 dist, against 68 for the canonical nameField. objectstack#6326 removed the identical read from two lint rules; objectui#7287 / PR fix(plugin-detail): resolveTitleField delegates to the shared ADR-0079 ladder #7585 removed it from resolveTitleField. These two survived it — and three of this repo's own changelogs already called the probe "not a spec property — always undefined" while the code kept honouring 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'.

  • names views.zod.ts → packages/types/src/zod/views.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/7762-object-grid-export-options-bare-array-refusal.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    What was measured, on this branch's base. The mirror declared NO exportOptions member at all, and BaseSchema is .passthrough(), so ObjectGridSchema.safeParse({ type: 'object-grid', objectName: 'accounts', exportOptions: ['csv', 'xlsx'] }) returned success: true with the array back VERBATIM — as did { formats: ['csv', 'pdf'], compression: 'gzip' }. Nothing on the render path parses, and ObjectGrid.tsx reads schema.exportOptions?.formats and only that, so the authored array then lost SILENTLY to the ['csv', 'json'] default: the useEffect that warns about dropped formats reads .formats too and returns early when it is absent, while !!schema.exportOptions kept the export button on screen. An author declared ['csv', 'xlsx'] and got csv/json with no error, no warning and no console line. The two authoring faces disagreed in the direction opposite to objectui#6956's: the TypeScript interface already declared the object form only, so TS refused what zod admitted.

.changeset/7895-timeline-settled-schema-gate.md

  • names ObjectTimeline.fetchGate-7895.test.tsx → packages/plugin-timeline/src/ObjectTimeline.fetchGate-7895.test.tsx — edited by this change

    The resolution half is now useSettledSchema from @object-ui/react, which settles on every exit — no source, no getObjectSchema, no object name, and a read that threw alike. That is what makes the gate safe: the replaced effect returned without settling on all four, which cost nothing while nothing waited on it and would have held a gated query open forever. Pinned by ObjectTimeline.fetchGate-7895.test.tsx, including a timeline whose adapter exposes no getObjectSchema and one whose definition read rejects — both still query, unexpanded.

.changeset/7997-detail-view-related-retired.md

  • names RecordDetailView.tsx → packages/app-shell/src/views/RecordDetailView.tsx — edited by this change

    Why it retired. @objectstack/spec declares no DetailView schema at all — every DetailView occurrence in packages/spec/src is prose about this repo's own RecordDetailView.tsx — so this array mirrored no protocol schema and drifted freely: it declared columns as TableColumn[] while the renderer it fed also accepted bare field names, { field, label } and legacy { name, label } spellings. The axis that carried the ruling was measured zero pull: no application code authored the member, both internal producers of a detail-view node (RecordDetailDrawer, renderers/record-details.tsx) synthesize it without related, and the only in-tree authorings carrying real columns were two documents — both rewritten here.

.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/8318-jsdoc-default-tag-corrections.md

  • names core/src/actions/ActionRunner.ts → packages/core/src/actions/ActionRunner.ts — edited by this change

    • CRUDDialogSchema.size / .closeOnOutsideClick / .closeOnEscape / .showClose — there is no register('crud-dialog' anywhere, so no node of that type ever reaches a renderer. Recorded once on the interface. Per key, the name census differs: two spellings occur nowhere outside the declaration and its zod twin, and showClose's one other occurrence (renderers/overlay/drawer.tsx:38) belongs to DrawerSchema. - ActionSchema.level — type: 'action' is not a rendered node type, and core/src/actions/ActionRunner.ts, which is what makes method / chainMode / reload / close live, does not read level. - CardSchema.variant — card IS registered, twice, and neither registration reads it: the ui route forwards the key to ui/card.tsx, which spreads onto a div and mentions variant nowhere, and the page route forwards only its designer props. - PageNodeSchema.isDefault — page IS registered, and PageRenderer neither reads the key nor forwards it: the wrapper element gets toDomProps(props), an allow-list that does not carry it.

.changeset/8434-unresolved-user-reference-honesty.md

  • names packages/core/src/utils/expand-fields.ts → packages/core/src/utils/expand-fields.ts — edited by this change

    The sentence is deliberately epistemic, not ontological. This branch has two populations and the renderer cannot tell them apart — it has no resolver at all, unlike LookupCellRenderer: an unexpanded sys_user id is the legitimate stored form (packages/core/src/utils/expand-fields.ts: "a user column that is NOT requested for expansion comes back as a raw user id", objectui#2032), and a name written into the column is dirty data. A "not found" claim would be false for the first, so the affordance states only what is true of both: this screen did not resolve it.

.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/8738-fields-warn-route1.md

  • names sectionFields.ts → packages/plugin-form/src/sectionFields.ts — edited by this change

    Top-level fields reads only bare field-name strings ({ name } tolerated) — a different vocabulary from sections[].fields, which also accepts the spec FormFieldSchema object (identity key field, e.g. { field: 'note', colSpan: 2 }). Moving one of those objects into a top-level fields array resolves to no name and used to vanish without a word; it is now reported once per distinct offender via console.warn, naming the skipped shape and the vocabulary difference, modelled on sectionFields.ts's existing warnOnMixedVocabulary.

.changeset/8755-bulk-lookup-depends-on.md

  • names plugin-grid/src/__tests__/bulkLookupDependsOnReach-8755.test.tsx → packages/plugin-grid/src/__tests__/bulkLookupDependsOnReach-8755.test.tsx — edited by this change

    ⚠️ The bulk param schema does not license the key, and must not be cited as if it did. @objectstack/spec's BulkActionParamSchema "accepts" dependsOn — and accepts a nonsense key in the same run, because it is not strict; the strict sibling ActionParamSchema refuses both. That accept is a null reading, pinned with both controls in plugin-grid/src/__tests__/bulkLookupDependsOnReach-8755.test.tsx leg B. Whether the key should become authorable by contract here — by closing that schema, or by giving the bulk surface the field-backed param route resolveActionParams gives the single-record dialog — is upstream of this repository and stays open.

  • names relationalMetaKeys.ts → packages/plugin-grid/src/relationalMetaKeys.ts — edited by this change

    Also in plugin-grid: the dependsOn row of relationalMetaKeys.ts carried the sentence "The grid supplies no dependent values, so that gate is permanent". Measured stale — objectui#7165, finished by objectui#7188, gave the inline cell editor the dependent record. The corrected note points at the test that re-derives the claim instead of restating an answer.

.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 views.ts → packages/types/src/views.ts — edited by this change

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.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/9722-bulk-executor-datasource-face.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • ObjectGrid.tsx → RecordDetailPanel: removing it type-checks GREEN. It was paying for nothing — both sides already declare the same DataSource | undefined from @object-ui/types. Removed outright. - ObjectGrid.tsx → BulkActionDialog: removing it reddens, and it was paying for TWO things at once — the optional-vs-required arm (the grid declares dataSource?: DataSource, the dialog demands one) AND the bulk-door contravariance. Only the first still needs erasing, so it is now a non-null assertion: today's runtime behaviour is preserved exactly, and any future drift of the face reddens at that site instead of passing silently.

.changeset/9853-grid-non-positive-page-size.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    Refuse a non-positive pageSize at all three of ObjectGrid.tsx's read points, instead of giving two different answers for one authored value (objectui#9853).

.changeset/9884-master-detail-fields-intersection.md

  • names sectionFields.ts → packages/plugin-form/src/sectionFields.ts — edited by this change

    So the rendered outcome is unchanged and the intersection stands: the parent field pool is built from fields first, and each section resolves its members against that pool. What changed is that the loss is audible. warnSectionMemberExcludedByFields (sectionFields.ts, beside the two warnings objectui#8738 and objectui#3090 added) names the section, the member and the two keys that collided, once per distinct pair, whenever a member the object really declares is dropped for the sole reason that fields omits it — including the expensive case where it was the section's last surviving member and the section disappears with its heading. A member the object never declares at all is deliberately NOT recruited into this warning: it resolves to nothing whether or not fields is authored, which is a different silence with a different remedy.

.changeset/action-keys-warning-text-5642.md

  • names ActionRunner.ts → packages/core/src/actions/ActionRunner.ts — edited by this change

    Both halves of the message's tail had outlived the change they described. It told the author the key was warned about rather than rejected because ActionDef "still carries [key: string]: any" — objectstack#4075 step 3 deleted that index signature, and actionKeys.pin.test.ts pins the deletion in the opposite direction ({ ActionDef: false, ActionContext: true }), while actionDef-closed-surface.test.ts pins that tsc now rejects exactly such a key at the construction site. And it prescribed promoting the key to an explicit field on ActionDef "(packages/core/src/actions/actionKeys.ts)" — that file holds the INVENTORY (ACTION_DEF_KEYS); the interface is in ActionRunner.ts. The wrong pointer had teeth: an author who followed it edited the inventory alone, which is precisely the half-change the pin test reddens on, since it re-derives the inventory from the interface's AST.

.changeset/console-form-container-specs-one-declaration-5596.md

  • names FormPage.tsx → apps/console/src/components/FormPage.tsx — edited by this change

    objectui#5542 converged the LEAF of this contract — the field spec — and left the two containers above it untouched, because converging them was a bigger call than a mechanical import. FormSectionSpec and FormViewSpec were each hand-declared twice under the same names, once in packages/app-shell's SchemaForm.tsx and once in apps/console's FormPage.tsx. Unlike the leaf — whose console copy was a clean subset — these two had already drifted, in both directions, so neither copy was a subset of the other and there were two live answers to "what may an author write":

.changeset/console-form-field-spec-one-declaration-5542.md

  • names FormPage.tsx → apps/console/src/components/FormPage.tsx — edited by this change

    objectui#5040 was not a missing key. It was that two hand-written descriptions of one contract drifted, and nothing could notice, because each was only ever checked against itself. PR metadata-admin: FormFieldSpec declares dependsOn, one declaration for both halves #5537 converged the two app-shell descriptions into views/metadata-admin/form-spec.ts. A third survived in apps/console: FormPage.tsx declared its own nine-key interface FormFieldSpec, under the same name, in a different package — so the same failure mode stayed fully available.

.changeset/console-formpage-runtime-default-seed-5727.md

  • names apps/console/src/components/FormPage.tsx → apps/console/src/components/FormPage.tsx — edited by this change

    readPrefill in apps/console/src/components/FormPage.tsx seeded every declared default unconditionally. A defaultValue may be a literal, or an instruction the server resolves per insert — a DEFAULT_VALUE_TOKENS token (NOW(), current_user) or a CEL Expression envelope. Seeding one of those literally put the text NOW() into a datetime input on both /forms/:name and the public /f/:slug route, and submitting it sent that string as the field's value — which is neither absent nor null, so ObjectQL.applyFieldDefaults never resolved the declared default and the column stored the token text instead of a timestamp.

  • names schemaDefaults.ts → packages/plugin-form/src/schemaDefaults.ts — edited by this change

    The seed is now guarded by isRuntimeDefault from @object-ui/core — the same published classifier @object-ui/plugin-form's schemaDefaults.ts guards its seeding with, and the one this renderer already reads once removed (through isServerOwnedValue) for the create-mode required carve-out. A runtime default leaves the key ABSENT rather than empty, because absent is precisely the case the engine resolves.

.changeset/console-formpage-visible-predicates-5594.md

  • names apps/console/src/components/FormPage.tsx → apps/console/src/components/FormPage.tsx — edited by this change

    apps/console/src/components/FormPage.tsx is a second, independent form renderer — its own buildSections, its own JSX — and it serves both the public /f/:slug route and the internal /forms/:name route. It read neither spelling of the FormView field visibility predicate: a repo-wide grep for a visibleWhen / visibleOn read inside that file returned zero. So a field an author conditioned on record.priority == 'urgent' — legal, spec-strict metadata that @objectstack/spec normalises to visibleWhen (ADR-0089), and that the metadata-admin designer both authors and honours — rendered unconditionally on both routes. Fail-open and silent: the author saw the field always, with no diagnostic.

  • names sectionFields.ts → packages/plugin-form/src/sectionFields.ts — edited by this change

    objectui#2212 recorded this exact symptom and PR fix(form): evaluate view-level FormField.visibleOn with the canonical CEL engine #2214 fixed it — in a different chain: ModalForm → resolveFormViewLayout → @object-ui/plugin-form sectionFields.ts → @object-ui/components renderers/form/form.tsx. FormPage.tsx is on that chain at no point, and Form-view FormField.visibleOn (CEL) is never evaluated — conditional fields always render #2212's regression pin lives with the chain it fixed, so nothing in the suite could see this copy. One contract, two implementations, each only ever checked against itself.

  • names FormPage.tsx → apps/console/src/components/FormPage.tsx — edited by this change

    objectui#2212 recorded this exact symptom and PR fix(form): evaluate view-level FormField.visibleOn with the canonical CEL engine #2214 fixed it — in a different chain: ModalForm → resolveFormViewLayout → @object-ui/plugin-form sectionFields.ts → @object-ui/components renderers/form/form.tsx. FormPage.tsx is on that chain at no point, and Form-view FormField.visibleOn (CEL) is never evaluated — conditional fields always render #2212's regression pin lives with the chain it fixed, so nothing in the suite could see this copy. One contract, two implementations, each only ever checked against itself.

.changeset/dashboard-expandable-family-convergence-5692.md

  • names recordFields.tsx → packages/plugin-dashboard/src/recordFields.tsx — edited by this change

    plugin-dashboard's two private copies of the reference-bearing field family now read @object-ui/core's published EXPANDABLE_FIELD_TYPES instead of restating it — LOOKUP_TYPES in recordFields.tsx and the inline disjunction inside computeLookupExpand in ObjectDataTable.tsx. Both carry the identity pin the already-converged consumers carry, so a member-identical private copy fails rather than quietly re-forking the table.

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — edited by this change

    plugin-dashboard's two private copies of the reference-bearing field family now read @object-ui/core's published EXPANDABLE_FIELD_TYPES instead of restating it — LOOKUP_TYPES in recordFields.tsx and the inline disjunction inside computeLookupExpand in ObjectDataTable.tsx. Both carry the identity pin the already-converged consumers carry, so a member-identical private copy fails rather than quietly re-forking the table.

.changeset/lookup-dropdown-cell-renderer-5492.md

  • names widgets/lookupColumnDisplay.tsx → packages/fields/src/widgets/lookupColumnDisplay.tsx — edited by this change

    Both surfaces now call one shared module — widgets/lookupColumnDisplay.tsx, which owns column normalisation, the field-descriptor enrichment from the referenced object's schema, and the render itself. The picker's own renderCellContent and columnFieldDescriptors are now thin calls into it, so there is a single renderer left to drift from. The dropdown's extra columns are rendered into the option row itself; the row's title keeps the full option label, which is what a truncated label needs, instead of a raw-value dump.

.changeset/plugin-detail-8937-parent-scope-residue.md

  • names RelatedList.tsx → packages/plugin-detail/src/RelatedList.tsx — edited by this change

    • packages/plugin-detail/README.md (it is in files[], so it ships). It said the node's filter is AND-combined with { [relationshipField]: parentId }, full stop. Since objectui#7299 the parent condition is compiled to match the relationship field's arity, so a multi-valued relationship gets { [relationshipField]: { $contains: parentId } } instead. The paragraph now states both spellings and names the arbiter (@objectstack/spec/data's isMultiValueField). - The claim that the SQL driver decides arity on that same predicate. It does not: driver-sql gates the equality family on its own storage question, which reads multiple as truthy on ANY type. The two rules therefore disagree for a type outside MULTI_CAPABLE_TYPES carrying multiple: true. objectui#9184 moved the arity compiler into @object-ui/core's parent-scope seam and carried the claim with it, so the correction is recorded there — the seam now states the driver's measured rule, records the divergence as a divergence, and points at the upstream card that owns which of the two rules is right (objectstack#17469). RelatedList.tsx's pointer comment and the objectui#7299 test header carried the same sentence and are corrected to match.

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3582.5 KB 3607.4 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-DqZRxnaK.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) 570.05KB 136.37KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
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.72KB 37.05KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 242.17KB 63.61KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.25KB 63.50KB
plugin-kanban (index.js) 48.46KB 15.12KB
plugin-list (index.js) 116.42KB 28.89KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
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
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 20:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit f61dab1 Sep 30, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11070-reference-to-round4 branch September 30, 2026 21:11
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.

2 participants