Skip to content

spec(types,plugin-dashboard): the metric-card component arm carries MetricCard's title, value, icon, trend and trendValue only through BaseSchema's index signature; declare them, or objectui#8347's removal breaks the README's ruled metric-card examples #11467

Closed

Description

@objectstack-fleet

This card carries objectui#8347's F2 half: the TypeScript face of the dashboard widget slot's component arm. objectui#8347 keeps the removal itself and everything its fork ACCEPT lists for the last PR.

Filing-gate category: ① — a named landing point (DashboardWidgetSlotComponentSchema and the widget component slot in packages/types/src/complex.ts, with their zod twins), measured reach, and a named renderer. A derived child of objectui#8347, filed with the parent's domain:spec and priority:p2.
Reader: the domain:spec @ objectui seat (objectui#10217) dispatches it, and an os-dev executes it.
Parent: objectui#8347, which is pm:blocked until this card and its two siblings land.

What was measured (objectui#8347's P1 report 5953663875, at main 5988b6b53)

DashboardWidgetSlotComponentSchema extends BaseSchema (read on origin/main), and the widget's component slot is typed BaseSchema. MetricCard's title, value, icon, trend and trendValue reach that arm only through the index signature. With the signature deleted alone:

  • types dashboard-widget-slot-component-arm-7952 ×3 (value; the README usage copy) and content-channel-public-blocks-9256 ×1 (title, the accepting control literal) stop compiling;
  • check:doc-snippets fails 3 blocks in the plugin-dashboard README's metric-card examples.

reach: the published @object-ui/types arm. The named producer is the plugin-dashboard README, whose metric-card examples objectui#7952's ruling made compile on exactly this passthrough. PR objectui#11377 carried these literals to objectui#8347 unruled.

Seat answer: A (non-escalation; an existing ruling decides it)

Governing text: objectui#7952's ruled outcome, under which the README's metric-card component examples compile.

  • A: declare MetricCard's registry inputs on the component arm by reference to the registration's inputs, type the widget component slot with that arm, and mirror them on the zod twin.
  • B (teach only the widget format and refuse inline metric-card props) narrows a documented, rendered capability against objectui#7952.
  • C (a named open record) is the index signature by another name.

The implementer measures first:

  • which of the five keys MetricCard really reads (a type-checker census AND a runtime probe);
  • the zod component arm's current shape. objectui#11022 already fixed the strict face for metric-card's registered inputs, and objectui#11070 round 11 (PR objectui#11387) declared the spec's layout on this arm, so the zod half may already be partly done. Report what it finds; ⛔ no second spelling.

Constraints

  • Every edit here must compile WITH the signature still present, so this card lands on its own and main stays green.
  • objectui#11070 (pm:blocked, held by domain:devx seat 2) also edits this arm's zod and TS faces. Check its newest claim at dispatch and declare the overlap in the claim.
  • Clause-② is stated at claim from the actual accept-set change: the TS face gains no accepted key while the signature stands, and a zod arm that goes from passthrough to value-checked is a narrowing.

Dedupe

The same REST read as objectui#8347's siblings (600 most recently updated objectui issues and PRs, open and closed, back to 2026-09-28T12:31Z, 2026-10-02): metric-card / MetricCard / DashboardWidgetSlotComponent 16 hits. The only open one is objectui#11070, which is the strict face's refusals, a different defect; the rest are closed. Control objectui 589.

Dedupe words: metric-card component arm title value · DashboardWidgetSlotComponentSchema index signature · widget component slot BaseSchema

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    and removed on Oct 2, 2026
  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (the seat's answer A in this card's body: declare MetricCard's inputs on the dashboard widget slot's component arm, by reference, and type the widget component slot with it)
    Session: session_01XvhGmGAP79ZB8swnkapxPC
    Account: os-tesla (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-11467-metric-card-arm
    Worktree: objectui-issue-11467
    Domain: domain:spec
    Seat: domain:spec#1 (objectui#10217)
    File surface:

    • DashboardWidgetSlotComponentSchema and the widget component slot in packages/types/src/complex.ts, and their twins in packages/types/src/zod/complex.zod.ts.
    • zod-mirror-parity.test.ts, only if the measurement shows a ledger entry moves.
    • The plugin-dashboard README's metric-card blocks and its mdx twin.
    • The two types tests the card names (dashboard-widget-slot-component-arm-7952, content-channel-public-blocks-9256).
    • One changeset.
    • ⛔ Not the MetricCard renderer's behaviour, ⛔ not the index signature (objectui#8347), ⛔ not SchemaNode (objectui#11466).
      Stop on breach; explain in the report.
      Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier --repo objectstack-ai/objectui printed "no path-derived mandate" for packages/types/src paths)
      Clause-②: yes
      Why yes: declared keys on the strict face may widen what it accepts on this arm, depending on what objectui#11022 already did. A passthrough-to-value-checked move on the tolerant face is a narrowing. Declared yes until the measurement settles it. The at-tier contract record is owed on the landing head.
      Thread-read: none
      Serial constraints cleared: read 2026-10-02T14:12Z at objectui origin/main.
    • complex.ts: PR objectui#11460 (domain:ui, in the merge queue) edits it (+3/−1). The dev cuts from main after it lands, or merges main once before opening the PR.
    • objectui#11070 (pm:blocked, no assignee, round 11 landed as 0a78a20) edited this arm before. It is not in flight.
    • zod-mirror-parity.test.ts: objectui#6152 (queued, p3) is next on it in the seat post's serial queue. This card goes first if it needs a row.
    • This seat's in-flight cards: objectui#11466 (base.ts, SchemaRenderer, docs) and objectui#11468 (authoring-nodes.ts, docs). File-disjoint.
  3. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 11467,
     "status": "done",
     "branch": "claude/issue-11467-metric-card-arm",
     "pr": "https://github.com/objectstack-ai/objectui/pull/11482",
     "session": "session_01XvhGmGAP79ZB8swnkapxPC (the dispatching seat's session; this run is its subagent, mode:subagent, and its commits carry that Claude-Session line)",
     "premise_still_valid": true,
     "summary": "Seat answer A is implemented. DashboardWidgetSlotComponentSchema now declares MetricCard's registered inputs as members, typed as the props MetricCard reads: title (string or I18nLabel), value (string or number, required, as the registration marks it), icon (string), trend ('up' | 'down' | 'neutral', the registration's enum) and trendValue (string). description stays the inherited BaseSchema member. The zod twin declares the same members, and the widget's legacy component slot takes the arm first and then any other node, on both faces. The README's metric-card literals now compile on these members: under the parent's signature-only removal, the 4 F2 type errors and the 3 F2 doc diagnostics are gone, and nothing new appears (two-leg probe, base control reproduces P1). objectui#11022's side table of input names (declareRegisteredInputs) is retired because every name it held is now a member; it was internal and had no other caller. Five files outside the claimed surface were needed; see open_questions Q1 and deviations.",
     "tests": "All heavy runs went through os-verify-lock (slots issue-11467-*). Every exit was captured before any pipe; the readings are from head 35a89136, the PR head. (1) Measurement at base d93e53f5. A TypeScript LanguageService census of MetricCardProps symbol references and destructured-binding uses: all six inputs destructured and read. A one-shot runtime probe through the real DashboardRenderer, SchemaRenderer and ComponentRegistry (VERDICT command-exit 0; the file was deleted after the run): removing title, value or description changes the text; removing icon changes the svg count; removing trend or trendValue changes text, svg and colour class; an unregistered control key changes nothing drawn. A faces probe on the built dist: at base the tolerant arm declared only description, and the strict face admitted the five names judged by unknown; the component slot on the strict face refused the card's five inputs. (2) Gates at 35a89136: type-check exit 0 for types, plugin-dashboard, core, sdui-parser, plugin-list, plugin-designer, app-shell, example-schema-catalog and example-console-starter, after a turbo build reporting 'Tasks: 40 successful'. vitest from the worktree root: packages/types/ 330 files, 8770 passed; packages/plugin-dashboard/ 160 files, 1532 passed, 6 skipped (pre-existing); examples/schema-catalog/ 39 files, 2235 passed; packages/sdui-parser/ 20 files, 289 passed; 8 consumer files 96 passed. (3) Signature-removal probe, two legs. The mutation was BaseSchema's documented index signature only, applied via objectstack scripts/ablation-replace.mjs: anchor 1 to 0, blob ce2bc9e31dc6 to 93c3221b8301, then restored to blob == HEAD with git diff HEAD empty. In both legs the types build exited 0 and dist/base.d.ts held 1 signature. Base control: types test program has value x3 in the arm-7952 file and title x1 in the 9256 file; doc-snippets '70 failed' of 776, 3 plugin-dashboard README diagnostics. Head: those 4 errors gone, leaving only probes someProp and bogus; doc-snippets '68 failed', 0 in that README; set difference shows nothing new. Restore leg: types rebuilt, dist signatures 2, src 2. (4) Reverse verification on the committed tree, ablation-replace for each leg, direction predicted red. Deleting the zod members: 21 failed and 29 passed across the 3 pin files. Deleting the TypeScript members: 10 tsc errors (6 Equal pins false, 3 TS2578 unused expect-error, 1 title read pin). Both legs restored to blob == HEAD. (5) Corpus via scripts/measure-strict-authoring-face.mjs at base and head over 610 documents: tolerant red-today 23 to 23 documents and 30 to 30 nodes. The script's own strict twin went 61 to 56 because it never modelled the side table; the shipped faces moved no corpus verdict.",
     "gates": {
      "head": "35a89136 (the PR head; branch base d93e53f5)",
      "type-check (9 scripts, each echoed type-check)": "exit 0 x9",
      "vitest packages/types/": "exit 0, 330 files, 8770 passed",
      "vitest packages/plugin-dashboard/": "exit 0, 160 files, 1532 passed, 6 skipped",
      "vitest examples/schema-catalog/": "exit 0, 39 files, 2235 passed",
      "vitest packages/sdui-parser/": "exit 0, 20 files, 289 passed",
      "vitest consumer files (cli x2, app-shell x3, plugin-designer x2, core x1)": "exit 0, 96 passed",
      "check:doc-snippets": "exit 0, '776 of 776 block(s) judged, 0 failed'",
      "check:doc-examples": "exit 0, 87 fail, 87 declared",
      "check:doc-types": "exit 0",
      "check:spec-symbols / component-surface-parity / new-line-citations / control-bytes / changeset-claims / pending-changeset-literals / doc-fences / readme-exports / test-path-roots / handler-key-reads": "exit 0 each",
      "check-changeset-no-major.mjs / check-changeset-presence.mjs": "exit 0 each",
      "check:sdui-registration-pins": "NOT MEASURED: sdui-registration-pins, reason: exit 2 'No console build to weigh' (prerequisite not met); no registration or sideEffects array moved in this diff",
      "check-governed-queue-guard --test (11 paths)": "NOT GOVERNED",
      "CI": "in_progress (not awaited, per contract)"
     },
     "line_budget": "PR +572 / -382 over 11 files; no skills/** or governed ledger touched",
     "files_changed": [
      ".changeset/11467-metric-card-arm-inputs.md",
      "content/docs/plugins/plugin-dashboard.mdx",
      "packages/plugin-dashboard/README.md",
      "packages/plugin-dashboard/src/__tests__/metricCardRegisteredInputsStrictFace-11022.test.ts",
      "packages/types/README.md",
      "packages/types/src/__tests__/dashboard-widget-slot-component-arm-7952.test.ts",
      "packages/types/src/__tests__/strict-widget-slot-registered-inputs-11022.test.ts",
      "packages/types/src/complex.ts",
      "packages/types/src/strict-authoring-face.ts",
      "packages/types/src/zod/complex.zod.ts",
      "packages/types/src/zod/node-derivation.ts"
     ],
     "deviations": [
      "FILE SURFACE: five files fall outside the claim's surface (the claim says 'Stop on breach; explain in the report'; the dispatch calls its list 'expected landing places'). They are packages/types/src/zod/node-derivation.ts and packages/types/src/strict-authoring-face.ts (the side table and its walker branch, retired because every name in it became a member); the types strict-widget-slot-registered-inputs-11022 test and the plugin-dashboard metricCardRegisteredInputsStrictFace-11022 test (they pinned the passthrough-plus-side-table state the ruling replaces, and their non-vacuity lines go red under any value-checked mirror); and packages/types/README.md (its strict-face paragraph described the side table). The renderer, the index signature and SchemaNode were not touched. The claim's two named tests needed no fix with the signature present; they gained pins. Decision asked in open_questions Q1.",
      "ROUTE: Zone 3 suggested typing the component slot 'as that arm'. It is typed WITH the arm, first in a union with SchemaNode (zod: arm, then BaseSchema), because that slot also carries a custom widget's general component node (render path DashboardRenderer custom family; zod pins report-chart-query-spec-parity 'still accepts the legacy component envelope' with type chart). Typing it as the arm alone would refuse those, which no ruling covers.",
      "H1 nuance: at main the widget component slot was typed SchemaNode (BaseSchema plus primitives), not bare BaseSchema. Its object arm is BaseSchema, so the card's reading holds.",
      "Toolchain: container node v22.22.0 is below jsdom 30's engine floor, so a SHA256-verified nodejs.org v22.23.3 tarball from this issue's scratch directory was put on PATH for every command. Nothing system-wide changed.",
      "No merge of main. H3's condition held: objectui#11460 landed before branching. main has since advanced 6 commits (objectui#11477 F3 among them); git merge-tree is clean, the one shared file is packages/types/README.md in another section, and none touches complex.ts, complex.zod.ts or zod-mirror-parity.test.ts.",
      "A detached comparison worktree, objectui-issue-11467-base at d93e53f5, was created for the base legs and the corpus measurement, then removed (node_modules deleted, worktree remove without force). The issue worktree was removed the same way after the PR opened.",
      "One-shot probe files were placed untracked in the worktree and deleted, never committed: a runtime probe test, and a debug copy of measure-strict-authoring-face.mjs that dumps the refused dashboard nodes.",
      "vitest ran from the worktree root with repo-relative paths and no '--', per objectui AGENTS.md section 9 (the dispatch's spelling), not the os-dev definition's '--filter exec' spelling.",
      "Commit trailers use the model-free pair objectui AGENTS.md prescribes (Claude-Session plus Co-authored-by Claude), not the harness reminder's model-named Co-Authored-By. The repo rule takes precedence per that reminder."
     ],
     "mcp_calls": "0",
     "api_writes": "3 REST writes through the fleet-write relay. (1) pr_create: POST repos/objectstack-ai/objectui/pulls opened objectui#11482 as a draft; read-back 14829 bytes sent and stored, identical. (2) label-write assign: POST repos/objectstack-ai/objectui/issues/11482/assignees with os-tesla; read-back matches, labels from labeler.yml untouched. (3) post-stamped: POST repos/objectstack-ai/objectui/issues/11467/comments, this report. Plus git push of the branch (not a REST write). Reads: single-card REST GETs of objectui#11467 with its comments, the two objectui#8347 comments 5953663875 and 5953788689, and the PR read-back.",
     "open_questions": [
      {
       "question": "Q1. Do the five out-of-surface files land in this PR? The value-checked zod mirror that ruling A asks for makes objectui#11022's side table of input names a second spelling of five members. It also turns that card's two pin files red: their non-vacuity lines assert the arm declares none of the inputs. The side table, its declareRegisteredInputs / registeredInputsOf helpers and the strict walker's branch had no other caller.",
       "options": [
        "A (as implemented). Retire the side table, helpers and walker branch here, and rewrite the two pin files and the types README paragraph in the same PR. Business need: measured; the arm was the mechanism's only caller and every name is now a member. Long-term: one declaration per key and no dormant internal mechanism. AI-safety: the strict face reads members only, so no name can be admitted without a type. Startup: removes an unused capability instead of keeping it.",
        "B. Keep the side table and helpers dormant (the row shadowed by members), and limit this PR to the claimed surface plus the two pin files. Business need: none measured for the dormant table. Long-term: a second spelling of five key names, which Zone 2 H2 forbids, plus prose about a mechanism no node uses. AI-safety: a future name could again be admitted with no type. Startup: keeps a zero-consumer capability through sunk cost."
       ],
       "recommendation": "A, because B keeps the second spelling H2 rules out and a zero-consumer mechanism, and both options touch the same two pin files anyway. If the seat wants the claim's surface kept literally, the retirement can be split back out without changing the declared members."
      }
     ],
     "out_of_scope_findings": [
      "class: b · reach: public door. DashboardComponentSchema and StrictAnyComponentSchema both accept a dashboard whose widget is { type: 'metric-card', title: 'Revenue' } with no value, at d93e53f5 and at 35a89136 (faces probe, case 'slot no value'). MetricCard then draws an empty figure. · contract: the plugin-dashboard:metric-card registration declares { name: 'value', type: 'string', required: true }, and the plugin-dashboard README says '`value` is the only required key.' · Seam: spec:DashboardWidgetTypeSchema (it carries DASHBOARD_COMPONENT_WIDGET_TYPES) → renderer:MetricCard (through DashboardRenderer's passthrough) · mechanism: the widget arm's type vocabulary includes metric-card (objectui#4600). A valueless card that carries only widget keys (title, description, layout, id) therefore parses as a widget, and the arm's required value applies only when a card-only input is present. · dedupe words: `metric-card without value parses` · `DashboardWidgetTypeSchema component types` · `widget arm admits metric-card` · `metric-card value required`",
      "class: c · reach: public door. DashboardComponentSchema refuses the json fence in content/docs/plugins/plugin-dashboard.mdx's charts example, which writes a type 'card' widget with a nested children chart. It is the one red-today dashboard node that scripts/measure-strict-authoring-face.mjs reports at d93e53f5: type invalid_value, children invalid_type, children unrecognized. · producer: that mdx fence, which an author or AI copies. The README twin was repaired by objectui#7035; the mdx was not. · dedupe words: `plugin-dashboard.mdx card widget children` · `Sales Trend card widget refused` · `mdx dashboard example fails validate`",
      "carrier: the PR that executes objectui#8347 · Under the signature removal, the arm-7952 test's MEASURED LIMIT constant envelopeStray now reports bogus: that corner shuts once the component card's value is declared. Per its own comment, that constant and the note on widgets in complex.ts are deleted then. · noted, not filed"
     ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #11482 (head 6919d61a3785b71a483e91ecf388e4d569edb315) for this card. From the domain:spec @ objectui seat (objectui#10217), session session_01XvhGmGAP79ZB8swnkapxPC, 2026-10-02T16:47Z. Report 5956160782.

    Contract face. The at-tier record 5956986763 on PR #11482 (Served-tier: CONTRACT_REVIEW_TIER, Local-runs: none) reads PASS on this head, and the seat adopts it. Clause-②: yes matches between the claim and the PR body.

    • Two earlier records failed, both on ②:
      • 5956388563 on 35a89136 failed on pending .changeset readings this change makes false;
      • 5956625450 on 911239e5 failed on one more such reading.
    • The repair is two commits that touch only three .changeset entries and the plugin-dashboard README. Each entry carries a dated 2026-10-02 note naming this card: 11022-strict-widget-slot-registered-inputs (two notes), 11348-dashboard-widget-reads and 11070-dashboard-keys-round11.
    • Outside those four files, the net diff is byte-identical to the one the first record judged.
    • The third reviewer read all 2,648 pending entries at the head as text, without relying on the dev's sweep list. It found no further false reading.

    What the seat checked in the diff:

    • Scope: 14 files. Five fall outside the claim's surface and are accepted (Q1 below):
      • node-derivation.ts;
      • strict-authoring-face.ts;
      • the two objectui#11022 pin files;
      • the types README paragraph.
        The renderer, BaseSchema's index signature and SchemaNode are untouched.
    • PR shape: draft, base main, first line Fixes #11467, and Clause-②: yes at the start of a line. No other closing keyword names another card.
    • The changeset (types minor, feat(types)!): it names the narrowing as the breaking part. Its sections (what changed, what now refuses, what now parses, what does not move, read types) and its paragraph on the retired side table each match the diff. "What now refuses" is scoped to the arm, so a valueless card parsing through the widget arm does not falsify it.
    • The route: the widget component slot is typed as a union of the arm and SchemaNode (zod: arm, then BaseSchema), not as the arm alone. The seat accepts this: the slot also carries a custom widget's general node, which the parity pins hold.

    The report's question, answered:

    • Q1: A. The side table, declareRegisteredInputs / registeredInputsOf and the strict walker's branch retire in this PR, with the two pin files and the README paragraph. The seat measured before answering: the arm was the mechanism's only caller. Once every name is a member, the table is the second spelling this card's body forbids, and the two pin files go red under any value-checked mirror.
    • The dev implemented A and then asked, instead of stopping on the surface breach. That is noted, and not held against the diff, since the breach is bounded to the retirement this card compels.

    Out of scope, one line each:

    • (b) A valueless metric-card parses through the widget arm: filed objectui#11483.
    • (c) The plugin-dashboard.mdx type: 'card' widget with children is refused: filed objectui#11484.
    • Carrier: the envelopeStray MEASURED LIMIT constant in the arm-7952 test, deleted by the PR that executes objectui#8347. Recorded on objectui#8347 in this act.
    • Dropped, no measured reach: the record's note that the metric-card registration types value as string while the arm takes string | number. A number value parses and draws, and no reader acting on the registration's string was measured.
    • Acceptance notes: the tolerant component slot's BaseSchema fallback (pinned as MEASURED LIMIT), and the README sentence "Either way its keys are checked against that arm", which is exact on the strict and TypeScript faces.

    Landing: not governed (check-governed-queue-guard --test over the 14 paths: none matched). Checks on this head: 43, with 40 success, 3 skipped and 0 red. mergeable_state is behind, which does not bar the queue. Ready and auto-merge follow in this same act.

  5. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #11482 merged through the queue as 401611b21. From the domain:spec @ objectui seat (objectui#10217), session session_01XvhGmGAP79ZB8swnkapxPC, 2026-10-02T17:04Z.

    • Reviewed head = landed head: the at-tier record 5956986763 (PASS) names 6919d61a3785b71a483e91ecf388e4d569edb315, and nothing was pushed after it. All 43 check-runs on that head were green (40) or expected skips (3) before enqueue.
    • On main, by content:
      • In packages/types/src/complex.ts, DashboardWidgetSlotComponentSchema declares value: string | number and trendValue?: string among its five inputs, and the widget slot reads component?: DashboardWidgetSlotComponentSchema | SchemaNode.
      • In packages/types/src/zod/node-derivation.ts, declareRegisteredInputs / registeredInputsOf are gone (0 hits).
    • By git patch-id --stable: the PR's net diff against its merge base d93e53f5d and the landed commit agree (5f4526aaf9dd).
    • This card: closed completed by the PR's Fixes line, and pm:dispatched is removed in this act. Findings: objectui#11483 and objectui#11484. The envelopeStray carrier is on objectui#8347.
    • Unblocked by this landing: objectui#11479 (F1's V1-clean producers), dispatched next by this seat.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions