Skip to content

fix(types,cli): element:number validates, with the spec's dataSource waiver (objectui#10872, batch 2) - #10908

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-10872-element-number-arm
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-10872-element-number-arm

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #10872
Clause-②: yes

The hold is lifted. Seat ruling option (c), posted on objectui#10872, held this PR behind objectui#10909. That card merged as PR objectui#10944 (dd0d78f74d): the element:number renderer now reads dataSource.object (with properties.filter AND-combined with the binding's), and the registration declares dataSource. So the dataSource form this arm accepts now renders its metric. The code here was never narrowed. A base merge of main gave one CI run on the combined tree before landing.

Batch 2 of objectui#10872: element:number, the one ADR-0080 public block with a spec ComponentPropsMap row that batch 1 held, now validates on both faces in both binding forms the spec accepts. Its row requires object, and the spec's props gate waives exactly that member beside a dataSource.object; the arm mirrors that waiver by reference. The card stays open for the blocks still held (Acceptance notes).

What changed

  • packages/types/src/zod/public-blocks.zod.ts: ElementNumberBlockSchema, a member of PublicBlockComponentSchema.
    • properties is the spec row by reference with object alone made optional (below).
    • dataSource is declared as the spec's ElementDataSourceSchema, by reference through stripImportedDefaults. It is the same schema PageComponentSchema.dataSource is.
    • A node refinement (elementNumberObjectIsSupplied) restores the row's requiredness wherever the waiver does not apply.
    • The module docblock gains the waiver section and drops element:number from the held list.
  • packages/types/src/zod/index.zod.ts: ElementNumberBlockSchema is on the ./zod barrel.
  • packages/types/src/zod/imported-defaults.ts: comment only. The walker docblock's lazy paragraph now names the one clean import it rebuilds (see "The waiver").
  • packages/types/src/zod/README.md: the public-block group lists the arm.
  • Ledgers:
    • one EXCLUSIONS row in zod-mirror-parity.test.ts;
    • two IMPORTED rows (ElementNumberPropsSchema, ElementDataSourceSchema) in imported-defaults-8317.test.ts, plus REBUILT_CLEAN (see "The waiver");
    • element:number added to the batch-1 pin file's measured population.
  • Ratchet: NAMESPACED_REFUSED_AT_TYPE falls 398 → 397 with the batch-1 messages. The bare pin stays 76. A batch-2 row names the armed key, and two in-process objectui validate rows cover the change end to end.
  • Changesets:
    • new: .changeset/10872-element-number-arm.md ('@object-ui/types': minor, the Clause-②: yes line);
    • dated notes in the objectui#10533 form, frontmatter byte-identical, on .changeset/10872-public-block-zod-arms.md (its "less element:number" and "Not armed in this release" item stop describing the release) and on .changeset/8317-strip-imported-defaults.md (its "identity function on a subtree with nothing to strip" no longer holds for ElementDataSourceSchema).
  • Merge: origin/main 9f0c84a448 was merged in, including objectui#10897 (Serial).

H1: measured on main (6a7f24e92), before any edit

Both forms are refused at type, on both faces (safeValidateSchema, the face objectui validate runs, and StrictAnyComponentSchema):

  • { "type": "element:number", "properties": { "object": "opportunity", "aggregate": "count" } } gives [{"code":"invalid_union","path":"type","message":"Invalid input"}], tolerant and strict alike.
  • { "type": "element:number", "dataSource": { "object": "opportunity" }, "properties": { "aggregate": "sum", "field": "amount" } } gives the same output, tolerant and strict alike.

Taught or shipped element:number documents, and the form each uses. Each zero was read beside a lit control on the same instrument:

  • objectui: 0 in content/docs, skills, examples (control: element:text hits skills/objectui/rules/protocol.md). The one producer is the page designer's block inspector (block-config.ts element:number fields): an object picker writing properties.object, which is the props form.
  • objectstack origin/main: 0 in examples (the showcase included; control: element:text hits 5 example page files). content/docs/ui/pages.mdx and content/docs/references/ui/page.mdx name the type in the vocabulary only, with no document.
  • objectstack spec fixtures (by test name):
    • "should accept element:number component" uses the props form;
    • "PageComponent dataSource integration" uses both forms at once (properties.object and dataSource.object);
    • the migration element-number-filter-rule-array prescribes a props-form bag with a rule-array filter.
  • Nothing taught or shipped uses the dataSource-only form for element:number. The spec gate's own dataSource-only specimen is element:record_picker.

