Skip to content

docs(core,guide,plugins): name the shape eight props tables describe - #6168

Merged
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-6086-prose-declared-surface
Aug 24, 2026
Merged

docs(core,guide,plugins): name the shape eight props tables describe#6168
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-6086-prose-declared-surface

Conversation

@yinlianghui-tw

Copy link
Copy Markdown
Collaborator

Part of #6086 — the card stays open for the deferred scoped-A DeclaredKeys component.

Round 3 of #6086, executing the PM ruling. Two bounded deliverables: name the shape on the property tables that named none, and enumerate which restatements are spec-owned. No gate, no parser, no component — Option B is rejected and blanket A and C with it.

Why this is not "enabling the gate"

The gate is rejected. The justification for this change is narrower and stands on its own: a props table that never says what it describes is worse documentation on its own terms — a reader cannot reach the source of truth from it. So this optimises for reader clarity, not machine parseability. Preserved optionality is a side effect, not the reason.

1. The eight tables named — and what I read to bind each

Every binding was made by reading the page and the declaration. The mechanical binder from round 2 is not trustworthy here: hand audit found it wrong on 9 of 14 tables outside api/schema-reference.md.

pagetable headingshape namedwhat I read to bind it
content/docs/core/app-schema.mdxBasic ConfigurationAppComponentSchemathe page's own example fence imports the type by name (import type { AppComponentSchema } from '@object-ui/types'); declared at packages/types/src/app.ts:355, and all 6 claimed keys are members
content/docs/core/app-schema.mdxLayout ConfigurationAppComponentSchemasame section, same declaration; its single claimed key layout is a member
content/docs/core/report-schema.mdxBasic ConfigurationReportComponentSchemathe page's example fence imports ReportComponentSchema; declared at packages/types/src/reports.ts:354, all 4 claimed keys are members
content/docs/guide/notifications.mdConfiguring the systemNotificationSystemConfigdeclared at packages/react/src/context/NotificationContext.tsx:215; its 7 members are the table's 5 rows plus the two legacy spellings the prose below the table already covers
content/docs/plugins/plugin-charts.mdxPropertiesBarChartSchemadeclared at packages/plugin-charts/src/types.ts:37, whose own JSDoc example is the page's fence; all 6 claimed keys are members (className via BaseSchema)
content/docs/plugins/plugin-editor.mdxPropertiesCodeEditorSchemadeclared at packages/plugin-editor/src/types.ts:35; all 6 claimed keys are members
content/docs/plugins/plugin-markdown.mdxPropertiesMarkdownSchemadeclared at packages/plugin-markdown/src/types.ts:15; both claimed keys are members
content/docs/plugins/plugin-report.mdxThe authoring shapeReport (ReportSchema)@objectstack/spec ships json-schema/ui/Report.json; all 12 claimed keys are properties of it, and the page's own quick start calls defineReport from @objectstack/spec/ui

Two of those need saying out loud:

  • ⚠️guide/notifications.md REPLACES a name rather than adding one. The lead read "NotificationProvider's config is the spec NotificationConfigSchema". @objectstack/spec declares no such symbol — its Notification* declarations are NotificationChannelSchema, NotificationPositionSchema, NotificationPreferencesSchema, NotificationSchema, NotificationSeveritySchema, NotificationTypeSchema, and no shipped JSON Schema carries defaultPosition or pauseOnHover. The shape that does declare those five keys is NotificationSystemConfig in @object-ui/react. Back this out if you would rather the wrong name stayed and the correction went to its own card.
  • ⚠️plugin-markdown.mdx names an ambiguous identifier, so the sentence qualifies it: @object-ui/types declares a different MarkdownSchema (content required, plus sanitize and components) from the plugin's own. The page documents the plugin, so the naming points there and says so.

Two shapes were applied deliberately rather than one: the ### TypeName heading where the section documents exactly one shape (charts, editor, markdown), and a lead sentence naming the shape and its declaring package where several grouped tables partition one shape (app-schema, report-schema) or where the heading carries real information a type name would destroy (The authoring shape, Configuring the system). Renaming both of app-schema.mdx's group headings to ### AppComponentSchema would have produced duplicate anchors and deleted the grouping.

