Repository navigation
feat(spec,ui): an inline object-metric tile declares polarity, so a lower-is-better KPI draws a rise red - #27361
Conversation
Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
…, skills line Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
…tric polarity Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
…eference Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 165 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 07c0d56e8d0141dd1e0540449b05f9a46772b0bc && git checkout 07c0d56e8d0141dd1e0540449b05f9a46772b0bc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 28872e68d39a8403fe38eee532476c693e2f960f ea930c31c0479557aadd4dfb25011c6655891ae9 && git checkout -B drift-repro 28872e68d39a8403fe38eee532476c693e2f960f && git merge --no-ff ea930c31c0479557aadd4dfb25011c6655891ae9
node scripts/docs-audit/affected-docs.mjs --json 28872e68d39a8403fe38eee532476c693e2f960f |
…rity Claude-Session: https://claude.ai/code/session_01JQwxbWhSYPUK8z26ntsh8g Co-authored-by: Claude <noreply@anthropic.com>
Seat note · contract review owed · seat
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: Triage seat, taking the hand-off and Generated by Claude Code |
Contract reviewServed-tier: Inputs read: card #27347 body and all four comments (triage grade 6485097275, dispatch order 6485179476, claim 6485222036, os-dev report 6487077276); PR #27361 body, its 17-path file list and all three PR comments (docs-drift, seat hand-off 6485-series, triage claim); the net diff against ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Should-fix list: S1 (docblock: arrow and polarity read the pre- Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#2 · os-warren · session_01JQwxbXpF772qWJjojJvRXw 席位核对ACCEPT · 席位意见:页面级
维护者速读(席位终稿)
|
|
domain:ui#2 · os-warren · session_01JQwxbXpF772qWJjojJvRXw Seat note · 2026-10-29T04:22Z The merge queue removed this PR at 04:22Z. The only failing check on the queue commit That is not a defect in this diff. The head Needed: approval from |
Requested by Warren · project thread
Fixes #27347
Clause-②: yes (widening)
Metadata-protocol: added (object-metric polarity)
维护者速读
object-metric指标卡新增可声明的polarity(higher/lower/neutral,与数据集度量同一词表),趋势徽标(compareTo计算的或静态trend)按它着色。packages/spec/src/ui/component.zod.ts的object-metric段(新键与拒收提示);ui/packages/plugin-dashboard/src/MetricWidget.tsx的徽标着色一行;ui/packages/plugin-dashboard/src/utils.ts的共享deltaToneClass。pnpm --dir ui exec vitest run packages/plugin-dashboard/src/__tests__/ObjectMetricWidget.polarity-27347.test.tsx,预期 7 个用例全绿。skills/**(Tier H),契约复核 PASS 与席位 ACCEPT 之后再批准合入。What changes
ObjectMetricPropsSchema, theComponentPropsMap['object-metric']row): an optional top-levelpolarity, the dataset measure's ownDatasetMeasurePolarityby reference (no second spelling, no new export). Any other value is refused at the key.betterWhen/goodDirection/trendDirectionare aliased topolarity. A top-leveldirectiongets a prescription naming both keys it could mean (trend.directionfor the static badge's arrow,polarityfor good news), because on this tile the dataset measure'sdirectionalias would send an author who meant the badge arrow to the wrong key.higherIsBetter/lowerIsBetterget the three values.compareTo: the badgeMetricWidgetdraws isderivedTrend ?? trend, and the statictrendbadge was coloured by direction alone too, so one key covers the computed and the static badge. The reason is in the key's docblock.@object-ui/plugin-dashboard):deltaToneClassmoved fromDatasetWidget.tsxtoutils.ts(one colouring rule, now also answering "no tone" for a static badge with nodirection).ObjectMetricWidgetforwardspolarity;MetricWidgetcolours the badge throughdeltaToneClassand mutes it underneutral. Both prop types name the key by reference (ObjectMetricProps['polarity']). Theobject-metricregistration publishes an enum input;sdui.manifest.jsonregenerated.authorable-surface/ui.json,content/docs/references/ui/component.mdx,sdui.manifest.json+ its record, andcontent/docs/ui/components/index.mdx/plugins.mdx(gen-sdui-reference).liveness/page.jsonrowregionsgains evidence entry[121] object-metric.polarity(read points cited in-repo with symbol anchors).skills/objectstack-ui/rules/dashboards.md, Tier H): the metric-widget bullet says an inlineobject-metricdeclares its ownpolarity.@objectstack/specminor,@object-ui/plugin-dashboardminor,@objectstack/skillspatch.Tests
packages/spec/src/ui/object-metric-polarity.test.ts: the three values parse and keep their value; the member ISDatasetMeasurePolarity(identity); absent materializes nothing;up/down/good/inverse/truerefused at the key; the three aliases point atpolarity; top-leveldirectionnamestrendandpolarity; the two boolean-flag spellings get the values; a tsc pin (@ts-expect-erroronpolarity: 'up').ui/packages/plugin-dashboard/src/__tests__/ObjectMetricWidget.polarity-27347.test.tsx, through the registeredobject-metricblock:lowerrise drawn bad and fall drawn good with the arrow kept;higheras before;neutralmuted both ways; CONTROL undeclared class strings byte-identical (field namedturnaround_days); the statictrendbadge coloured by the same key; CONTROL a static badge with nodirectiontakes no tone.Acceptance notes
@object-ui/typeshas no diff:ObjectMetricBlockSchematakes the spec row by reference (propsBag('object-metric', SpecObjectMetricPropsSchema)), andObjectMetricBlockNodederives from that arm, so the key reaches the ui zod face and the authoring type with no edit there. No@object-ui/typeschangeset row, since the package's own files do not change.dataset.zod.ts: a shared constant would be a new public export (ui/index.tsre-exportsdataset.zodwhole) or a new module outside the claimed file surface.rules/dashboards.md6214 to 6216 tokens of ceiling 6221 (no raise). Paid by cutting the bullet'soptions.trendreaches no renderer clause, whose surviving home is the same file's Widgetoptionssection (a dataset-bound widget'strendstyles nothing).Verification
Readings taken on the merged tree (spec and plugin-dashboard closure built first).
Ablation (one-time, restored)
scripts/ablation-replace.mjsonMetricWidget.tsx:deltaToneClass(shownTrend.direction, polarity),todeltaToneClass(shownTrend.direction, undefined),(anchor 1 to 0, blob changed); the pin went 3 failed / 4 passed: the twolowercases and the static-badge case red. Restored: blob equals HEAD,git diff HEADempty. Theneutraland CONTROL rows stay green under the mutation by design.Reverse check of the tsc pin: replacing
polarity: 'up'with'neutral'under the@ts-expect-errorturnedcheck:test-typecheckred (1 type error inobject-metric-polarity.test.ts); restored to HEAD.Suites and type-checks
src/ui/+type-alias-convention.pin.test.ts: 165 files, 5854 tests passed.pnpm --dir ui exec vitest run packages/plugin-dashboard/: 192 files, 1874 passed, 6 skipped.apps/console/src/__tests__/registry-inputs-spec-parity.test.ts: 261 passed.@object-ui/plugin-dashboardtype-check (src + tests) exit 0;@objectstack/spectypecheck exit 0.pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date after the last merge.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 122 families (check:sdui-referencejoined with the regenerated pages), all run, all exit 0 on headea930c31(--ran: 122 derived, 122 run, 0 NOT-MEASURED). Three first exited 3 on prerequisites, not findings, and were re-run green: the plugin-teardown self-test (shallow clone; fixture commit fetched),check:skill-examples(client-react built) andcheck:dual-build-cjs-loads(full build). Also local:check-dev-prereqs,check:skill-frame-freshness, uicheck:new-line-citations(0 new), eslint on the changed files (0 errors).Generated by Claude Code