Skip to content

fix(types): dashboard dateRange takes the spec authoring member on both faces (objectui#10334) - #10338

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10334-dashboard-daterange
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10334-dashboard-daterange

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Closes #10334
Part of #7759
Clause-②: yes

What

DashboardComponentSchema.dateRange now takes @objectstack/spec's DashboardSchema.dateRange authoring member by reference, on both faces. This settles the objectui#7759 group F residue row complex.zod.ts#DashboardComponentSchema::dateRange.

  • dateRange left DASHBOARD_SPEC_EXCLUDED. Both the Zod mirror (specFieldsExcept(stripImportedDefaults(SpecDashboardSchema).shape, …)) and the TypeScript twin (Omit over the spec Dashboard input type) read that one list, so both now project the spec member.
  • The hand-written mirror element (a stripping z.object with defaultRange: z.string()) and the hand-written TS member were deleted.
  • Ledger: 'dateRange' left WiderThanDeclared['complex.zod.ts#DashboardComponentSchema'], and the WIDER_ARMS row was removed. The WIDER header figures were re-derived to 8 / 10 / 11 and 5 / 5 / 0 / 1 (after merging main at 2d76f4e67; main read 8 / 11 / 12 and 5 / 6 / 0 / 1), with a history sentence. The pin 'the WIDER ledger's header figures are derived from its ARMS' is green on them.
  • page-app-dashboard-spec-parity.test.ts: dateRange was removed from the Dashboard omitted list.
  • Docs: content/docs/guide/dashboard-filters.md now says the shape is the spec's and that unknown presets and keys are refused.

Measurement (step 1): why the mirror was wider

I ran a tsc probe of the old mirror input against the old TS member, key by key. It gave Type '"defaultRange"' is not assignable to type 'never', so only defaultRange was wider: a bare string against the 14-member DateRangeDefaultRange. field and allowCustomRange agreed. The mirror also stripped unknown keys, which is not type-visible, whereas the spec object is strict.

Rule applied: rule 1 (spec-declared) and F1

The spec declares the key in both places I checked:

  • installed 17.4.0 (dist/ui/index.d.ts): dateRange: ZodOptional of a strict ZodObject, with field? string, defaultRange a ZodDefault enum of the 14 names (DATE_RANGE_DEFAULT_RANGES, the presets plus custom), and allowCustomRange a ZodDefault boolean;
  • objectstack main b81da66d (packages/spec/src/ui/dashboard.zod.ts): the same key set, strictObject with named alias refusals, and z.enum(DATE_RANGE_DEFAULT_RANGES).default('this_month').

The read site, resolveDashboardFilterDefs in @object-ui/core, already implements every arm: each preset lifts to { preset } through PRESET_RANGES, custom starts empty, field falls back to created_at, and allowCustomRange is read with !== false. So no renderer change was needed, and I judge the protocol right. The mirror still authors no default, because every crossing goes through stripImportedDefaults.

Evidence (HEAD 2d76f4e67, after merging origin/main past objectui#10300 a05c3506)

  • pnpm --filter @object-ui/types type-check (tsc, examples, test config): exit 0, run under the verify lock.
  • pnpm exec vitest run packages/types/: Test Files 228 passed (228), Tests 5099 passed (5099). core and plugin-dashboard type-check, and the three dateRange consumer suites pass (84 tests). Spec Main Shape Gate: a spec built from objectstack main 9d81af71 was injected, and types type-checks with 0 errors.
  • New pin packages/types/src/__tests__/dashboard-daterange-spec-10334.test.ts, 7 tests:
    • type equality of the twin and of the mirror input with Dashboard['dateRange'], plus a negative control;
    • invalid_value at dateRange.defaultRange for last_7_dayz;
    • unrecognized_keys at dateRange;
    • all 14 names accepted;
    • no default injected;
    • verdict parity with the spec schema over 8 samples.
  • Reverse verification, with the fix committed first and a trap restore that uses absolute paths: I wrote the base-commit complex.zod.ts and complex.ts into place. The on-disk markers confirmed the mutation (bare-string defaultRange 1, 'dateRange' in the exclusion list 1, old TS member 1).
    • The pin gave Tests 4 failed | 3 passed (7).
    • tsc -p tsconfig.test.json exited 2 with 3 errors: the pin's mirror equality, plus WiderLedgerMismatch on complex.zod.ts#DashboardComponentSchema and on dateRange.
    • Restore used git checkout HEAD -- FILE. Afterwards git diff HEAD was 0 bytes, and the HEAD and disk blob hashes matched for both files.
  • Forward compatibility (Spec Main Shape Gate reproduced locally). I built @objectstack/spec from objectstack main b81da66d, ran npm pack, then node scripts/spec-main-shape-gate.mjs inject, which exited 0 and re-pointed zod 4.6.1. Against that spec:
    • types type-check exit 0;
    • the pin, the parity ledger, the Dashboard spec parity and twins-9736 gave 72 passed.
    • The pinned 17.4.0 install was restored afterwards with pnpm install --force, verified by the spec 17.4.0 manifest declaring zod ^4.4.3.
  • Downstream: turbo build of the @object-ui/plugin-dashboard^... closure (12 successful, 12 total), then pnpm --filter @object-ui/core type-check and pnpm --filter @object-ui/plugin-dashboard type-check, all exit 0. The three dateRange consumer suites (DashboardRenderer.filters, dashboardAuthoredInputs, core dashboard-filters) gave 80 passed.
  • eslint --no-inline-config --format json over the 5 changed TS files: 5 files, 0 errors. There are 13 no-explicit-any warnings, all on untouched pre-existing lines. Type-aware linting is not configured (no parserOptions.project in eslint.config.js), so the diff cannot move any untouched file's verdict.
  • These gates exited 0: check-changeset-presence, check:spec-symbols, check:new-line-citations (0 new citation(s)), check-changeset-no-major, check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check:installed-pin-claims, check:doc-fences, check-doc-links.
  • NOT MEASURED: check:doc-snippets. Reason: it exited 2 with PREREQUISITE (it needs a scoped workspace build), so it is left to CI. The doc edit adds no code block.

Changeset

.changeset/10334-dashboard-daterange-spec-authoring.md is minor on @object-ui/types, and it states the breaking change to the validator only. An unknown defaultRange and unknown keys inside dateRange are now refused where before they were accepted or stripped. The TS type is unchanged in effect.

Acceptance notes


Generated by Claude Code

…th faces

DashboardComponentSchema.dateRange was on DASHBOARD_SPEC_EXCLUDED and
restated locally: the Zod mirror typed defaultRange as a bare string (and
stripped unknown keys) while the TypeScript twin bound it to the spec's
DateRangeDefaultRange, so the validator admitted preset names tsc refused.

The spec declares the key, so per the group F rule 1 / F1 ruling both faces
now take the spec's authoring member by reference: dateRange leaves the
exclusion list, the local element and member are deleted, and the
WiderThanDeclared / WIDER_ARMS rows are removed. A pin asserts type
equality with the spec member and the spec's verdict and issue envelope.

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

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 28 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. objectui#7804's TreeViewSchema slice 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/6687-chatbot-surface-authorable.md

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

    Measured on both declaration faces before the fix, each with a control that had to hit: schema.surface appeared 0 times in renderer.tsx against schema.placeholder at 3 (one per registration) and schema.processVisibility at 1; and ChatbotSchema (packages/types/src/complex.ts) declared 34 keys, not this one. Two faces agreeing is what made the zero a reading rather than a bad query.

.changeset/6939-kanban-column-cards.md

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

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

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

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

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

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

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

.changeset/7295-chat-message-avatar-keys.md

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

    packages/plugin-chatbot/src/index.tsx:173–178 reads message.avatar || userAvatarUrl and message.avatarFallback || userAvatarFallback (and the assistant twins), the authoring-to-runtime seam spreads every unlisted key through (chatMessageAdapter.ts, ...passthrough), and the SDUI renderer feeds the authored messages[] straight in — a per-message avatar override renders, is documented, and no authoring-facing type declared it. ChatMessage in packages/types/src/complex.ts has no index signature (objectui#5155, deliberately — none is added here), so an author annotating ChatbotSchema.messages was told a value that renders is an error (TS2353); the zod mirror ChatMessageSchema is a plain strip-mode z.object, so the value parsed green and was silently DROPPED from the parsed output.

.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/7655-chatbot-registration-authoring-faces.md

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

    New published symbol: ChatbotSharedKey, the string-literal union of the twenty keys all three registrations read. It is exported from complex.ts because an exported interface may not extend a Pick over a private name (TS4022), so it is emitted into dist/complex.d.ts and is reachable through the published @object-ui/types/complex subpath (it is not re-exported from the package entry). It is a census, not an authoring face.

.changeset/7703-chatbot-dark-keys-retired.md

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

    Each member goes to ?: never on packages/types/src/complex.ts and to retirementTombstone(...) on packages/types/src/zod/complex.zod.ts — both halves, in lockstep, the convention MarkdownSchema.sanitize (objectui#6972), TimelineSchema.timeScale (objectui#6355) and ObjectViewSchema.viewTabBar (objectui#7779) already carry. Each refusal names the key, says why it is retired, and points at what to write instead; one string feeds both the parse-time message and the .describe() metadata, so the two cannot drift.

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

    Each member goes to ?: never on packages/types/src/complex.ts and to retirementTombstone(...) on packages/types/src/zod/complex.zod.ts — both halves, in lockstep, the convention MarkdownSchema.sanitize (objectui#6972), TimelineSchema.timeScale (objectui#6355) and ObjectViewSchema.viewTabBar (objectui#7779) already carry. Each refusal names the key, says why it is retired, and points at what to write instead; one string feeds both the parse-time message and the .describe() metadata, so the two cannot drift.

.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/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/8415-filter-builder-condition-id.md

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

    Breaking for authored metadata: a filter-builder CONDITION must now declare id (objectui#8415). It is declared on both published faces — the TypeScript interface FilterBuilderCondition in complex.ts and the Zod mirror FilterBuilderConditionSchema in zod/complex.zod.ts — so a key the renderer has always required is finally validated instead of silently discarded.

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

    Breaking for authored metadata: a filter-builder CONDITION must now declare id (objectui#8415). It is declared on both published faces — the TypeScript interface FilterBuilderCondition in complex.ts and the Zod mirror FilterBuilderConditionSchema in zod/complex.zod.ts — so a key the renderer has always required is finally validated instead of silently discarded.

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

.changeset/8478-zod-pins-complex.md

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

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

    The remaining 6 addresses (zod/complex.zod.ts) stayed out of scope for this PR and returned to the queue rather than riding this PR's scope — the card did not close here.

.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/8801-object-kanban-allow-collapse-retired.md

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

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/8802-8257-8008-kanban-gantt-family-retirement.md

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

    What each retirement was, measured. Three of the four were registration-only: no schema face in @object-ui/types ever declared kanban-ui, kanban-enhanced or gantt as a component node type, so unregistering is the whole retirement. The bare kanban key was the exception — it had a declared arm on both faces (KanbanSchema in complex.ts and its Zod mirror), and a plain deletion there would have been the objectui#7664 failure: BaseSchema is .passthrough(), so a document naming a dropped key validates green and renders nothing. It therefore retires as a named refusal: the Zod union keeps an arm claiming the literal and answers a { "type": "kanban" } document with a message naming object-kanban as the remedy, while the TypeScript half is the absence of the arm from ComplexSchema and of the key from SchemaRegistry, so tsc refuses it at the authoring site.

.changeset/8871-page-node-refuses-breadcrumbs.md

  • names page-app-dashboard-spec-parity.test.ts → packages/types/src/__tests__/page-app-dashboard-spec-parity.test.ts — edited by this change

    Scope. One key, by name; the node is not strict. Only 2 of the 23 passthrough- surviving undeclared keys land on a real SDUI page node (actions and this one); the rest belong to different declarations that merely spell type: 'page'. Strictness would also have reddened a living pin — page-app-dashboard-spec-parity.test.ts, "the component envelope still passes unknown renderer props through" — which stays green and is re-asserted from this card's side.

.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/9628-kanban-column-collapsed-honoured.md

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/calendar-readme-schema-keys-5045.md

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

    README.md's "Schema API / CalendarView" block described a CalendarViewSchema that does not exist. Measured against the interface itself (packages/types/src/complex.ts) and its zod mirror: events — the schema's only required key besides type — was published as events?, so a reader following the README omits it and TypeScript rejects the node; defaultDate was string where the schema says string | Date; and onDateClick was listed as a schema key when it is a CalendarViewProps component prop, sending readers to a different package's surface for a key calendar-view does not have (the schema's key is onDateChange). The block also listed 6 of the schema's 13 keys with nothing saying it was a summary (objectui#5045).

.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.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 170fcb6df (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 1355 pending declaration(s) that publish a body (1928 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3039.7 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-CVGfW6xn.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.68KB 6.20KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
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.41KB 1.23KB
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.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 541.41KB 129.42KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 222.47KB 61.83KB
fields (index.js) 253.34KB 64.00KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 33.36KB 10.88KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.15KB 11.05KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 51.44KB 14.62KB
plugin-charts (index.js) 71.82KB 20.13KB
plugin-chatbot (index.js) 198.27KB 47.18KB
plugin-dashboard (index.js) 133.06KB 35.21KB
plugin-designer (index.js) 216.12KB 44.37KB
plugin-detail (index.js) 260.65KB 67.82KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 144.77KB 37.17KB
plugin-gantt (index.js) 167.99KB 41.37KB
plugin-grid (index.js) 215.46KB 58.88KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 113.90KB 28.11KB
plugin-map (index.js) 21.74KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.68KB 8.95KB
plugin-tree (index.js) 10.56KB 3.71KB
plugin-view (index.js) 85.18KB 21.05KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 110.46KB 36.33KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a72f5f9b855b69eb2e791f4019fddabda0196f51

① Derived judgments

Spec shape: confirmed on both sides. The installed 17.4.0 (dist ui/index.js:9386) and objectstack main (dashboard.zod.ts:1478-1486) both declare dateRange the same way:

  • strictObject({ field?: string, defaultRange: z.enum(DATE_RANGE_DEFAULT_RANGES).default('this_month'), allowCustomRange: z.boolean().default(true) }).
  • It carries 14 names: 13 presets plus custom.
  • The two alias maps are the same. The two blocks differ only in quote style.

Only defaultRange was wider: confirmed. The old mirror element was a non-strict (stripping) z.object with defaultRange: z.string(). The TS member already bound it to SpecDateRangeDefaultRange. So the TS face is unchanged in effect, and the zod face narrows on exactly two axes: the value set and the key set.

Rule 1 / F1: PASS.

  • dateRange leaves DASHBOARD_SPEC_EXCLUDED.
  • Zod face: specFieldsExcept(stripImportedDefaults(SpecDashboardSchema).shape, …) (complex.zod.ts:1188).
  • TS face: Omit over the spec Dashboard, which is z.input in both versions. Because it is the input type, the defaulted members stay optional. It is the authoring schema, not a read model.

Accept set narrowed: probed directly.

input result
defaultRange: 'last_7_dayz' refused, invalid_value at dateRange.defaultRange
{ field, bogus } refused, unrecognized_keys
{ preset: 'today' } refused, with the spec's alias message: "Did you mean preset → defaultRange?"
{}, { defaultRange: 'custom' }, { field: 'created_at' } accepted

No default is injected: confirmed. The walker handles default, object (it preserves the .strict() catchall and the error map) and optional. The mirror parses { field: 'created_at' } to exactly { field: 'created_at' }. The raw spec parse of the same input adds defaultRange: 'this_month' and allowCustomRange: true. Batch #90 holds.

No stored document is refused. Every dateRange site outside tests uses only canonical keys and values: last_30_days ×11, this_quarter, last_90_days and custom, across the schema-catalog examples, plugin-dashboard.mdx, dashboard-filters.md and the plugin README. The last_7_dayz hits are globalFilters examples. Nothing matches in skills/.

Published symbols. SpecDateRangeDefaultRange was only a local import alias and was never re-exported. @object-ui/core's DATE_RANGE_DEFAULT_RANGES re-export is untouched. Nothing published is removed.

Forward compatibility. The shape is identical on main at source level, so the projection is identical. The types type-check was reproduced at exit 0.

Ledger. WIDER_ARMS loses one CONCRETE row, giving 17 / 25 / 29 and 5 / 20 / 0 / 4, which is correct. The page-app-dashboard-spec-parity omitted list drops dateRange.

② Semver level

minor on @object-ui/types is correct under check-changeset-no-major. The body's "Breaking (validator only)" section names both narrowings, including the alias prescriptions. It also states that the TS type is unchanged in effect and that no default is written.

③ Boundary flags

Implemented-by: claude/issue-10334-dashboard-daterange
Reviewed-by: session_01877XiBYSaRCk2CU7cMSg3S

VERDICT: PASS


Generated by Claude Code

…shboard-daterange

# Conflicts:
#	packages/types/src/__tests__/zod-mirror-parity.test.ts
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 21:34
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3042.7 KB 3104.5 KB
Main entry chunk (gzip) 148.4 KB 350 KB
Entry file index-C4x3RzgI.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.68KB 6.20KB
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.18KB 10.59KB
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.41KB 1.23KB
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) 26.08KB 7.56KB
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) 542.11KB 129.60KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 224.12KB 62.26KB
fields (index.js) 254.86KB 64.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
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) 34.99KB 11.45KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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.44KB 14.62KB
plugin-charts (index.js) 72.58KB 20.29KB
plugin-chatbot (index.js) 198.27KB 47.18KB
plugin-dashboard (index.js) 133.19KB 35.25KB
plugin-designer (index.js) 216.12KB 44.37KB
plugin-detail (index.js) 260.94KB 67.93KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.34KB 37.47KB
plugin-gantt (index.js) 168.31KB 41.45KB
plugin-grid (index.js) 215.22KB 58.85KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.17KB 28.16KB
plugin-map (index.js) 21.74KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.72KB 8.96KB
plugin-tree (index.js) 10.74KB 3.76KB
plugin-view (index.js) 85.86KB 21.38KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit ce6bd99 Sep 24, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10334-dashboard-daterange branch September 24, 2026 21:58
os-bill pushed a commit that referenced this pull request Sep 24, 2026
…-unread-keys

Resolves the WIDER ledger header: objectui#10334 (via #10338) retired the
DashboardComponentSchema::dateRange row, and this branch retires the
HeaderBarSchema::logo entry. Re-derived figures: 7 / 9 / 9, split
5 / 4 / 0 / 0, with both history sentences kept in order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01877XiBYSaRCk2CU7cMSg3S
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
… left, center, height) (objectui#10387) (objectstack-ai#10395)

Fixes objectstack-ai#10387
Clause-②: yes

Retires **all eight** `HeaderBarSchema` keys the `header-bar` renderer
never reads (`title`, `logo`, `nav`, `left`, `center`, `right`,
`sticky`, `height`) from both faces. It uses the ADR-0049 tombstone
shape objectui#10286 used for `variant`: `?: never` on the TS face
beside a `retirementTombstone(...)` refusal in the zod mirror. It also
migrates the only in-tree authors (docs and a type-check example) to
`crumbs` / `actions`.

Authority: objectui#7759 ruling 5617465269 rule 2 (objectui-own key, so
the read site is the truth), D1-(ii) (both faces dead and the spec
silent, so retire), and ADR-0049 enforce-or-remove. The PM seat ruled
**decision A** on the four keys that had authors (`title`, `logo`,
`right`, `sticky`): retire them in this PR and migrate the authors.

## 1. The read set, measured at runtime (not by grep)

A one-off probe (deleted afterwards, not committed) rendered `{ type:
'header-bar', crumbs: [{ label: 'Home' }] }` through the real
`SchemaRenderer` and the real registry inside `SidebarProvider`. It
varied one key at a time and compared `innerHTML` with the render that
omitted the key:

```
title="PROBEX"                         identical=true
logo="/probex.svg" | node | [node]     identical=true
nav=[{label,href}]                     identical=true
left / center / right = node | [node]  identical=true
sticky=true | false                    identical=true
height="PROBEX" | 99                   identical=true
className="probex-cls"                 identical=true
rightContent=node   (lit control)      identical=false, probe text present
```

The renderer's one function takes only `{ schema }` and reads `crumbs`,
`search`, `actions` and `rightContent`. No spread props reach it.

Spec: `packages/spec/src` at objectstack `9d81af71` and the installed
17.4.0 `dist` have zero `header-bar` / `HeaderBar` hits. The control
word `breadcrumb` hits in both.

## 2. Authors, and how they migrated

| key | in-tree authors before | migration |
|:--|:--|:--|
| `title` | `packages/types/README.md` (Composable example),
`content/docs/guide/objectos-integration.mdx` (page `header`),
`packages/types/examples/dashboard.ts`; inert `title: 'H'` filler in
`header-bar-right-content-numeric-falsy.test.tsx` (3 sites) and
`node-slot-numeric-falsy.test.tsx` (1 site) | README and guide: `crumbs:
[{ label: 'My App' }]`; dashboard: `crumbs: [{ label: 'Object UI
Dashboard' }]`; tests: filler dropped (it never rendered, so each test's
`CHROME` baseline is unchanged) |
| `logo` | `packages/types/examples/dashboard.ts` (`'/logo.svg'`) |
dropped |
| `right` | `packages/types/examples/dashboard.ts` (Profile button) |
the button moved to `actions` |
| `sticky` | `packages/types/examples/dashboard.ts` (`true`) | dropped |
| `nav`, `left`, `center`, `height` | none | none needed |

The guide's header also carried an undeclared `navigation` bag that
nothing reads (the page's `sidebar` already holds the same items). It
was dropped together with `title`, because the node was rewritten
anyway.

The `header-bar` registration `inputs` offer only `crumbs`, `search`,
`actions` and `rightContent`, so the designer never offered any of the
eight.

## 3. Ledger (`zod-mirror-parity.test.ts`)

- The `navigation.zod.ts#HeaderBarSchema` `WiderThanDeclared` entry
(`logo`) and its `WIDER_ARMS` row (`['CONCRETE', 'CONCRETE']`) are
removed. A note records where the entry stood.
- The WIDER header now reads **7 entries / 9 keys / 9 arms, split 5
SCHEMA-NODE / 4 CONCRETE / 0 MIXED / 0 unions**. Its history now has
this PR's sentence (it read 8 / 10 / 11, split 5 / 5 / 0 / 1) above
objectui#10334's sentence (8 / 11 / 12, split 5 / 6 / 0 / 1). The
derivation pin 'the WIDER ledger's header figures are derived from its
ARMS' reads these figures and is green.
- Instrument repair: the non-vacuity pin 'the enumeration can tell a
union slot from a plain one' required the **ledger** to hold at least
one union key. `logo` was the last one, so the pin went red on a
shrinking ledger even though the unwrapper worked. Its union direction
now also measures a fixed control slot outside the ledger,
`SidebarSchema.content` (single-or-list, two arms). The single-arm
direction still reads the ledger's own rows. After the merge,
`WIDER_ARMS` holds zero multi-arm rows, so that control slot is now the
only two-arm reading the pin gets. It passes.
- origin/main is merged twice, never rebased: `00cdaff1f` (merge
`2ea13bc2a`), then `ce6bd99a5`, which is PR objectstack-ai#10338 / objectui#10334
(merge `a74282b89`). The second merge conflicted only on the WIDER
header paragraph. It was resolved by re-deriving the figures and keeping
both history sentences in order. The merge changed nothing on this
branch beyond the ledger figures: `navigation.ts`, `navigation.zod.ts`
and the pin test are byte-identical to `968cd7698`.

## 4. Pins and reverse verification

`packages/types/src/__tests__/header-bar-unread-keys-10387.test.ts` has
19 tests. They cover:
- each of the eight keys refused BY NAME for a value from every arm
either face admitted (including `logo` as a URL string, a node and a
node list);
- the refusal's `code` and message head, naming what the renderer reads
instead;
- a lit control: a document without the keys, and one with an undeclared
key, both parse;
- a row showing that the keys the renderer does read still parse;
- a `@ts-expect-error` per key, checked by `tsc -p tsconfig.test.json`.

Reverse verification ran from the committed state via objectstack
`scripts/ablation-replace.mjs`: the anchor must hit, the blob must move,
the file is restored to the HEAD blob, and `git diff HEAD` must be
empty.

| leg | mutation | red / total |
|:--|:--|:--|
| nav (on `095636b79`) | old `z.array(NavLinkSchema)` arm restored | 2 /
11 |
| height (on `095636b79`) | old string-or-number arm restored | 2 / 11 |
| logo (on `968cd7698`) | old node-or-node-list arm restored | 2 / 19 |
| title (on `968cd7698`) | old `z.string()` arm restored | 2 / 19 |

In every leg the lit control stayed green. The restore is proven by the
blob matching HEAD (`385141979aac` on `968cd7698`) and an empty `git
diff HEAD`.

## 5. Verification (head `a74282b89`; ablation and shape gate as noted)

- `pnpm --filter '@object-ui/components^...' build`, then `pnpm --filter
@object-ui/types type-check` (src, examples and test projects) and `pnpm
--filter @object-ui/components type-check`: exit 0.
- `vitest run packages/types/ examples/schema-catalog/` plus the three
header-bar renderer tests: 265 files / 7437 tests passed on `a74282b89`.
- **Spec Main Shape Gate** (run on `968cd7698`, NOT re-run on
`a74282b89`: that merge brought in main's objectui#10334 type change, so
the shape-gate reading below is for the pre-merge tree; CI runs it on
the merge):
- built `@objectstack/spec` from objectstack `origin/main`
`9d81af714f49` in a throwaway worktree and packed it;
- `node scripts/spec-main-shape-gate.mjs inject --tarball ... --sha
9d81af714f49... --upstream-checkout ...`: exit 0;
- `TURBO_FORCE=true turbo run type-check --filter=@object-ui/types
--filter=@object-ui/components`: 10/10 tasks;
- `report --status 0`: "objectui type-checks against `@objectstack/spec`
at that commit".
- These gates exit 0: `check:component-surface-parity`,
`check:designer-field-key-parity`, `check:prompt-keys`,
`check:doc-types`, `check:doc-fences`, `check:new-line-citations`,
`check:control-bytes`, `check:changeset-claims`,
`check:pending-changeset-literals`, `check:spec-symbols` (on the normal
install), `check:doc-example-readers`, `check:test-path-roots`,
`check-changeset-presence`, `check-changeset-no-major`.
- NOT MEASURED: `check:doc-snippets` and `check:doc-examples`. Reason:
PRECONDITION NOT MET (exit 2); they need a 34-package build, so they are
left to CI.
- ESLint, narrowed to the 7 changed `.ts`/`.tsx` files
(`--no-inline-config --format json`): 7 files, 0 errors, 6 warnings. All
six are `no-explicit-any` on lines this PR did not edit. The population
is `files: ['**/*.{ts,tsx}']` in `eslint.config.js`. Type-aware linting
is off, so this diff cannot change the lint result of any file it does
not touch.

## Acceptance notes (not filed)

- `className` is also unread by `header-bar` (see the probe row above).
The PM seat is filing it.
- `NavLinkSchema` / `NavLink` lose their `header-bar` consumer.
`SidebarSchema.nav` still uses them.
- The Schema block in `header-bar.mdx` lists only `crumbs` and the base
keys. Its usage note says the height is a fixed `h-16`, but the renderer
uses `h-14 sm:h-16`. Neither is touched here.
- `check:spec-symbols` exits 1 while spec **main** is injected into the
store. Spec main now exports names (for example `ObjectTimelineProps`
and `ObjectTreeProps`) that plugin packages already declare locally.
This comes from the spec bump, not from this diff: it is green on the
normal install.

## Changeset

`.changeset/10387-header-bar-unread-keys-retired.md`: `minor` on
`@object-ui/types`. The body has the breaking notes and the migration
for all eight keys.

Implemented by the os-dev agent dispatched from PM seat
`session_01877XiBYSaRCk2CU7cMSg3S` (`domain:spec#1`).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant