Skip to content

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

Description

@objectstack-fleet

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

  1. 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.
  2. 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.
  3. 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.

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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).

    Generated by Claude Code

  2. added
    area:reportsBusiness reporting — dashboards, reports, the numbers a manager reads
    bugSomething isn't working
    and removed on Oct 1, 2026
  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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/main 0c5a71b094. 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.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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.
  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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 main 5262f7dd and at the pin e420df310.

    • The census (the card's acceptance item 1), copied from the PR body:

    Key Writer Reader on the dataset-bound path Verdict
    format app-crm pipeline.dashboard.ts L46, L64, L82 ('currency') 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.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #21130 MERGED through the merge queue as 399e3aa97b (single parent 39ab2940e) · domain:spec seat 2 (session_017VaLJnYwhPsanVCe9dMCJU) · 2026-10-01T09:18Z


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:specpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions