Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/8314-calendar-record-source-authoring-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@object-ui/plugin-calendar': patch
---

`object-calendar` / `view:calendar` now DECLARE the last three spec-carried keys the renderer
already honoured — `data`, `staticData` and `loading` — so the html tier stops reporting working
metadata as `unknown-prop`. Each declared description names the POSITION the key is honoured at:
`data` replaces the calendar's own query, `staticData` is read below `data` and above `objectName`,
and `loading` is honoured only alongside an array `data` (objectui#8314, slice 2b of
objectui#8201). Every `object-calendar` key the spec declares is now discoverable.
44 changes: 38 additions & 6 deletions apps/console/src/__tests__/public-block-binding-reach.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
*
* That distinction — a plausible value for EVERY input is not the same as a
* plausible CONFIGURATION — is the lesson this file keeps re-learning, and it is
* recorded instance by instance rather than as a slogan: four in
* {@link SUPERSEDES_BINDING}, the fifth and sixth in {@link sampleFor}. Six is
* the count because every one of them cost a red or, worse, a green for the
* recorded instance by instance rather than as a slogan: five in
* {@link SUPERSEDES_BINDING}, the sixth and seventh in {@link sampleFor}. Seven
* is the count because every one of them cost a red or, worse, a green for the
* wrong reason. Read them before adding a sample.
*
* A block that declares `objectName` and asks the data layer for something else
Expand Down Expand Up @@ -242,10 +242,42 @@ const declaresObjectName = (cfg: { inputs?: Array<{ name?: string }> }) =>
* intact. That is the "plausible value ≠ plausible configuration" lesson a
* fourth time.
*
* `staticData` is the third entry, and it arrived exactly as the other two did —
* as a red on this probe the moment objectui#8314 declared `object-calendar`'s
* full authoring surface. It is RUNG 2 of the shared record-source ladder, one
* below `data` and one ABOVE `objectName`
* (`packages/core/src/utils/record-source.ts`):
*
* if (schema.staticData) {
* return { provider: 'value', items: schema.staticData };
* }
*
* if (schema.objectName) {
* return { provider: 'object', object: schema.objectName };
* }
*
* The published contract says the same from both faces — `ObjectMapSchema
* .objectName` / `ObjectGanttSchema.objectName` gloss it as *"the THIRD record
* source `getDataConfig` resolves, after `data` and `staticData`"* — and the
* consuming renderers act on it: `ObjectCalendar.tsx` takes its
* `hasInlineData && dataProvider === 'value'` branch, calls `setData(dataItems)`
* and never reaches `dataSource.find`. So filling `staticData` is the author
* telling the block not to fetch, and reporting "objectName never reached the
* data layer" from it would be the probe manufacturing its own finding, exactly
* as it would for `data`. Length-sensitive like `customFields`: `sampleFor`
* returns a non-empty `['name']` for an array input, and an empty one would have
* left the binding intact. That is the "plausible value ≠ plausible
* configuration" lesson a fifth time.
*
* ⚠️ Worth knowing for the next declaration that lands on a ladder block: the
* five blocks sharing this ladder (calendar, gantt, grid, map, tree) all read
* `staticData`, so any of them declaring it belongs here on the same reasoning,
* and this entry covers them without a further edit.
*
* Add to this list only with the guard quoted, so the next reader can check the
* claim instead of trusting it.
*/
const SUPERSEDES_BINDING = new Set(['data', 'customFields']);
const SUPERSEDES_BINDING = new Set(['data', 'staticData', 'customFields']);

/**
* A plausible value for one declared input.
Expand All @@ -255,7 +287,7 @@ const SUPERSEDES_BINDING = new Set(['data', 'customFields']);
* its empty state without asking for data would read here as an unbound
* binding.
*
* `sections` is the FIFTH instance of the lesson counted in
* `sections` is the SIXTH instance of the lesson counted in
* {@link SUPERSEDES_BINDING}, and the one objectui#3840 was filed for. The
* generic `array` sample is `['name']`, and a bare string is not a section:
* `@objectstack/spec`'s `FormViewSchema.sections` rejects it at parse —
Expand All @@ -271,7 +303,7 @@ const SUPERSEDES_BINDING = new Set(['data', 'customFields']);
* being the spec shape, not the fact that a different sample stops the crash
* (which is true either way).
*
* `formType` is the SIXTH, and it is why `object-master-detail-form` read GREEN
* `formType` is the SEVENTH, and it is why `object-master-detail-form` read GREEN
* while carrying the identical latent crash. That block declares `formType` as a
* bare `string` — not the enum `object-form` declares — so the default branch
* below handed it `'x'`, a value the form family has no path for. The crashing
Expand Down
66 changes: 51 additions & 15 deletions apps/console/src/__tests__/registry-inputs-spec-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,31 @@ const UNPUBLISHED_EXEMPTIONS: Record<string, string> = {
// answer; the `pins` prose on each entry says which. `object-calendar`'s
// three (`data`, `staticData`, `loading`) are untouched by that card — they
// are the other package, split out under the Q2 = B ruling on objectui#8201.
// ⚠️ objectui#8314 THEN TOOK THOSE THREE by the same exit — slice 2b, the
// other half of the Q2 = B split — and with them the LIST'S WHOLE
// `object-calendar` REMAINDER: `data`, `staticData` and
// `loading` are all declared on both `plugin-calendar` registrations, with
// a `MEMBER_PINS` entry apiece for the two array keys (registered below,
// never an exemption — objectui#8212's three-part obligation). Every
// `object-calendar` key the spec declares is now discoverable, and with
// objectui#8313's four gone too the entire remaining backlog is one key:
// `object-kanban.quickAdd`, the escalated one.
// ⚠️ The `loading` clause above was slice 1's REASONING and objectui#8314
// measured it: authored `loading: true` replaces a `data`-fed calendar with
// its loading placeholder and does nothing at all to a `staticData`-fed
// one. The prediction held, so the key was declared on exactly those terms
// — its published description names the coupling rather than hiding it.
// ⚠️ One correction that outlives the entries, recorded because a future
// reader would otherwise inherit the cheap reading of `data`. "An authored
// `data` skips the calendar's own fetch" is TRUE ON BOTH CARRIERS and
// therefore cannot fail as a pin: `SchemaRenderer` spreads non-metadata
// node keys as React props, so the same array is `rest.data` at the
// renderer boundary AND `schema.data` inside the component, where the
// record-source ladder returns it verbatim as a config that carries no
// `provider` and so matches no fetch branch either. Measured by ablation on
// objectui#8314: with the boundary's forward removed, "no query was issued"
// stayed green while every row asserting the authored ROWS ARE DRAWN went
// red. The pin registered below rests on the rows.
// ⚠️ One correction that outlives the entry, because the rider note that
// stood here asserted the opposite and a future reader would inherit it:
// `sort` is NOT a pass-through key, and its member claim is NOT the
Expand All @@ -1073,12 +1098,6 @@ const UNPUBLISHED_EXEMPTIONS: Record<string, string> = {
// is a SHARPER member claim than a pass-through, not a weaker one.
'object-kanban.quickAdd':
'Newly JUDGED rather than newly missing: the console registers this block with `registerLazy`, so it sat outside the population of this gate entirely until objectui#8176 loaded it. A DECLARATION SOMEONE OWES, not a ruled carve-out — nobody has yet asked, per key, against the read sites in the renderer, whether it should be published or carved out. objectui#8201 owns that question and its answer deletes this entry. objectui#8176.',
'object-calendar.data':
'Newly JUDGED rather than newly missing: the console registers this block with `registerLazy`, so it sat outside the population of this gate entirely until objectui#8176 loaded it. A DECLARATION SOMEONE OWES, not a ruled carve-out — nobody has yet asked, per key, against the read sites in the renderer, whether it should be published or carved out. objectui#8201 owns that question and its answer deletes this entry. objectui#8176.',
'object-calendar.staticData':
'Newly JUDGED rather than newly missing: the console registers this block with `registerLazy`, so it sat outside the population of this gate entirely until objectui#8176 loaded it. A DECLARATION SOMEONE OWES, not a ruled carve-out — nobody has yet asked, per key, against the read sites in the renderer, whether it should be published or carved out. objectui#8201 owns that question and its answer deletes this entry. objectui#8176.',
'object-calendar.loading':
'Newly JUDGED rather than newly missing: the console registers this block with `registerLazy`, so it sat outside the population of this gate entirely until objectui#8176 loaded it. A DECLARATION SOMEONE OWES, not a ruled carve-out — nobody has yet asked, per key, against the read sites in the renderer, whether it should be published or carved out. objectui#8201 owns that question and its answer deletes this entry. objectui#8176.',

// ── record:reference_rail.entries — a nested collection, newly JUDGED ──────
// (1 key)
Expand Down Expand Up @@ -2066,6 +2085,10 @@ const MEMBER_PINS: Record<string, MemberPin> = {
file: 'packages/components/src/__tests__/text-input-inputs-spec-parity.test.ts',
pins: 'The I18nLabel trio — see `element:text_input.description` (objectui#5717).',
},
'object-calendar.data': {
file: 'packages/plugin-calendar/src/__tests__/ObjectCalendar.recordSourceMembers-8314.test.tsx',
pins: 'The members are RECORDS, and the keys read inside one are the fields the declared `calendar` config names plus `id`: every member arrives in authored order, `allDay` is derived PER MEMBER from the declared end field, a member with no value in the start field is counted in the unscheduled area rather than dropped or given a fabricated date (objectui#7071 at the member level), and a member with no `id` gets a synthesised one rather than being discarded. Asserted through the REAL `SchemaRenderer`, because this key\'s sink is the props channel `index.tsx` resolves (`resolveExternalData`), not the schema. ⛔ NOT pinned on "the internal query is skipped": an authored array reaches this renderer on BOTH carriers, and the schema-channel one returns it as a config with no `provider`, so no query is issued on a tree where the boundary drops `data` either — measured by ablation, that row stays green while the ROWS-ARE-DRAWN rows go red, so it is kept as a labelled companion and the rows carry the claim. The spec side cannot supply any of it: the row is `z.array(z.unknown())`, so every coarse member kind parses and the read site is the whole member contract (objectui#8314).',
},
'object-calendar.filter': {
file: 'packages/plugin-calendar/src/__tests__/ObjectCalendar.filterIsNotAConfigSlot-7711.test.tsx',
pins: 'The members are ObjectQL `$filter` elements, and this renderer adds nothing to that contract and subtracts nothing from it: the authored value reaches `dataSource.find` as `$filter` BY IDENTITY (`toBe`, so a normalising rewrite cannot pass), and the retired `filter.calendar` member spelling yields NO configuration — one authored key read twice with two incompatible meanings is the member-level defect objectui#7711 closed here and objectui#4034 closed on the map. Both member forms are covered (the object form and the array-of-arrays form). The spec side cannot supply this: the `object-calendar` `filter` row is `z.unknown()`, so the wire is the only member contract there is (objectui#7711, registered as a pin by objectui#8176 once objectui#8186 declared the key).',
Expand All @@ -2074,6 +2097,10 @@ const MEMBER_PINS: Record<string, MemberPin> = {
file: 'packages/plugin-calendar/src/__tests__/ObjectCalendar.sortMembersReachTheWire-8171.test.tsx',
pins: 'The members are `{ field, order }` and those two keys are the whole of what this renderer reads inside one: `{ field, order }` lowers to the `field -> direction` map on `$orderby`, an omitted `order` is ascending rather than a dropped member (the objectui#4022 regression), every member arrives in authored order, and a member with no usable `field` is dropped rather than given an invented one — with an unauthored `sort` reaching the wire as `undefined` as the control. Also carries objectui#7711\'s case transposed: a sort on a field named `calendar` stays a sort and the config still comes from the declared `calendar` container. ⛔ NOT an identity pin, unlike the two `filter` entries: `ObjectCalendar.tsx` writes `$orderby: convertSortToQueryParams(schema.sort)`, which builds a new map, so `toBe` is false about this key — the pin asserts what is read inside the member instead, which is the sharper claim. The spec side cannot supply any of it: the `sort` row is unconstrained (an array, a bare string and a bare number all parse), so the wire is the whole member contract (objectui#8171).',
},
'object-calendar.staticData': {
file: 'packages/plugin-calendar/src/__tests__/ObjectCalendar.recordSourceMembers-8314.test.tsx',
pins: 'The members are read EXACTLY as `data`\'s are (same record keys, same per-member unscheduled treatment), plus the two POSITION claims its description makes and no other direction of this gate can see: it is rung 2 of the shared record-source ladder, so an authored `data` wins and this key contributes nothing, and it is read ABOVE `objectName`, so a calendar carrying both draws the inline rows and never queries the object. Both negatives are proven through the same wait a CONTROL row shows a real query completing in, so "no query" can never read as a race. The spec row is `z.array(z.unknown())` — unconstrained members, read site is the whole contract (objectui#8314).',
},
'object-grid.data': {
file: 'packages/plugin-grid/src/__tests__/gridDataInputContract.test.ts',
pins: 'The `object` arm is `ViewDataSchema` discriminated on `provider`: each of the four providers parses, none of them is an array, the declaration is one shape across both registered tags so the alias cannot drift, and it is pinned at compile time too (objectui#5090).',
Expand Down Expand Up @@ -2930,10 +2957,12 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
// direction — greening a fresh divergence on these two blocks by writing a
// nineteenth entry instead of declaring the input.
//
// FOUR is the MEASURED backlog today, not a budget: one undiscoverable key
// on `object-kanban`, three on `object-calendar`. The number has come down
// four times and every step was this ceiling's paired EXIT — a declaration
// retiring its own cover — rather than a re-derivation:
// ONE is the MEASURED backlog today, not a budget: a single
// undiscoverable key on `object-kanban`, and NONE on `object-calendar` —
// objectui#8313 emptied the board and objectui#8314 emptied the calendar,
// in that order. The number has come down five times and every step was
// this ceiling's paired EXIT — a declaration retiring its own cover —
// rather than a re-derivation:
//
// 18 these blocks enter the population (objectui#8176)
// 16 objectui#8186 lands objectui#7712's two `filter` declarations
Expand All @@ -2942,19 +2971,26 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
// 4 objectui#8313 lands its second — the four array/object-armed keys
// on `object-kanban`, each with the `MEMBER_PINS` entry objectui#8212
// made part of the same obligation
// 1 objectui#8314 lands slice 2b — `object-calendar`'s `data`,
// `staticData` and `loading`, paying the same three-part obligation
// for its two array-armed keys; `MEMBER_PIN_EXEMPTION_CEILING` did
// not move on either card
//
// ⚠️ The prose that stood here read "Sixteen … ten on `object-kanban`, six
// on `object-calendar`" while both assertions already read 15: objectui#8223
// moved the numbers and not the sentence describing them. Corrected in
// passing, and worth a line because a stale count here is exactly the kind
// of drift that makes a reader distrust the assertions instead of the prose.
// Both numbers and both sentences moved together on objectui#8313 and
// again on objectui#8314.
//
// A new divergence on these blocks is a plain defect and gets declared,
// exactly as it would on any other covered block.
//
// ⚠️ THE ONLY `object-kanban` KEY LEFT MAY NOT LEAVE BY A DECLARATION AT
// ALL — and after objectui#8313 it is the whole of this block's remainder,
// so the next reader must not take "one key left" for "nearly done".
// ⚠️ THE ONLY KEY LEFT MAY NOT LEAVE BY A DECLARATION AT ALL — and after
// objectui#8313 and objectui#8314 it is the whole of this list's
// remainder, not just this block's, so the next reader must not take
// "one key left" for "nearly done".
// `object-kanban.quickAdd` is measured NOT honoured by this renderer
// (objectui#8201): `KanbanImpl` gates the control on `quickAdd &&
// onQuickAdd`, and no producer on the `ObjectKanban` path supplies that
Expand All @@ -2976,14 +3012,14 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide)
'a new unpublished-key exemption was added on a block objectui#8176 newly ' +
'judged — declare the input at its registration site instead; the ' +
'backlog list is shrink-only',
).toBeLessThanOrEqual(4);
).toBeLessThanOrEqual(1);
// Lower it here when the owning cards land, so the ceiling keeps ratcheting
// rather than banking the headroom their fixes free up.
expect(
backlog.length,
'the objectui#8176 backlog shrank — lower the ceiling above to match, in ' +
'the same change that declared the input',
).toBe(4);
).toBe(1);
});

it('the eight tombstoned keys are recognised, not exempted — and not published either', () => {
Expand Down
Loading
Loading