The waiver, and how it is by reference

  • The spec gate: validateComponentProps (@objectstack/lint) skips, per DATASOURCE_SUPPLIED_PROP / suppliedByDataSource, the row issue at object when dataSource.object is a non-empty string, and judges a bag only when present. Its test "does not report the required object prop when dataSource supplies it" pins that.
  • The bag: stripImportedDefaults(ElementNumberPropsSchema).partial({ object: true }).
    • zod 4's .partial(mask) clones the row's own def: strictness and the custom unknown-key guidance travel. The row's own registry metadata does not, because a clone sets no parent. The row has no top-level .describe(), so nothing is lost.
    • It wraps only the masked member in ZodOptional around the row's own object schema, and hands every other member through as the same object.
    • A pin asserts this member by member: every non-object member is toBe the row's, and object unwraps to the row's.
    • .partial() throws on an object carrying refinements. That is the loud answer if the spec ever adds one: the waiver is then re-decided rather than silently losing the check. The row has none on installed 17.4.0 or on objectstack origin/main.
  • The refinement: a bag that is a record and omits object is refused at properties.object (code custom, params.code ELEMENT_NUMBER_OBJECT_REQUIRED), unless dataSource is a record whose object is a non-empty string (the gate's strName). The message names both fixes. It runs with when: () => true, the objectql.zod.ts named-view precedent, so a missing object is reported beside another bag issue, as the gate reports it.
  • One deliberate difference from the gate's code:
    • The waiver covers an OMITTED object, which is how the gate's own docblock states it ("the one prop whose absence this rule does NOT report") and how this card's order states it ("may omit properties.object").
    • The gate's suppliedByDataSource matches the issue by path alone, so it also passes "object": 7 beside a binding. The arm leaves that to the row to refuse.
    • A pin row holds it: "the waiver covers an OMITTED object only".
  • Reading of "neither": a node carrying a bag with no object and no binding is refused. A node with no bag at all ({ "type": "element:number" }) is accepted, as the gate skips a node without properties and as every batch-1 arm leaves its bag optional (the batch-1 minimal-document row covers it).
  • dataSource through the boundary: ElementDataSourceSchema carries no default but reaches a z.lazy (the recursive filter clause), and the walker's lazy arm always rebuilds. So it is the first clean import the boundary returns as an equal-answering clone rather than the spec's own object. Measured: the lazy count stays 4, because it is the same node already in the graph.
    • imported-defaults-8317.test.ts went red on exactly its two invariants: the identity row for ElementDataSourceSchema, and the lazy-owner row whose message says "update the docblock".
    • So the file now names the set: REBUILT_CLEAN, held equal to what the graph re-derives, asserted not reference-equal. The accept-set differential already covers every IMPORTED row.
    • The walker docblock says the same. No strip moved.

Pins and ablation

New pin file packages/types/src/__tests__/element-number-arm-10872.test.ts, the batch-1 pin population, and the ratchet file.

  • Red on base, green on head.
    • public-blocks.zod.ts and index.zod.ts were set to base 6a7f24e92 (on disk, ElementNumberBlockSchema count 0 in both) at head 3293f7a084: 3 files, 27 failed | 107 passed (134). Restored with git checkout HEAD --, the HEAD blob hash equal to the disk hash and git diff HEAD empty.
    • Red on base:
      • minimal properties.object document, both faces;
      • dataSource.object document with no properties.object, both faces;
      • neither, refused at properties.object;
      • reported beside another bag issue;
      • empty or non-string dataSource.object supplies nothing;
      • omitted-only waiver;
      • spec-invalid member refused at the member, by reference (format, and the record-form filter the spec migration retired);
      • undeclared bag member refused by name;
      • taught and shipped documents validate;
      • named export and union member;
      • the three by-reference rows;
      • the differential's 5 accept-verdict probes;
      • ratchet: the count row, and the batch-2 row;
      • the two objectui validate rows;
      • the batch-1 file's element:number rows and its union-population row.
    • The differential's 9 refuse-verdict probes are green on base by construction: base refuses every element:number document at type.
    • Head: 5 passed (5), 354 passed (354) over the new pin file, the batch-1 pin file, imported-defaults-8317, zod-mirror-parity and the ratchet file, at 278ee02ff2.
  • Ablation: remove the waiver.
    • At head 3293f7a084, the line if (dataSourceSuppliesObject(node)) return; was replaced by a marker comment through a script that refuses unless the anchor count is exactly 1. On disk after the mutation: anchor count 0, marker count 1. A trap restored it: HEAD blob c848ddf759 equals the disk hash, anchor 1, marker 0, git diff HEAD empty.
    • Result 3 failed | 131 passed (134), and exactly the dataSource-form rows reddened:
      • "a dataSource.object document with no properties.object is accepted on both faces";
      • the differential row "bag without object, dataSource.object";
      • the objectui validate row "validates element:number in both of its binding forms".
    • The subject resolves to src in both suites (relative imports in packages/types; the root vitest alias maps @object-ui/types/zod to src for the CLI file), so no dist build sits between the mutation and the reading.

The arm-list consumer sweep (a gate)

  • Search: git grep -l over every test file for AnyComponentSchema, nodeOracles, KNOWN_SCHEMA_TYPES, PUBLIC_BLOCKS, PublicBlockComponentSchema, block-config-schema-parity-8216, registry-inputs-spec-parity, StrictAnyComponentSchema, stripImportedDefaults, ElementDataSourceSchema, and handler-key-read-sites (the handler-key checker's arm table). That is 111 files at the final tree.

  • Runs, all under the verify lock:

    • packages/types/ and packages/cli/ whole, plus the 8 scripts hits: 294 passed (294) files, 6398 passed (6398) tests, at 379833972c;
    • the 46 other hits: 46 passed (46) files, 824 passed (824) tests, at 379833972c;
    • zod-subpath-single-module-8598.dist.spec.tsx in the opt-in dist project on a rebuilt @object-ui/types: 1 passed (1).
    • Before the merge, the same population (110 files) ran green at 3293f7a084: 63 + 46 files.
  • Every file's verdict is PASS:

  • apps/console (7): component-input-union-specimens.test.ts, element-data-source-input-injection.test.tsx, ga-honoured-inputs-author-reach.test.ts, html-tier-manifest.test.ts, public-contract.test.ts, registry-inputs-spec-parity.test.ts, unfulfilled-chart-stubs-8760.test.tsx

  • examples/schema-catalog (2): overlay-trigger-mirror-6939.test.tsx, safe-validate-corpus-6318.test.ts

  • packages/app-shell (4): ReportView.dataSourceObjectKey.test.tsx, phase1-page-blocks.render.test.tsx, block-config-schema-parity-8216.test.ts, page-tabs-item-value-8278.test.tsx

  • packages/cli (8): check-known-types.test.ts, check-unknown-type-case-suggestion.test.ts, check-validity-recogniser.test.ts, registered-types-validate-ratchet-10859.test.ts, retired-view-dashboard-key-9533.test.ts, unfulfilled-chart-stubs-retired-8760.test.ts, validate-root-path-line.test.ts, validate-union-arm-selection.test.ts

  • packages/components (6): page-container-authorable-keys.test.tsx, react-page-scope.test.tsx, record-picker-inputs-spec-parity.test.ts, text-input-inputs-spec-parity.test.ts, container-declaration-census.test.tsx, container-declaration-ratchet.test.tsx

  • packages/core (2): html-tier-intrinsics.test.ts, public-tier.test.ts

  • packages/layout (1): page-header-authorable-keys.test.tsx

  • packages/plugin-calendar (3): ObjectCalendar.recordSourceMembers-8314.test.tsx, ObjectCalendar.sortMembersReachTheWire-8171.test.tsx, dataKeysAreDeclaredAndHonoured-8314.test.ts

  • packages/plugin-dashboard (3): dashboardAuthoredInputs.test.tsx, objectMetricDrillDownMembers-8071.test.tsx, objectMetricTrendMembers-8071.test.tsx

  • packages/plugin-detail (5): detail-handler-slots-7804.test.tsx, detail-view-handler-slots-9447.test.tsx, detailSectionHeaderColorEnum-6955.test.ts, recordDetailsInputs.spec-parity.test.ts, recordRelatedListInputs.spec-parity.test.ts

  • packages/plugin-form (1): ObjectForm.elementDataSource.test.tsx

  • packages/plugin-grid (3): gridArrayArmOrderby-8973.test.tsx, gridDataInputContract.test.ts, gridFilterInputSpelling.test.tsx

  • packages/plugin-kanban (1): handlerKeyDispositionsMeasured-7804.test.tsx

  • packages/plugin-map (1): index.bareMapKeyRetired-10393.test.tsx

  • packages/plugin-tree (2): ObjectTree.overlayTitleI18n.test.tsx, registration.publicTier-10064.test.tsx

  • packages/plugin-view (1): objectViewHostSurface.test.tsx

  • packages/react (2): ElementDataSourceGate.malformedFilter-10789.test.tsx, ElementDataSourceGate.test.tsx

  • packages/sdui-parser (1): member-kind-8067.test.ts

  • packages/test-support (1): spec-tombstones.test.ts

  • packages/types (49): action-callback-retired-7068.test.ts, ai-insights-retired-8800.test.ts, ai-node-faces-agree-10874.test.ts, ai-zod-arms-10859.test.ts, any-component-union-fanout.test.ts, app-hidden-catalogue-flag-7542.test.ts, arm-named-export-8784.test.ts, bare-kanban-node-key-retired-8802.test.ts, block-family-retired-4895.test.ts, breadcrumb-object-tree-nameable-7917.test.ts, calendar-flat-color-allday-8466.test.ts, chart-axis-config-7690.test.ts, chat-message-avatar-keys-7295.test.ts, combobox-command-optional-6033.test.ts, content-channel-e3-residual-9256.test.ts, content-channel-family-d-9256.test.ts, content-channel-per-component-8284.test.ts, dashboard-daterange-spec-10334.test.ts, detail-view-field-options-10296.test.ts, element-number-arm-10872.test.ts, gantt-flat-config-declared-keys.test.ts, imported-defaults-8317.test.ts, imported-defaults-describe-9034.test.ts, imported-defaults-rest-less-tuple-9088.test.ts, input-wrapper-class-mirrored-8072.test.ts, kanban-calendar-filter-sort-8174.test.ts, nav-separator-mirror-refusal-10867.test.ts, navigation-spec-parity.test.ts, node-recursion-point-8344.test.ts, node-slot-registered-arms-8499.test.ts, object-chart-axis-config-10518.test.ts, object-kanban-group-by-limit-7322.test.ts, object-view-unmirrored-keys-7779.test.ts, objectql-union-arms-7363.test.ts, page-body-arity-8310.test.ts, phase2-schemas.test.ts, public-block-arms-10872.test.ts, registry-meta-carry-9102.test.ts, shorthand-input-type-refusal-8762.test.ts, spec-subschema-parity.test.ts, strict-authoring-face-8345.test.ts, text-value-retired-6951.test.ts, timeline-item-element-6356.test.ts, timeline-readme-schema-example-10824.test.ts, tree-view-data-retired-6951.test.ts, zod-lazy-getter-identity-7918.test.ts, zod-mirror-authors-no-defaults-7735.test.ts, zod-mirror-parity.test.ts, zod-subpath-single-module-8598.dist.spec.tsx

  • scripts (8): body-dialect-census.test.ts, check-component-surface-parity.test.ts, check-handler-key-read-sites.test.ts, check-installed-spec-pin-claims.test.ts, check-prompt-component-keys.test.ts, component-node-vocabulary-7434.test.ts, known-schema-types-derivation-5115.test.ts, spec-symbol-ledger-types-7265.test.ts

Fix round 1 (head fe8f6a6aad)

Contract review at 278ee02ff2 was FAIL on sequencing, not code; see the held note at the top. It asked for one text-and-test commit:

  • Changeset wording. .changeset/10872-element-number-arm.md now says an undeclared prop "is refused by name (unrecognized_keys on properties)". The out-of-range format and the record-form filter stay "refused at that member", which is what the row reports for them. The dataSource example is kept: it becomes true of the renderer when objectui#10909 lands.

  • Docblock. The .partial() docblock no longer claims the row's .describe() text travels. The clone sets no parent, so registry metadata does not inherit, and the row carries no top-level description anyway. Comment only.

  • REBUILT_CLEAN, measured where the rebuild happens. Four new rows in element-number-arm-10872.test.ts go through dataSource.filter, which is the member whose subtree reaches the recursive filter clause's z.lazy, rebuilt by the boundary:

    • a record filter, accepted;
    • a nested $and / $or, accepted;
    • filter: 5, refused;
    • a non-object clause inside $and, refused.

    Each row asserts the spec's own ElementDataSourceSchema verdict first (non-vacuity), then the same verdict on the arm's member, on PageComponentSchema, and on the node through both faces. The strict face rebuilds that lazy a second time. -t 'through the rebuilt binding answers' gives 4 passed | 28 skipped (32).

  • Retitle. The differential is titled as what it is: the spec node schema plus the test's model of the spec gate's documented reading (specGateAccepts), not the gate itself.

  • Readings at fe8f6a6aad, all under the verify lock:

    • element-number-arm-10872.test.ts, imported-defaults-8317.test.ts and the ratchet file give Test Files 3 passed (3) and Tests 228 passed (228);
    • pnpm --filter @object-ui/types run type-check exits 0;
    • check:control-bytes, check:new-line-citations (0 new), check-changeset-presence, check-changeset-no-major and check:changeset-claims all exit 0.
  • Merge-tree: git merge-tree --write-tree against a fresh origin/main 733fd5ac65 is clean (exit 0). main was not merged in.

Gates

Derived by hand from the root package.json and .github/workflows/: objectui has no dispatch-gates.mjs. Each line gives the verdict the gate prints, at head 278ee02ff2 unless noted.

  • node scripts/check-changeset-presence.mjs: exit 0, "9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)".
  • check-changeset-no-major.mjs and check-changeset-fixed.mjs: exit 0.
  • pnpm check:changeset-claims: exit 0. It is report-only, and it flagged 22 pending changesets naming a touched file. Each was read. The two sentences made false got the dated notes above. Of the rest:
    • 8344-node-recursion-point-redirect.md's module counts are unchanged (no new category module);
    • 8317-strip-imported-defaults.md's "107 component arms" census has been past since batch 1 and is left as a reading.
  • pnpm check:pending-changeset-literals: exit 0.
  • pnpm check:handler-key-reads: exit 0, "OK 129 arm(s) … 50 judged".
  • pnpm check:element-data-source-declaration: exit 0.
  • pnpm check:spec-symbols: exit 0.
  • pnpm check:new-line-citations: "VERDICT new-cross-file-line-citations: 0 new citation(s)".
  • pnpm check:control-bytes: exit 0.
  • pnpm check:installed-pin-claims: OK.
  • pnpm check:test-path-roots: exit 0.
  • node scripts/check-governed-queue-guard.mjs --test over the 12 changed paths: "NOT GOVERNED".
  • Type checks, at 379833972c, after the merge:
    • pnpm --filter @object-ui/types run type-check: exit 0. Its tsconfig.test.json includes the pin files; it reported a TS2559 in the new pin before the fix.
    • pnpm --filter @object-ui/types run build: "dist completeness: 1 package(s) complete".
    • pnpm --filter @object-ui/cli run type-check: exit 0, with the ratchet file in its program (--listFiles), on the @object-ui/cli^... closure built under the lock.
  • ESLint, a declared narrowing.
    • Population: the 8 changed .ts files, each resolved through the root eslint.config.js.
    • The --format json output has 8 files, 0 errors and 0 warnings.
    • Invariance: the config enables no type-aware linting (no parserOptions.project / projectService), and no local rule in eslint-rules/ reads another file. So the diff cannot move the verdict of an untouched file.
  • NOT MEASURED: check:readme-exports.
    • Reason: PREREQUISITE NOT MET. 27 of 40 packages are unbuilt, and the gate's own population floor refuses the run ("the population COLLAPSED").
    • This diff's one README edit (packages/types/src/zod/README.md) is a prose sentence and one name in a bullet list, with no fenced block and no import binding.
    • The readme-exports workflow builds and runs it.

Serial

Acceptance notes (what stays held)

  • Still refused at type, unchanged from batch 1 per ruling 5862923926:
  • Not declared here: the flat-props channel and the other envelope keys (events, responsiveStyles, aria, visibility, responsive). dataSource is declared on element:number only, because this waiver needs it.
  • The ratchet only falls: namespaced 397, bare 76.
  • The renderer gap, now objectui#10909, which this PR is held behind: measured at runtime. The element:number renderer (ElementNumberRenderer, @object-ui/components) reads its object from the properties bag only and never reads dataSource.
    • Probe: a real SchemaRenderer, the real registry, and an adapter with aggregate / find spies.
    • { type: element:number, properties: { object: contact, aggregate: count } } calls aggregate('contact', …) and paints 7.
    • { type: element:number, dataSource: { object: contact }, properties: { aggregate: count } } calls neither aggregate nor find and paints —.
    • So the dataSource form that the spec gate waives, and that this arm accepts, would validate on both doors (os validate and objectui validate) and render an empty metric if this PR landed first. That is why it is held.
    • This PR does not narrow the validator around it: a declared-but-unhonoured binding is an implementation gap on the renderer side.
    • Seam: spec:PageComponentSchema.dataSource (ElementDataSourceSchema.object) → renderer:ElementNumberRenderer.
  • Filed upstream by the seat: the spec gate's suppliedByDataSource waives any issue at object, not only its absence (finding(lint): validateComponentProps's dataSource waiver matches by path alone, so it also silences a wrong-typed object (e.g. object: 7) beside a binding, while its docblock scopes it to a missing object objectstack#20400). The arm follows the gate's docblock and refuses a wrong-typed object beside a binding.
  • Spec drift ahead: the installed spec (17.4.0) types ElementDataSourceSchema.filter as the record form, while objectstack origin/main has moved it to the rule-array form (migration element-data-source-and-object-block-filter-rule-array). The arm follows at the next spec bump, by reference. Today the spec fixture's rule-array dataSource.filter is refused by the installed spec and by this arm alike, which is why the taught-document row carries that fixture without its filter.

Session session_014mXUNuFomfj24w7s1pZzhN (domain:ui seat 2 dispatch, os-dev, mode:subagent).


Generated by Claude Code

…its required object

The bag is ComponentPropsMap['element:number'] by reference with `object`
alone made optional (`.partial({ object: true })` after the import
boundary); a node refinement restores the requiredness wherever the spec
gate's waiver does not apply (dataSource.object a non-empty name). The node
declares `dataSource` as the spec's ElementDataSourceSchema, by reference.

Ledgers: the EXCLUSIONS row in the mirror-parity census, the two IMPORTED
rows in the boundary differential, and the one clean import the boundary's
lazy arm rebuilds (ElementDataSourceSchema) named in that differential and
in the walker docblock.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…the ratchet (397)

The pins measure the arm against the installed spec: both binding forms
accepted on both faces, a bag with neither refused at properties.object, an
empty or non-string dataSource.object supplying nothing, a spec-invalid member
refused by the row, taught spec documents accepted, a per-probe differential
against PageComponentSchema plus the gate's waiver, and the bag and binding
read by reference. The namespaced ratchet falls to 397; objectui validate
takes element:number in both forms end to end.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…a dated note on batch 1's changeset

The batch-1 changeset's "less element:number" and "not armed in this
release" item stop describing the release once this lands, so it carries a
dated note; its frontmatter is unchanged. The pin's oracle takes the node as
a plain record so the test project type-checks.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…port is now rebuilt

ElementDataSourceSchema carries no default but reaches a z.lazy, so the
boundary returns an equal-answering clone of it; the entry's "identity
function on a subtree with nothing to strip" no longer holds for that one
import. Frontmatter unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…tted object only

Comment only. The spec gate's code waives any issue at `object` beside a
binding; its docblock and this arm waive absence only.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types tests package: cli labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 21 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/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/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/7344-handler-string-any-mirrors.md

.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/7694-chart-series-chart-type-alias-refusal.md

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

    • Before: series: [{ name: 'revenue', chartType: 'line' }] validated green through @object-ui/types/zod (safeValidateSchema, objectui check / objectui validate, any pipeline that keeps parse()'s output) — and the key was gone from the output, so a consumer of the parse result drew that series in the chart's own family, precisely what the author was overriding. On the TypeScript face the key was merely an excess property on a fresh literal; a widened object carrying it assigned structurally. - After: the same document REFUSES at series[i].chartType (issue code invalid_type) with one message on both channels — the parse-time issue and the .describe() metadata: Unrecognized key(s) on this chart series: \chartType`. Did you mean `chartType` → `type`? …followed by the reason and the remedy. Writetype: 'bar' | 'line' | 'area'. On the TypeScript face ChartDataSeries.chartTypeis a?: nevertombstone, so both the fresh literal and the widened assignment aretsc errors. - **Both written** ({ type: 'bar', chartType: 'line' }) is refused at chartTypealone — the key is not folded ontotypeand no precedence is minted between the two spellings. - **Which documents to scan.** The narrowing does not stop atChartDataSeriesSchema; it reaches every document through the parents that embed it — ChartSchema.series (zod/data-display.zod.ts, z.array(ChartDataSeriesSchema)) and, one level further out, ReportSectionSchema.chart (zod/reports.zod.ts, ChartSchema.optional()). Authors meet it through safeValidateSchema() (zod/index.zod.ts, which parses AnyComponentSchema) and through the CLI's objectui validate command (packages/cli/src/cli.ts). In practice: every chartnode'sseries[], and every report section whose chart` carries one.

.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/7917-export-breadcrumb-object-tree-zod-schemas.md

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

    AnyComponentSchema declares 107 node component types. 105 of them could be named on the ./zod barrel — ButtonSchema.safeParse(node), which is what a designer, a form builder or a targeted test needs. The arms declaring type: 'breadcrumb' (navigation.zod.ts) and type: 'object-tree' (objectql.zod.ts) could not: both were already export const in their own module, but index.zod.ts — the package's only zod entry point — did not re-export them, so the schemas existed, were maintained, and were applied by the union while no consumer could name them.

.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/8344-node-recursion-point-redirect.md

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

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

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

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names zod/public-blocks.zod.ts → packages/types/src/zod/public-blocks.zod.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — a second category module — objectui#10872. Later still in this same release zod/public-blocks.zod.ts joins them as well and imports zod/base.zod.ts too, so both counts in the mechanical note above are two higher, not the one the objectui#10859 note says; the reason the arm cannot be an import is unchanged. The rest of this entry, and that note, are kept as their readings.

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

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

    Why 49 is acceptable here where 2 was a defect: the 2 were data blocks a consumer had a standing reason to validate on their own, and objectui#7917 (PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777, open at the time of writing, and the holder of zod/index.zod.ts) exists to export exactly those. The 47 added here are HTML primitives and two input aliases — they have no per-tag consumer to serve, and exporting a SemanticElementSchema / HtmlElementSchema pair would publish a NAMED authoring surface (z.enum families, not per-tag schemas) that this card's ruling does not cover: the ruling is "arm the registered renderers", not "add public exports to @object-ui/types". So the metric is left to move and said out loud instead. ⇒ Whoever next runs that measurement should expect 49, and whoever wants the number back down should treat naming these families as its own decision. If PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777 lands first, the same movement reads 0 to 47.

.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/8598-zod-subpath-single-bundled-module.md

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

    The defect. This package declares "sideEffects": false, and src/zod/index.zod.ts fills the node recursion point as the initializer of its AnyComponentSchema const. tsc emitted that barrel as a module whose only other content is re-exports — so a bundler resolving import { CardSchema } from '@object-ui/types/zod' followed the re-export to dist/zod/layout.zod.js, needed nothing from the barrel's own body, and the flag let it drop that body whole. The fill went with it, and every child slot then validated against the pre-spec(types): redirect the node recursion point from BaseSchemaCore to AnyComponentSchema — measured at 9 newly-refused documents, and it drops 118 phantom strict refusals #8344 BaseSchemaCore arm — the ~21 base keys and nothing type-specific — with no error, no warning and no way for the guard inside the dropped code to notice. objectui#8344 shipped that window DECLARED and pointed here to close it.

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

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

    Two new exported symbols, both on packages/types/src/zod/index.zod.ts:

.changeset/9491-walkabledef-rest-null.md

  • names zod/imported-defaults.ts → packages/types/src/zod/imported-defaults.ts — edited by this change

    This is the declaration, not a behaviour change. Nothing here changes what either walker does with the value; the accept set of every exported schema is untouched. objectui#9088 already repaired the one arm the inaccurate type misled — the tuple arm in zod/imported-defaults.ts, which normalised the absent case to undefined because the declared type said that was the absent case, and so rebuilt every rest-less tuple through a === comparison that could never match. This change corrects the type that licensed it, so the next arm written against it is told the truth and tsc agrees with the truth instead of with the mistake.

.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/9659-node-recursion-point-inert-clause.md

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

    Prose amended where this change falsified it, ⛔ not rewritten. zod/index.zod.ts said defineNodeComponentUnion "wraps it rather than replacing it" — true only while a wrapper existed. zod/base.zod.ts's note on the loose parameter bound still names ChatbotSchema's record body in the present tense; the paragraph is kept for the reason it records and carries an AMENDED note saying the exclusion set that pin reads is now empty.

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3095.6 KB 3104.5 KB
Main entry chunk (gzip) 148.4 KB 350 KB
Entry file index-hE4_L-SI.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.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
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.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.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) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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: 278ee02ff2caedcb8db4628a1fa787e4c9ee7cef

① Derived judgments

Landing before the renderer honours dataSource: (c). Hold this PR behind objectui#10909. The PR's code does not need to change.

Evidence, all from reading:

  • What the spec declares. At the installed 17.4.0, the spec's DECLARATIONS for this node are two:
    • ComponentPropsMap['element:number'], which requires object;
    • PageComponentSchema.dataSource, a generic node key described as "Per-element data binding for multi-object pages".
  • Where the waiver lives. The waiver is not a spec declaration. It lives in @objectstack/lint as an advisory rule (validateComponentProps, warnings only, "nothing rejects this today"). Its only justification is a runtime claim: "objectui's element renderers read it FIRST (ds.object ?? props.object)". The gate's tests measure that claim on element:record_picker alone. For element:number the claim is false: ElementNumberRenderer reads only props.object.
  • objectui's own registration agrees with the renderer, not the waiver. It declares object required: true and no dataSource input. element:number does not go through elementDataSourceBlock, so withElementDataSourceInput never injects one. The sdui-parser html tier (BASE_PROPS has no dataSource) therefore reports has no prop "dataSource" on that node.
  • What landing as is would do. After (a), objectui would carry two declarations of element:number that disagree:
    • the zod arm, which accepts dataSource.object as the object's source;
    • the registration and manifest, which say dataSource does not exist there.
  • The only armed block that declares dataSource. After this PR, element:number is the only armed public block whose arm declares dataSource. It is also the one whose renderer ignores it. record:related_list's renderer does honour it through the seam, yet batch 1's strict face still refuses the key there.

The four axes:

  • Real demand. H1 found no taught or shipped element:number document in the dataSource form. The waiver serves no measured author. The props form, the one real form, is served whichever option is chosen.
  • Long-term soundness. The base rule is "declared but not honoured is an implementation gap: implement it or retire it, never narrow at the consumer". Implementing first satisfies that rule and needs no validator narrowing. The spec's own element:number row already records this ordering: "Sequenced consumer-first (the 2026-08-25 Option-A ordering ruling): objectui#6828 … re-measured before this declaration moved."
  • Keeping AI from writing errors. Under (a), objectui validate goes from refusing { type: 'element:number', dataSource: { object: 'contact' }, properties: { aggregate: 'count' } } to accepting it, and the metric renders an empty dash with no signal. That is exactly the shape "never let AI declare a capability the runtime does not honour" forbids. The changeset also teaches this form as a positive example.
  • Startup focus. "A declaration surface with no pull is handled implementation-first" points the same way.

What each other option costs:

  • (b) makes two spec-side readers disagree on a spec-valid document: the advisory gate says nothing, while objectui validate errors. The cost is bounded, because the remedy the refusal names (set properties.object) produces a working metric. It is still churn, since the waiver must be re-added later.
  • (c) costs nothing but time. Grade and dispatch objectui#10909, land it, and then merge this PR unchanged. The fix is dataSource.object ?? properties.object for the fetch guard, aggregate/find and the invalidation key. It should go through elementDataSourceBlock so the registration and manifest declare dataSource and relax object's required, and the html tier then agrees. After that, both validators, the html tier and the renderer tell one story.
  • If the maintainer wants the props form sooner, the fallback is (b) as a cut: the batch-1 propsBag row, no waiver and no dataSource member, with the waiver moving into objectui#10909's PR.
  • If the maintainer overrides to (a), the changeset must add this sentence after the dataSource example: "ElementNumberRenderer does not yet read dataSource (objectui#10909): a metric bound only through dataSource.object validates but renders empty until that lands — author properties.object."

Waiver fidelity: correct against the gate's documented reading.

Case Arm Gate (strName)
empty dataSource.object supplies nothing same
non-string dataSource.object supplies nothing same
dataSource that is not a record supplies nothing same
dataSource without object refused twice: spec binding at dataSource.object, refinement at properties.object reports object
properties.object present beside a binding left to the row's own verdict, no double report (pinned ['invalid_type']) —
  • 'object': 7 beside a binding. The arm refuses it and the gate's code waives it. Diverging here is right:

    • the row, which is the declaration, types object as z.string();
    • the gate's docblock says "absence";
    • the gate is advisory, so objectui validate is already stricter than it on every bag issue.

    The gate's path-only match (issue.path[0] === 'object') is itself a defect against its own docblock. The seat should file it upstream; the dev noted it but did not file it.

  • A node with no bag is accepted. This matches the gate on the raw door, and batch 1, whose bag-less record:related_list / record:path / element:text / element:button / record:reference_rail rows also omit required members. Two caveats, both inherited from batch 1 and belonging to the card, not this PR:

    • on the defineStack door, PageComponentSchema.properties is .optional().default({}), so the gate does judge a bag-less node there;
    • a bag-less, binding-less { type: 'element:number' } is literally "neither" and renders empty.
  • By reference. stripImportedDefaults(row).partial({ object: true }) keeps every other member reference-equal (pinned). The refinement restates only the gate's predicate, not a spec member. The always-true when param is honoured: zod 4.4.3 superRefine(fn, params) passes params through.

  • Docblock nit. The claim that ".partial() … its .describe() text travel[s]" is false in general: util.clone(schema, def) sets no _zod.parent, so registry metadata does not inherit. It is vacuous here because the row carries no top-level .describe(). Drop that clause.

REBUILT_CLEAN: sound and non-vacuous.

  • The set is held equal to the graph-derived list of clean lazy owners, and the one row is asserted not reference-equal.
  • The edited docblock in imported-defaults.ts is true, and the lazy count of 4 is consistent: the filter clause's lazy is already one of the four.
  • Equivalence holds by construction: cloneWithDef keeps def, checks, error maps and registry metadata, and there is no default below it.
  • Gap: it is not measured where the rebuild actually happens. None of the generic PROBES reaches filter, and all of them lack object. Nor do the six probes in the arm test's dataSource-member differential.
  • Suggested probes (non-blocking): { object: 'o', filter: { status: 'won' } }, a nested $and, and { object: 'o', filter: 5 }.

Pins.

  • Every order pin is red on base by construction. On base, element:number is refused only at type, so every issuesAt(properties.*) or acceptance assertion fails. The two CLI rows need Schema is valid and dataSource.object in the output, which base cannot produce.
  • The dev's 27 failed | 107 passed on base and 354 passed on head are consistent with this. The 9 refuse-verdict differential rows are green on base, as disclosed; they are extras, not order pins.
  • The differential's specGateAccepts is the test's own model of the gate's documented reading, not the gate. Its title ("answers as the spec does") overstates that. Nit.
  • Ablation isolates the waiver's accept half. Removing the one dataSourceSuppliesObject line reddens exactly the three dataSource-form rows. The commits after 3293f7a touch public-blocks.zod.ts in comments only.
  • Ratchet 397 is right. It was 398 after batch 1, minus the one literal. The only main commit since the merge-base (7ea8118) is auth-only.

Changesets.

  • 10872-element-number-arm.md: each sentence is true of the validator, with two exceptions:
    • "or an undeclared prop is refused at that member" is wrong: the refusal is unrecognized_keys at properties. Replace it with "or an undeclared prop is refused by name (unrecognized_keys on properties)".
    • The dataSource example is true of safeValidateSchema but, before objectui#10909, teaches a form that renders empty (see (c)).
  • Both dated notes are in the objectui#10533 form, append-only, with frontmatter byte-identical.
    • In 10872-public-block-zod-arms, the falsified sentences ("less element:number" and the first held item) are the right ones.
    • The 8317 note is accurate. The "identity function on a subtree with nothing to strip" sentence was already false in general under the walker's always-rebuild-lazy rule; this change made it observable on a real import.

README: "(21)" matches the 21 names listed, and the new sentence is true of the code.

② Semver level

'@object-ui/types': minor with Clause-②: yes is right. The change is a pure widening: one type literal and one new export. Nothing that parsed before is refused, because every element:number was refused at type on base. No BREAKING banner is needed and its absence is correct. No @object-ui/cli source changed, and imported-defaults.ts changed in comments only. Never major.

③ Boundary flags

  • CI on head: 43 check runs, all completed. 40 success, 3 skipped (Test (coverage), Test (coverage shard ${{ matrix.shard }}/4), dependabot), 0 failure. Type Check, Lint, Test shards 1 to 8, dist pins, README Export Check and Changeset Bump Policy all succeeded.
  • git merge-tree --write-tree against main 7ea8118: clean, exit 0, tree 556a57900e.
  • Open PRs: none touch public-blocks.zod.ts, imported-defaults*, the ratchet test or zod-mirror-parity.test.ts. The only overlap is the release PR objectui#5400, which consumes .changeset/8317-strip-imported-defaults.md; that is a bot regeneration, not a conflict.
  • check-governed-merges.mjs: NOT governed, 0 of 12 paths; 581 changed lines, at most 5000.
  • Draft: yes. Assignee: os-elon-musk. Labels include needs:contract-review.
  • Other: the branch's merge commit carries git's default message and no trailer pair. That is harmless if the queue squashes. objectui#10909 is open and ungraded (label finding only), and (c) depends on it being graded and dispatched.

Implemented-by: claude/issue-10872-element-number-arm
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

What stops landing now:

  1. Sequencing. objectui#10909 (the renderer resolves dataSource.object ?? properties.object, through elementDataSourceBlock so the registration and manifest declare dataSource) must land first. This PR then merges unchanged, after one boundary-flag re-check. If the maintainer rules otherwise, take either:
    • the (b) cut: batch-1 propsBag row, no waiver, no dataSource member; or
    • (a) with the changeset sentence quoted in ①.
  2. Changeset wording, to fix in any case: replace "or an undeclared prop is refused at that member" with "or an undeclared prop is refused by name (unrecognized_keys on properties)".

Non-blocking:

  • drop the .describe() clause from the .partial() docblock;
  • add filter probes for the rebuilt ElementDataSourceSchema;
  • the seat files the gate's path-only over-waiver upstream.

… changeset wording, docblock

- The changeset says an undeclared prop is refused by name
  (`unrecognized_keys` on `properties`), not at a member.
- The `.partial()` docblock no longer claims the row's `.describe()` text
  travels: a clone sets no parent, so registry metadata does not inherit.
- Four probes reach the recursive filter clause through the rebuilt
  ElementDataSourceSchema, each held to the spec's own verdict on the member
  and on the node through both faces.
- The differential is titled as what it is: the spec node schema plus this
  file's model of the gate's documented reading.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-CXe0iCYr.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.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
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.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.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) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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

akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…ts name 23 objectui issues that answer 404, and re-qualify 19 bare objectstack numbers (objectui#10803, batch 6) (objectstack-ai#10914)

Part of objectstack-ai#10803
Clause-②: no

Dispatched implementation of the `domain:ui` seat objectstack-ai#1 claim (comment
`5864334310`) on objectui#10803, batch 6, session
`https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk`. Citations
only: no sentence's claim moves, and every edited pending changeset's
frontmatter is byte-identical. The one runtime string that moves is
`InputSchema.wrapperClass`'s zod `.describe()` text in
`@object-ui/types`, which loses its dead pointer and nothing else
(amendment `5860244997`, Q1 = A; `patch` changeset). No test pins any
changed text: the literal-anchor sweep below finds no specific anchor,
so no test file is edited.

This batch carries the release note `5863776648`'s three lists:
- the last **23 family numbers**;
- the **18 bare objectstack numbers above 10900** from PR
objectui#10892's C0 census, plus **#13086**, a 404 the C0 instrument
cannot see because it shares a token with #13337 (**Premise**);
- the live-but-wrong bare `objectstack-ai#3391` at the two `rowCrudAffordances.ts`
sites beside `objectstack#3720`.

## Why `Part of`, not a closing line

The order says `Part of`. With this PR the family list (all 115 numbers
of amendment `5860244997` Q2) and the C0 above-10900 list both read 0.
Whether the card now closes, or carries the dead `objectstack#N` class
measured in **Acceptance notes** 1, is the seat's call.

## Premise, re-measured on `origin/main` `9f0c84a44` (the branch point)

- **The 23 family numbers.** REST `GET
/repos/objectstack-ai/objectui/issues/N`: 23 of 23 answer 404, and a
second read of each answers 404 again. `GET .../pulls/N` answers 404 for
all 23. Lit controls: objectui#10533 and objectui#7714 answer 200.
- **No new family member.** The distinct `objectui#N` citations in the
two in-scope classes at the branch point, less those at batch 1's head
`6c3ad7c80`, are 24 numbers. Each was read once: 24 of 24 answer 200. So
the family list is still batch 5's 23.
- **#14026 was a sister-repo card written as objectui's.**
objectui#14026 answers 404 as an issue and as a pull. objectstack#14026
answers 301 to objectui#10102, the card it was transferred to. That
card's measurement-round claim (branch
`claude/issue-14026-import-mapping-selector-probe`) is the session of
`ecf14190e`, the commit that added `14026-list-import-mappings-pin.md`,
and six sibling in-scope lines already write `objectstack#14026` for the
same card.
- **C0, the bare-number census** (the instrument of PRs objectui#10875
and objectstack-ai#10892, at the branch point):

```
git grep -hoP '(?:^|(?<=[^\w#&/.\-]))#\d+(?![0-9A-Za-z_])' 9f0c84a -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | sort -u | wc -l
```

- 984 distinct at `9f0c84a44`, against 998 at batch 5's `b2683a2c0`. The
14 that batch 5 re-pointed are gone, and none is new.
- Above 10900 there are 23: the same 5 CSS colours and the same 18
numbers. Each of the 18 answers 404 as an objectui issue and as an
objectui pull on two reads.
- In objectstack, 16 of the 18 answer 200 with the sentence's own
subject (the **C0 mapping** below). #13033 and #13413 answer 404 there
too, as issues and as pulls. Their squash commits exist in objectstack's
history (a full, not shallow, clone): `c459da6bc` "narrow the per-option
`default` key out of the form-view options vocabulary … (#13033)" and
`89448a52b` "remove the inert AUTH_SSO_PROVIDER_SCHEMA export (#13413)".
Both are ancestors of objectstack `main` (`git merge-base
--is-ancestor`, exit 0).
- **The C0 instrument's blind spot.** Its lookbehind refuses a `#`
preceded by `/`, `-`, `.` or `&`. That form (`#A/#B`, `-#N`) carries 109
distinct numbers at the branch point. The 17 that no earlier batch read
were each read once. 16 answer 200. **#13086 answers 404**, as an issue
and as a pull, in objectui and in objectstack. It sits in
`6985-wizard-card-r-alignment.md` as "the #13337/#13086 fence", in the
same token as #13337, so it rides here. objectstack's
`check-yaml-examples.ts` header, at its landing `2ebfe7e9f` (PR
objectstack#13267, which answers 200), reads "Check YAML Examples
(anti-drift for the AUTHORING format, #13086)" and "Ruled 2026-08-29
(#13086)".
- **objectstack-ai#3391.** objectui#3391 answers 200 with an unrelated subject (a
record-header api action placeholder). objectstack#3391 answers 200 as
the apiMethods whitelist contract card ("跟踪:UI 操作按钮与 apiMethods
白名单一致性契约落地"), and objectstack#3720's own title calls itself "objectstack-ai#3391
遗漏的第四个面".
- Every edited changeset is pending: it is present in `.changeset/` on
`main`. The checkout is not shallow.

## Census (the enumeration pin for this batch)

The instrument PR objectui#10854 printed and PRs objectui#10869 / objectstack-ai#10875
/ objectstack-ai#10892 reused, with this batch's 23 numbers substituted (REF = a
commit or tree):

```
git grep -nE '(objectui#|#|issues/)(8072|8127|8137|8204|8229|8248|8307|8408|9231|9241|9244|9365|9373|9375|9542|9553|9585|10117|10119|10120|10129|10132|14026)([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | grep -v 'objectstack#' | wc -l
```

- REF = `9f0c84a44` (branch point): **104** lines.
- One more line is hidden by the `grep -v 'objectstack#'` filter:
`9943-viewtype-totals-page-row-retired.md` names objectui#8127 beside an
`objectstack#` citation.
- So the true population is **105** lines: 28 changeset lines in 26
files, and 77 src lines in 42 files.
- Unfiltered, REF reads 111. The other 6 lines are live
`objectstack#14026` lines.
- REF = `2b3d2061a` (this head): **0**. Unfiltered it reads 7, all live:
the six `objectstack#14026` lines and the re-qualified one.
- This head merged with a fresh `main` (`8522396c0`, `git merge-tree
--write-tree`, clean, tree `f19d17884`): **0**. REF = `8522396c0` alone:
104.
- **C0**, the 19 numbers, bare and not `objectstack#`-qualified: 28
lines in 23 files at the branch point; 0 at this head. At this head C0
reads 966 distinct numbers, and above 10900 only the five CSS colours
are left.
- Lit control, the printed instrument over live objectui#7714 at this
head: 17 lines. Hex-colour false positives (a number followed by a hex
letter) at the branch point: 0.
- **Out of scope, as it stands** (the 23 numbers, filtered, whole tree
at this head):
  - 94 test lines in 36 files;
  - 5 `.github` lines in 1 file (`ci.yml`, objectui#9241);
  - 2 `apps/console` lines in 1 file (`FormPage.tsx`, objectui#8408);
  - 1 line of the root `vitest.config.mts`;
  - 0 scripts, 0 governed, 0 `CHANGELOG.md`.
- For the 19 C0 numbers: 13 test lines in 10 files, and 1 line of
`packages/plugin-tree/README.md`.

## Citation form

- **Landing shas.** The 9-character backticked sha of the commit on
`main` that landed the change the sentence rests on, as in the earlier
batches. All 15 distinct objectui shas below are ancestors of `main`:
`git merge-base --is-ancestor`, exit 0 each. Control legs in the same
checkout: the head of PR objectui#10902 (`496c63c06`) answers exit 1,
and the known ancestor `5f789538d` answers exit 0. `git rev-parse
--short=9` returns the same 9 characters for each.
- **Own-card pointers** in a changeset are dropped, not replaced. The
configured changelog generator (`@changesets/cli/changelog`, per
`.changeset/config.json`) prefixes each released entry with the hash of
the commit that added the file, which is that landing.
- **The claim lived only on the card.** Where the sentence rests on
something that lived only on the dead card, the sha only locates it:
"the card behind SHA".
- **Nothing answers.** The pointer is dropped and the sentence names the
card by role (objectstack-ai#9553 only).
- **Sister-repo numbers.**
- A bare sister-repo number becomes `objectstack#N` after reading it
there.
- "objectstack PR #N" becomes "PR objectstack#N", since the qualifier
now carries the repository. "framework #N", "upstream #N" and "spec #N"
keep their word, as batch 4 kept "framework PR objectstack#6942".
- Where the objectstack pull request itself answers 404, the sentence
cites its commit in that repository as objectstack `SHA`, the spelling
the tree already uses for an objectstack commit ("Measured on
objectstack `9bd4344e4`").
- **Runtime text** carries no sha: see **Special cases** 6.

## Mapping, the 23 family numbers

Lines / files are the branch-point census for that number. "Method" is
how the landing was found; for every source site, `git log -S
'objectui#N' -- FILE` names the commit that wrote the citation, and it
is the landing below unless the row says otherwise.

| dead number | resolution | method | lines / files | why the commit
carries it |
|:--|:--|:--|:--|:--|
| objectstack-ai#8072 | `c974edf14`; own-card pointer dropped; runtime pointer dropped
| changeset's adding commit, subject "(objectui#8072)" | 5 / 3 | it
mirrors `wrapperClass` on `InputSchema`, which is what both comments and
the `7722` changeset say happened |
| objectstack-ai#8127 | `ca3942729`; "the card behind `ca3942729`" twice (**Special
cases** 2) | `git log -S` names `ca3942729` for the first citation in
all 10 source files; `05a49f2ee` (objectui#9880) and `8a7e09fa1` (the
`9943` changeset's landing) wrote three later sentences that cite the
same fix | 17 / 11 | it derives `ViewType` from `@objectstack/spec`
instead of re-declaring it, and its message records the
`page`-degrades-like-a-typo measurement the sentences cite |
| objectstack-ai#8137 | own-card pointer dropped (landing `0fa7a9c83`) | changeset's
adding commit | 1 / 1 | the only site is its own changeset's pointer |
| objectstack-ai#8204 | own-card pointer dropped (landing `580b0fdf4`) | changeset's
adding commit | 1 / 1 | the pointer opened a paragraph as its own
sentence |
| objectstack-ai#8229 | "a separate finding" (changeset); "the finding `8b7ea3945`
reconciled" (`flex.tsx`) | `8b7ea3945`'s message: "Reconciles the third
face objectui#8229 found" | 2 / 2 | the finding lived on the dead card;
`8b7ea3945` is the commit that reconciled it, and it is the `7735`
changeset's own landing |
| objectstack-ai#8248 | own-card label dropped from the second list item (landing
`d02942b0e`) | changeset's adding commit; its message covers
objectui#8458 and objectui#8248 | 1 / 1 | the changeset lists the two
cards its one landing closed |
| objectstack-ai#8307 | `5591f03bd`; own-card pointer dropped | changeset's adding
commit | 11 / 4 | it makes a lane header over a windowed fetch say
`77+`, and it wrote every comment that cites the card |
| objectstack-ai#8408 | own-card pointer dropped; "seam 2 of the card behind
`8241a4400`" | changeset's adding commit; `8241a4400`'s message names no
seams | 2 / 2 | the seam list lived on the dead card |
| objectstack-ai#9231 | `383502b23`; own-card pointer dropped | changeset's adding
commit | 3 / 2 | it gives the create dialog's confirm control its own
accessible name, the rule both `i18n.ts` comments state |
| objectstack-ai#9241 | own-card pointer dropped (landing `250429c8f`) | changeset's
adding commit | 1 / 1 | the only site is its own changeset's heading |
| objectstack-ai#9244 | `bd0995738` | `git log --grep`: its message names the card; it
wrote all three comments | 3 / 2 | it emits one col-span class per
breakpoint tier, not one for the widest |
| objectstack-ai#9365 | own-card pointer dropped (landing `0970a0e00`) | changeset's
adding commit | 1 / 1 | the only site is its own changeset's pointer |
| objectstack-ai#9373 | `c1006ed8e`; own-card pointer dropped | changeset's adding
commit | 2 / 2 | it resolves the inline locale map before the
interpolation options, the `ListView` comment's subject |
| objectstack-ai#9375 | own-card pointer dropped (landing `e427e9c00`) | changeset's
adding commit | 1 / 1 | the only site is its own changeset's pointer |
| objectstack-ai#9542 | `43c0d1710`; own-card pointer dropped; "filed as a separate
card" (**Special cases** 3) | changeset's adding commit; `git log -S`
names it for the `action-button` / `action-icon` comments too | 9 / 6 |
it accepts and resolves an inline `I18nLabel` on `resultDialog` and
makes `ResultDialogSpec` derive its label members |
| objectstack-ai#9553 | nothing answers: "a separate card carried that census" | `git
log --grep` finds only `40f34b4ba`, which points at the card; no commit
lands it | 1 / 1 | see **Special cases** 4 |
| objectstack-ai#9585 | "`ee70287e4`'s pin measures it NOT GATED"; in its own
changeset, "a pin in this change measures it" | no commit names it;
`ee70287e4` adds the test "NOT GATED: the renderer paints the very node
the mirror refuses, without parsing it" | 2 / 2 | see **Special cases**
5 |
| objectstack-ai#10117 | `4c6f549ef`; own-card pointer dropped | changeset's adding
commit | 3 / 2 | it resolves a lookup title candidate in `page:header`
and adds the record-key safety net both comments describe |
| objectstack-ai#10119 | `73a3c89af`; own-card pointer dropped | changeset's adding
commit | 4 / 2 | it makes a nav ancestor's gates reach its subtree and
stops a group outliving its children |
| objectstack-ai#10120 | `80c54122e`; own-card pointer dropped | changeset's adding
commit; for each file `git log -S` names it, or a later commit that
cites its gate: `e0f820246` (objectui#10563) or `b809375ac`
(objectui#10163) | 16 / 13 | it makes a form neither submit nor offer a
field the caller may read but not edit, the FLS half every site names |
| objectstack-ai#10129 | `6cc910b6d`; own-card pointer dropped | changeset's adding
commit | 10 / 5 | it routes a field-backed action param to its record
picker and refuses an unreadable one |
| objectstack-ai#10132 | `061f5e829`; own-card pointer dropped | changeset's adding
commit | 8 / 4 | it makes the two declared-translatable dashboard
surfaces resolve, including the axis `title` forward |
| #14026 | re-qualified `objectstack#14026` | see **Premise** | 1 / 1 |
it is the card the pin's measurement answered |

## Mapping, the 19 bare sister-repo numbers

| number | resolution | what objectstack says | lines / files |
|:--|:--|:--|:--|
| objectstack-ai#11289 | `objectstack#11289` ("upstream") | "record:details sections
cannot survive an empty record: `hideEmpty` / `collapsible` /
`showBorder` are honoured by the renderer but undeclared" | 2 / 2 |
| #11662 | PR `objectstack#11662` | "feat(spec): declare hideEmpty /
collapsible / showBorder on record:details sections" | 1 / 1 |
| #12616 | PR `objectstack#12616` | "feat(spec): declare record:details
section headerColor as a closed six-token enum" | 1 / 1 |
| #12718 | PR `objectstack#12718` | "feat(spec): retire preview mode —
the RuntimeMode 'preview' value and the whole PreviewModeConfig block" |
1 / 1 |
| #13033 | objectstack `c459da6bc` | the pull request answers 404; its
squash commit narrows the per-option `default` key out of the form-view
options vocabulary, the ruling `form-spec.ts` says was executed upstream
| 1 / 1 |
| #13337 | `objectstack#13337` | "docs(objectui): layout-dsl teaches
only shapes the live schemas accept" | 1 / 1 |
| #13086 | `objectstack#13267` | the card answers 404; PR
objectstack#13267 landed its ruled YAML-examples gate (see **Premise**)
| the same line |
| #13413 | objectstack `89448a52b` | the pull request answers 404; its
squash commit removes an inert schema export and leaves a note recording
the absence as a choice, the shape the `base.zod.ts` note cites as
precedent | 1 / 1 |
| #13632 | `objectstack#13632` | "[spec] `FieldSchema` accepts a
`lookup`/`master_detail` with no `reference` target …", the card
17.3.0's refinement answered | 3 / 3 |
| #13733 | PR `objectstack#13733` | "feat(spec): wizard view v1 —
declaration-and-refusal tightening of FormViewSchema type:'wizard' (Card
S)" | 1 / 1 |
| #13855 | `objectstack#13855` | the field-grouping decision card whose
option B is a section `group` reference, landed by `39404f3d9` (#13897)
"a layout section can reference a declared field group" | 1 / 1 |
| #13906 | `objectstack#13906` ("framework") | "two more
`computeExecCtx` seams read "failed" and "not wired" as one value …" | 6
/ 3 |
| #14274 | PR `objectstack#14274` | "fix(sdui-parser): refuse an
authored `type` attribute on the html tier …" | 1 / 1 |
| #14945 | `objectstack#14945` | "A flow cannot REFUSE with per-record
text …", honoured by `cca699149` "the flow `end` node honours `outcome:
'refused'`" | 1 / 1 |
| #15469 | `objectstack#15469` ("spec") | "`GanttConfigSchema` is
`strictObject(...).passthrough()` …", landed as `9c270bba0` "close the
gantt/tree config .passthrough() windows … (#15469)" | 2 / 2 |
| #15948 | `objectstack#15948` | "fix(plugin-auth)!: session payload
`positions[]` is the security axis, not the better-auth role scalar" | 1
/ 1 |
| #17493 | `objectstack#17493` | "three residues of #17322's node-door
refusal …", landed as `2c1011b01` "refuse a blank string in a flow
node's predicate slot" | 1 / 1 |
| #20051 | `objectstack#20051` | "judge a flattened view overlay's
top-level `options.KIND` …", whose door half is `6a4aec71d` | 1 / 1 |
| #20160 | PR `objectstack#20160` | "fix(spec): a joined report refuses
a top-level dataset / rows / columns / values, pointing each onto
blocks[]" | 2 / 2 |

The live-but-wrong number: "(`/me/permissions` `apiOperations`, objectstack-ai#3391)"
becomes "… `apiOperations`, objectstack#3391)" at the two
`rowCrudAffordances.ts` sites.

## Special cases (the judgement calls)

1. **#14026 is re-qualified, not dropped as an own-card pointer.** The
card behind `14026-list-import-mappings-pin.md` was never an objectui
card: it was objectstack#14026, now objectui#10102. Dropping the pointer
would lose which hypothesis the pin refuted. Re-qualifying it matches
the six sibling lines.
2. **objectstack-ai#8127, the card by role twice.**
- `CreateViewDialog.tsx`: "one of the sites the card records as
"drifted, …"" becomes "one of the sites the card behind `ca3942729`
records as …". That record lived on the card.
- `plugin-list` `ViewSwitcher.tsx`: "objectui#8127 was filed against the
two in `@object-ui/types`, and the maps below were described there as
total …" becomes "the card behind `ca3942729` was filed against …".
- `normalize-list-view.ts`: "The bug objectui#8127 records" becomes "The
bug `ca3942729` records". That commit's message carries the measurement:
`page` resolved "to exactly what it resolved a typo to".
3. **objectstack-ai#9542 in `8648-ui-action-four-undeclared-keys.md`.**
- The superseded paragraph keeps its pre-landing voice: "so it is filed
as a separate card and ⛔ not guessed at here".
- The superseding paragraph reads "`43c0d1710` landed the derivation".
- "(Noted here by the objectui#9542 seat, …)" becomes "(Noted here by
the seat that landed `43c0d1710`, …)".
4. **objectstack-ai#9553, nothing answers.** `base.zod.ts` said "No count of authored
`events` keys is stated here — objectui#9553 carries that census." No
commit landed that card. `40f34b4ba` only points at it, and the
`AGENTS.md` fix `7550728a6` names a different card. So the sentence now
reads "— a separate card carried that census". The census is not
restated, per AGENTS.md objectstack-ai#9.
5. **objectstack-ai#9585, the measurement's instrument.** The card measured the render
path NOT GATED, and no commit names it. But the commit that wrote both
citing sentences, `ee70287e4`, adds a pin that re-measures exactly that:
"NOT GATED: the renderer paints the very node the mirror refuses,
without parsing it".
- `disclosure.ts` now reads "(`ee70287e4`'s pin measures it NOT GATED)".
- Its own changeset, `8236-collapsible-open-intercept-retire.md`, whose
landing is `ee70287e4`, reads "(a pin in this change measures it NOT
GATED)".
6. **The runtime string.** Only the listed text moves.

| file | member | before | after |
|:--|:--|:--|:--|
| `types/src/zod/form.zod.ts` | `InputSchema.wrapperClass` `.describe()`
| "Classes on the wrapper div around the input and its label
(objectui#8072)" | "Classes on the wrapper div around the input and its
label" |

No test, doc or changeset quotes it: a whole-tree search for the old
string returns only the source line.
7. **objectstack-ai#8229 in `flex.tsx`.** "(objectui#8229, folded into objectui#7735's
ruling)" becomes "(the finding `8b7ea3945` reconciled, folded into
objectui#7735's ruling)". objectui#7735 answers 200 and stays.
8. **#13413's sentence.** "Precedent of the same shape:
objectstack#12009 / PR #13413." becomes "… objectstack#12009 /
objectstack `89448a52b`." objectstack#12009 answers 404 too, but it is a
qualified sister-repo number outside this batch's lists, so it is left
and listed (**Acceptance notes** 1).
9. **#20160 across a line break.** In
`10746-joined-report-clears-binding.md`, "(objectstack PR" ends one line
and "#20160: …" opens the next. They become "(PR" and
"objectstack#20160: …".

## The literal-anchor sweep (both test-pin classes, ruling `5861900779`)

- **Instrument.** Every string, template, numeric and regex literal in
all 4068 tracked test and script files (119016 distinct literals),
parsed with TypeScript.
- **Candidate filter.** A literal is a candidate only if it matches the
diff's removed lines with two lines of context, raw or
comment-flattened. That leaves 2839.
- **Test applied to each candidate.** Does its occurrence count DROP
between `9f0c84a44` and `2b3d2061a` in any of the 92 files this PR
changes, in raw text or in a comment-flattened form? 229 distinct
literals drop.
- **Every one is a generic token.** Digits and short numbers,
punctuation, single letters, `objectui#`, `objectui#1`, and whole-tree
scanners such as `/#\d+/` and `/objectui#\d+/`. The three `objectui#\d+`
matchers (`ActionRunner.disabledGate`, `registry-inputs-spec-parity`,
`catalog-gallery-render`) assert over their own test data and read no
changed file.
- **No specific anchor.** None is a batch number, a changed phrase, or
the changed `.describe()` string. So no test pins changed runtime text
(class one), and no source-reading test pins a changed comment or
docblock citation (class two).
- Test titles and comments that name these numbers are out of the card's
classes and stay (**Census**, out of scope).

## Held

**By the serial rule: nothing.** Re-mapped before the push, on 9 open
PRs: objectui#10910, objectstack-ai#10908, objectstack-ai#10907, objectstack-ai#10906, objectstack-ai#10901, objectstack-ai#10891, objectstack-ai#10777,
objectstack-ai#10278 and the release PR objectstack-ai#5400 (objectstack-ai#10902 and objectstack-ai#10904 had merged since the
claim).

Two of them share files with this PR. Their hunks were read against
their merge-bases; each file is identical at its merge-base and at
`9f0c84a44`, so the lines map directly.
- **objectui#10907, `types/src/zod/form.zod.ts`.** It inserts a refusal
constant before `InputSchema`, two tombstone members further down
`InputSchema`, and a comment in the `InputShorthandSchema` arm. This
PR's three edited lines (the `wrapperClass` comment and description, and
the arm's "shrank that row" comment) sit outside every one of its hunks
and their 3-line context.
- **objectui#10906, `metadata-admin/i18n.ts` and
`previews/ViewPreview.tsx`.** Its `i18n.ts` insertions are far from the
two `createDraft` comments, and its `ViewPreview.tsx` hunks are far from
the `options` fold docblock.

Trial merges with this head (`git merge-tree --write-tree`):
- clean for objectstack-ai#10910, objectstack-ai#10908, objectstack-ai#10907, objectstack-ai#10906, objectstack-ai#10901, objectstack-ai#10891 and objectstack-ai#10777;
- objectstack-ai#10278 (`eab4c8e52`) conflicts in `ObjectGrid.tsx`,
`plugin-grid/README.md` and `content/docs/plugins/plugin-grid.mdx`, and
conflicts identically against `9f0c84a44` alone;
- objectstack-ai#5400 (Version Packages) regenerates and is not a hold.

objectui#10891 shares no file with this PR.

## Changesets

- `.changeset/10803-dead-citation-sweep-sixth-batch.md`, EMPTY
frontmatter. It covers the comment-only edits in 15 released packages;
no published behaviour changes through them. It points at the second
file for `@object-ui/types`' runtime text.
- `.changeset/10803-sixth-batch-runtime-strings.md`,
`'@object-ui/types': patch`: the `wrapperClass` description loses its
pointer. No key, path, issue code, accept set, refusal or severity
moves.

## Proof of prose-only (C4), against `9f0c84a44`, on this head
`2b3d2061a`

- **Source.** Each of the 53 touched `.ts` / `.tsx` files was parsed at
`9f0c84a44` and at this head with TypeScript 6.0.3's `createSourceFile`,
and re-printed by `createPrinter({ removeComments: true })`.
  - 52 of 53 prints are identical.
- `form.zod.ts` is equal once the one listed substitution (**Special
cases** 6) is applied to the base print, matched once.
  - 0 parse diagnostics.
- Lit controls on the same instrument: editing a string literal moves
the print; re-spacing a comment does not.
- **Changesets.** The frontmatter block of every one of the 37 edited
changesets is byte-identical at `9f0c84a44` and this head (37 of 37, by
md5). The overwrite gate below agrees.
- **Scope of the diff:** 92 files, +175 / −140. That is 37 edited and 2
new changesets, and 53 non-test source files in 15 released packages. No
test file.

## Gates, on this head `2b3d2061a`

Each line is the gate's own verdict and exit code, captured by
redirect-then-`$?`.

- `node scripts/check-changeset-presence.mjs`, exit 0: "53 source
file(s) of 15 released package(s) changed, and this change declares 2
changeset(s): .changeset/10803-dead-citation-sweep-sixth-batch.md,
.changeset/10803-sixth-batch-runtime-strings.md."
- `pnpm changeset:check`, exit 0: "All workspace packages are in the
changeset fixed group." / "No changeset declares a `major` bump."
- `node scripts/check-changeset-overwrite.mjs` (report-only), exit 0: "2
changeset(s) added, 37 modified, 0 deleted". `declared at base` equals
`declares now` for 37 of 37.
- `pnpm check:changeset-claims` (report-only), exit 0:
- "Every one of those 1 address(es) either names the tree it was read
from, or points at a line this change does not move";
- "Every package declared across those 31 body(ies) is either not
negated …";
- the standing notice "75 pending changeset(s) describe a file this
change touches".
- Read against the diff: a pending changeset quoting a replaced pointer
would itself carry the dead number and sit in the census, which reads 0.
No pending changeset quotes the changed description.
- `pnpm check:control-bytes`, exit 0: "check-control-bytes: OK (scanned
9178 tracked text file(s); skipped 85 binary)."
- `pnpm check:new-line-citations`, exit 0: "VERDICT
new-cross-file-line-citations: 0 new citation(s), enforcement
report-only -> exit 0".
- `pnpm check:pending-changeset-literals`, exit 0: "No test source names
a pending changeset."
- Also run over the touched comments:
  - `pnpm check:installed-pin-claims`, exit 0 ("OK");
- `pnpm check:comment-mask-corpus`, exit 0 (1 disagreeing file, within
the ceiling objectui#7882 holds open);
- `pnpm check:handler-key-reads`, exit 0 ("every judged read is a
declared member of it").
- The governed-surface predicate over the 92 paths, exit 0: "NOT
GOVERNED — 92 path(s) checked against 5 governed surface(s); none
matched." Lit control `AGENTS.md`: exit 3.

**Tests**, through the shared verify lock, on `2b3d2061a`. Each is
`VERDICT command-exit 0`.
- `scripts/__tests__/`, the whole directory, whose whole-tree scanners
read the touched files: `Test Files 177 passed | 2 skipped (179)`,
`Tests 5322 passed | 2 skipped (5324)`. The two skipped files are the
network-escape fixtures that run only as a child.
- `packages/types/`, the whole package, whose runtime text moved: `Test
Files 264 passed (264)`, `Tests 5854 passed (5854)`. `pnpm --filter
@object-ui/types type-check`, exit 0.
- **The pins nearest the re-pointed text:**
- in `types`: `input-wrapper-class-mirrored-8072`, `zod-mirror-parity`,
`wrapper-class-declared-7722`, `zod-mirror-authors-no-defaults-7735`,
`collapsible-open-refusal-8236`, `tree-view-config-readers-8253` and
`layout-default-jsdoc-7361`;
- in `components`: `collapsible-open-intercept-8236`,
`registration-defaults-match-renderer-8229` and
`action-undeclared-keys-8648`;
- in `plugin-view`: `ViewSwitcher` and
`ViewSwitcher.viewTypeTotalsBothLegs-9943`;
  - in `plugin-kanban`: `laneCountHonesty-8307`;
  - in `plugin-grid`: `rowCrudAffordances` and `rowCrudEffectiveOps`.
  - Result: `Test Files  15 passed (15)`, `Tests  381 passed (381)`.
- No red leg: the sweep found no anchor to move, so there is no pin
whose old copy should fail.

**Declared narrowing.** NOT MEASURED locally:
- the full suites and type-check of the 14 other touched packages, and
eslint.
- Reason: the comment-stripped syntax tree of 52 of 53 touched source
files is identical to `main`, and the 53rd differs only by the listed
literal.
- CI runs the full farm.

## Acceptance notes

1. **A sister-repo class of the same defect: 30 `objectstack#N`
citations answer 404 in objectstack.**
- **Measurement.** Every distinct `objectstack#N` in the two in-scope
classes at the branch point is 359 numbers. Each was read once: 328
answer 200, 1 answers 301 (objectstack#14026 to objectui#10102), and 30
answer 404, each confirmed by a second read.
- **The 30:** objectstack-ai#5970 objectstack-ai#5976 objectstack-ai#6038 objectstack-ai#6124 objectstack-ai#6281 objectstack-ai#6331 objectstack-ai#6450 objectstack-ai#6483 objectstack-ai#6515
objectstack-ai#9933 objectstack-ai#9934 objectstack-ai#10354 objectstack-ai#10485 objectstack-ai#10695 objectstack-ai#11330 objectstack-ai#11507 objectstack-ai#11513 #11658 #11703
#11753 #11846 #12009 #12868 #13117 #13670 #16126 #17147 #17762 #17987
#18012.
- **Sites at this head:** 82 lines in 63 files, 26 of them changeset
lines.
- **Three of them sit in sentences this PR edits, and are left as they
are:**
- objectstack#11846, "(objectstack#11846, landed as PR
objectstack#12718)" in `6748-preview-mode-provenance-ratchet.md`;
     - objectstack#12009, in `base.zod.ts`'s precedent line;
- objectstack#12868, in `form-spec.ts`'s "RULED 2026-08-28
(objectui#6263 / objectstack#12868, …)".
- **Why left.** They were not in this batch's lists, and the class is
the seat's to scope. objectstack's own card for dead tracker citations
in its tree is objectstack#19123, whose landing `66e266c93` counts
#12868 among the dead numbers it measured. Triage item 3 would put a
dead number in these classes on this card. The fix shape measured here
for such a number is the objectstack commit, as with `c459da6bc`.
2. **The rest of the bare `objectstack-ai#3391` population.** Only the two sites the
claim names were re-qualified. 28 more in-scope lines in 10 files write
the apiMethods whitelist card as a bare `objectstack-ai#3391`, which resolves to the
unrelated objectui#3391:
   - `ObjectDataPage.tsx`, `ObjectView.tsx` (app-shell);
   - `managedBy.ts`;
- `MePermissionsProvider.tsx`, `PermissionContext.ts`,
`PermissionProvider.tsx`;
   - `fieldWriteGate.ts`;
   - `ImportWizard.tsx`, `ObjectGrid.tsx`;
   - `ListView.tsx`.

It is live, not a 404, so it is outside the family pin. Two of those
lines pair it with a bare 3546 that means objectstack#3546 ("detail/form
面的 edit/delete 按钮接入服务端 effective 操作集"), while objectui#3546 is an
unrelated i18n card. Carrier: none.
3. **A stale claim beside a re-pointed sentence.** The same
`base.zod.ts` paragraph says "AGENTS.md's abridged protocol sketch shows
`events?: Record…` and its action-system commandment authors one".
`7550728a6` removed both from `AGENTS.md`, so that sentence is now
false. It is not a citation, and this PR does not touch it. Carrier:
none.
4. **The C0 blind spot, for any later census.** The bare-number
instrument's lookbehind hides a number written after `/`, `-`, `.` or
`&` (`#13337/#13086`, `-objectstack-ai#2231`). This batch read the 17 such numbers no
batch had read, and #13086 was the only 404. A later census can narrow
the lookbehind to refuse only a word character, `#` or `&` before the
`#`, and drop URL fragments by hand.
5. **Filenames are not citations.** Pending changeset and test FILENAMES
carry several of these numbers. They stay, as in PRs objectui#10707,
objectstack-ai#10797, objectstack-ai#10854, objectstack-ai#10869, objectstack-ai#10875 and objectstack-ai#10892.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_

Co-authored-by: Claude <noreply@anthropic.com>
…10872-element-number-arm

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3102.9 KB 3104.5 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-DMPh34mE.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.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.56KB 133.87KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.61KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 197.72KB 46.92KB
plugin-dashboard (index.js) 137.82KB 36.70KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.51KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.15KB 22.35KB
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.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.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) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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: fe8f6a6aade17aa3e1d5cff716daa27faee7be38

This is an in-seat delta review of fix round 1 (278ee02ff2 to fe8f6a6aa, one text-and-test commit), against record 5864946806 (FAIL: sequencing, plus one changeset sentence). It is posted at landing: objectui#10909 merged as PR objectui#10944 (dd0d78f74d), and a mechanical base merge of that main took the branch to 141035cfd8. That changed none of this PR's own lines (the patch-id dafa85c0e5 is identical before and after), and it re-ran the five shared test files green on the combined tree (363 of 363).

① Derived judgments

  • Sequencing: satisfied at landing. Seat ruling 5864951915 (option c) held this PR behind objectui#10909. This record posts only after objectui#10909 merged, so the renderer reads dataSource.object when this arm starts accepting it. ⛔ The PR's code is unchanged by the hold: the waiver and the dataSource member stay.

  • The changeset sentence: fixed. It now reads "a format outside number / currency / percent, or a record-form filter … is refused at that member, and an undeclared prop is refused by name (unrecognized_keys on properties)". The ordered phrase is verbatim, and the dev kept "at that member" for the two cases the row does report at the member. That is more accurate than a literal substitution and is accepted.

  • Docblock: fixed. The .partial() docblock no longer claims .describe() text travels. Comment only, and the PR body's copy of the same clause is corrected too.

  • REBUILT_CLEAN, measured where the rebuild happens: added. Four rows go through dataSource.filter, the member whose subtree reaches the rebuilt z.lazy:

    • a record filter, accepted;
    • a nested $and / $or, accepted;
    • filter: 5, refused;
    • a non-object clause inside $and, refused.

    Each row asserts the spec's own ElementDataSourceSchema verdict first (non-vacuity), then the same verdict on the arm's member, on PageComponentSchema, and on the node through both faces.

  • Differential retitle: taken. It names itself as the spec node schema plus this file's model of the gate's documented reading.

  • Nothing else moved: 3 files, 38 insertions and 9 deletions, with no code line outside comments.

  • Upstream: the gate's path-only waiver is filed as finding(lint): validateComponentProps's dataSource waiver matches by path alone, so it also silences a wrong-typed object (e.g. object: 7) beside a binding, while its docblock scopes it to a missing object objectstack#20400. The PR body names it.

② Semver level

'@object-ui/types': minor, Clause-②: yes, a pure widening. No BREAKING banner is needed. ⛔ Not major.

③ Boundary flags

  • CI: the landing waits for every check-run on the landing head 141035cfd8 to be final and green, and re-runs git merge-tree against current main first.
  • The base merge: PR objectui#10927 (merged) shares five files with this PR: index.zod.ts, zod/README.md, the ratchet, zod-mirror-parity.test.ts and imported-defaults-8317.test.ts. Every hunk is disjoint; the ratchet reads bare 73 and namespaced 397 on the combined tree.
  • NOT GOVERNED (12 paths).
  • Draft, assigned to os-elon-musk, with needs:contract-review.

Implemented-by: claude/issue-10872-element-number-arm
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: cli package: types tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants