Repository navigation
feat(lint): os validate, os build and os lint flag unread dashboard widget options keys by name - #21204
Conversation
…ashboard metadata Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d
…geset Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d40ba8ac64eef48425fea532f750370590ccfd65 && git checkout d40ba8ac64eef48425fea532f750370590ccfd65
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1 5fb667f29a7545c20ec7552ce36c579b6f3b8a68 && git checkout -B drift-repro 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1 && git merge --no-ff 5fb667f29a7545c20ec7552ce36c579b6f3b8a68
node scripts/docs-audit/affected-docs.mjs --json 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1
|
Contract reviewServed-tier: Isolated reviewer, read-only. Inputs: card #21131 (body and all 3 comments), PR #21204 body, its 10-file list, the net diff ① Derived judgments
② Semver level
③ Boundary flagsThe dev report (card comment 5935805280) declares
Implemented-by: VERDICT: PASS |
…s extras (objectstack-ai#21205) Part of objectstack-ai#21131 This is the governed half of the card (`skills/**`, Tier H). The code half is PR objectstack-ai#21204, and this PR is its docs-only companion. Land it after objectstack-ai#21204 or together with it: the new sentence about the `os validate` warning is true from the moment objectstack-ai#21204 lands. ## What this does `skills/objectstack-ui/rules/dashboards.md` stops teaching widget `options` keys that no renderer reads. It points to the homes that `DashboardWidgetOptionsSchema`'s doc comment names. - **About `:185`, the worked example.** `options: { icon: 'DollarSign' }` becomes `colorVariant: 'success'`. On a dataset-bound widget, `icon` in `options` styles nothing, while `colorVariant` is the declared accent. The comment now says that the measure's own `format` + `currency` drive the number. - **About `:341`, the `options` paragraph.** It said "presentation extras (`icon`, `trend`, `density`, …) pass through untouched" and that a typo is "an author-time type error". It now says the following: - a dataset-bound widget reads no key outside the five; - `os validate` warns on each such key (`unconsumed-widget-option`); - presentation lives on the measure (`format` / `currency`), on `colorVariant` and on `chartConfig`; - a typo such as `sortDirection` parses and draws that warning. The typo claim was measured: a `tsc --noEmit` probe against the built spec accepts `options: { sortDirection: 'desc' }`, while it rejects `sortOrder: 'sideways'` (TS2322). - **About `:254`, the `compareTo` metric bullet.** It said "Authors should not hand-author `options.trend` when `compareTo` is set; the renderer wins", which implies `options.trend` works when `compareTo` is not set. It now says `options.trend` reaches no renderer on a dataset-bound widget, and that `compareTo` is the trend. This is the same unread-extra sentence class, in the same file. ## Line and token readings Budget: at most 0 net lines in `skills/**`. Met: +8 / -8. | reading | before (`cb45469e`) | after (`b5b45306`) | | :--- | ---: | ---: | | `skills/objectstack-ui/rules/dashboards.md`, lines | 468 | 468 | | `skills/objectstack-ui/rules/dashboards.md`, tokens (`check-skills-token-ratchet` unit, ceiling 6252) | 6211 | 6234 (+23) | | whole package, the sum of all 10 `skills/**/SKILL.md`, lines | 4395 | 4395 | | whole package, the sum of all 10 `skills/**/SKILL.md`, tokens | 52090 | 52090 | | `skills/objectstack-ui/**/*.md` (9 files), lines | 2109 | 2109 | | shipped bundle total, tokens (gate's own line) | 154116 | 154139 | The +23 tokens are the longer sentences that name the declared homes. They fit under the file's existing ceiling (headroom 18 after this change). No ceiling moved, and no line was bought by re-wrapping. ## Gates `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at `b5b45306` derived 23 families. All 23 were run, every one exited 0, and `--ran` reconciled 23 derived, 23 run, 0 NOT-MEASURED. The run covered `check-skills-token-ratchet`, `check:skill-compatibility`, `check:skill-frame-sync`, `check:skill-identifier-liveness`, `check:doc-authoring`, `check:nul-bytes` and `check:pm-governed-merges`. `check:doc-formula-expressions` first answered exit 3 (prerequisite: `formula` and `lint` not built in this worktree). After that build it answered exit 0. No changeset: nothing under `skills/` is in any package's `files[]`. Measured: the old paragraph's text has zero hits in the built `spec`, `cli`, `create-objectstack` and `lint` outputs and in `spec/prompts` and `spec/llms.txt`. The positive control, `validateDashboardWidgetOptions`, is found in `packages/lint/dist`. ## Acceptance notes - Tier H: this PR waits for the maintainer's click, and no seat readies, queues or arms it. - Not touched, noted: the same file's Cartesian `compareTo` bullet says to override per series with `series.dashArray` / `series.opacity`. On a dashboard widget, `chartConfig.series` is refused by name (`content/docs/ui/dashboards.mdx`, "What `chartConfig` does not carry"). That is a different key family from this card's. ## 维护者速读(草稿) **改了什么**:只改了对外发布的 skill 文件 `skills/objectstack-ui/rules/dashboards.md` 里三处文字。它原来教 AI 在仪表盘组件的 `options` 里写 `icon`、`trend` 这类"展示用额外键",现在改成:这些键在绑定数据集的组件上不会被任何渲染器读取。展示应写到已声明的位置,即度量的 `format` / `currency`、组件的 `colorVariant` 和 `chartConfig`。净行数为 0。 **为什么改**:AI 照着 skill 写元数据,写出来的键能通过校验,页面上却什么效果都没有。CRM 和 Todo 两个示例应用就曾因此带着无效键发布。代码半边(PR objectstack-ai#21204)已让 `os validate` 对这类键按名告警,教材也要同步纠正,否则 AI 会继续批量写错。 **风险与代价(含回滚)**:纯文档改动,不影响任何发布包,也不改运行时行为。token 增加 23(该文件仍在上限之内)。回滚就是还原这一个文件。需要注意:文中"`os validate` 会告警"这句话要等 objectstack-ai#21204 合入后才成立,建议与它同时或在它之后合入。 **席位意见**: **你要做的**:确认措辞后批准并合入本 PR(Tier H,只能由你点击)。建议在 objectstack-ai#21204 合入之后操作。 --- _Generated by [Claude Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #21131
Clause-②: yes
What this does
os validate,os buildandos lintnow runcheckDashboardWidgetOptionsfrom@objectstack/sdui-parserover dashboard metadata (*.dashboard.ts,defineStack({ dashboards })). Anoptionskey that no renderer reads is flagged by name asunconsumed-widget-option, at the check's one level,warning. Until now the check had one caller, the SDUI tree validator (packages/sdui-parser/src/validate.ts), so a dashboard declared as metadata never met it, andDashboardWidgetOptionsSchema(.passthrough()) parsed any key in silence.packages/lint/src/validate-dashboard-widget-options.ts(new): an adapter, not a second check. It calls the check once per widget, with an array that holds only that widget at its own index, and turns each diagnostic into a finding atdashboards[N].widgets[M].options. Code, severity and message come from the check unchanged. ⛔ No key list: the read set isCONSUMED_WIDGET_OPTION_KEYS, whichcheck:widget-option-censusderives from the spec. The check's own exemptions (nodataset, legacycomponentwidgets, expression-valuedoptions,suppressWarnings: ['unconsumed-widget-option']) apply because the check is what runs.packages/lint/src/authoring-rules.ts: one registry entry,tier: 'advisory',commands: ALL,input: 'parsed'. The surface is CLI-only, and thesurfaceReasonsays why: the per-writedashboardsnapshot would carry its input, but crossing an advisory rule onto the Studio/REST/MCP door is a rollout decision over stored tenant rows.packages/lint/src/index.ts: exportsvalidateDashboardWidgetOptionsandDashboardWidgetOptionFinding.content/docs/ui/dashboards.mdx: the page stops teachingicon,columns,stripedanddensityas working extras, and theoptionsrow stops saying "Renderer extras". A new table names the declared homes fromDashboardWidgetOptionsSchema's doc comment: the dataset measure'sformatandcurrency, the widget'scolorVariant, and the widget'schartConfig..changeset/21131-dashboard-widget-options-door.md:@objectstack/lintminor, with theClause-②: yesline.No CLI wiring was needed. All three commands call
runAuthoringRules(packages/cli/src/commands/validate.ts,compile.ts,lint.ts), and a registry entry reaches all three. That is measured below.The level: one check, one level
The check emits
warning. The spec declares the bag open, and the ruling recorded in the check's header is that open extras stay open and stop being silent. This PR uses that same level at the new door. The triage pin says "refused by name"; here it is read as "flagged by name at that level", as the dispatch decided.os validate --strictdoes refuse it, because--stricttreats warnings as errors. The measurement did not argue for a different level: the in-repo corpus writes zero such keys today (below), and PR #21130 removed the keys from the only producers the card names.Measured
Tree:
objectstack-ai/objectstackat5fb667f2(code unchanged since6b19d334).Doors reached. A throwaway probe built the CRM example (
examples/app-crm) withoptions: { icon, sortOrder }written onto widgettotal_pipelinebeforedefineStackran:unconsumed-widget-optionfindingsos validateseverity: warning, pathdashboards[0].widgets[0].options, keyiconos validate --jsonwarnings[]os build --jsonwarnings[]os lintsortOrder(a read key) on the same widgetThe probe files were deleted after the run. The CLI prints "Running author-time rules (49)".
Example corpus (assumption 4).
os validate --jsonover every example with a dashboard:app-crm0,app-todo0,app-showcase0 (3 dashboards),app-multi-package0 (no dashboards). So no example producer needed a fix. The other dashboard sources in the tree (packages/platform-objects/src/apps/dashboards/system_overview.dashboard.tsand thecreate-objectstackblank template) write no widgetoptions.Tests.
pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2: 119 files, 5497 tests passed.pnpm --filter @objectstack/lint typecheck:tsc --noEmitclean, andcheck:test-typecheckOK (no new debt).pnpm --filter @objectstack/sdui-parser test: 13 files, 217 tests passed.validate-dashboard-widget-options.test.ts(8 tests). It pins the following:unconsumed-widget-optionand severitywarning, and the finding names the key.os validate,os buildandos linteach report it throughrunAuthoringRules.Ablation (one-time proof, committed state
6b19d334).node scripts/ablation-replace.mjsreplaced the check call with an empty diagnostic list. The anchor count went 1 to 0, and the blob wente6339a78to4f3895a6. Result: 6 of 8 tests red (every flagging pin plus the three per-command pins). The 2 green ones are the control and the exemptions, which assert an empty result. The restore was proven: blob equals HEADe6339a78, andgit diff HEADis empty.Gates.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat5fb667f2derived 89 families. All 89 were run at that head, every one exited 0, and--ranreconciled 89 derived, 89 run, 0 NOT-MEASURED. Notes:check-plugin-teardown-shape --self-test: a shallow clone; fixed by fetching the pinned fixture commit.check:skill-examples: neededclient-reactbuilt.check:dual-build-cjs-loads: needed the rest of the workspace built.check:docs-transcript-driftwas red at first, a real finding: four CLI transcripts printed "author-time rules (48)". They now print 49 (see below).In-place corrections (declared)
content/docs/deployment/cli.mdx,deployment/validating-metadata.mdx,getting-started/build-with-claude-code.mdx,ui/react-pages.mdx: the transcript count went from 48 to 49. The new registry entry is what changed the derived count, andcheck:docs-transcript-driftholds these lines equal to it.content/docs/ui/dashboards.mdx, thestageOrderrow: it said the key "parses and is never consulted" on a non-funnel type. The schema refuses it there (checkDashboardWidgetStageOrder,packages/spec/src/ui/dashboard.zod.ts). This is the same section and the same defect class (the page misstating what happens to anoptionskey).content/docs/ui/dashboards.mdx, the misspelling sentence: it said a misspelled key (sortDirection,granularity) "is an author-time error". Measured: atsc --noEmitprobe inpackages/lintagainst the built spec acceptsoptions: { sortDirection: 'desc', granularity: 'month' }with no error. A wrong value for a declared key,sortOrder: 'sideways', is TS2322 in the same run. The sentence now says the value is checked, while the misspelled key parses and draws this warning.Acceptance notes
/meta, MCP publish) still does not run this check over dashboard writes. The entry records why insurfaceReason. Crossing it is a rollout decision.DashboardWidgetOptionsSchema's doc comment (packages/spec/src/ui/dashboard.zod.ts) still says a typo such assortDirection"is now an author-time type error". Thetscprobe above measures that as false. Fixing it is apackages/specTSDoc edit (check:docsregeneration), which this PR leaves to whoever next edits that file. The docs page here no longer repeats the claim.wherefield carries the metadata location next to it.skills/objectstack-ui/rules/dashboards.md) is a governed surface. It goes in its own docs-only PR, part of the same card.Generated by Claude Code