No naming sentence asserts closure. Under the ruling's adopted definition, "declares a / b / c" would have converted eight honest subset tables into eight omission defects. Where the tables are subsets, the sentence says so.

2. Ten tables stopped and reported — no heading invented, no type minted

page:lineheadingwhy no single shape exists
core/theme-schema.mdx:265Border RadiusTheme.borderRadius is an anonymous inline object type (packages/types/src/theme.ts:173). Naming it needs minting a type.
core/theme-schema.mdx:280Shadowssame — Theme.shadows is inline and anonymous (theme.ts:184).
guide/flow-designer.md:51Search and keyboard⚠️not a props table at all — a keyboard-shortcut table whose first column is headed Key. A lexical false positive of the round-2 extractor; it restates no declared surface.
plugins/plugin-chatbot.mdx:132Properties25 rows spanning three shapes plus four keys declared nowhere. The authored surface is an unnamed intersection written inline at the registration site: packages/plugin-chatbot/src/renderer.tsx:62 types the node as ChatbotSchema intersected with an anonymous object carrying showTimestamp, userAvatarUrl, maxHeight, autoResponse, onSend and the rest. surface comes from ChatbotEnhancedProps.
plugins/plugin-dashboard.mdx:295Widget optionsalready names its shape — the sentence above the table names DashboardWidgetOptionsSchema, and the table is deliberately the renderer read set, not the declared key set. Round 2 recorded this as unnamed; that was my error, caused by keying the resolver on JSON Schema file names (DashboardWidgetOptions.json) while the prose uses the zod export name.
plugins/plugin-grid.mdx:102Column Definitionalready names its shape, two lines above the table: "ListColumn is declared by @objectstack/spec/ui (ListColumnSchema)". Round 2 recorded this as unnamed; that was my error too — the mechanical binder attached PaginationConfig and the audit recorded the rejection, not the reading.
plugins/plugin-kanban.mdx:94PropertiesKanbanSchema is declared twice with disagreeing shapes (packages/types/src/complex.ts:100 vs packages/plugin-kanban/src/types.ts:61), and the table's limit is on neither.
plugins/plugin-kanban.mdx:103Column PropertiesKanbanColumn declared four times — #6155, left for triage. Three of the four are inside @object-ui/plugin-kanban itself, so a package qualifier does not disambiguate it either.
plugins/plugin-kanban.mdx:113Card PropertiesKanbanCard, same four-way split, same reason.
plugins/plugin-timeline.mdx:113Propertiesthe only named candidate is TimelineSchema, and it disagrees on 7 of 8 rows: it declares events / orientation / position, the table lists variant / items / dateFormat / timeScale / rowLabel / minDate / maxDate. The renderer reads that undeclared vocabulary through BaseSchema's index signature. Naming TimelineSchema here would have manufactured exactly the defect this card is about.

Correction to the round-2 measurement: "18 tables name no shape at all" was two too high. plugin-dashboard.mdx:295 and plugin-grid.mdx:102 both name their shape in prose; my round-2 hand audit recorded a null for each because the mechanical binder's answer was wrong, which is not the same finding. The honest count is 16 tables that name no shape, of which 8 are named here and 8 could not be.

3. The spec-owned enumeration — the candidate surface for the deferred component

Owner determined per shape, checking @objectstack/spec's shipped JSON Schemas under both the shape name and its zod-export spelling (the suffix trap: spec's ReportSchema ships as Report.json). Of 55 bound restatements (8 inline enumerations, 43 key tables, 4 closure claims), 7 are spec-owned:

