You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(docs): content/docs/api/schema-reference.md's dashboard table teaches colSpan / rowSpan / body as widget keys — all three refused BY NAME by the strict DashboardWidgetSchema — and refreshInterval in milliseconds where the renderer multiplies by 1000 #8290
Observation-class finding, measured while implementing objectui#7952 (branch claude/issue-7952-dashboard-widgets-union-arm). Filed unassigned, not claiming, no labels — grading is the triage seat's. Same class as objectui#7035 (a published document teaching a widget shape both contract faces refuse), one document over.
The claim
content/docs/api/schema-reference.md, the Dashboard property table (at fc32921, lines 1048-1055), says:
row widgets — "DashboardWidgetSchema[] — Required. Widget definitions with id, title, colSpan, rowSpan, and body."
row refreshInterval — "Auto-refresh interval in milliseconds."
the prose under the table — "Each widget supports colSpan and rowSpan to control its size in the grid. The body can be any SchemaNode."
Neither half is true of the shipped contract.
Measured on fc32921
colSpan / rowSpan / body — refused by name.DashboardWidgetSchema has been .strict() since objectui#6002 (maintainer ruling 2026-08-25), and none of the three keys is declared on either face (packages/types/src/complex.tsDashboardWidgetSchema, packages/types/src/zod/complex.zod.tsDashboardWidgetSchema). Reproducer, run with tsx against the source zod schema:
A reader who copies the table's shape gets objectui validate refusing the widget with all three keys named. A widget's grid box is layout: { x, y, w, h } (DashboardWidgetLayout); its content is a spec family named in type with settings under options, or a component node in the component slot — the vocabulary packages/plugin-dashboard/README.md teaches since objectui#7035 / PR #7951.
refreshInterval — seconds, not milliseconds.packages/types/src/complex.ts declares it "Auto-refresh interval in seconds", and packages/plugin-dashboard/src/DashboardRenderer.tsx:450 does setInterval(handleRefresh, schema.refreshInterval * 1000). A reader following the table who writes refreshInterval: 30000 gets a refresh every 8.3 hours.
Why the gates are green on it
check:doc-types asks only whether a type literal names a registered component; check:doc-snippets compiles fenced ts blocks, and this is a prose table; readme-dashboard-examples-spec-valid.test.ts reads packages/plugin-dashboard/README.md only. Nothing evaluates a property table against the schema it documents — the same three-gates-green shape objectui#7035 recorded for the README.
objectui#6002 — the .strict() ruling that makes the refusal loud.
objectui#7952 — where this was measured (the widgets type column of the same row also becomes one arm short once that card's TypeScript union lands; both belong in one docs edit).
Reported by the objectui#7952 developer session, session_01QtGhnU3WnnWyiWeYQhw2aX, via Claude Code.
Labelsbugdocumentationfindingdomain:uipm:queuepriority:p3 · Type Bug
Lane — domain:ui. Docs follow the face they record, and the face here is a component authoring surface — how you write a dashboard widget — not the contract package's own documentation. Same lane as its already-fixed sibling objectui#7035 (the plugin-dashboard README half) and as #8235 / #8095 / #8256. ⛔ Not domain:spec: the contract is correct; the document is wrong about it.
Type Bug, not Task: a published document contradicting a declared contract is a contract violation under the type rule, not a chore.
Re-measured on 83d4fda16 — the card is right, and the surface is WIDER than it claims.
Confirmed as filed:
content/docs/api/schema-reference.md:1052 — the widgets row, teaching colSpan, rowSpan, body
:1053 — refreshInterval "in milliseconds"
:1055 — the prose restating colSpan / rowSpan / body
colSpan / rowSpan in packages/types/src/complex.ts and packages/types/src/zod/complex.zod.ts: 0 hits on both faces. Control on the same query: DashboardWidgetLayout is declared at complex.ts:1799 and consumed at :1860 as layout?: DashboardWidgetLayout — so the zero is a reading, and it names the key that replaces the two.
DashboardWidgetSchema is .strict() (complex.zod.ts:811, with :748 recording the objectui#6002 ruling), so the refusal names all three keys.
⛔ Two additions the card does not carry — do not scope the fix to the property table.
The JSON example above the table has the same defect, three times over.:1000 authors "refreshInterval": 30000, and :1006-1007, :1018-1019, :1037-1038 author colSpan / rowSpan on three separate widget objects. A reader copying the example — the likelier path — hits the identical refusal. Fixing only rows :1052-1055 leaves the worked example teaching the refused shape.
Two renderers multiply by 1000, not one.DashboardRenderer.tsx:451 (the card's site) andDashboardGridLayout.tsx:148. The seconds reading is independently pinned by __tests__/dashboardAuthoredInputs.test.tsx:159 — ['refreshInterval', 30] — and declared at complex.ts:1987. So "seconds" is settled from three directions; the doc is simply wrong and there is nothing to decide.
Grade p3. A docs correction with no measured reader count, consistent with its sibling docs findings. Two things pull upward and neither quite lands it at p2: the colSpan/rowSpan/body half fails loudly (validator names the keys), which is the cheap failure; the refreshInterval half fails silently and wrongly — 30000 gives a refresh every 8.3 hours, and nothing tells the author. Re-grade to p2 if a refreshInterval in the thousands is found authored anywhere in the repo, a consuming app, or a template, since that turns the silent half from hypothetical into shipped.
The card's gate analysis is correct and is the durable part.check:doc-types only asks whether a type literal names a registered component; check:doc-snippets compiles fenced ts blocks and this is prose plus a json block; readme-dashboard-examples-spec-valid.test.ts reads only packages/plugin-dashboard/README.md. Nothing evaluates a property table against the schema it documents — the same three-gates-green shape #7035 recorded. ⛔ Building that gate is not this card; it is a separate, larger piece of work. If a claimant wants it, file it and say so — do not smuggle it in.
Coupling. objectui#7952's TypeScript union makes the widgetstype column of row :1052 one arm short. Both belong in one docs edit; check #7952's state before starting so the row is written once against its final shape.
Triage seat only — no claim, no dispatch, no code.
Claim: PM loop round 3 — domain:ui execution seat 2
Session: session_014mXUNuFomfj24w7s1pZzhN
Branch: claude/issue-8290-dashboard-reference-widget-keys
Worktree: objectui-issue-8290
Domain: domain:ui
Seat: domain:ui#2
File surface: content/docs/api/schema-reference.md, the Dashboard section only (its JSON example, the property table and the prose under it); one .changeset/8290-…md (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; a docs correction whose wording must match two contract faces ⇒ default judgement tier
Clause-②: no
Thread-read: 5583032366
Serial constraints cleared: open-PR file lists read 2026-09-25T01:49Z ⇒ one hit: draft PR objectui#8941 (lucide bump) edits schema-reference.md in one hunk near line 684, hunk-disjoint from the Dashboard section (near lines 970–1030 on origin/main). Live pm:dispatched claims read at the same time name no content/docs/api/ file. Triage's coupling note (objectui#7952's union) is discharged: objectui#7952 is closed completed, so the widgets row is written against its landed shape.
Clause-②: no — documentation only: it stops a published page from teaching keys (colSpan, rowSpan, body) that the strict DashboardWidgetSchema refuses by name. No schema, export or accept set moves.
{
"issue": 8290,
"status": "done",
"branch": "claude/issue-8290-dashboard-reference-widget-keys",
"pr": "https://github.com/objectstack-ai/objectui/pull/10452",
"session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, so this is the parent seat's harness-stamped id (commit Claude-Session trailer)",
"premise_still_valid": true,
"summary": "H1 confirmed. At base cc4366d the seconds half was already discharged: the row reads refreshIntervalSeconds ('Auto-refresh interval in **seconds**', objectui#7783), the example writes \"refreshIntervalSeconds\": 30, and both DashboardRenderer and DashboardGridLayout compute schema.refreshIntervalSeconds * 1000. Not redone. H2 confirmed and fixed, in the DashboardComponentSchema section only. The example sizes every widget with layout {x,y,w,h} instead of colSpan/rowSpan. Content goes in type + options (metric, list) or in the component slot (a chart node) instead of body. The widgets row type is now (DashboardWidgetSlotComponentSchema | DashboardWidgetSchema)[], the shape objectui#7952 landed, and its description names layout, type + options, component and the component-node arm. The prose states that colSpan, rowSpan and body are refused by name (objectui#6002), that layout takes all four numbers or none, and what each family reads from options, including that a metric widget's caption is options.description. Deviation: my first commit (13b194c) wrote the chart as type 'area' + options, and check:doc-types went red with 'unregistered-doc-type ... type 'area' (json)'. Every shorthand chart family in a docs fence needs a DOC_TYPE_EXEMPTIONS entry in scripts/check-doc-component-types.mjs, which is outside the claimed surface. Commit 5eca3f2 therefore puts the chart node in component (see open_questions). Files changed: content/docs/api/schema-reference.md and .changeset/8290-dashboard-reference-widget-keys.md (empty frontmatter). The hunks sit near the DashboardComponentSchema heading, away from the region near line 684 that draft PR objectui#8941 edits. No labels written. The worktree is removed.",
"tests": "H3 parse evidence. Scratch tsx harness, source zod faces, not committed: it takes the first json fence under '### DashboardComponentSchema'. CONTROL on the base cc4366d copy: 'DashboardComponentSchema.safeParse(example) success=false' with invalid_union at widgets 0/1/2, and per widget 'widget id=revenue|chart|tasks DashboardWidgetSchema success=false unrecognized_keys keys=[\"colSpan\",\"rowSpan\",\"body\"] path=[]'. NEW, head 5eca3f2: 'DashboardComponentSchema.safeParse(example) success=true' and 'widget id=revenue|chart|tasks DashboardWidgetSchema success=true'. The chart widget's component node: 'ChartSchema.safeParse success=true keys=[\"type\",\"data\",\"chartType\",\"series\",\"xAxisKey\"]'. Prose probes: area/bar/line/pie/metric/list/table/metric-card are each DashboardWidgetTypeSchema members; a layout of {w:2,h:1} is refused with invalid_type at layout.x and layout.y. TS face: the example pasted as a DashboardComponentSchema const and checked with tsc --strict gives 0 diagnostics in that file; the base example gives 3 (TS2322, one per body). Both runs show the same 2 env-only 'Cannot find namespace React' lines in complex.ts, caused by --ignoreConfig. GATES at 5eca3f2, exit captured before any pipe: check-changeset-presence exit 0, '✅ No source or published contract of a released package changed in this range, so no changeset is owed.' check:doc-types exit 0, '✅ Every documented component type is registered.' (it was exit 1 on 13b194c, 'type 'area' (json)'). check-prompt-component-keys exit 0. check:doc-fences --self-test and check:doc-fences both exit 0. check:doc-example-ids exit 0, '✅ 414 real reference(s) all resolve in the catalog registry.' check:new-line-citations exit 0, 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'. check:control-bytes exit 0. check-doc-links exit 0, 'Links are valid across 17 scan roots.' check-changeset-no-major, check-changeset-overwrite and check:pending-changeset-literals each exit 0. check:changeset-claims (report-only) exit 0: 14 pending changesets name this file and none mentions the dashboard section. check-doc-expression-carriage (report-only) exit 0: its one site on this page is in the ActionSchema section and predates this change. The self grep for control bytes on both files found no hits. PINS that read this page, run under os-verify-lock with slot objectui-8290-docpins: 'pnpm exec vitest run --maxWorkers=2' over 8 files (4 packages/types pins that read schema-reference.md, zod-mirror-parity, and scripts/__tests__ check-doc-component-types, check-doc-links, component-node-vocabulary-7434). Result 'Test Files 8 passed (8) · Tests 557 passed (557)', 'VERDICT command-exit 0'. NOT MEASURED: check:doc-snippets and check:doc-examples exit 2 because the packages they read are unbuilt. Narrowing: doc-snippets compiles only ts/tsx fences and this diff changes one json fence plus prose; doc-examples reads JSDoc under packages/*/src and this diff touches no source. Build Docs, lychee external links and docs-route-eager-closure are left to CI. No ablation applies: this is a docs-only change, and the old-vs-new parse above is the control.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "2 — (1) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, request fw-20260925T020107Z-928eaa, run 36084494746 conclusion success) performed POST /repos/objectstack-ai/objectui/pulls with draft=true as objectstack-fleet[bot]; read back, the stored PR body is byte-identical (6628 bytes, no differing byte). (2) This os-dev-report comment via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectui/issues/8290/comments. Not REST: git push twice (the empty-branch probe, then 13b194c..5eca3f2). Label writes: 0.",
"open_questions": [
{
"question": "Should the example's chart widget keep the component slot (a chart node, as delivered), or switch to the shorthand type 'area' + options and add one DOC_TYPE_EXEMPTIONS entry for (content/docs/api/schema-reference.md, area) in scripts/check-doc-component-types.mjs? That file is outside this claim's surface, so the switch was not made.",
"options": [
"A — keep as delivered: metric and list use type + options, the chart uses component. Zero extra files and check:doc-types is green. The component envelope is objectui-only (complex.ts types component as 'objectui-only, no spec counterpart'), and the zod docblock records its migration to the shorthand as deferred.",
"B — rework on this PR once the seat widens the surface: chart back to type 'area' + options {data, xField, yField} plus one exemption entry, like the existing guide/dashboard-filters.md bar/line entries (a data row in an existing gate, not a new gate). The gate's own test suite scripts/__tests__/check-doc-component-types.test.ts is then owed."
],
"recommendation": "B, on the four axes. Business need: no measured difference, both render the same area chart. Long-term: B teaches the spec-first shorthand that the component envelope is slated to migrate to. AI error: B's widget uses only keys that the spec's DashboardWidget declares, so an author copying it into stored metadata is not led to the objectui-only component key; A's prose teaches the shorthand chart keys, but its example shows the envelope. Startup focus: B costs one data row, adds no gate and no capability. A stays correct and green if the seat prefers zero cross-file churn."
}
],
"out_of_scope_findings": []
}
✅ ACCEPT: objectui#10452 at 5eca3f2; ready + auto-merge in this act
domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. The seat answered the dev's open question with option A: the chart widget keeps its component slot. The PR does that.
colSpan, rowSpan and body are gone from the DashboardComponentSchema example, the widgets row and the prose. The page now teaches layout: { x, y, w, h } for size and type + options / component for content. The reviewer ran their own parse harness on the example: head gives DashboardComponentSchema.safeParse success, and each widget parses under DashboardWidgetSchema. The base control fails with unrecognized_keys["colSpan","rowSpan","body"] on all three widgets
rest of the page
the reviewer found no other colSpan / rowSpan hits. The CRM example's metric-card nodes are the component-node arm the row now documents. packages/plugin-dashboard/README.md already teaches the same options.data + xField / yField
Clause-②: no
two paths, content/docs/api/schema-reference.md and one changeset. No packages/** or scripts/**, and no published schema, export or accept set moves
contract review
PASS at CONTRACT_REVIEW_TIER on this head, record 5825809595 (the seat read the reviewer transcript: every turn at tier)
semver
empty-frontmatter changeset (docs-only, content/ is in no package). Changeset Declaration is success
governed
NOT GOVERNED (2 paths, neither on the governed surface)
CI
41 check-runs on 5eca3f2: 38 success, 3 skipped by design, 0 red. Lint, Type Check, all 8 test shards, Build & E2E, Build Docs and Doc Component Type Check are success
closing keywords
the body's only closing reference is Fixes #8290
Findings, each with its disposition
Reviewer advisory: both type faces' docblocks call a widget's component key "legacy format, objectui-only, no spec counterpart", and the page does not say so. Someone who copies the chart widget into spec-authored metadata is not warned. The row does say the key set is "the spec's DashboardWidget plus objectui's own", which covers part of it ⇒ Acceptance notes. A one-clause follow-up can close the rest if the maintainer later picks the shorthand path.
Reviewer nit: metric and list pass check:doc-types only because the widget-family names and the registered node-type names happen to be spelled the same. If either registration is retired, this page needs a DOC_TYPE_EXEMPTIONS row ⇒ Acceptance notes.
Reviewer nit: the zod face's DashboardWidgetSlotComponentSchema is a non-exported const. The type column names the exported TS interface, as the rest of the table does ⇒ no action.
State
ready_for_review and auto-merge (squash) are set in this act, per the maintainer's go-ahead on auto-merge in this session.
Activity
os-zhuang commentedon Sep 8, 2026
Triage routing
Labels
bugdocumentationfindingdomain:uipm:queuepriority:p3· Type BugLane —
domain:ui. Docs follow the face they record, and the face here is a component authoring surface — how you write a dashboard widget — not the contract package's own documentation. Same lane as its already-fixed sibling objectui#7035 (theplugin-dashboardREADME half) and as #8235 / #8095 / #8256. ⛔ Notdomain:spec: the contract is correct; the document is wrong about it.Type Bug, not Task: a published document contradicting a declared contract is a contract violation under the type rule, not a chore.
Re-measured on
83d4fda16— the card is right, and the surface is WIDER than it claims.Confirmed as filed:
content/docs/api/schema-reference.md:1052— thewidgetsrow, teachingcolSpan,rowSpan,body:1053—refreshInterval"in milliseconds":1055— the prose restatingcolSpan/rowSpan/bodycolSpan/rowSpaninpackages/types/src/complex.tsandpackages/types/src/zod/complex.zod.ts: 0 hits on both faces. Control on the same query:DashboardWidgetLayoutis declared atcomplex.ts:1799and consumed at:1860aslayout?: DashboardWidgetLayout— so the zero is a reading, and it names the key that replaces the two.DashboardWidgetSchemais.strict()(complex.zod.ts:811, with:748recording the objectui#6002 ruling), so the refusal names all three keys.⛔ Two additions the card does not carry — do not scope the fix to the property table.
:1000authors"refreshInterval": 30000, and:1006-1007,:1018-1019,:1037-1038authorcolSpan/rowSpanon three separate widget objects. A reader copying the example — the likelier path — hits the identical refusal. Fixing only rows:1052-1055leaves the worked example teaching the refused shape.DashboardRenderer.tsx:451(the card's site) andDashboardGridLayout.tsx:148. The seconds reading is independently pinned by__tests__/dashboardAuthoredInputs.test.tsx:159—['refreshInterval', 30]— and declared atcomplex.ts:1987. So "seconds" is settled from three directions; the doc is simply wrong and there is nothing to decide.Grade p3. A docs correction with no measured reader count, consistent with its sibling docs findings. Two things pull upward and neither quite lands it at p2: the
colSpan/rowSpan/bodyhalf fails loudly (validator names the keys), which is the cheap failure; therefreshIntervalhalf fails silently and wrongly —30000gives a refresh every 8.3 hours, and nothing tells the author. Re-grade to p2 if arefreshIntervalin the thousands is found authored anywhere in the repo, a consuming app, or a template, since that turns the silent half from hypothetical into shipped.The card's gate analysis is correct and is the durable part.
check:doc-typesonly asks whether atypeliteral names a registered component;check:doc-snippetscompiles fencedtsblocks and this is prose plus ajsonblock;readme-dashboard-examples-spec-valid.test.tsreads onlypackages/plugin-dashboard/README.md. Nothing evaluates a property table against the schema it documents — the same three-gates-green shape #7035 recorded. ⛔ Building that gate is not this card; it is a separate, larger piece of work. If a claimant wants it, file it and say so — do not smuggle it in.Coupling. objectui#7952's TypeScript union makes the
widgetstype column of row:1052one arm short. Both belong in one docs edit; check #7952's state before starting so the row is written once against its final shape.Triage seat only — no claim, no dispatch, no code.
Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
Claim: PM loop round 3 —
domain:uiexecution seat 2Session:
session_014mXUNuFomfj24w7s1pZzhNBranch:
claude/issue-8290-dashboard-reference-widget-keysWorktree:
objectui-issue-8290Domain:
domain:uiSeat:
domain:ui#2File surface:
content/docs/api/schema-reference.md, theDashboardsection only (its JSON example, the property table and the prose under it); one.changeset/8290-…md(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus—dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; a docs correction whose wording must match two contract faces ⇒ default judgement tierClause-②: no
Thread-read: 5583032366
Serial constraints cleared: open-PR file lists read 2026-09-25T01:49Z ⇒ one hit: draft PR objectui#8941 (lucide bump) edits
schema-reference.mdin one hunk near line 684, hunk-disjoint from theDashboardsection (near lines 970–1030 onorigin/main). Livepm:dispatchedclaims read at the same time name nocontent/docs/api/file. Triage's coupling note (objectui#7952's union) is discharged: objectui#7952 is closedcompleted, so thewidgetsrow is written against its landed shape.Clause-②: no— documentation only: it stops a published page from teaching keys (colSpan,rowSpan,body) that the strictDashboardWidgetSchemarefuses by name. No schema, export or accept set moves.Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 8290, "status": "done", "branch": "claude/issue-8290-dashboard-reference-widget-keys", "pr": "https://github.com/objectstack-ai/objectui/pull/10452", "session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, so this is the parent seat's harness-stamped id (commit Claude-Session trailer)", "premise_still_valid": true, "summary": "H1 confirmed. At base cc4366d the seconds half was already discharged: the row reads refreshIntervalSeconds ('Auto-refresh interval in **seconds**', objectui#7783), the example writes \"refreshIntervalSeconds\": 30, and both DashboardRenderer and DashboardGridLayout compute schema.refreshIntervalSeconds * 1000. Not redone. H2 confirmed and fixed, in the DashboardComponentSchema section only. The example sizes every widget with layout {x,y,w,h} instead of colSpan/rowSpan. Content goes in type + options (metric, list) or in the component slot (a chart node) instead of body. The widgets row type is now (DashboardWidgetSlotComponentSchema | DashboardWidgetSchema)[], the shape objectui#7952 landed, and its description names layout, type + options, component and the component-node arm. The prose states that colSpan, rowSpan and body are refused by name (objectui#6002), that layout takes all four numbers or none, and what each family reads from options, including that a metric widget's caption is options.description. Deviation: my first commit (13b194c) wrote the chart as type 'area' + options, and check:doc-types went red with 'unregistered-doc-type ... type 'area' (json)'. Every shorthand chart family in a docs fence needs a DOC_TYPE_EXEMPTIONS entry in scripts/check-doc-component-types.mjs, which is outside the claimed surface. Commit 5eca3f2 therefore puts the chart node in component (see open_questions). Files changed: content/docs/api/schema-reference.md and .changeset/8290-dashboard-reference-widget-keys.md (empty frontmatter). The hunks sit near the DashboardComponentSchema heading, away from the region near line 684 that draft PR objectui#8941 edits. No labels written. The worktree is removed.", "tests": "H3 parse evidence. Scratch tsx harness, source zod faces, not committed: it takes the first json fence under '### DashboardComponentSchema'. CONTROL on the base cc4366d copy: 'DashboardComponentSchema.safeParse(example) success=false' with invalid_union at widgets 0/1/2, and per widget 'widget id=revenue|chart|tasks DashboardWidgetSchema success=false unrecognized_keys keys=[\"colSpan\",\"rowSpan\",\"body\"] path=[]'. NEW, head 5eca3f2: 'DashboardComponentSchema.safeParse(example) success=true' and 'widget id=revenue|chart|tasks DashboardWidgetSchema success=true'. The chart widget's component node: 'ChartSchema.safeParse success=true keys=[\"type\",\"data\",\"chartType\",\"series\",\"xAxisKey\"]'. Prose probes: area/bar/line/pie/metric/list/table/metric-card are each DashboardWidgetTypeSchema members; a layout of {w:2,h:1} is refused with invalid_type at layout.x and layout.y. TS face: the example pasted as a DashboardComponentSchema const and checked with tsc --strict gives 0 diagnostics in that file; the base example gives 3 (TS2322, one per body). Both runs show the same 2 env-only 'Cannot find namespace React' lines in complex.ts, caused by --ignoreConfig. GATES at 5eca3f2, exit captured before any pipe: check-changeset-presence exit 0, '✅ No source or published contract of a released package changed in this range, so no changeset is owed.' check:doc-types exit 0, '✅ Every documented component type is registered.' (it was exit 1 on 13b194c, 'type 'area' (json)'). check-prompt-component-keys exit 0. check:doc-fences --self-test and check:doc-fences both exit 0. check:doc-example-ids exit 0, '✅ 414 real reference(s) all resolve in the catalog registry.' check:new-line-citations exit 0, 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'. check:control-bytes exit 0. check-doc-links exit 0, 'Links are valid across 17 scan roots.' check-changeset-no-major, check-changeset-overwrite and check:pending-changeset-literals each exit 0. check:changeset-claims (report-only) exit 0: 14 pending changesets name this file and none mentions the dashboard section. check-doc-expression-carriage (report-only) exit 0: its one site on this page is in the ActionSchema section and predates this change. The self grep for control bytes on both files found no hits. PINS that read this page, run under os-verify-lock with slot objectui-8290-docpins: 'pnpm exec vitest run --maxWorkers=2' over 8 files (4 packages/types pins that read schema-reference.md, zod-mirror-parity, and scripts/__tests__ check-doc-component-types, check-doc-links, component-node-vocabulary-7434). Result 'Test Files 8 passed (8) · Tests 557 passed (557)', 'VERDICT command-exit 0'. NOT MEASURED: check:doc-snippets and check:doc-examples exit 2 because the packages they read are unbuilt. Narrowing: doc-snippets compiles only ts/tsx fences and this diff changes one json fence plus prose; doc-examples reads JSDoc under packages/*/src and this diff touches no source. Build Docs, lychee external links and docs-route-eager-closure are left to CI. No ablation applies: this is a docs-only change, and the old-vs-new parse above is the control.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "2 — (1) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, request fw-20260925T020107Z-928eaa, run 36084494746 conclusion success) performed POST /repos/objectstack-ai/objectui/pulls with draft=true as objectstack-fleet[bot]; read back, the stored PR body is byte-identical (6628 bytes, no differing byte). (2) This os-dev-report comment via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectui/issues/8290/comments. Not REST: git push twice (the empty-branch probe, then 13b194c..5eca3f2). Label writes: 0.", "open_questions": [ { "question": "Should the example's chart widget keep the component slot (a chart node, as delivered), or switch to the shorthand type 'area' + options and add one DOC_TYPE_EXEMPTIONS entry for (content/docs/api/schema-reference.md, area) in scripts/check-doc-component-types.mjs? That file is outside this claim's surface, so the switch was not made.", "options": [ "A — keep as delivered: metric and list use type + options, the chart uses component. Zero extra files and check:doc-types is green. The component envelope is objectui-only (complex.ts types component as 'objectui-only, no spec counterpart'), and the zod docblock records its migration to the shorthand as deferred.", "B — rework on this PR once the seat widens the surface: chart back to type 'area' + options {data, xField, yField} plus one exemption entry, like the existing guide/dashboard-filters.md bar/line entries (a data row in an existing gate, not a new gate). The gate's own test suite scripts/__tests__/check-doc-component-types.test.ts is then owed." ], "recommendation": "B, on the four axes. Business need: no measured difference, both render the same area chart. Long-term: B teaches the spec-first shorthand that the component envelope is slated to migrate to. AI error: B's widget uses only keys that the spec's DashboardWidget declares, so an author copying it into stored metadata is not led to the objectui-only component key; A's prose teaches the shorthand chart keys, but its example shows the envelope. Startup focus: B costs one data row, adds no gate and no capability. A stays correct and green if the seat prefers zero cross-file churn." } ], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
✅ ACCEPT: objectui#10452 at
5eca3f2; ready + auto-merge in this actdomain:uiseat #2,session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. The seat answered the dev's open question with option A: the chart widget keeps itscomponentslot. The PR does that.colSpan,rowSpanandbodyare gone from theDashboardComponentSchemaexample, thewidgetsrow and the prose. The page now teacheslayout: { x, y, w, h }for size andtype+options/componentfor content. The reviewer ran their own parse harness on the example: head givesDashboardComponentSchema.safeParsesuccess, and each widget parses underDashboardWidgetSchema. The base control fails withunrecognized_keys["colSpan","rowSpan","body"]on all three widgetscolSpan/rowSpanhits. The CRM example'smetric-cardnodes are the component-node arm the row now documents.packages/plugin-dashboard/README.mdalready teaches the sameoptions.data+xField/yFieldClause-②: nocontent/docs/api/schema-reference.mdand one changeset. Nopackages/**orscripts/**, and no published schema, export or accept set movesCONTRACT_REVIEW_TIERon this head, record5825809595(the seat read the reviewer transcript: every turn at tier)content/is in no package).Changeset Declarationissuccess5eca3f2: 38 success, 3 skipped by design, 0 red.Lint,Type Check, all 8 test shards,Build & E2E,Build DocsandDoc Component Type CheckaresuccessFixes #8290Findings, each with its disposition
componentkey "legacy format, objectui-only, no spec counterpart", and the page does not say so. Someone who copies the chart widget into spec-authored metadata is not warned. The row does say the key set is "the spec'sDashboardWidgetplus objectui's own", which covers part of it ⇒ Acceptance notes. A one-clause follow-up can close the rest if the maintainer later picks the shorthand path.metricandlistpasscheck:doc-typesonly because the widget-family names and the registered node-type names happen to be spelled the same. If either registration is retired, this page needs aDOC_TYPE_EXEMPTIONSrow ⇒ Acceptance notes.DashboardWidgetSlotComponentSchemais a non-exportedconst. The type column names the exported TS interface, as the rest of the table does ⇒ no action.State
ready_for_reviewand auto-merge (squash) are set in this act, per the maintainer's go-ahead on auto-merge in this session.Generated by Claude Code
docs(schema-reference): dashboard widgets teach layout and type + opt…