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 }}
Repository navigation
spec(ui): DashboardWidgetOptionsSchema declares none of the renderer extras real producers write through its passthrough — app-crm's metric options { format, currency } is spec-valid only by catchall and red on objectui's strict face #20960
Filing gate: ① product defect, reach: a named real producer: objectstack examples/app-crm/src/dashboards/pipeline.dashboard.ts L46, L64 and L82 write options: { format: 'currency', currency: 'USD' } on three metric widgets. Filed by domain:devx seat 2 (objectstack-ai/objectui#10917), session_01TdiauJaVCHuj45EzZGUxHh, as an execution parameter of the director's ruling C on objectstack-ai/objectui#11228 (5921018991, 2026-09-30T22:45Z): "the adjacent defect … the seat files it as its own measured card (catchall census first), routed to the domain:spec lane upstream, ⛔ not folded into this round."
Who acts on it: the objectstack domain:spec seat. Its first step is the catchall census below; its PR lands the spec declarations. objectui follows by reference in its own round.
Duplicate check (semantic issue search, open and closed): objectstack "strict authoring face refuses dashboard widget options format currency passthrough catchall" → 10 hits; objectui "strict authoring face refuses spec passthrough catchall keys dashboard metric options format currency" → 3 hits. None is this defect. The nearest are objectstack#14148 (options.sortBy resolution), objectui#10463 (buildFieldMeta / currencyConfig) and objectui#10406 (a grid column currency key). All are closed and each is a different mechanism.
The mechanism (read at objectstack origin/main, 2026-09-30)
packages/spec/src/ui/dashboard.zod.ts builds DashboardWidgetOptionsSchema with .passthrough() and describes it as "declared query keys + open renderer extras". Its declared members (dateGranularity, sortBy, sortOrder, limit, stageOrder, …) include neither format nor currency.
So app-crm's pipeline dashboard is spec-valid only through the catchall. The protocol does not say what format: 'currency' means, or that currency is an ISO code.
objectui's strict authoring face (packages/types/src/strict-authoring-face.ts) closes spec catchalls (catchall: z.never()). It therefore refuses these keys by name on a document a real producer wrote.
The ruling on objectui#11228 keeps that derivation: option B, "keep the catchall open", was not taken.
This seat has not re-run the refusal. It is the ruling's reading, and objectui#11070's strict-face census class.
The maintainer's standing ruling (objectstack#12708 ledger 5552700625), verbatim: 「本项目以协议为基准。所以开发应该对其协议,协议有问题应该立卡修改协议」. So the gap is fixed in the protocol, not by reopening consumer tolerance.
What the card asks
Catchall census first. List every key that objectstack's example apps and packaged templates write through DashboardWidgetOptionsSchema's passthrough (and through any other passthrough on the dashboard widget). For each key, name its reader in objectui's dashboard renderer (the metric tile, DashboardRenderer), or record that it has none.
Per key, under ADR-0049 (enforce-or-remove):
written by a real producer AND read by a renderer ⇒ declared in the spec, typed (for currency, the ISO 4217 code);
written but unread ⇒ removed from the producer;
neither ⇒ nothing.
The census decides whether format / currency are declared; this card does not assume it.
objectui's strict face then reads the declared keys by reference, and app-crm's pipeline dashboard goes green there. That objectui follow-up is the objectui domain:spec lane's, or objectui#11070's, not this card's PR.
Out of scope
The inline dashboard dialect (options.{data, xField, yField, value, description, trend}, component.{chartType, xAxisKey, series}). It was ruled C (retire) on objectui#11228 and is executed as objectui#11070's next round.
The renderer's own inline read, which retires later on its own card.
Acceptance
The census table is on this card, one row per key: writer, reader, verdict.
One spec PR lands the declarations and removals, with a changeset.
app-crm's pipeline.dashboard.ts passes objectui's strict face at the first objectui main that carries the follow-up. Link that run when this card closes.
Triage: first grade — bug · priority:p2 · area:reports · pm:queue (domain:spec as filed). Census first, then enforce-or-remove per key
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T02:55Z. ⛔ Not a claim, ⛔ not a dispatch.
Why p2. A real producer, app-crm's pipeline.dashboard.ts, is spec-valid only through a catchall, and objectui's strict face refuses it by name. The maintainer's standing rule (「本项目以协议为基准……协议有问题应该立卡修改协议」) puts the fix in the protocol, and objectui#11228's ruling C keeps the strict face's closure.
Direction. It is the card's own direction, confirmed:
Census: every key objectstack's examples and templates write through DashboardWidgetOptionsSchema's passthrough (and any other passthrough on the widget). One row per key: writer, reader, verdict.
Per key, under ADR-0049:
written and read ⇒ declared and typed (currency as an ISO 4217 code);
written but unread ⇒ removed from the producer;
neither ⇒ nothing.
⛔ No key is declared without a reader.
One spec PR with a changeset. objectui's strict face then reads the declared keys by reference, as objectui's own follow-up.
Out of scope, as the card says: the inline dashboard dialect (objectui#11228, ruled C).
Claim: PM loop round 1 · 2026-10-01T04:51Z
Session: session_017VaLJnYwhPsanVCe9dMCJU
Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-20960-widget-options-census
Worktree: objectstack-issue-20960
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface:
packages/spec/src/ui/dashboard.zod.ts, limited to DashboardWidgetOptionsSchema (about :233-:288) and any other passthrough on the dashboard widget the census finds, plus its tests in packages/spec/src/ui/.
Producers under examples/** and the packaged templates, only where the census verdict is "written but unread ⇒ removed from the producer".
An ADR-0087 entry and the regenerated migrations/registry.ts, only if the registration gate asks for them.
The regenerated spec artefacts the declarations move.
One .changeset/20960-*.md.
⛔ Not the inline dashboard dialect: objectui#11228 ruled it C, and it is executed there. ⛔ Not objectui's strict authoring face; that is objectui's follow-up. Stop on breach and explain in the report.
Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: Clause-② suspect surface packages/spec/src/**; build at the default tier, at-tier contract review owed before enqueue).
Clause-②: yes (the census decides the arm). Declaring a key enlarges the declared public surface, and typing its value (currency as an ISO 4217 code) narrows what the catchall accepted. The PR carries the measured arm.
Thread-read: 5923800404
Serial constraints cleared: read at this stamp against origin/main0c5a71b094. Of the 31 open PRs, only this seat's PR #21053 (#20958, in the merge queue) touches dashboard.zod.ts, and only in other regions: the import line, the new check after checkDashboardWidgetMetricMeasureArity (:675 on), and DashboardWidgetSchema (:1005, :1162). This card's region is DashboardWidgetOptionsSchema. No fleet pm:dispatched claim names dashboard.zod.ts or app-crm's dashboards. The dev merges main after PR #21053 lands, before opening the PR.
Pointer for this card's catchall census: one more consumed key that the strict face refuses. From domain:devx seat 2 (objectui#10917), session_01TdiauJaVCHuj45EzZGUxHh, 2026-10-01T05:26Z. ⛔ Not a claim.
The key:widgets[].options.description.
objectui#11228's ruling C kept it refused by name, as one of the nine inline-dialect keys.
Yet objectui's DatasetWidget reads it on the dataset-bound path, as a metric tile's sub-caption (CONSUMED_WIDGET_OPTION_KEYS in @object-ui/sdui-parser).
Who writes it: no producer in either repository, as measured by objectui#11070 round 6's dev (report 5925279994, PR objectui#11333).
What to do with it: add one census row: written by none, read by objectui. Your step 2 then decides between declaring it and having the reader drop it. ADR-0049 points to the reader dropping it unless a producer appears.
os-dev-report
{
"issue": 20960,
"status": "done",
"branch": "claude/issue-20960-widget-options-census",
"pr": "#21130",
"session": "session_017VaLJnYwhPsanVCe9dMCJU",
"premise_still_valid": true,
"summary": "Census first (H1), and it decided no declaration. Two examples write keys no renderer reads: app-crm pipeline.dashboard.ts writes options.format and options.currency on 3 metric tiles, and app-todo task.dashboard.ts writes color x4, suffix, showLegend x2, horizontal and showDataLabels. objectui DatasetWidget.tsx (main 5262f7dd; pin e420df310 has the same read set) reads only dateGranularity, sortBy, sortOrder, limit, stageOrder and description, and every widget with a dataset routes there. So each written key is written but unread and was removed from its producer. The tiles take their currency from the dataset measure, which already declares format 0,0 and currency USD. The seat-note row: options.description is read (DatasetWidget :1118 sub-caption) but has no authored writer (0 hits; translateDashboard only overlays it from a subCaption bundle entry, and this repo ships none), so it is not declared and the reader-side drop is objectui follow-up. In the spec, only the DashboardWidgetOptionsSchema doc comment changed. It no longer says the renderer understands icon, trend, columns, striped and density. It now states the measured read set, the rule (declare a key only with both a writer and a dataset-bound reader) and the declared homes for presentation (measure format and currency, colorVariant, chartConfig). It ships in dist/ui/index.d.ts, hence a patch changeset with Clause-② no. H4: a scratch replica of the strict-face walker refused crm-before 3x(format,currency) and todo-before 8 keys; crm-after and todo-after PASS; the control zzProbe is refused. H3: 0 values typed, 0 stored rows affected.",
"tests": "Final head 9efd8ff; consumer tests in the upstream-dependency direction (turbo build --filter=PKG... closures first). @objectstack/spec build exit 0; check:generated: All 15 generated artifacts are up to date. spec test (at ef4a1f5 = this diff on 2821e9f, pre-merge): 588 files / 17239 passed. spec typecheck (tsc + scripts + test-typecheck): OK. example-crm test 45 passed + typecheck Done; example-todo test 238 passed + typecheck Done (both at 9efd8ff). Consumers at ef4a1f5: sdui-parser 217 passed; service-analytics 3498 passed / 10 skipped; platform-objects 948; lint 5452; metadata-protocol 2921 / 19 skipped; objectql 6874; metadata 836. cli unit tier, 4 shards (shard 1 at ef4a1f5, shards 2-4 at 9efd8ff): 1005 + 843 + 778 + 793 passed, every lock VERDICT command-exit 0. A shard-4 run whose outer timeout cut the VERDICT line was rerun to get the verdict. cli integration tier declared to CI. eslint (narrowed, 9efd8ff): 3 touched TS files, --format json reports 3 files / 0 errors / 0 warnings; --print-config resolves all 3, so none is ignored; eslint.config.mjs:327 states no type-aware linting, so untouched files are invariant. No ablation: no new guard or test; the change is producer removal plus doc prose. H4 probe output is in the PR body.",
"mcp_calls": "0",
"api_writes": "3 - all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (#21130, read-back 13472/13472 bytes identical); (2) label-write --assign os-bill → POST /repos//issues/21130/assignees (read-back MATCHES); (3) this os-dev-report comment → POST /repos//issues/20960/comments. No label writes: none were named, and skip-changeset does not apply because spec ships.",
"open_questions": [],
"out_of_scope_findings": [
"class: c · reach: named producer (examples/app-crm pipeline.dashboard.ts carried 3 and examples/app-todo task.dashboard.ts carried 8 unread options keys on main, and no gate flagged them) · evidence: checkDashboardWidgetOptions has a single caller, packages/sdui-parser/src/validate.ts:318, which covers SDUI page dashboard nodes only; nothing in lint/cli/metadata-protocol runs it over .dashboard.ts metadata, so an unread widget options key is silent at os validate / os build · same family as this card; route to its closing card if one exists · dedupe words: unconsumed-widget-option dashboard metadata, checkDashboardWidgetOptions os validate, widget options silent validate",
"class: c · reach: named producer (app-crm / app-todo wrote such keys) · evidence: teaching surfaces still present unread options extras as working. content/docs/ui/dashboards.mdx:138 says "keys the renderer understands (icon, columns, striped, density, ...) pass through untouched". skills/objectstack-ui/rules/dashboards.md:185 writes options: { icon: DollarSign } and :341 says presentation extras pass through. DatasetWidget at objectui 5262f7dd reads none of them. The skill is Tier H governed, so it was not edited here · same family as this card · dedupe words: dashboards skill options icon, widget options presentation extras docs, options open bag renderer understands",
"carrier: 承接者:无 · noted, not filed (in PR Acceptance notes): the app-todo completion_rate tile is titled Completion Rate but renders task_count; the app-crm pipeline.dashboard.ts header comment still describes retired categoryGranularity month bucketing; the removed app-todo keys have declared homes (chartConfig.showDataLabels, type horizontal-bar) that were not used because they change the render; DashboardWidgetOptionsSchema .describe() is kept because sdui-parser and objectui quote it in lockstep; packages/spec has no shared ISO 4217 code schema (z.string().length(3) spelled at field.zod.ts:485, :509 and dataset.zod.ts:288)."
],
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) at 9efd8ff derived 80 commands; all 80 run on 9efd8ff; --ran reconciliation: 80 derived famil(ies) accounted for — 79 run, 1 NOT-MEASURED. NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (needs the whole workspace built, 86 dists; CI Build Core). Verdict lines: check-adr-0087-registration --base origin/main exit 0 (this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)); check-changeset-no-major --base origin/main exit 0; check-empty-changeset --base origin/main exit 0; check:api-surface exit 0 (public API surface + factory signatures unchanged); check:docs exit 0 (227 generated files in sync); check:widget-option-census exit 0 (5 key(s) declared ... all in CONSUMED_WIDGET_OPTION_KEYS (6 member(s)); 1 non-declared member(s) each still witnessed); check:objectui-pin-citations exit 0; check-spec-docblock-symbol-anchors exit 0; check:doc-authoring exit 0; check:nul-bytes exit 0 (no raw ASCII control bytes); check:lean-entry-closure exit 0; check:doc-formula-expressions exit 0; check:spec-changes / authorable-surface / migration-registry inside check:generated all up to date. The PM-derived list (75 at 0c5a71b) is a subset of the 80.",
"line_budget": "none owed (no skills/* in diff)",
"deviations": [
"Landing site: no declarations. The census verdicts are all written-but-unread (removed from producers) or read-but-unwritten (description, not declared). The spec change is the DashboardWidgetOptionsSchema doc comment only.",
"Changeset level: patch with Clause-② no, not the minor arms the dispatch anticipated. The claim carried Clause-② yes with the arm left to the census; the measured arm is no (accept set unchanged).",
"The files changed include examples/app-todo task.dashboard.ts, which is in the claim surface (producers under examples/**). The census found it alongside app-crm.",
"Merged origin/main e07566b (PR #21053 included) through scripts/pm/os-regen-merge.sh before pr_create; the branch delta is the 4 files only. origin/main has since moved by 1 unrelated commit (5e470f8, lint).",
"H4 measured with a scratch replica of the objectui strict-face walker over the built spec DashboardSchema, not objectui’s own face; the real-run link is owed by the closer."
],
"files_changed": [
".changeset/20960-widget-options-census.md",
"examples/app-crm/src/dashboards/pipeline.dashboard.ts",
"examples/app-todo/src/dashboards/task.dashboard.ts",
"packages/spec/src/ui/dashboard.zod.ts"
]
}
ACCEPT — PR #21130 at head 9efd8ffa5d · domain:spec seat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T08:39Z
Reviewed against GitHub (the PR object, its file list, its diff and the head's check-runs), not against the dev's report on this card.
Shape: draft, base main, first line Fixes #20960 and no other closing keyword. Clause-②: no is at line start, with its reason. The PR assignee is os-bill.
Scope: 4 files, inside claim 5924978148:
the DashboardWidgetOptionsSchema doc comment in packages/spec/src/ui/dashboard.zod.ts;
the two producers examples/app-crm/src/dashboards/pipeline.dashboard.ts and examples/app-todo/src/dashboards/task.dashboard.ts;
a patch changeset.
check-governed-merges.mjs --pr 21130 says NOT governed.
The claim's arm, measured: the claim carried Clause-②: yes with the arm left to the census. The census found no key with both a writer and a reader, so nothing is declared or typed. The zod expression is byte-identical and the measured arm is no, which the at-tier review confirms.
Contract review: the at-tier record 5927832648 on the PR is a PASS at 9efd8ffa5d2c, the head this lands. It verified the census independently: the writers at the head, and objectui DatasetWidget's read set at main5262f7dd and at the pin e420df310.
The census (the card's acceptance item 1), copied from the PR body:
None. The metric tile formats with the dataset measure's own metadata: formatMeasure(value, f?.format, f?.currency, …) at :1126, where f = measureField(values[0]) at :1043. opportunity.dataset.ts already declares format: '0,0', currency: 'USD' on total_amount and avg_amount.
Written, unread. Removed from the producer.
currency
app-crm, same three widgets ('USD')
None (same read as format)
Written, unread. Removed.
color
app-todo, four metric tiles (hex colours)
None. A tile's accent is widget.colorVariant (metricAccentTextClass(widget?.colorVariant) at :1071).
Written, unread. Removed.
suffix
app-todo completion_rate ('%')
None
Written, unread. Removed.
showLegend
app-todo pie and donut (true)
None. The legend comes from widget.chartConfig (chartConfigPresentation(widget?.chartConfig, …) at :1800), where showLegend defaults to true.
Written, unread. Removed.
horizontal
app-todo bar (true)
None. The declared spelling is type: 'horizontal-bar'.
Written, unread. Removed.
showDataLabels
app-todo line (true)
None. The declared home is chartConfig.showDataLabels.
Written, unread. Removed.
description
No authored writer. The single-line grep and every dashboard file read by hand both found none, and the grep's control hit is content/docs/ui/dashboards.mdx. translateDashboard (system/i18n-resolver.ts) only overlays the key from a subCaption bundle entry, and this repo ships no such entry.
Metric sub-caption: pickLocalized(options.description, …) at :1118
Read, unwritten. Not declared. The seat note (5925333725) records objectui#11228 ruling C, which keeps this key refused by name as an inline-dialect key, so dropping the read is objectui's follow-up.
Evidence read:
H3: 0 values typed and 0 stored rows affected.
H4: a scratch replica of objectui's strict-face walker refuses app-crm before the change (3 × format/currency) and app-todo before (8 keys); both pass after; the control key is refused.
Consumer suites: 13 packages green, plus the two example apps.
Gates: 80 derived, 79 run, all exit 0. check:dual-build-cjs-loads is NOT MEASURED (whole-workspace dist), which is CI's.
CI at 9efd8ffa5d, read at this stamp: landing waits for every check.
(d) objectui's description read-drop must reconcile with translateDashboard still writing options.description from a subCaption bundle. The seat files the objectui coordination child after landing and carries this point into it.
Landed · PR #21130 MERGED through the merge queue as 399e3aa97b (single parent 39ab2940e) · domain:spec seat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T09:18Z
Verified by content, not by the merged flag. All 4 files have the same blob in the merge commit as at the ACCEPTed head 9efd8ffa5d, the head the at-tier PASS 5927832648 covers. The merge commit is an ancestor of origin/main.
Closing keywords: the body carried Fixes #20960 only. This card closed completed, and no other card was named.
Acceptance items:
Item 1 (the census table on the card) is ACCEPT 5927851290.
Item 2 (one spec PR with a changeset) is this landing.
Item 3 (app-crm passing objectui's strict face, run linked): app-crm no longer writes the refused keys. The real objectui run is owed at objectui's first run over this main, and the seat's objectui child carries it.
Pointer · domain:spec seat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T09:20Z: the objectui follow-ups ride objectstack-ai/objectui#11070 (in flight, round 8), which already cites this card. No duplicate child was filed. The pointer there, 5928509607, carries two items:
the options.description read-drop, reconciled with translateDashboard still writing it from a subCaption bundle;
acceptance item 3's real strict-face run link for app-crm.
Pointer: the app-crm strict-face run that this card's acceptance item 3 asks for. No claim, and no reply needed. From domain:devx seat 2 @ objectui (objectstack-ai/objectui#10917), session_01TdiauJaVCHuj45EzZGUxHh, 2026-10-01T12:42Z. The domain:spec seat's pointer 5928509607 on objectstack-ai/objectui#11070 routed the run to that card's next round. objectstack-ai/objectui#11070 round 10 ran it.
Instrument: the SHIPPED strict face, StrictAnyComponentSchema from the built @object-ui/types/zod dist. That is the call objectui's validate wiring makes. The run used neither the measure script's own walker nor the tolerant CLI.
Shas: objectui main0a3e5409, re-run at be58a328 with an identical result. objectstack f0cc16e8d; 399e3aa97b is an ancestor, and examples/app-crm is byte-identical since.
Result:
PipelineDashboard as a dashboard node passes the tolerant face and the strict face, with 0 refusals. options.format and options.currency do not appear.
CrmWelcomePage (page arm) and CrmApp (app arm) pass both faces.
Lit control: the same dashboard at 399e3aa97b^ (39ab2940e) fails the strict face with unrecognized_keys [format, currency] at widgets.0/1/2.options.
Observation, not a new defect: a stored page as PageView serves it (type: 'home') fails both faces at type with no matching discriminator. That is the documented page-kind collision (objectui#9642, PAGE_SPEC_EXCLUDED).
Activity
objectstack-fleet commentedon Oct 1, 2026
Triage: first grade —
bug·priority:p2·area:reports·pm:queue(domain:specas filed). Census first, then enforce-or-remove per keyTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T02:55Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. A real producer, app-crm's
pipeline.dashboard.ts, is spec-valid only through a catchall, and objectui's strict face refuses it by name. The maintainer's standing rule (「本项目以协议为基准……协议有问题应该立卡修改协议」) puts the fix in the protocol, and objectui#11228's ruling C keeps the strict face's closure.Direction. It is the card's own direction, confirmed:
DashboardWidgetOptionsSchema's passthrough (and any other passthrough on the widget). One row per key: writer, reader, verdict.currencyas an ISO 4217 code);Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Claim: PM loop round 1 · 2026-10-01T04:51Z
Session:
session_017VaLJnYwhPsanVCe9dMCJUAccount:
os-bill(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20960-widget-options-censusWorktree:
objectstack-issue-20960Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface:
packages/spec/src/ui/dashboard.zod.ts, limited toDashboardWidgetOptionsSchema(about:233-:288) and any other passthrough on the dashboard widget the census finds, plus its tests inpackages/spec/src/ui/.examples/**and the packaged templates, only where the census verdict is "written but unread ⇒ removed from the producer".migrations/registry.ts, only if the registration gate asks for them..changeset/20960-*.md.⛔ Not the inline dashboard dialect: objectui#11228 ruled it C, and it is executed there. ⛔ Not objectui's strict authoring face; that is objectui's follow-up. Stop on breach and explain in the report.
Container & model:
M,mode:subagent,model: opus(dispatch-gates--tier: Clause-② suspect surfacepackages/spec/src/**; build at the default tier, at-tier contract review owed before enqueue).Clause-②: yes (the census decides the arm). Declaring a key enlarges the declared public surface, and typing its value (
currencyas an ISO 4217 code) narrows what the catchall accepted. The PR carries the measured arm.Thread-read: 5923800404
Serial constraints cleared: read at this stamp against
origin/main0c5a71b094. Of the 31 open PRs, only this seat's PR #21053 (#20958, in the merge queue) touchesdashboard.zod.ts, and only in other regions: the import line, the new check aftercheckDashboardWidgetMetricMeasureArity(:675on), andDashboardWidgetSchema(:1005,:1162). This card's region isDashboardWidgetOptionsSchema. No fleetpm:dispatchedclaim namesdashboard.zod.tsor app-crm's dashboards. The dev mergesmainafter PR #21053 lands, before opening the PR.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Pointer for this card's catchall census: one more consumed key that the strict face refuses. From
domain:devxseat 2 (objectui#10917),session_01TdiauJaVCHuj45EzZGUxHh, 2026-10-01T05:26Z. ⛔ Not a claim.widgets[].options.description.DatasetWidgetreads it on the dataset-bound path, as a metric tile's sub-caption (CONSUMED_WIDGET_OPTION_KEYSin@object-ui/sdui-parser).5925279994, PR objectui#11333).objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{
"issue": 20960,
"status": "done",
"branch": "claude/issue-20960-widget-options-census",
"pr": "#21130",
"session": "session_017VaLJnYwhPsanVCe9dMCJU",
"premise_still_valid": true,
"summary": "Census first (H1), and it decided no declaration. Two examples write keys no renderer reads: app-crm pipeline.dashboard.ts writes options.format and options.currency on 3 metric tiles, and app-todo task.dashboard.ts writes color x4, suffix, showLegend x2, horizontal and showDataLabels. objectui DatasetWidget.tsx (main 5262f7dd; pin e420df310 has the same read set) reads only dateGranularity, sortBy, sortOrder, limit, stageOrder and description, and every widget with a dataset routes there. So each written key is written but unread and was removed from its producer. The tiles take their currency from the dataset measure, which already declares format 0,0 and currency USD. The seat-note row: options.description is read (DatasetWidget :1118 sub-caption) but has no authored writer (0 hits; translateDashboard only overlays it from a subCaption bundle entry, and this repo ships none), so it is not declared and the reader-side drop is objectui follow-up. In the spec, only the DashboardWidgetOptionsSchema doc comment changed. It no longer says the renderer understands icon, trend, columns, striped and density. It now states the measured read set, the rule (declare a key only with both a writer and a dataset-bound reader) and the declared homes for presentation (measure format and currency, colorVariant, chartConfig). It ships in dist/ui/index.d.ts, hence a patch changeset with Clause-② no. H4: a scratch replica of the strict-face walker refused crm-before 3x(format,currency) and todo-before 8 keys; crm-after and todo-after PASS; the control zzProbe is refused. H3: 0 values typed, 0 stored rows affected.",
"tests": "Final head 9efd8ff; consumer tests in the upstream-dependency direction (turbo build --filter=PKG... closures first). @objectstack/spec build exit 0; check:generated: All 15 generated artifacts are up to date. spec test (at ef4a1f5 = this diff on 2821e9f, pre-merge): 588 files / 17239 passed. spec typecheck (tsc + scripts + test-typecheck): OK. example-crm test 45 passed + typecheck Done; example-todo test 238 passed + typecheck Done (both at 9efd8ff). Consumers at ef4a1f5: sdui-parser 217 passed; service-analytics 3498 passed / 10 skipped; platform-objects 948; lint 5452; metadata-protocol 2921 / 19 skipped; objectql 6874; metadata 836. cli unit tier, 4 shards (shard 1 at ef4a1f5, shards 2-4 at 9efd8ff): 1005 + 843 + 778 + 793 passed, every lock VERDICT command-exit 0. A shard-4 run whose outer timeout cut the VERDICT line was rerun to get the verdict. cli integration tier declared to CI. eslint (narrowed, 9efd8ff): 3 touched TS files, --format json reports 3 files / 0 errors / 0 warnings; --print-config resolves all 3, so none is ignored; eslint.config.mjs:327 states no type-aware linting, so untouched files are invariant. No ablation: no new guard or test; the change is producer removal plus doc prose. H4 probe output is in the PR body.",
"mcp_calls": "0",
"api_writes": "3 - all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (#21130, read-back 13472/13472 bytes identical); (2) label-write --assign os-bill → POST /repos//issues/21130/assignees (read-back MATCHES); (3) this os-dev-report comment → POST /repos//issues/20960/comments. No label writes: none were named, and skip-changeset does not apply because spec ships.",
"open_questions": [],
"out_of_scope_findings": [
"class: c · reach: named producer (examples/app-crm pipeline.dashboard.ts carried 3 and examples/app-todo task.dashboard.ts carried 8 unread options keys on main, and no gate flagged them) · evidence: checkDashboardWidgetOptions has a single caller, packages/sdui-parser/src/validate.ts:318, which covers SDUI page dashboard nodes only; nothing in lint/cli/metadata-protocol runs it over .dashboard.ts metadata, so an unread widget options key is silent at os validate / os build · same family as this card; route to its closing card if one exists · dedupe words: unconsumed-widget-option dashboard metadata, checkDashboardWidgetOptions os validate, widget options silent validate",
"class: c · reach: named producer (app-crm / app-todo wrote such keys) · evidence: teaching surfaces still present unread options extras as working. content/docs/ui/dashboards.mdx:138 says "keys the renderer understands (icon, columns, striped, density, ...) pass through untouched". skills/objectstack-ui/rules/dashboards.md:185 writes options: { icon: DollarSign } and :341 says presentation extras pass through. DatasetWidget at objectui 5262f7dd reads none of them. The skill is Tier H governed, so it was not edited here · same family as this card · dedupe words: dashboards skill options icon, widget options presentation extras docs, options open bag renderer understands",
"carrier: 承接者:无 · noted, not filed (in PR Acceptance notes): the app-todo completion_rate tile is titled Completion Rate but renders task_count; the app-crm pipeline.dashboard.ts header comment still describes retired categoryGranularity month bucketing; the removed app-todo keys have declared homes (chartConfig.showDataLabels, type horizontal-bar) that were not used because they change the render; DashboardWidgetOptionsSchema .describe() is kept because sdui-parser and objectui quote it in lockstep; packages/spec has no shared ISO 4217 code schema (z.string().length(3) spelled at field.zod.ts:485, :509 and dataset.zod.ts:288)."
],
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) at 9efd8ff derived 80 commands; all 80 run on 9efd8ff; --ran reconciliation: 80 derived famil(ies) accounted for — 79 run, 1 NOT-MEASURED. NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (needs the whole workspace built, 86 dists; CI Build Core). Verdict lines: check-adr-0087-registration --base origin/main exit 0 (this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)); check-changeset-no-major --base origin/main exit 0; check-empty-changeset --base origin/main exit 0; check:api-surface exit 0 (public API surface + factory signatures unchanged); check:docs exit 0 (227 generated files in sync); check:widget-option-census exit 0 (5 key(s) declared ... all in CONSUMED_WIDGET_OPTION_KEYS (6 member(s)); 1 non-declared member(s) each still witnessed); check:objectui-pin-citations exit 0; check-spec-docblock-symbol-anchors exit 0; check:doc-authoring exit 0; check:nul-bytes exit 0 (no raw ASCII control bytes); check:lean-entry-closure exit 0; check:doc-formula-expressions exit 0; check:spec-changes / authorable-surface / migration-registry inside check:generated all up to date. The PM-derived list (75 at 0c5a71b) is a subset of the 80.",
"line_budget": "none owed (no skills/* in diff)",
"deviations": [
"Landing site: no declarations. The census verdicts are all written-but-unread (removed from producers) or read-but-unwritten (description, not declared). The spec change is the DashboardWidgetOptionsSchema doc comment only.",
"Changeset level: patch with Clause-② no, not the minor arms the dispatch anticipated. The claim carried Clause-② yes with the arm left to the census; the measured arm is no (accept set unchanged).",
"The files changed include examples/app-todo task.dashboard.ts, which is in the claim surface (producers under examples/**). The census found it alongside app-crm.",
"Merged origin/main e07566b (PR #21053 included) through scripts/pm/os-regen-merge.sh before pr_create; the branch delta is the 4 files only. origin/main has since moved by 1 unrelated commit (5e470f8, lint).",
"H4 measured with a scratch replica of the objectui strict-face walker over the built spec DashboardSchema, not objectui’s own face; the real-run link is owed by the closer."
],
"files_changed": [
".changeset/20960-widget-options-census.md",
"examples/app-crm/src/dashboards/pipeline.dashboard.ts",
"examples/app-todo/src/dashboards/task.dashboard.ts",
"packages/spec/src/ui/dashboard.zod.ts"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
ACCEPT — PR #21130 at head
9efd8ffa5d·domain:specseat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T08:39ZReviewed against GitHub (the PR object, its file list, its diff and the head's check-runs), not against the dev's report on this card.
Shape: draft, base
main, first lineFixes #20960and no other closing keyword.Clause-②: nois at line start, with its reason. The PR assignee isos-bill.Scope: 4 files, inside claim
5924978148:DashboardWidgetOptionsSchemadoc comment inpackages/spec/src/ui/dashboard.zod.ts;examples/app-crm/src/dashboards/pipeline.dashboard.tsandexamples/app-todo/src/dashboards/task.dashboard.ts;patchchangeset.check-governed-merges.mjs --pr 21130says NOT governed.The claim's arm, measured: the claim carried
Clause-②: yeswith the arm left to the census. The census found no key with both a writer and a reader, so nothing is declared or typed. The zod expression is byte-identical and the measured arm isno, which the at-tier review confirms.Contract review: the at-tier record
5927832648on the PR is a PASS at9efd8ffa5d2c, the head this lands. It verified the census independently: the writers at the head, and objectuiDatasetWidget's read set atmain5262f7ddand at the pine420df310.The census (the card's acceptance item 1), copied from the PR body:
formatpipeline.dashboard.tsL46, L64, L82 ('currency')formatMeasure(value, f?.format, f?.currency, …)at:1126, wheref = measureField(values[0])at:1043.opportunity.dataset.tsalready declaresformat: '0,0', currency: 'USD'ontotal_amountandavg_amount.currency'USD')format)colorwidget.colorVariant(metricAccentTextClass(widget?.colorVariant)at:1071).suffixcompletion_rate('%')showLegendtrue)widget.chartConfig(chartConfigPresentation(widget?.chartConfig, …)at:1800), whereshowLegenddefaults totrue.horizontaltrue)type: 'horizontal-bar'.showDataLabelstrue)chartConfig.showDataLabels.descriptioncontent/docs/ui/dashboards.mdx.translateDashboard(system/i18n-resolver.ts) only overlays the key from asubCaptionbundle entry, and this repo ships no such entry.pickLocalized(options.description, …)at:1118format/currency) and app-todo before (8 keys); both pass after; the control key is refused.check:dual-build-cjs-loadsis NOT MEASURED (whole-workspace dist), which is CI's.9efd8ffa5d, read at this stamp: landing waits for every check.os validatenever runs the widget-option check over*.dashboard.ts: one family. Filed finding: unread dashboard widgetoptionskeys pass silently —os validatenever runs the widget-option check over*.dashboard.ts, and the docs page and skill still teach unread extras (icon,columns,striped) as working #21131.descriptionread-drop must reconcile withtranslateDashboardstill writingoptions.descriptionfrom asubCaptionbundle. The seat files the objectui coordination child after landing and carries this point into it.main, and the objectui child carries it.optionskeys pass silently —os validatenever runs the widget-option check over*.dashboard.ts, and the docs page and skill still teach unread extras (icon,columns,striped) as working #21131. Out-of-scope 3 (five notes on adjacent prose and naming) goes to Acceptance notes, not filed. Carrier: none.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Landed · PR #21130 MERGED through the merge queue as
399e3aa97b(single parent39ab2940e) ·domain:specseat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T09:18Z9efd8ffa5d, the head the at-tier PASS5927832648covers. The merge commit is an ancestor oforigin/main.Fixes #20960only. This card closedcompleted, and no other card was named.5927851290.main, and the seat's objectui child carries it.pm:dispatchedcomes off in this act. finding: unread dashboard widgetoptionskeys pass silently —os validatenever runs the widget-option check over*.dashboard.ts, and the docs page and skill still teach unread extras (icon,columns,striped) as working #21131 carries the same family's two open positions.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Pointer ·
domain:specseat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T09:20Z: the objectui follow-ups ride objectstack-ai/objectui#11070 (in flight, round 8), which already cites this card. No duplicate child was filed. The pointer there,5928509607, carries two items:options.descriptionread-drop, reconciled withtranslateDashboardstill writing it from asubCaptionbundle;Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Pointer: the app-crm strict-face run that this card's acceptance item 3 asks for. No claim, and no reply needed. From
domain:devxseat 2 @ objectui (objectstack-ai/objectui#10917),session_01TdiauJaVCHuj45EzZGUxHh, 2026-10-01T12:42Z. Thedomain:specseat's pointer5928509607on objectstack-ai/objectui#11070 routed the run to that card's next round. objectstack-ai/objectui#11070 round 10 ran it.5931680973on spec(types): 32 keys the renderers read are refused by the strict authoring face — declare each or rule it runtime-only, the ruled precondition beforeobjectui validategoes strict (objectui#5250) objectui#11070 (PR feat(types): a form field of type grid declares the grid widget's field-level keys (objectui#11070 round 10) objectui#11381). Measure only; nothing was changed for it.StrictAnyComponentSchemafrom the built@object-ui/types/zoddist. That is the call objectui's validate wiring makes. The run used neither the measure script's own walker nor the tolerant CLI.main0a3e5409, re-run atbe58a328with an identical result. objectstackf0cc16e8d;399e3aa97bis an ancestor, andexamples/app-crmis byte-identical since.PipelineDashboardas adashboardnode passes the tolerant face and the strict face, with 0 refusals.options.formatandoptions.currencydo not appear.CrmWelcomePage(page arm) andCrmApp(app arm) pass both faces.399e3aa97b^(39ab2940e) fails the strict face withunrecognized_keys [format, currency]atwidgets.0/1/2.options.PageViewserves it (type: 'home') fails both faces attypewith no matching discriminator. That is the documented page-kind collision (objectui#9642,PAGE_SPEC_EXCLUDED).feat(types): a form field of type grid declares the grid widget's fie…
fix(examples,spec): dashboard widget options census — examples drop t…