Commit 38472ce
fix(spec): declare allDayField on CalendarConfigSchema, the key the object-calendar prescription already names (#17877)
Fixes #17054
`CalendarConfigSchema` now declares **`allDayField`**, the fifth field
binding on a calendar config.
## The two sentences, and which one was wrong
The `object-calendar` door refuses a flat `allDayField` and prescribes,
verbatim from its own diagnostic:
> Write this as a key of the `calendar` config object instead —
`calendar: { startDateField, endDateField, titleField, colorField,
allDayField }`.
`CalendarConfigSchema` was a `strictObject` of exactly four keys and
refused that shape by name.
**The round measured which half was wrong rather than picking the
convenient one, and the answer is (a) — the schema was missing a key
that is honoured.** It is not (b): trimming the prescription would leave
a shipped, honoured capability with no protocol carrier.
The evidence, read at the objectui pin this repo builds against
(`.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694`, read with
`git show PIN:path`, not at that checkout's HEAD):
- `packages/plugin-list/src/ListView.tsx` — `collectViewFields` reads
`v.allDayField` off `schema.calendar` and `schema.options.calendar` at
two sites, feeding the `$select` projection and the `$expand` set. The
authored nested key already changes what the server is asked for.
- The same file's `case 'calendar':` branch spreads `...(schema.calendar
|| {})` onto the `object-calendar` node, so the nested key reaches the
block.
- `packages/plugin-calendar/src/ObjectCalendar.tsx` —
`getCalendarConfig` resolves it into the calendar config.
- `packages/app-shell/src/views/ObjectView.tsx` — the dev-mode Spec
Compliance warning lists `allDayField` among the flat keys an author
must move under `viewDef.calendar`: a third face prescribing the nested
spelling.
- `packages/types/src/zod/objectql.zod.ts` — the mirror keeps
`.passthrough()` and names this key as its reason: *"the renderers grow
config knobs ahead of the protocol (calendar's `allDayField`, for one),
and stripping them here would silently disable a shipped capability."*
- Post-pin, on objectui `main`, the renderer makes it load-bearing:
`allDay: allDayField ? Boolean(record[allDayField]) : !endDate`.
⭐ A widening is not made acceptable by the diagnostic having promised
it. This one is right because the renderer honours the key — the
prescription merely happened to be the accurate half.
**The countervailing reading, stated plainly.** objectui `main` carries
a comment declaring `allDayField` objectui-LOCAL, in the same class as
its sanctioned `defaultView`, and concluding "honouring `allDayField`
widens no accept set". That is a true statement about what objectui
needed in order to honour it, and it does not bind what the protocol may
declare. The two keys are not the same class from this side:
`defaultView` is the renderer's initial view mode, a UI preference that
already has a declared home as an `object-calendar` component prop;
`allDayField` is a field binding, the same kind as its four neighbours,
and it had no home at all. `defaultView` stays refused on this config,
pinned.
## A correction to the card's framing, measured
The card reads as though the prescribed shape is refused at the door
that printed the prescription. It is not.
`ObjectCalendarPropsSchema.calendar` is `z.unknown()`, so the block
accepts `calendar: { …, allDayField }` today. The second refusal lands
one door over, on **stored view metadata** —
`ListViewShapeSchema.calendar` is `CalendarConfigSchema` — which is how
calendars are actually authored in this product. The trap is real; the
two doors are just not the same door. Both readings are in the
before/after table.
## Measured against the BUILT dist, before and after
Build first and confirm both passes finished (`check-dts-emitted: 34/34
declared declaration file(s) present`), then parse through the package's
own `./ui` export.
| input | door | before | after |
|---|---|---|---|
| `{ objectName, allDayField }` flat | `object-calendar` | REFUSED
`unrecognized_keys` keys=`["allDayField"]` | REFUSED, unchanged |
| `calendar: { four, allDayField }` | `object-calendar` | ACCEPTED |
ACCEPTED |
| `{ four, allDayField }` | `CalendarConfigSchema` | REFUSED
`unrecognized_keys` keys=`["allDayField"]` | ACCEPTED |
| `calendar: { four, allDayField }` | `ListViewSchema` | REFUSED
`unrecognized_keys` at `path: ["calendar"]` | ACCEPTED |
| `{ four }` | `CalendarConfigSchema` | ACCEPTED (positive control) |
ACCEPTED |
| `{ four, bogusKeyXy }` | `CalendarConfigSchema` | REFUSED (negative
control) | REFUSED |
The exact refusal texts, before:
- flat, on `object-calendar`: `Unrecognized key(s) on this
\`object-calendar\`: \`allDayField\`.` followed by the prescription
quoted above.
- nested, on the config: `Unrecognized key(s) on this calendar
configuration: \`allDayField\`. Until these shapes were closed an
unknown key was dropped silently — the view still rendered, without
whatever the key was meant to configure.`
After, the first is byte-identical and the second is gone — replaced by
acceptance. The bogus-key control still produces that second text
verbatim with `keys=["bogusKeyXy"]`, which is what proves the message
did not change, only the membership.
## Pins, both directions
`packages/spec/src/ui/calendar-config-allday-prescription-17054.test.ts`,
9 cases. They import `./view.zod` and `./component.zod` — **`src/`, not
`dist/`**, so no rebuild leg is needed for the ablation, and that is
measured rather than assumed.
**Accepted:** the prescribed shape at the config schema; the same shape
through the stored-view door where the second refusal used to land; the
same view without the key as a control.
⭐ The lead pin is written on the defect CLASS, not on one key: it reads
the key list out of the `calendar: { … }` shape the runtime's own
prescription prints and asks the config schema to accept each name, with
a floor on the extracted list so an empty extraction cannot make it
vacuously true. Any future diagnostic that names a non-member goes red
here, including a key nobody has thought of yet.
**Still refused — what the widening did NOT cost:** the flat
`allDayField` on `object-calendar` (one key per concept, and the refusal
still carries the prescription); `defaultView` on the config, so the
opening is exactly one key wide; an unknown key, in the same message
shape, at the config and at `path: ["calendar"]`; and `startDateField`
is still required, so `allDayField` alone is not a calendar binding.
## Ablation
Mutation: rename the declaration to `allDayFieldAblated` in
`packages/spec/src/ui/view.zod.ts`. Absolute paths, `trap '…' EXIT INT
TERM`.
On-disk proof read FIRST, before the run: declaration occurrences `1 →
0`, injected text `0 → 1`, and the file's `git hash-object` moving
`3ecc02a254265786fc29c146408479ed072ee462 →
560dc1d3299460e582e04d0a727a89600e6d23ba`. The run then aborts itself if
the injected text is not present exactly once.
Predicted direction: RED. Observed: `3 failed | 6 passed (9)`, exit 1 —
exactly the three acceptance pins, with the lead pin failing on its own
sentence: *"the prescription names `allDayField`, which
CalendarConfigSchema refuses"*. GREEN after restore: `9 passed (9)`,
exit 0.
Restore proven by hash, not by an exit code: `git checkout HEAD --
ABSOLUTE_PATH` (never the bare form, which reads the index), restored
hash `3ecc02a254265786fc29c146408479ed072ee462` equal to the HEAD blob,
with an empty-hash guard treating a missing read as FAILURE, and `git
diff HEAD` empty.
## Changeset
`.changeset/17054-calendar-config-all-day-field.md`, grade **`minor`** —
a published accept set widens, and `minor` is the floor for this class.
Not `skip-changeset`, measured rather than assumed, with `npm pack
--dry-run --json` after a build and controls in both directions over the
packed file list (2012 files):
- subject `allDayField` → **52** published files.
- positive control `startDateField` (a sibling key that must publish) →
**55**.
- negative control `bogusKeyXy`, which lives only in the new test file →
**0**.
- the new test file is **absent** from the packed list, and 0
`*.test.ts` files publish at all.
- 1 parent 6ec467b commit 38472ce
7 files changed
Lines changed: 233 additions & 9 deletions
File tree
- .changeset
- content/docs/references
- api
- data
- ui
- packages/spec
- authorable-surface
- src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1641 | 1641 | | |
1642 | 1642 | | |
1643 | 1643 | | |
1644 | | - | |
| 1644 | + | |
1645 | 1645 | | |
1646 | 1646 | | |
1647 | 1647 | | |
| |||
1726 | 1726 | | |
1727 | 1727 | | |
1728 | 1728 | | |
1729 | | - | |
| 1729 | + | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | 1732 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
375 | 375 | | |
376 | 376 | | |
377 | 377 | | |
378 | | - | |
| 378 | + | |
379 | 379 | | |
380 | 380 | | |
381 | 381 | | |
| |||
0 commit comments