Skip to content

feat(lint): os validate, os build and os lint flag unread dashboard widget options keys by name - #21204

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-21131-dashboard-widget-options-door
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-21131-dashboard-widget-options-door

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21131

Clause-②: yes

What this does

os validate, os build and os lint now run checkDashboardWidgetOptions from @objectstack/sdui-parser over dashboard metadata (*.dashboard.ts, defineStack({ dashboards })). An options key that no renderer reads is flagged by name as unconsumed-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, and DashboardWidgetOptionsSchema (.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 at dashboards[N].widgets[M].options. Code, severity and message come from the check unchanged. ⛔ No key list: the read set is CONSUMED_WIDGET_OPTION_KEYS, which check:widget-option-census derives from the spec. The check's own exemptions (no dataset, legacy component widgets, expression-valued options, 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 the surfaceReason says why: the per-write dashboard snapshot 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: exports validateDashboardWidgetOptions and DashboardWidgetOptionFinding.
  • content/docs/ui/dashboards.mdx: the page stops teaching icon, columns, striped and density as working extras, and the options row stops saying "Renderer extras". A new table names the declared homes from DashboardWidgetOptionsSchema's doc comment: the dataset measure's format and currency, the widget's colorVariant, and the widget's chartConfig.
  • .changeset/21131-dashboard-widget-options-door.md: @objectstack/lint minor, with the Clause-②: yes line.

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 --strict does refuse it, because --strict treats 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/objectstack at 5fb667f2 (code unchanged since 6b19d334).

Doors reached. A throwaway probe built the CRM example (examples/app-crm) with options: { icon, sortOrder } written onto widget total_pipeline before defineStack ran:

door exit unconsumed-widget-option findings
os validate 0 1, severity: warning, path dashboards[0].widgets[0].options, key icon
os validate --json 0 the same, in warnings[]
os build --json 0 the same, in warnings[]
os lint 0 the same
control: sortOrder (a read key) on the same widget 0 at every door

The probe files were deleted after the run. The CLI prints "Running author-time rules (49)".

Example corpus (assumption 4). os validate --json over every example with a dashboard: app-crm 0, app-todo 0, app-showcase 0 (3 dashboards), app-multi-package 0 (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.ts and the create-objectstack blank template) write no widget options.

Tests.

  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2: 119 files, 5497 tests passed.
  • pnpm --filter @objectstack/lint typecheck: tsc --noEmit clean, and check:test-typecheck OK (no new debt).
  • pnpm --filter @objectstack/sdui-parser test: 13 files, 217 tests passed.
  • New file validate-dashboard-widget-options.test.ts (8 tests). It pins the following:
    • An unread key is flagged with code unconsumed-widget-option and severity warning, and the finding names the key.
    • The control: all six consumed keys pass on a parsed widget.
    • Parity: the findings' code, severity and message equal the check's own diagnostics on the equivalent SDUI node.
    • The check's exemptions are honoured.
    • An id-less widget keeps its index.
    • os validate, os build and os lint each report it through runAuthoringRules.

Ablation (one-time proof, committed state 6b19d334). node scripts/ablation-replace.mjs replaced the check call with an empty diagnostic list. The anchor count went 1 to 0, and the blob went e6339a78 to 4f3895a6. 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 HEAD e6339a78, and git diff HEAD is empty.

Gates. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at 5fb667f2 derived 89 families. All 89 were run at that head, every one exited 0, and --ran reconciled 89 derived, 89 run, 0 NOT-MEASURED. Notes:

  • Three families first answered exit 3 (prerequisite not met). Each went to 0 once its prerequisite was met:
    • check-plugin-teardown-shape --self-test: a shallow clone; fixed by fetching the pinned fixture commit.
    • check:skill-examples: needed client-react built.
    • check:dual-build-cjs-loads: needed the rest of the workspace built.
  • check:docs-transcript-drift was 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, and check:docs-transcript-drift holds these lines equal to it.
  • content/docs/ui/dashboards.mdx, the stageOrder row: 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 an options key).
  • content/docs/ui/dashboards.mdx, the misspelling sentence: it said a misspelled key (sortDirection, granularity) "is an author-time error". Measured: a tsc --noEmit probe in packages/lint against the built spec accepts options: { 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

  • The fourth door (Studio, REST /meta, MCP publish) still does not run this check over dashboard writes. The entry records why in surfaceReason. Crossing it is a rollout decision.
  • DashboardWidgetOptionsSchema's doc comment (packages/spec/src/ui/dashboard.zod.ts) still says a typo such as sortDirection "is now an author-time type error". The tsc probe above measures that as false. Fixing it is a packages/spec TSDoc edit (check:docs regeneration), which this PR leaves to whoever next edits that file. The docs page here no longer repeats the claim.
  • The finding's message is the check's own text, which opens with the host type in angle brackets (the SDUI node spelling). The where field carries the metadata location next to it.
  • The skill sentence (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

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 9 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d40ba8ac64eef48425fea532f750370590ccfd65 — the merge of head 5fb667f29a7545c20ec7552ce36c579b6f3b8a68 into base 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 0d421041d14cfc716e85e9bdd1da3d57d4eb3aa1 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5fb667f29a7545c20ec7552ce36c579b6f3b8a68
Local-runs: none

Isolated reviewer, read-only. Inputs: card #21131 (body and all 3 comments), PR #21204 body, its 10-file list, the net diff origin/main...head (merge-base ebdb6f2a; main-side drift since then on the touched paths is nil: sibling .changeset/* files and packages/sdui-parser/objectui-lockstep.json only), and the check-runs on the head. Code read on origin/main with git show / git grep only.

① Derived judgments

  1. Public surface, @objectstack/lint: adds validateDashboardWidgetOptions (function) and DashboardWidgetOptionFinding (type) from the package root. Additive; the finding shape (severity / rule / where / path / message / hint, path dashboards[i].widgets[j].options) and the recordsOf indexing match the sibling rules validate-widget-bindings.ts and validate-dashboard-action-refs.ts. Right.
  2. Registry entry (authoring-rules.ts): tier: 'advisory', input: 'parsed', commands: ALL, surfaces: CLI_ONLY with a surfaceReason. Matches the AuthoringRule contract (surfaceReason is REQUIRED when runtime-publish is omitted). The advisory claim holds against the wiring test's own predicate (the function body forwards d.severity; no severity: 'error' literal). Note, not a defect: the adapter forwards the check's level verbatim, so the tier is exactly as true as the check's level; the file header already says a level change is a two-door decision, and the registry tier would be part of that decision. Right.
  3. Accept set: unchanged. packages/spec is untouched; DashboardWidgetOptionsSchema stays .passthrough(); the check keeps warning and unconsumed-widget-option; no second key list exists in the diff (the adapter calls checkDashboardWidgetOptions, and the read set stays CONSUMED_WIDGET_OPTION_KEYS). The sentence "which check:widget-option-census derives from the spec" is substantiated: scripts/check-widget-option-census.mjs exists (root package.json) and reads dashboard.zod.ts. Right.
  4. Door behaviour: os validate, os build, os lint gain one advisory warning class; exit codes move only under --strict, and both flags exist (validate.ts:81 "Treat warnings as errors", exit at :922; lint.ts:794). os build has no --strict and the changeset claims none. The dict-form and id-less paths are pinned by the tests. In-repo producers on origin/main: no *.dashboard.ts writes widget options (app-crm and app-todo carry comments saying so; app-showcase's two options: [ hits are dashboard filter option arrays, not widget options), so "zero findings, no example exit changes" is consistent with the tree. Right.
  5. content/docs/ui/dashboards.mdx (hand-written tree; no new page, so no meta.json edit owed). Each rewritten claim checked against the code: (a) every widget is dataset-bound and the render path reads only the consumed set — the check's header; (b) stageOrder is refused on a non-funnel type at parse — checkDashboardWidgetStageOrder via .superRefine in dashboard.zod.ts, so the old "parses and is never consulted" row was false; (c) a misspelled key parses (passthrough; z.input of a passthrough object admits extra keys) while a wrong value for a declared key is a type error — so the old "misspelling is an author-time error" sentence was false. The declared-homes table matches the schema doc comment, WidgetColorVariantSchema, and the dataset measure's format / currency (dataset.zod.ts:278, :290). Right. Pre-existing and untouched: the page says five declared keys while the warning's list has six (description sits in the consumed set as the translation pipeline's write target) — not introduced here.
  6. Transcript counts 48 to 49 in the four transcript pages on the file list (deployment/cli.mdx, deployment/validating-metadata.mdx, the getting-started/ build walkthrough, ui/react-pages.mdx): origin/main's registry holds 48 entries and all four transcripts print 48; a commands: ALL entry makes 49 on every command (compile.ts:505 prints registered.length). Mechanical and gate-forced (check:docs-transcript-drift). Right.
  7. Finding message: the check's verbatim text, which opens with the SDUI host-type spelling in angle brackets even at the metadata door. Cosmetic; lockstep-bound (the parser is a byte-equal port of objectui's); declared in Acceptance notes. Accepted as-is.
  8. Governed surfaces: none in the file list (Governed Surface Queue Guard: success). Documentation Guardrails: no references/, releases/ or CHANGELOG.md edits. Right.

② Semver level

  • .changeset/21131-dashboard-widget-options-door.md: '@objectstack/lint': minor. Right. An additive export plus a new advisory rule; packages/lint/CHANGELOG.md lands the same class ("os validate, os build and os lint now emit one advisory warning...") under Minor Changes in 17.5.0. Not major: nothing is refused and no accept set narrows; the --strict exit flip is the flag's documented meaning and the changeset says so explicitly. No @objectstack/cli changeset is owed: no cli code changes; the printed count moves because the registry the cli reads grew.
  • Changeset prose checked against the code: the read-set listing equals CONSUMED_WIDGET_OPTION_KEYS; the exemptions listed (no dataset, legacy component, suppressWarnings) are the check's own; "does not run on the Studio/REST/MCP publish path" equals surfaces: CLI_ONLY. Right.
  • Clause-②: yes — on the card's claim (the governing limb), restated in the PR body and in the changeset; consistent, and right: the diff touches content/docs/** and .changeset prose and adds a public export.

③ Boundary flags

The dev report (card comment 5935805280) declares open_questions: [], seven deviations and four out-of-scope findings.

  • D1 (four transcript pages): necessary, mechanical, gate-forced — accepted.
  • D2 (two in-place corrections in dashboards.mdx): both measured true above; same section, same defect class, declared in the PR body — accepted under Prime Directive chore: version packages #10's note-do-not-expand reading.
  • D3 (the skill PR's third site) and D7 (no Clause-②: line on the skill PR's body) — judged in PR docs(skills): the dashboards rule stops teaching unread widget options extras #21205's record.
  • D4 (git fetch --depth=1 of a fixture commit into the shared object store): a read into the object store, not a tree edit; no effect on the diff — noted.
  • D5 (trailers): the three commits carry the session trailer pair and no model identifier — consistent with AGENTS.md; accepted.
  • D6 (labels: assignee only, no skip-changeset because a changeset exists): right.
  • OOS-1 — DashboardWidgetOptionsSchema's doc comment still says a typo "is now an author-time type error" (dashboard.zod.ts:245), measured false, and that docblock lifts into the generated reference page. ESCALATED: this is the card's own class (a teaching surface misstating what happens to an options key), with evidence, and it has no carrier. Recommendation to the adopting seat: file it as a card for the packages/spec lane (one TSDoc edit plus check:docs regeneration) rather than leaving it to "whoever next edits that file". Not blocking: outside the claimed file surface, and the PR's own page no longer repeats the claim.
  • OOS-2 — the Studio/REST/MCP door does not run the check: recorded in surfaceReason; the card's direction names os validate / os build only — accepted.
  • OOS-3 (skill's Cartesian series.dashArray bullet) — judged in PR docs(skills): the dashboards rule stops teaching unread widget options extras #21205's record.
  • OOS-4 — angle-bracket host type in the message: accepted (lockstep).
  • Check-runs on the head, read at 2026-10-01T16:44Z: 33 completed (31 success; 2 skipped: Console Pin Gate, Packed-tarball smoke opt-in) — Lint & Repo Gates (which carries check:docs-transcript-drift and the governed-prose gates), Governed Surface Queue Guard, Check Changeset, every Type Check job and five of six Test Core shards among the green — and 1 still in_progress at reading time: Test Core (5/6). It is reported as running, never as passed. This record judges the contract (landing pre-check ①); pre-check ② (every check green) is the owning seat's and is not met until that shard finishes green.

Implemented-by: claude/issue-21131-dashboard-widget-options-door
Reviewed-by: session_01UtnxvdiN376GF3sgXwAw4d

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 16:51
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit ce8a6d2 Oct 1, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21131-dashboard-widget-options-door branch October 1, 2026 17:18
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants