test(app-shell): pin the list-view calendar cross-field refusal at the client gate - #8311
Merged
os-justin merged 1 commit intoSep 7, 2026
Merged
Conversation
…e client gate
@objectstack/spec 17.3.0 refuses a list view whose
`appearance.allowedVisualizations` offers 'calendar' without a
`calendar: { startDateField }` block, and made `calendar.titleField`
optional in the same release. metadata-admin's client validation opens
the refined doors directly (`ViewMetadataSchema` on edit,
`ViewItemSchema`/`ViewSchema` on create), so both facts are now pinned
there, with exact paths and the message's rule-naming first clause.
Pinned here rather than in `packages/types`' spec-parity suites: those
mirrors are `z.object(kept).partial()` rebuilds of the spec shape's
entries, so a spec refinement is structurally unreachable in them and
the mirror accepts the refused body cleanly.
Four control legs are pinned clean on both doors so the refusal is a
reading: empty `appearance`, a grid-only `allowedVisualizations`, a
`calendar:` block with no `allowedVisualizations`, and the declared
pair. An empty `calendar: {}` is pinned as the neighbouring, distinct
refusal one level deeper.
Test-only; the changeset declares no release.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Contributor
✅ Console Performance Budget
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
Size Limits
|
os-justin
marked this pull request as ready for review
September 7, 2026 10:26
os-justin
enabled auto-merge
September 7, 2026 10:26
os-justin
deleted the
claude/issue-7122-listview-crossfield-calendar-parity
branch
September 7, 2026 11:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7122
Pins
@objectstack/spec17.3.0's list-view cross-field refusal — a view whoseappearance.allowedVisualizationsoffers'calendar'must declarecalendar: { startDateField }— through metadata-admin's real client-validationgate, on both doors. Test-only; the changeset declares no release.
Where, and why not in
packages/typesRuled Q1 = B by the PM on the card. The decisive fact was measured, not argued:
objectui's
packages/typesmirrors cannot carry a spec refinement, ever.ListViewSchemathere is built fromspecFieldsExcept(SpecListViewSchema.shape, …),and
specFieldsExceptisz.object(kept).partial()over the spec shape's entries —a brand-new object schema. The spec's
.superRefinechain is not omitted, it isstructurally unreachable, and the mirror was measured accepting the refused body
clean. A pin there would have asserted a property of the spec's schema while sitting
in a suite whose documented remit is objectui-vs-spec key drift.
clientValidation.ts'sviewentry imports the refined doors themselves —ViewMetadataSchemaon edit,ViewItemSchema/ViewSchemaon create — so what ispinned here is a refusal a Console author actually meets, and the suite already pins
exact path + message through that gate (its cases are named
CANARY: …). No remitchange was needed.
What the pin asserts
Both doors carry a body. That is not redundancy: create and edit are judged by
different schemas, so a green on one says nothing about the other. Measured, the
refusal's path follows the body's own nesting rather than the door.
config: { … })config.calendarlist: { … })list.calendarcalendar(edit only — the authoring gate refuses that shape outright, for reasons unrelated to this rule, so create is deliberately not asserted for it)Four control legs are pinned clean on both doors, so the refusal is a reading and
not an artefact — empty
appearance; a grid-onlyallowedVisualizations; acalendar:block with noallowedVisualizationsat all; and'calendar'offeredwith the block declared. That last one also discharges the card's item 1 at this
door: the block carries
startDateFieldand notitleField, which 17.3.0 madeoptional (title falls back to the ADR-0079 record display name).
A fifth canary pins the neighbouring, distinct refusal: an empty
calendar: {}answersconfig.calendar.startDateField/expected string, one level deeper. It is what makesthe path assertions above mean something.
Deliberate message coupling, with a named owner
These assertions couple to the spec's message text, as every
CANARYin this filealready does deliberately. Only the message's first clause is asserted — the part
that names the rule — never the whole string, so the spec stays free to reword its
guidance prose.
Owner of the coupling:
@objectstack/spec's list-view refinement, objectstack#14075(the PR addressing objectstack#13817). If that message is reworded, this pin is what tells
objectui. The fix is then to re-measure the clause — never to drop the assertion. This
is written into the suite's docblock so the next reader inherits it.
Accepted cost, recorded rather than waved off (the PM's ruling says the same): this
is a
domain:ui-owned suite, so a spec-contract fact now lives outside the spec lane,where a UI refactor could delete it without a spec seat noticing.
One thing the docblock states that a reader would otherwise get wrong
This refusal does not travel through the union-expansion machinery the rest of this
file exists to pin. Measured: it arrives as one issue already addressed to the right
path — the spec addresses it itself and the mapping leaves it alone. A green here is
therefore not evidence about that expansion, and the docblock says so.
Verification
Every exit code captured before any pipe; verdicts read from each tool's own printed
line. Heavy runs went through the container's shared verify lock.
pnpm exec vitest run …/clientValidation.viewDiagnostics.test.ts— 80 passed (80),9 of them new. Run from the repo root, never
--filter … exec(objectui#3378).pnpm --filter '@object-ui/app-shell^...' build— exit 0.pnpm --filter @object-ui/app-shell type-check— exit 0 (it runstsc --noEmitandtsc -p tsconfig.test.json). The test file is proven inside that program bytsc -p tsconfig.test.json --listFiles: 1 hit for this file, 0 for a control file thatmust not be there, out of 4454 listed.
pnpm exec vitest run --project unit packages/app-shell/— 204 files, 2586 passed, 1 skipped.pnpm exec vitest run --project dom --project dom-heavy packages/app-shell/src/views/metadata-admin/— 160 files, 1360 passed.
eslint .inpackages/app-shell— exit 0, 1101 files, 0 errors.check-changeset-presenceexit 0 (its own line: "Every one of them has an EMPTYfrontmatter — declared as releasing nothing, which is the explicit exemption and a
complete answer to this gate"),
check-changeset-no-major0,check-control-bytes0,check-comment-mask-corpus0,check-vi-mock-specifiers0,check-vi-mock-inherit0,check-governed-queue-guard --teston both changed paths: NOT GOVERNED.Ablation — the pin is proven able to fail
Run from the committed state, mutation proven on disk before the run, restore proven
by state and never by an exit code. The mutation removed the trigger from the shared
fixture (
allowedVisualizationsdropped'calendar'), which is the condition the rulegates on.
Restore, by state:
git diff HEADempty,git status --porcelainempty, andgit hash-object==git rev-parse HEAD:PATH(5c8a75c7…both). Re-run after restore:80 passed (80).
The
calendar: {}canary stayed green under that ablation, which is the informativeresult rather than a gap: it pins the block's own required key, a different rule.
Notes for the lander
Fixes #7122is the default line and the card's executable ask is discharged by thisPR. If the card is still wanted open as the holder for the two items the PM's ruling
carried forward (the
type: 'calendar'axis finding, and the ObjectCalendar datum forThe calendar refusal screen names keys an interface page cannot author — reword it for the page door (objectui#7547's highest-value remaining piece) #8170), downgrade this line to
Part of #7122before merging.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code