#locationshapespec artifacta DeclaredKeys candidate?
1guide/layout.md:288PageHeaderPropsui/PageHeaderProps.jsonyes — and it is the live defect. #5923 / PR #6082, ⛔ not touched here
2layout/page-header.mdx:66PageHeaderPropsui/PageHeaderProps.jsonyes#6083, domain:ui lane, ⛔ not touched here
3plugins/plugin-grid.mdx:102ListColumnui/ListColumn.jsonyes — 14 rows, the largest clean spec-owned key table in the tree
4plugins/plugin-report.mdx:45Reportui/Report.jsonyes — 12 rows; omits description plus 8 protection/provenance envelope keys
5plugins/plugin-grid.mdx:322PaginationConfigui/PaginationConfig.jsonno — a negative closure claim ("there is no showSizeChanger"). A generated key list cannot express an absence
6guide/slotted-pages.md:89RecordRelatedListPropsui/RecordRelatedListProps.jsonno — a claim about one key's default value, not a key list
7plugins/plugin-dashboard.mdx:295DashboardWidgetOptionsui/DashboardWidgetOptions.jsonno — deliberately the renderer read subset; generating the declared list would contradict the section's entire point

⭐ Two results that bear on the deferred decision:

  • All 23 api/schema-reference.md tables are @object-ui/types-owned, not spec-owned — every one resolves to packages/types/src/*, none to a spec JSON Schema. The ruling's "do not extend it to api/schema-reference.md" is not merely a scope line: that page is not in the candidate surface at all. The one near miss was ActionSchema, where spec does ship ui/Action.json — but the table's 17 rows match the @object-ui/types declaration exactly (0 unmatched) and the spec shape on only 6 of 17, so it restates the types copy.
  • Of the 4 real candidates, only 3 can host a component today.guide/layout.md is .md, and zero .md pages in content/docs use JSX with no remark transclusion plugin configured — the same constraint round 2 measured. So scoped A reaches layout/page-header.mdx, plugin-grid.mdx and plugin-report.mdx as-is, and needs a .md answer for the fourth.

Verification

Round-2 sweep re-run, before and after — the only thing that moved is the count this change was meant to move:

BEFORE S1: 8 occurrences, 8 bound to a RESOLVABLE shape, files=6
S2: 56 occurrences, 35 bound to a RESOLVABLE shape, files=18
S3: 17 occurrences, 0 bound to a RESOLVABLE shape, files=11
S4: 4 occurrences, 4 bound to a RESOLVABLE shape, files=4
AFTER S1: 8 occurrences, 8 bound to a RESOLVABLE shape, files=6
S2: 56 occurrences, 43 bound to a RESOLVABLE shape, files=18
S3: 17 occurrences, 0 bound to a RESOLVABLE shape, files=11
S4: 4 occurrences, 4 bound to a RESOLVABLE shape, files=4

S2 bound rises by exactly 8, the number named. A per-table diff of the binder output confirms exactly 8 rows changed and each binds to the shape I intended — AppComponentSchema, AppComponentSchema, ReportComponentSchema, NotificationSystemConfig, BarChartSchema, CodeEditorSchema, MarkdownSchema, Report — and no other table's binding moved.

The fenced-block population is byte-identical before and after, so the snippet gate's judged surface is untouched. Extraction of every fenced block from all 7 files at the merge-base versus at HEAD: 1268 lines each, diff exit 0. Non-vacuity, because an empty diff proves nothing without a positive control: planting one line inside a fence in the base copy flipped that same diff to exit 1 and the marker appeared in the extraction; restoring under trap ... EXIT INT TERM printed TRAP_RESTORE_RAN, a grep for the marker across the base copy and content/ exits 1, and the identity reading returns.

Gates, at the final commit 71c77888a, git status --porcelain empty, each exit code captured by redirecting before any pipe, each line quoted from the gate's own output:

gateexitits own verdict line
check-control-bytes0check-control-bytes: OK (scanned 5093 tracked text file(s); skipped 85 binary).
check-doc-component-types0Every documented component type is registered.
check-doc-links0Links are valid across 15 scan roots.
check-doc-snippet-types0Every covered documentation snippet compiles against the built types. (250 of 250 blocks judged, 0 failed)
check-changeset-presence0No source of a released package changed in this range, so no changeset is owed.

check-doc-snippet-types was run for real, not narrowed away: its 20-package build was produced with turbo run build ... --concurrency=2 as its workflow does, and packages/fields/dist and packages/types/dist were confirmed present in this worktree before the gate ran.

Root vitest, over every test file that mentions content/docs — 33 files, the full population by that criterion, not a sample:

Test Files 33 passed (33)
Tests 586 passed (586)

Exit 0. Declared narrowing: the root suite was scoped to those 33 files. The population was derived by grep rather than assumed, and three of them read the pages this PR edits (check-doc-links.test.ts, doc-version-claims.test.ts, owner-retired-contract-twins.test.ts). CI runs the whole farm regardless.

⛔ Out of scope and untouched, as ordered: guide/layout.md's PageHeaderProps defect (#5923 / PR #6082, live on main), layout/page-header.mdx (#6083), api/schema-reference.md, #6155, and any gate, parser or component.


Generated by Claude Code

Eight property tables across seven pages never said what they described, so a
reader could not reach the declaration behind them. Each now names its shape,
following `api/schema-reference.md`'s `### TypeName` convention where the
section documents exactly one shape, and with a lead sentence naming the shape
and its declaring package where several grouped tables partition one shape.
Every binding was made by READING the page and the declaration, not by a
mechanical binder — an earlier mechanical pass bound 9 of 14 such tables to the
wrong shape.
- `core/app-schema.mdx` (two tables) - `AppComponentSchema`
(`packages/types/src/app.ts`).
- `core/report-schema.mdx` - `ReportComponentSchema`
(`packages/types/src/reports.ts`), the name the page's own example imports.
- `guide/notifications.md` - `NotificationSystemConfig`
(`packages/react/src/context/NotificationContext.tsx`). This one REPLACES a
name rather than adding one: the lead said "the spec
`NotificationConfigSchema`", and `@objectstack/spec` declares no such symbol.
- `plugins/plugin-charts.mdx` - `BarChartSchema`.
- `plugins/plugin-editor.mdx` - `CodeEditorSchema`.
- `plugins/plugin-markdown.mdx` - `MarkdownSchema`, qualified to the plugin's
own copy because `@object-ui/types` declares the same name differently.
- `plugins/plugin-report.mdx` - the spec's `Report` (`ReportSchema`).
Ten further tables were left alone and reported instead: two describe an
anonymous inline object, three sit on a name declared four times with
disagreeing shapes, one is a keyboard-shortcut table and not a props table at
all, two already name their shape, one describes an unnamed intersection
declared at a registration site, and one has no candidate that agrees with it.
No fenced block changed: the fenced-block extraction over all seven files is
byte-identical before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
@yinlianghui-tw
yinlianghui-tw marked this pull request as ready for review August 24, 2026 22:16
@yinlianghui-tw
yinlianghui-tw added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit e5263edAug 24, 2026
21 checks passed
@yinlianghui-tw
yinlianghui-tw deleted the claude/issue-6086-prose-declared-surface branch August 24, 2026 22:27
yinlianghui-tw pushed a commit that referenced this pull request Aug 24, 2026
The page asserted `FormField` is "declared once, in `@object-ui/types`".
That is a closure claim and it is false: `@objectstack/spec` ships a
same-named `FormField` in `json-schema/ui/FormField.json`.
Qualify the name to the copy the page means, following the shape PR #6168
used for `MarkdownSchema` on plugin-markdown.mdx. The 21-row table is
unchanged — it restates the `@object-ui/types` copy exactly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
yinlianghui-tw pushed a commit that referenced this pull request Aug 24, 2026
The page called its subject `ReportSchema` in four prose/frontmatter
sites while its own example fence, its property tables (PR #6168) and
the actual declaration all say `ReportComponentSchema`
(packages/types/src/reports.ts:359).
`ReportSchema` is not a dead name: `@objectstack/spec` exports it for
the dataset-bound report shape (json-schema/ui/Report.json — name /
label / dataset / rows / columns / values), which is what
content/docs/plugins/plugin-report.mdx documents. A reader following
the old name landed on a real but wrong declaration. `@object-ui/types`
already re-exports that spec symbol under the prefixed name
`SpecReportSchema` "to avoid collision", so the prose was the last
place using the bare name for the component shape.
Prose and frontmatter only. No source change, no fence touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@yinlianghui-tw@claude