…ode level
`content/docs/api/schema-reference.md`'s `ObjectViewSchema` example authored
`listViews["my-deals"].default: true`. `NamedListView` declares no `default`
member, and `ObjectView.tsx` picks the opening view from the NODE-level
`schema.defaultListView` (falling back to the first key) — nothing reads an
entry-level flag. `BaseSchema` is passthrough, so the example validated green
while the view it labelled "default" never opened first, twenty rows above the
same page's property table that documents `defaultListView` correctly.
The example now spells `"defaultListView": "my-deals"` on the node and drops the
undeclared key. `my-deals`' two remaining members are reordered so the filter
line keeps its trailing comma: `object-view-unmirrored-keys-7779.test.ts` holds
that line verbatim, comma included, and that file is held by another PR.
A new fixture pin, `schema-reference-named-list-view-keys-7923.test.ts`, holds
the example's `listViews` keys to `NamedListView`'s declared members, both sides
parsed off disk. A type annotation would catch nothing here: `BaseSchema` closes
with `[key: string]: any`, and the fence is JSON so no snippet gate compiles it.
The example's filter DIALECT is deliberately untouched — which spelling
`mergeFilterNodes` runs is unmeasured, and that is objectui#2890's half.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
Fixes #7923
content/docs/api/schema-reference.md'sObjectViewSchemaexample authored anentry-level key that nothing declares and nothing reads, so the view it labelled
"default" never opened first — twenty rows above the same page's property table
that documents the correct key.
Before / after
"my-deals": { - "label": "My Deals", "filter": [["owner", "=", "${currentUser.id}"]], - "default": true + "label": "My Deals" } }, + "defaultListView": "my-deals", "table": {Why the two remaining members are reordered rather than left alone: the
sibling pin
packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts:529holds the filter line verbatim including its trailing comma —
— so deleting
"default": truewhilefilterstayed last would have dropped thatcomma and reddened a pin held by draft PR #7969.
filtertherefore keeps a memberafter it. No content was invented to fill the slot.
Premises, re-measured on
origin/maine97e8e8NamedListView(packages/types/src/objectql.ts:1983) declares nodefault;ObjectView.tsx:701readsschema.defaultListViewlabelpresent; renderer line quoted belowdefaultListViewcontent/docs/api/schema-reference.md:885defaultgit grep -n '"default": true' -- content/docsreturns exactly this one hit; the twelvedefault: truehits are all(default: true)prose in property comments, a different shapecheck:doc-snippets' scan surfaceUNGATED_DOCSentries; butTS_FENCE_LANGUAGES = {ts, tsx, typescript}, and this example is a```jsonfence, so it is collected and never compiled (#5250). That is exactly the hole the fixture pin fillsThe pin, and why it is a fixture pin and not a type annotation
packages/types/src/__tests__/schema-reference-named-list-view-keys-7923.test.tsparses both sides off disk on every run — the shape #7925 points at
(
packages/plugin-calendar/src/readme-calendar-view-schema.test.ts): thedeclaration's own top-level members are brace-matched out of the interface source
(brace matching, not a line regex, because
NamedListViewcarries JSDoc blocks andfour nested object members that a regex would miscount), and the example is located
by its
### ObjectViewSchemaheading and parsed as JSON. Restating the member listin the test would reproduce the original defect one layer up.
An annotation on the example would catch nothing.
BaseSchemacloses with[key: string]: any(#7927), so every invented key type-checks through the indexsignature — #7927 measured it directly (probe P5: renaming
titleFieldtotitleFielddinside an annotated block leftcheck:doc-snippetsat exit 0). Theannotation checks a key's type and never its name. What makes a name-level
comparison possible here at all is that
NamedListViewis a standalone interfacethat does not extend
BaseSchema, so its declared members are a genuinely closedlist.
The pin asserts four things: a live control (the declaration parsed to >30 members
including the required
label, and the doc parsed to exactly the two named views),the predicate itself (every authored
listViewskey is declared), the node-leveldefaultListViewnaming a key that exists, and a positive control that runs onevery invocation — it plants
defaultinto the parsed object and asserts thesame predicate function returns
['default'], so the harness cannot go quietlygreen. That control opens by asserting
NamedListViewdoes not declaredefault,which is the tripwire if that ever changes.
The pin imports only
vitestand node builtins and reads files off disk, so itneeds no build.
Reverse verification (post-commit, trap-protected, restore proven by blob hash)
Each leg mutated the committed doc, proved the mutation actually landed (on-disk
hash ≠ the
HEADblob hash87e58fa…, so no leg was a silent no-op), ran the pin,then restored with
git checkout HEAD -- ABSOLUTE_PATHand proved the restore byhash equality and an empty
git diff HEAD."default": trueback intomy-dealsdefaultauthors no key NamedListView does not declare…expected [ 'default' ] to deeply equal []"defaultListView"linemust spell \defaultListView` … expected 'undefined' to be 'string'`Error: "### ObjectViewSchema" heading not found … the example moved,Tests no testsTest Files 1 passed (1),Tests 4 passed (4)The positive control was among the 3 passing tests in every red leg, so it is
demonstrably executed rather than merely present.
Gates
object-view-unmirrored-keys-7779.test.ts(the sibling pin on the filter line)Test Files 2 passed (2),Tests 94 passed (94)pnpm check:doc-snippetsSemantic phase: 495 of 495 block(s) judged, 0 failed.(after its own scoped--build-filterbuild, 34 tasks, exit 0; the first run's exit 2 was "I could not run", not a verdict)pnpm check:doc-fencesevery TypeScript block in 227 document(s) is fenced ts/tsx/typescriptnode scripts/check-doc-links.mjsLinks are valid across 17 scan roots.pnpm check:doc-typesEvery documented component type is registered.pnpm check:control-bytesscanned 6430 tracked text file(s); skipped 85 binarynode scripts/check-changeset-presence.mjspnpm changeset:checkNo changeset declares a major bump.check-governed-queue-guard --test(all 3 paths)NOT GOVERNED — 3 path(s) checked against 5 governed surface(s)pnpm --filter @object-ui/types type-checktsc -p tsconfig.test.json --listFilesshows the new test IS in the program, so this is a measurement of it and not around itpnpm --filter @object-ui/types lint273 problems (0 errors, 273 warnings), all pre-existing; the new file contributes 0 errors, 0 warnings (--format json, 1 file)pnpm exec vitest run packages/types/Test Files 131 passed (131),Tests 2418 passed (2418)pnpm check:doc-example-readersnode scripts/check-doc-expression-carriage.mjspnpm check:docs-route-closureReaders of the page —
git grep -l 'schema-reference.md' -- scripts/ packages/ .github/ content/returns 16 sites; every test among them was run together and passed:Test Files 10 passed (10),Tests 354 passed (354)(object-view-unmirrored-keys-7779,component-docs-retired-handler-keys-7340,kanban-plugin-dialect-authoritative-7664,object-grid-title-mirrored,tree-view-data-retired-6951,zod-mirror-parity,table-column-contract,check-doc-component-types,check-doc-links, plus the new pin).Lint narrowing — type-aware linting is not enabled in
eslint.config.js(noproject/projectService), and markdown is not in eslint's file population, sothis diff cannot move the verdict on any file it does not contain; the whole
@object-ui/typespackage was linted anyway.Deviation from the dispatch
The dispatch predicted
check-changeset-presencewould owe nothing ("docs + testonly — expect none owed"). It owes one. The gate reads any file under a
released package's
src/, tests included:Answered the way the gate itself names as the legitimate pass in this repo — an
empty-frontmatter
.changeset/*.mddeclaring that nothing releases (⛔ not theskip-changesetlabel, which this repo does not honour):⛔ The filter dialect is untouched, deliberately
Line 863's
"filter": [["owner", "=", "${currentUser.id}"]],is byte-identicalto
origin/main. Triage split this card in two and ruled the dialect half(the ObjectQL tuple form here versus the
{ field, operator, value }objectspackages/plugin-view/README.mdandcontent/docs/plugins/plugin-view.mdxteach)out of scope until which spelling
mergeFilterNodesactually runs is measured —NamedListView.filteris an untyped array, so both are declared-legal and guessingwould risk making a correct page wrong. That is #2890's half. It was not measured
here. The new pin cannot and does not adjudicate it: it compares key names
against the declaration, and
filteris a declared member under either dialect.🤖 Generated with Claude Code
https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
Generated by Claude Code