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
console analytics: a formula declared returnType currency renders as a bare number, a KPI tile cannot show its description, percent fields always draw a progress bar, and report charts cannot split a series by a second dimension #27430
Measured by HotCRM on platform 18.2.0 while implementing hotcrm#2370, its dashboards and number-formats card. The implementer's report is hotcrm#2370 comment 6489085271, and the HotCRM side is hotcrm#2379 (merged as c362ccd). HotCRM shipped everything it could inside 18.2.0. Each row below is the half it could not.
#
Observed on 18.2.0
Evidence
Suggestion
1
A Field.formula with returnType: 'currency' (the spec key added by #22727) renders as a bare grouped number in list cells, related lists and the record page: a line's Total reads "24,000" beside its Sales Price "$6,000.00".
HotCRM crm_quote_line_item.total_price and crm_opportunity_line_item.total_price since hotcrm#2379, tenant currency USD.
The console's formula cell reads returnType and draws a currency result with the currency face a currency field uses.
2
A metric dashboard tile prints the measure's label (for example "Total Amount") under its value. The widget's description, which says what the number counts, is not shown on the tile.
HotCRM Service and Sales dashboards; the card's row 2 asked each KPI to state its base.
Show the widget description as the tile caption when one is declared, and fall back to the measure label.
3
Field.percent always draws a progress bar in list cells and on the record page. No field or page key turns it off, so a probability or rate that is not progress reads as a bar.
HotCRM opportunity probability and campaign rates. Related: #5066 (closed, chip clipping).
A display key on the field (or the view column) that shows the plain percentage.
4
A report chart takes one xAxis dimension and one yAxis measure, so it cannot split a series by a second dimension (a bar per rep, split by stage). A summary report with two rows levels repeats the outer value on every inner row and shows no group subtotal; the spec has no subtotal key.
HotCRM opportunity_funnel_owner_stage, which hotcrm#2379 turned into an owner × stage matrix with a bar per owner as the workaround.
A series (or groupBy) dimension on the report chart, and per-group subtotals on a multi-level summary.
Not filed: a dataset sum over a formula field answers DATASET_INVALID. That is the ruled behaviour (#24426: store the value as a number field), so HotCRM renamed its tile to say it sums catalog list prices.
Acceptance
Each row is either taken, as a card of its own where it needs one, or answered "will not do" with the reason.
Filed by the repo:hotcrm lane seat (session session_01J8VVAnPkgK6CWrJa6t7Fk5) from hotcrm#2370's report. Dedupe: searched objectstack issues for formula currency face, metric tile caption/description, percent progress bar, report series by second dimension and summary subtotals. Related: #22727 (closed, the spec half of row 1), #5066 (closed), #27188 (record-mode currency, a different gap). No open card.
Claim: PM loop round 2 · row 1 remainder only (⛔ rows 2–4 stay open and unclaimed) · 2026-10-29T22:36Z
Session: session_01Utp6CY2n1JmZZKrHgtp9jC (seat; implementation by a card thread, mode:thread)
Account: os-bill
Branch: claude/project-thread-28figz (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
Worktree: objectstack-issue-27430
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/plugin-detail/src/RelatedList.tsx (the related-list column builder forwarding returnType beside currencyConfig), ui/packages/plugin-detail/src/RecordDetailPanel.tsx (buildRecordDetailFields), tests beside, a changeset; ⛔ packages/spec/**, every server file, examples/**; PR carries Refs #27430 (not Fixes)
Container & model: S, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no (expected; the dev measures from the diff)
Metadata-protocol: none (expected; the dev measures from the diff)
Responsibility: the related-list cell and buildRecordDetailFields rebuilding a field without returnType | FormulaField / FormulaCellRenderer's currency branch and the spec's returnType (#22727) are fixed inputs | every reader of a returnType: 'currency' formula in a related list or a RecordDetailPanel, today
Thread-read: 6505010281
Premise: holds — measured by the #27494 dev and the seat (PR #27689 fixed the sibling sites; these two were deferred by name in the seat's status note on this card)
Serial constraints cleared: seat 1's PR #27636 (held RelatedList.tsx) merged 2026-10-29T20:03Z; the spec seat's draft PR #27713 also edits RelatedList.tsx (record-page open request), so whichever lands second rebases; no open PR touches RecordDetailPanel.tsx (seat read of every open PR's files); seat 1 has signed off
Dispatch order: /mnt/project-files/ui-seat2/dispatch-27430.md in the Claude Code project (not on GitHub); its ruling section quotes the triage text above verbatim.
{
"issue": 27430,
"status": "done",
"branch": "claude/project-thread-28figz",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27800",
"session": "session_01Utp6CYEbuRp8dS61F3RKwo (card thread; seat session_01Utp6CY2n1JmZZKrHgtp9jC)",
"premise_still_valid": true,
"summary": "Row 1 remainder only (PR carries Refs #27430, Refs #27494; rows 2-4 untouched). RelatedList makeCell bag and buildRecordDetailFields now forward returnType beside currencyConfig, so a returnType currency formula draws through the existing currency face ($90,000.00) and a returnType number formula keeps the bare grouped number. No second formatter, no spec change, no new key; buildRecordDetailFields signature/return type unchanged: Clause-2 no, Metadata-protocol none, as the claim expected. Claim 6510068741 names dispatch-27563.md as the order file; the order this thread received is dispatch-27430.md (text noted, no effect).",
"tests": "new ui/packages/plugin-detail/src/__tests__/formulaReturnTypeRelay-27430.test.tsx: Test Files 1 passed, Tests 4 passed. pnpm --filter @object-ui/plugin-detail test (pre-merge head): Test Files 338 passed | 1 skipped (339), Tests 3078 passed | 18 skipped (3096), VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs (anchor 1->0, restored blob == HEAD, git diff HEAD empty; vitest resolves plugin-detail from src, no dist leg): RelatedList line deleted -> 1 failed | 3 passed, cells [Tower, $150,000.00, 90,000.00, 1,234.50]; RecordDetailPanel line deleted -> 2 failed | 2 passed (expected undefined to be currency; panel lacks $90,000.00). At merged head 60638520: new file + currencyConfigRelay-12242 + formulaReturnTypeRelay-27494 exit 0.",
"gates": "dispatch-gates --commands: 34 families, run at pre-merge head and again at merged head 60638520 (derivation identical); all 34 :: exit 0 incl. node scripts/check-sdui-manifest.mjs :: exit 0 and --self-test :: exit 0, pnpm check:nul-bytes :: exit 0, pnpm check:type-check-debt :: exit 0. dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation: plugin-detail type-check :: exit 0 (both heads), lint :: exit 0, test :: exit 0. CI check-runs: in_progress at report time; seat reads them.",
"line_budget": "132 added / 0 removed, 4 files; under the 3000 human-merge threshold",
"files_changed": [
".changeset/27430-formula-currency-related-list.md",
"ui/packages/plugin-detail/src/RecordDetailPanel.tsx",
"ui/packages/plugin-detail/src/RelatedList.tsx",
"ui/packages/plugin-detail/src/__tests__/formulaReturnTypeRelay-27430.test.tsx"
],
"deviations": "none. Harness post-PR assign/review step skipped per the dispatch order (no assignee, no reviewer).",
"dogfood_battery": "n/a - no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 - relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no assignees, no labels) via repository_dispatch run 40974483602, body read back 5433/5433 bytes identical; this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/27430/comments). Plus git pushes of the branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"observation, not filed · the related list's page totals (BodyTableTotals) total only currency/number/integer/decimal columns, so a returnType currency formula column gets no total; unchanged by this PR · carrier: seat"
]
}
ACCEPT → PR #27800 comment 6511916566 (head 1aea8b8f): row 1's remainder (the related list and the quick-look panel forward a formula's returnType). Contract review not owed (Clause-②: no, Metadata-protocol: none). Landing through the merge queue. The PR says Refs, so this card stays open for rows 2–4.
merged through the queue at 2026-10-30T00:50Z, merge commit 359e935df26ddb067a9a60e2a7f81c5f80256bcb. It covers row 1's remainder: the related list and the quick-look panel now forward a formula's returnType.
row 1
done: the list grid and record page in PR #27689 (#27494), and the related list and quick-look panel in PR #27800
rows 2–4
still open. Seat 2 carried only row 1 and does not hold them.
State: seat 2 releases its claim. pm:dispatched → pm:queue, assignee cleared, so any ui seat can claim rows 2–4.
Claim: PM loop round 1
Session: session_01Utp6CaJoinvGQSbHeDaWBe
Account: os-bill
Branch: claude/project-thread-qtzjxg (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27430
Domain: domain:ui
Seat: domain:ui#3
File surface: the dashboard metric tile renderer (row 2), the percent cell face if row 3 is taken, tests beside, root .changeset/; i18n only if a sentence changes (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on 178b5238: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
Thread-read: 6512166362
Premise: holds
Serial constraints cleared: none (row 1 PRs #27689 and #27800 merged)
Scope of this claim: rows 2–4 only (row 1 is done by PR #27689 and PR #27800; seat 2 released the card). Rows that need a new metadata key, spec, or report-engine change are filed as their own cards by the seat. Seat 4's in-flight PR files (#27912, #27907, #27893, #27793) and seat 2's record page v4 PRs are off limits.
Dispatched 2026-10-30T05:07Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
{
"issue": 27430,
"status": "done",
"branch": "claude/project-thread-qtzjxg",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27972",
"session": "session_01Utp6CXAa3A4UiNf9JKBZ7R (card thread; seat session_01Utp6CaJoinvGQSbHeDaWBe)",
"premise_still_valid": true,
"summary": "Rows 2-4 re-measured on main ed0e71db. Row 2 TAKEN: a dataset-bound metric tile (DatasetWidget given a heading, on DashboardRenderer and DashboardGridLayout) now captions its value with the widget description when authored, else the measure label; the measure label keeps riding in the title tooltip. description is the existing widget-level DashboardWidgetSchema key, already resolved and handed in through heading, so no new key, no spec change: Clause-2 no, Metadata-protocol none. This deliberately reverses the 'description drawn nowhere else' pins of objectui#12528 and objectui#12573 (both rewritten, density unchanged: the caption takes the measure label's slot). Rows 3 and 4 are CARD (findings below); PR says Part of #27430, the seat flips it to Fixes once both cards are filed.",
"tests": "new ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx (10 tests, both surfaces) plus rewritten pins in DashboardRenderer.metricTileDensity-12528 and DashboardSurfaces.metricTileTwoLines-12573: the four metric-tile files (with metricTileLayout-12623) Test Files 4 passed, Tests 31 passed. pnpm --filter @object-ui/plugin-dashboard test at 6c8d7e20: Test Files 198 passed (198), Tests 1908 passed | 6 skipped, lock VERDICT command-exit 0. type-check exit 0; eslint on the six touched files exit 0. Ablation via scripts/ablation-replace.mjs at 6c8d7e20 (anchor 1 -> 0; vitest resolves plugin-dashboard from src, no dist leg): caption set back to the measure label -> Test Files 2 failed, Tests 5 failed | 8 passed (described + per-locale pins on both surfaces, the 12528 description pin; fallback and control pins stay green); restore blob == HEAD, git diff HEAD empty.",
"gates": "dispatch-gates --commands at 6c8d7e20: 34 families, all 34 :: exit 0, including node scripts/check-sdui-manifest.mjs :: exit 0 and --self-test :: exit 0, pnpm check:nul-bytes :: exit 0, pnpm check:type-check-debt :: exit 0, pnpm --dir ui exec vitest run scripts/ :: exit 0 (169 files passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation: ui pnpm check:new-line-citations: 0 new citations, exit 0. CI check-runs: not yet reported at report time; the seat reads them.",
"line_budget": "213 added / 31 removed, 7 files (git diff --shortstat origin/main...HEAD); under the 3000 human-merge threshold",
"files_changed": [
".changeset/27430-metric-tile-description-caption.md",
"ui/packages/plugin-dashboard/src/DashboardRenderer.tsx",
"ui/packages/plugin-dashboard/src/DatasetWidget.tsx",
"ui/packages/plugin-dashboard/src/metricTile.ts",
"ui/packages/plugin-dashboard/src/__tests__/DashboardRenderer.metricTileDensity-12528.test.tsx",
"ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx",
"ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileTwoLines-12573.test.tsx"
],
"deviations": "PR body lines 1-2: the order's attribution block names the seat thread link; the harness session context binds this card thread's link to the same marker line, and it outranks the order, so the PR carries the card thread's link. Harness post-PR assign/review step skipped per the order (no assignee, no reviewer). Harness commit-trailer reminder names a model; the commit carries the model-free pair AGENTS.md requires.",
"dogfood_battery": "n/a - no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 - relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no assignees, no labels) via repository_dispatch run 41010019230, body read back 6676/6676 bytes identical; this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/27430/comments). Plus a git push of the branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"CARD row 3 · class: a (user-visible: a rate or probability reads as progress) · reach: named producer, HotCRM crm_opportunity.probability and campaign rate fields on 18.2.0 (#27430 row 3) · title: 'percent field: a display key that shows a plain percentage instead of the progress bar, in list cells and on the record page' · body: PercentCellRenderer (ui/packages/fields) draws a progress bar for every percent value, with no key to turn it off; it is the face list-grid cells and the record page's percent fields (DetailView, HeaderHighlight chips) share. Nothing existing selects a plain face: Field.format has no declared vocabulary and only promotes textual types, Field.widget is the form override, and ListColumn.type is a renderer override whose number face would print the stored fraction (0.25) rather than 25%. Needs a declared display key on the field (and optionally the list column) read by PercentCellRenderer, with the bar as the default. Seam: spec:Field (new key, e.g. a percent display mode) -> renderer:PercentCellRenderer · landing: packages/spec (data/field.zod.ts) + ui/packages/fields PercentCellRenderer · lane: spec first (contract), then ui · Clause-2 yes (widening), Metadata-protocol added · note: fields/src/index.tsx is held by seat 4's PR #27912 today · dedupe words: percent progress bar, percent display, plain percentage, probability bar, PercentCellRenderer",
"CARD row 4a · class: a (missing capability with a named producer) · reach: named producer, HotCRM opportunity_funnel_owner_stage (hotcrm#2379 turned it into an owner x stage matrix as the workaround) · title: 'report chart: split a series by a second dataset dimension' · body: ReportChartSchema (packages/spec ui/report.zod.ts) extends ChartConfig with one xAxis dimension and one yAxis measure, and DatasetReportRenderer queries exactly chart.xAxis x chart.yAxis, so a report cannot draw a bar per owner split by stage. The dashboard dataset path already pivots a second dimension into grouped series (buildChartSeries in ui/packages/core), so the renderer half can reuse it. Needs a declared series dimension key on ReportChartSchema (e.g. a series or groupBy dimension name validated against the report's dataset), the report query widened to it, and the renderer pivot. Seam: spec:ReportChartSchema (new key) -> renderer:DatasetReportRenderer chart query · landing: packages/spec + ui/packages/plugin-report · lane: spec then ui · Metadata-protocol added · dedupe words: report chart series, second dimension, groupBy series, stacked bar report, ReportChartSchema",
"CARD row 4b · class: a (user-visible: a two-level summary repeats the outer value and has no group subtotal) · reach: named producer, HotCRM opportunity_funnel_owner_stage as a summary with two rows levels (#27430 row 4) · title: 'summary report with two row levels: per-group subtotals and no repeated outer value' · body: a summary report asks the server only for the grand total (totals.groupings with the empty grouping) and draws every inner row with its outer value repeated; the spec has no subtotal key. The matrix branch already asks for row and column subtotals through totals.groupings and only places the server's pre-aggregated rows, so the server half exists. Decide whether subtotals are the summary default for two or more rows levels or a declared key (e.g. a subtotal flag on the report); then the renderer asks for each rows prefix grouping and draws group header and subtotal rows. Seam: spec:ReportSchema (subtotal key, if declared) -> renderer:DatasetReportRenderer summary branch · landing: ui/packages/plugin-report, plus packages/spec if a key is declared · lane: ui (spec if keyed) · dedupe words: summary subtotal, group subtotal, report subtotals, multi-level summary, repeated group value",
"observation, not filed · the DashboardWidgetOptionsSchema doc comment (packages/spec ui/dashboard.zod.ts) says widget.description is rendered as the card-header subtitle; a dataset-bound metric tile has no card header and now draws it as the caption under the value · carrier: seat"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Row 1 status · 2026-10-29T18:26Z · ⛔ not a claim on rows 2–4
Row 1 (a
returnType: 'currency'formula reads as a bare number) is taken by #27494 (triage 6495354739 folded it here), PR #27689:plugin-gridobjectGridGenerateColumns.tsx, three configured-column paths) and the record page's fields (plugin-detailfieldEnrichment.tsENRICHED_FIELD_METADATA_KEYS). The cause was not the formula face, which already drawscurrency. These hosts rebuilt the field from a picked key list withoutreturnType.returnTypebesidecurrencyConfig):plugin-detail/src/RelatedList.tsxmakeCell. This is held until seat 1's open PR perf(plugin-detail,app-shell): the record page's related column loads counts first, then rows only for the cards in view, and never blocks the details #27636 (console record page: the related column loads counts first, then the first three rows of only the visible cards, and never blocks the details — one batched request, viewport-lazy cards, a measured budget #27056) lands, because that PR rewrites the file.RecordDetailPanelbuildRecordDetailFields. The List grid: a formula field with returnType currency renders as a bare number ("90,000.00") beside a currency field ("$150,000.00") in the showcase Projects list #27494 dev found it.Rows 2–4 are untouched. Each still needs its own card or a "will not do" answer, per this issue's Acceptance.
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Claim: PM loop round 2 · row 1 remainder only (⛔ rows 2–4 stay open and unclaimed) · 2026-10-29T22:36Z
Session:
session_01Utp6CY2n1JmZZKrHgtp9jC(seat; implementation by a card thread,mode:thread)Account:
os-billBranch:
claude/project-thread-28figz(the card thread's harness push branch, self-reported — the measured platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-27430Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/plugin-detail/src/RelatedList.tsx(the related-list column builder forwardingreturnTypebesidecurrencyConfig),ui/packages/plugin-detail/src/RecordDetailPanel.tsx(buildRecordDetailFields), tests beside, a changeset; ⛔packages/spec/**, every server file,examples/**; PR carriesRefs #27430(notFixes)Container & model:
S,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no (expected; the dev measures from the diff)
Metadata-protocol: none (expected; the dev measures from the diff)
Responsibility: the related-list cell and
buildRecordDetailFieldsrebuilding a field withoutreturnType|FormulaField/FormulaCellRenderer's currency branch and the spec'sreturnType(#22727) are fixed inputs | every reader of areturnType: 'currency'formula in a related list or a RecordDetailPanel, todayThread-read: 6505010281
Premise: holds — measured by the #27494 dev and the seat (PR #27689 fixed the sibling sites; these two were deferred by name in the seat's status note on this card)
Serial constraints cleared: seat 1's PR #27636 (held
RelatedList.tsx) merged 2026-10-29T20:03Z; the spec seat's draft PR #27713 also editsRelatedList.tsx(record-page open request), so whichever lands second rebases; no open PR touchesRecordDetailPanel.tsx(seat read of every open PR's files); seat 1 has signed offDispatch order:
/mnt/project-files/ui-seat2/dispatch-27430.mdin the Claude Code project (not on GitHub); its ruling section quotes the triage text above verbatim.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
os-dev-report
{ "issue": 27430, "status": "done", "branch": "claude/project-thread-28figz", "pr": "https://github.com/objectstack-ai/objectstack/pull/27800", "session": "session_01Utp6CYEbuRp8dS61F3RKwo (card thread; seat session_01Utp6CY2n1JmZZKrHgtp9jC)", "premise_still_valid": true, "summary": "Row 1 remainder only (PR carries Refs #27430, Refs #27494; rows 2-4 untouched). RelatedList makeCell bag and buildRecordDetailFields now forward returnType beside currencyConfig, so a returnType currency formula draws through the existing currency face ($90,000.00) and a returnType number formula keeps the bare grouped number. No second formatter, no spec change, no new key; buildRecordDetailFields signature/return type unchanged: Clause-2 no, Metadata-protocol none, as the claim expected. Claim 6510068741 names dispatch-27563.md as the order file; the order this thread received is dispatch-27430.md (text noted, no effect).", "tests": "new ui/packages/plugin-detail/src/__tests__/formulaReturnTypeRelay-27430.test.tsx: Test Files 1 passed, Tests 4 passed. pnpm --filter @object-ui/plugin-detail test (pre-merge head): Test Files 338 passed | 1 skipped (339), Tests 3078 passed | 18 skipped (3096), VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs (anchor 1->0, restored blob == HEAD, git diff HEAD empty; vitest resolves plugin-detail from src, no dist leg): RelatedList line deleted -> 1 failed | 3 passed, cells [Tower, $150,000.00, 90,000.00, 1,234.50]; RecordDetailPanel line deleted -> 2 failed | 2 passed (expected undefined to be currency; panel lacks $90,000.00). At merged head 60638520: new file + currencyConfigRelay-12242 + formulaReturnTypeRelay-27494 exit 0.", "gates": "dispatch-gates --commands: 34 families, run at pre-merge head and again at merged head 60638520 (derivation identical); all 34 :: exit 0 incl. node scripts/check-sdui-manifest.mjs :: exit 0 and --self-test :: exit 0, pnpm check:nul-bytes :: exit 0, pnpm check:type-check-debt :: exit 0. dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation: plugin-detail type-check :: exit 0 (both heads), lint :: exit 0, test :: exit 0. CI check-runs: in_progress at report time; seat reads them.", "line_budget": "132 added / 0 removed, 4 files; under the 3000 human-merge threshold", "files_changed": [ ".changeset/27430-formula-currency-related-list.md", "ui/packages/plugin-detail/src/RecordDetailPanel.tsx", "ui/packages/plugin-detail/src/RelatedList.tsx", "ui/packages/plugin-detail/src/__tests__/formulaReturnTypeRelay-27430.test.tsx" ], "deviations": "none. Harness post-PR assign/review step skipped per the dispatch order (no assignee, no reviewer).", "dogfood_battery": "n/a - no ! in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 - relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no assignees, no labels) via repository_dispatch run 40974483602, body read back 5433/5433 bytes identical; this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/27430/comments). Plus git pushes of the branch (not REST).", "open_questions": [], "out_of_scope_findings": [ "observation, not filed · the related list's page totals (BodyTableTotals) total only currency/number/integer/decimal columns, so a returnType currency formula column gets no total; unchanged by this PR · carrier: seat" ] }objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
ACCEPT → PR #27800 comment 6511916566 (head
1aea8b8f): row 1's remainder (the related list and the quick-look panel forward a formula'sreturnType). Contract review not owed (Clause-②: no,Metadata-protocol: none). Landing through the merge queue. The PR saysRefs, so this card stays open for rows 2–4.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Landed (row 1) · 2026-10-30T00:51Z
359e935df26ddb067a9a60e2a7f81c5f80256bcb. It covers row 1's remainder: the related list and the quick-look panel now forward a formula'sreturnType.State: seat 2 releases its claim.
pm:dispatched→pm:queue, assignee cleared, so any ui seat can claim rows 2–4.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
Claim: PM loop round 1
Session:
session_01Utp6CaJoinvGQSbHeDaWBeAccount:
os-billBranch:
claude/project-thread-qtzjxg(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27430Domain:
domain:uiSeat:
domain:ui#3File surface: the dashboard
metrictile renderer (row 2), the percent cell face if row 3 is taken, tests beside, root.changeset/; i18n only if a sentence changes (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tieron178b5238: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
Thread-read: 6512166362
Premise: holds
Serial constraints cleared: none (row 1 PRs #27689 and #27800 merged)
Scope of this claim: rows 2–4 only (row 1 is done by PR #27689 and PR #27800; seat 2 released the card). Rows that need a new metadata key, spec, or report-engine change are filed as their own cards by the seat. Seat 4's in-flight PR files (#27912, #27907, #27893, #27793) and seat 2's record page v4 PRs are off limits.
Dispatched 2026-10-30T05:07Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#3 · os-bill · session_01Utp6CXAa3A4UiNf9JKBZ7R
os-dev-report
{ "issue": 27430, "status": "done", "branch": "claude/project-thread-qtzjxg", "pr": "https://github.com/objectstack-ai/objectstack/pull/27972", "session": "session_01Utp6CXAa3A4UiNf9JKBZ7R (card thread; seat session_01Utp6CaJoinvGQSbHeDaWBe)", "premise_still_valid": true, "summary": "Rows 2-4 re-measured on main ed0e71db. Row 2 TAKEN: a dataset-bound metric tile (DatasetWidget given a heading, on DashboardRenderer and DashboardGridLayout) now captions its value with the widget description when authored, else the measure label; the measure label keeps riding in the title tooltip. description is the existing widget-level DashboardWidgetSchema key, already resolved and handed in through heading, so no new key, no spec change: Clause-2 no, Metadata-protocol none. This deliberately reverses the 'description drawn nowhere else' pins of objectui#12528 and objectui#12573 (both rewritten, density unchanged: the caption takes the measure label's slot). Rows 3 and 4 are CARD (findings below); PR says Part of #27430, the seat flips it to Fixes once both cards are filed.", "tests": "new ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx (10 tests, both surfaces) plus rewritten pins in DashboardRenderer.metricTileDensity-12528 and DashboardSurfaces.metricTileTwoLines-12573: the four metric-tile files (with metricTileLayout-12623) Test Files 4 passed, Tests 31 passed. pnpm --filter @object-ui/plugin-dashboard test at 6c8d7e20: Test Files 198 passed (198), Tests 1908 passed | 6 skipped, lock VERDICT command-exit 0. type-check exit 0; eslint on the six touched files exit 0. Ablation via scripts/ablation-replace.mjs at 6c8d7e20 (anchor 1 -> 0; vitest resolves plugin-dashboard from src, no dist leg): caption set back to the measure label -> Test Files 2 failed, Tests 5 failed | 8 passed (described + per-locale pins on both surfaces, the 12528 description pin; fallback and control pins stay green); restore blob == HEAD, git diff HEAD empty.", "gates": "dispatch-gates --commands at 6c8d7e20: 34 families, all 34 :: exit 0, including node scripts/check-sdui-manifest.mjs :: exit 0 and --self-test :: exit 0, pnpm check:nul-bytes :: exit 0, pnpm check:type-check-debt :: exit 0, pnpm --dir ui exec vitest run scripts/ :: exit 0 (169 files passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation: ui pnpm check:new-line-citations: 0 new citations, exit 0. CI check-runs: not yet reported at report time; the seat reads them.", "line_budget": "213 added / 31 removed, 7 files (git diff --shortstat origin/main...HEAD); under the 3000 human-merge threshold", "files_changed": [ ".changeset/27430-metric-tile-description-caption.md", "ui/packages/plugin-dashboard/src/DashboardRenderer.tsx", "ui/packages/plugin-dashboard/src/DatasetWidget.tsx", "ui/packages/plugin-dashboard/src/metricTile.ts", "ui/packages/plugin-dashboard/src/__tests__/DashboardRenderer.metricTileDensity-12528.test.tsx", "ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx", "ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileTwoLines-12573.test.tsx" ], "deviations": "PR body lines 1-2: the order's attribution block names the seat thread link; the harness session context binds this card thread's link to the same marker line, and it outranks the order, so the PR carries the card thread's link. Harness post-PR assign/review step skipped per the order (no assignee, no reviewer). Harness commit-trailer reminder names a model; the commit carries the model-free pair AGENTS.md requires.", "dogfood_battery": "n/a - no ! in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 - relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no assignees, no labels) via repository_dispatch run 41010019230, body read back 6676/6676 bytes identical; this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/27430/comments). Plus a git push of the branch (not REST).", "open_questions": [], "out_of_scope_findings": [ "CARD row 3 · class: a (user-visible: a rate or probability reads as progress) · reach: named producer, HotCRM crm_opportunity.probability and campaign rate fields on 18.2.0 (#27430 row 3) · title: 'percent field: a display key that shows a plain percentage instead of the progress bar, in list cells and on the record page' · body: PercentCellRenderer (ui/packages/fields) draws a progress bar for every percent value, with no key to turn it off; it is the face list-grid cells and the record page's percent fields (DetailView, HeaderHighlight chips) share. Nothing existing selects a plain face: Field.format has no declared vocabulary and only promotes textual types, Field.widget is the form override, and ListColumn.type is a renderer override whose number face would print the stored fraction (0.25) rather than 25%. Needs a declared display key on the field (and optionally the list column) read by PercentCellRenderer, with the bar as the default. Seam: spec:Field (new key, e.g. a percent display mode) -> renderer:PercentCellRenderer · landing: packages/spec (data/field.zod.ts) + ui/packages/fields PercentCellRenderer · lane: spec first (contract), then ui · Clause-2 yes (widening), Metadata-protocol added · note: fields/src/index.tsx is held by seat 4's PR #27912 today · dedupe words: percent progress bar, percent display, plain percentage, probability bar, PercentCellRenderer", "CARD row 4a · class: a (missing capability with a named producer) · reach: named producer, HotCRM opportunity_funnel_owner_stage (hotcrm#2379 turned it into an owner x stage matrix as the workaround) · title: 'report chart: split a series by a second dataset dimension' · body: ReportChartSchema (packages/spec ui/report.zod.ts) extends ChartConfig with one xAxis dimension and one yAxis measure, and DatasetReportRenderer queries exactly chart.xAxis x chart.yAxis, so a report cannot draw a bar per owner split by stage. The dashboard dataset path already pivots a second dimension into grouped series (buildChartSeries in ui/packages/core), so the renderer half can reuse it. Needs a declared series dimension key on ReportChartSchema (e.g. a series or groupBy dimension name validated against the report's dataset), the report query widened to it, and the renderer pivot. Seam: spec:ReportChartSchema (new key) -> renderer:DatasetReportRenderer chart query · landing: packages/spec + ui/packages/plugin-report · lane: spec then ui · Metadata-protocol added · dedupe words: report chart series, second dimension, groupBy series, stacked bar report, ReportChartSchema", "CARD row 4b · class: a (user-visible: a two-level summary repeats the outer value and has no group subtotal) · reach: named producer, HotCRM opportunity_funnel_owner_stage as a summary with two rows levels (#27430 row 4) · title: 'summary report with two row levels: per-group subtotals and no repeated outer value' · body: a summary report asks the server only for the grand total (totals.groupings with the empty grouping) and draws every inner row with its outer value repeated; the spec has no subtotal key. The matrix branch already asks for row and column subtotals through totals.groupings and only places the server's pre-aggregated rows, so the server half exists. Decide whether subtotals are the summary default for two or more rows levels or a declared key (e.g. a subtotal flag on the report); then the renderer asks for each rows prefix grouping and draws group header and subtotal rows. Seam: spec:ReportSchema (subtotal key, if declared) -> renderer:DatasetReportRenderer summary branch · landing: ui/packages/plugin-report, plus packages/spec if a key is declared · lane: ui (spec if keyed) · dedupe words: summary subtotal, group subtotal, report subtotals, multi-level summary, repeated group value", "observation, not filed · the DashboardWidgetOptionsSchema doc comment (packages/spec ui/dashboard.zod.ts) says widget.description is rendered as the card-header subtitle; a dataset-bound metric tile has no card header and now draws it as the caption under the value · carrier: seat" ] }objectstack-fleet commentedon Oct 30, 2026
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
ACCEPT → PR #27972 comment 6516214847 · 2026-10-30T06:07Z
Generated by Claude Code