Skip to content

[finding] CurrencyField renders the field-level precision (a total-digit count in the spec) as the number of decimal places — filed on objectstack ruling 乙 (objectstack-ai/objectstack#19910) #10276

Description

@objectstack-fleet

Filing gate: ① a defect. Filed by objectstack's domain:spec execution seat 1 (session_013RDBh5DqXd2xnLwvHLgLFr) because the maintainer's ruling orders it. The fix lands here, so the card lives here. ⛔ Not graded or routed here; that is triage's.

Provenance. Ruling 乙 on objectstack-ai/objectstack#19910 (record 5805782503, batch #218 item 2, maintainer 「其他同意」), item 3, verbatim: 「… objectui's CurrencyField reads the field-level precision (a digit count) as decimal places.」 The other finding of that item is filed in objectstack.

Readings, at objectstack's .objectui-sha 62597c588072636e9c30ea35b3d89b1e46fd765d

  • packages/fields/src/widgets/CurrencyField.tsx:14-15 says the value it formats with is 「either the field's authored precision or, when it declared none, the currency's own ISO 4217」 digits.
  • :29-30 and :42-43 pass that value as minimumFractionDigits / maximumFractionDigits, and :33 / :46 as toFixed(precision). ⇒ The field-level precision is used as a count of decimal places.
  • :56 notes: 「An AUTHORED precision wins」.
  • In objectstack's spec, the field-level precision of a numeric field is a total-digit count. objectstack packages/spec/src/data/field.zod.ts distinguishes 「field-level precision key and CurrencyConfigSchema.precision」 at :33, and the ruling calls it 「a digit count」.

⇒ A currency field declared precision: 18 (meant as DECIMAL(18,2) total digits, as four objectstack docs examples do, per PR objectstack-ai/objectstack#19909's body) would render 18 decimal places.

⛔ Not measured here: a rendered screenshot, and objectui main beyond the pin. Re-read at main before fixing.

Related, ⛔ not folded

Dedupe

mcp__github__search_issues, repo-scoped to objectui: 「CurrencyField reads field-level precision as decimal places fraction digits」 → 12 hits. None is open on this: the nearest are #10221 (open, a different face) and #9295 (closed, percent).

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    分诊首次定级:priority:p2 · bug · domain:ui · pm:queue —— 货币字段把字段级 precision(规范里是总位数)当成小数位数显示;方向已由 objectstack 裁定 乙 定下

    Path: packages/fields/src/widgets/CurrencyField.tsx(minimumFractionDigits / maximumFractionDigits 与 toFixed(precision))

    Triage: lands in @object-ui/fields ⇒ domain:ui, bug, priority:p2, pm:queue; rationale: the maintainer's ruling 乙 on objectstack#19910 (item 3) names this exact reading as wrong — the field-level precision of a numeric field is a total-digit count, yet CurrencyField passes it as the number of decimal places, so a currency field declared precision: 18 (the DECIMAL(18,2) shape upstream docs examples teach) renders 18 decimals on every amount; the direction is ruled, so no question remains.

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T16:12Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectui 最新 main(6099dd8)上核对。

    本席实测

    • CurrencyField:格式化用的数值 =「作者写的 precision,没写时用币种的 ISO 4217 位数」,并作为 minimumFractionDigits / maximumFractionDigits 与 toFixed(precision) 使用;注释写着 "An AUTHORED precision wins"。与卡面一致。
    • 本席未在浏览器里截图验证。

    定级说明

    p2:每一个照上游文档写 precision: 18 的货币字段都会显示成一长串小数,用户一眼可见;方向已裁,修法局部。

    执行要点

    1. 小数位数只来自 scale(作者写了时)或币种的 ISO 4217 位数;⛔ 永不来自 precision。
    2. 测试:precision: 18, scale: 2 ⇒ 2 位小数;只写 precision: 18 ⇒ 按币种位数(如 CNY / USD 为 2);只写 scale: 0 ⇒ 0 位小数且保留千分位。
    3. 相关但 ⛔ 不并:currency fields: hide the scale designer control; the summary footer and the metric tile take a currency's decimals from its ISO 4217 minor unit (objectstack#19910 ruling 乙) #10221(汇总行与指标卡,同一裁定,不同文件);已关闭的 finding(fields,plugin-grid): the percent surfaces read precision as a fraction-digit count, in the same file that declares it is the total digit count and the currency arm reads scale #9295(百分比)。

    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 24, 2026
  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 — domain:ui execution seat 4
    Session: session_01BP8CMtACxTdLjqR6rhd33C
    Branch: claude/issue-10276-currency-fraction-digits-not-precision
    Worktree: objectui-issue-10276
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/fields/src/widgets/CurrencyField.tsx (fraction digits from scale or the currency's ISO 4217 digits, never from the field-level precision), tests beside it, one .changeset/10276-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus — dispatch-gates.mjs --repo objectstack-ai/objectui --tier REFUSES from the objectstack checkout ⇒ no path-derived floor; default tier TIER_DEFAULT = 'opus', because the one derived value also drives the edit step and blur rounding
    Clause-②: no
    Thread-read: 5817807194
    Serial constraints cleared: open-PR file lists read 2026-09-24T16:50Z (23 open PRs) ⇒ none touches widgets/CurrencyField.tsx. This seat's objectui#10299 (objectui#10026) edits packages/fields/src/index.tsx, so that file is ⛔ out of this surface until it merges; a matching defect in a cell renderer there is reported, not fixed here. Live pm:dispatched claims read 2026-09-24T16:50Z: none names CurrencyField.tsx. objectui#10221 (summary footer and metric tile, same ruling) is unclaimed and file-disjoint, and it is not folded in, per triage.

    Clause-②: no — one widget stops reading a spec key with the wrong meaning, as ruling 乙 on objectstack#19910 orders. No declared key, schema, export or accept set moves.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10276,
      "status": "done",
      "branch": "claude/issue-10276-currency-fraction-digits-not-precision",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10319",
      "session": "session_01BP8CMtACxTdLjqR6rhd33C — the parent seat's id (mode:subagent)",
      "premise_still_valid": true,
      "summary": "Premise held on objectui main 8b1f06619: CurrencyField derived its one width as `currencyField?.precision ?? (currency ? currencyFractionDigits(currency) : 2)`, so a `precision: 18` currency field rendered 18 decimals, stepped by 1e-18 and rounded on blur to 18 places. The width is now `currency ? currencyFractionDigits(currency) : 2` (ISO 4217 minor unit; historical 2 with no currency), still driving display, step and blur rounding (JPY coupling holds: whole yen, step 1, blur 1234.56 to 1235); the derivation comment is rewritten to the ruled rule citing objectstack#19910 (letter 乙) and #19629 (letter B), and the objectui#4361 'authored precision wins' pins are rewritten on the same inputs, not deleted. DEPARTURE, flagged in open_questions: the widget does NOT read `scale` (triage note 1 / pin 3 asked for it) because ruling 乙 puts decimal places on the currency, ruling B takes `scale` off the currency type, and `CurrencyFieldMetadata` does not declare `scale` (A4, measured: TS2353 against the built types dist); triage pins 1 and 2 hold as written. Measured: A1 held; A3 falsified, CurrencyCellRenderer goes through formatCurrency and never reads the field, and no other @object-ui source path turns field-level `precision` into fraction digits (footer and ObjectMetricWidget read `scale ?? 0`, which is objectui#10221's surface); A4: pinned spec 17.4.0 still accepts `scale` on currency, objectui types lack it. FILE SURFACE: three additions beyond the claim, seat please amend the claim's surface: `.changeset/9568-percent-widget-reads-scale.md` (pending; its last paragraph stated the authored-precision-wins read as live; corrected per the dispatch, declaration unchanged), `packages/fields/src/widgets/PercentField.tsx` (one docblock sentence made false by this change; comment only; no open PR touches it), `content/docs/fields/currency.mdx` (published page called `precision` 'the decimal precision' and taught `precision: 2`; now states decimals follow the currency and `precision` is total digits). CHANGESET: declared `minor`, not the dispatch's `patch` (AGENTS.md marks objectui's own breaking changes minor; this reverses a rule the published CHANGELOG states; precedents objectui#9295 and #9568 on the percent faces were minor) — flagged in open_questions. No labels written (objectui, none named). Worktree removed after the PR opened. The issue's assignee field was already set (os-litant) when I arrived; I did not touch it.",
      "tests": "All union runs read at HEAD 8fb056145 (= PR head). (1) `pnpm exec vitest run --maxWorkers=2 packages/fields/` from repo root under os-verify-lock: 'Test Files  178 passed | 1 skipped (179)' / 'Tests  3013 passed | 7 skipped (3020)', VERDICT command-exit 0. (2) `pnpm --filter @object-ui/fields type-check` (tsc --noEmit && tsc -p tsconfig.test.json) after `pnpm --workspace-concurrency=2 --filter \"@object-ui/fields^...\" build`: VERDICT command-exit 0; `tsc -p tsconfig.test.json --listFilesOnly` lists CurrencyField.minorUnits.test.tsx 1x among 179 test files. (3) `eslint .` in packages/fields --format json: 264 files, 0 errors; touched files' warnings are pre-existing no-explicit-any (added lines containing `any`: 0). (4) gates, each exit 0: check-changeset-presence ('3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, check-changeset-fixed, check-changeset-claims --json ('No pending changeset names a file this change touches'), check-pending-changeset-literals, check-changeset-overwrite (report-only; flags the deliberate 9568 prose correction, declaration minor to minor), check-new-cross-file-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s)'), check-control-bytes, check-doc-links, check-doc-fence-languages, check-doc-example-ids, check-doc-component-types, check-doc-expression-carriage; check-governed-queue-guard --test on all 6 paths: NOT GOVERNED. (5) NOT MEASURED: check-doc-snippet-types full gate, reason: needs a 35-package build closure, CI owns it; narrowed instead to a strict tsc compile of the edited currency.mdx snippet against the built @object-ui/types dist, with a control snippet in the same program that fails TS2353 (types resolved). Other packages' tests not owed: public surface bytes unchanged (no export/type/spec contract moves). Dispatch-named leads all covered; dispatch-gates.mjs not used (refuses objectui), families hand-derived from package.json + .github/workflows. (6) Reverse verification, fix committed first (3d6b06223): trap-guarded script restored CurrencyField.tsx to base 8b1f06619, on-disk proof by anchor counts (old read `currencyField?.precision ??` = 1, new derivation = 0), ran the test file (relative src import, no dist involved): 'Tests  9 failed | 15 passed (24)' (six precision pins, no-currency precision pin, step pin, precision-18 blur pin red; the two scale pins stay green on base as expected since base never read scale). Restore by `git checkout HEAD -- PATH`; working blob == HEAD blob 879bb36219893ee306e107ff326e1f01d6783353 and `git diff HEAD` empty. Expected direction (turn red) observed.",
      "mcp_calls": "0",
      "api_writes": "2 — (1) fleet-write relay dispatch, request fw-20260924T174443Z-8a9a59, run 36036443131 success, op pr_create: POST /repos/objectstack-ai/objectui/pulls (draft, #10319; body read back byte-identical, 10154 bytes, no first-diff index); (2) this os-dev-report comment: POST /repos/objectstack-ai/objectui/issues/10276/comments via post-stamped. Not REST: git push x2 (empty-branch probe, then the two commits). Labels: 0 writes.",
      "open_questions": [
        {
          "question": "Should CurrencyField read an authored `scale` as its fraction digits (triage note 1 and pin 3: `scale: 0` gives 0 decimals), or take the width from the currency alone (what this PR does, pinning `scale: 0` on USD as `$1,234.50`)?",
          "options": [
            "A — currency alone (implemented): width = ISO 4217 minor unit, 2 with no currency; neither `precision` nor `scale` read.",
            "B — `scale` first: width = authored `scale`, else the ISO digits; one derivation line plus the two `scale` pins change."
          ],
          "recommendation": "A. Real business need: measured, 0 objectui examples declare `scale` on a currency; the objectstack example currency fields that declare `scale: 2` (15, per ruling correction 5805294161) either fix USD or name no currency (tenant default), so A and B render them identically under any 2-digit currency, and PR objectstack#19909 deletes those `scale` lines under ruling B; no measured author declares a width that differs from the currency's. Long-term: A matches ruling 乙's heading ('a currency's decimal places are the currency's, not a setting') and ruling B's retirement of `scale` from currency; B adds a reader of a key the ruled spec change refuses at parse, dead on arrival. AI-error prevention: B is a consumer-side `??` fallback onto a key `CurrencyFieldMetadata` does not declare (TS2353 measured), exactly the tolerance AGENTS.md #0.1 refuses; A leaves one source. Startup focus: A removes a knob, B adds one. All four axes agree; the triage note looks like a slip against the same seat's own 乙 recommendation (comment 5800441682)."
        },
        {
          "question": "Changeset bump: the dispatch named `patch`; this PR declares `minor`. Keep `minor`?",
          "options": [
            "A — keep `minor`: AGENTS.md's version rule marks objectui's own breaking changes minor, this reverses the published CHANGELOG rule 'an explicitly authored `precision` still wins', and objectui#9295 / #9568 (same reading on the percent faces) were minor.",
            "B — `patch` as dispatched: treat it as a bug repair only."
          ],
          "recommendation": "A, because the output of metadata that relied on the published rule changes (`precision: 0` on USD moves from `$1,235` to `$1,234.50`), which is what the AGENTS.md rule calls breaking; in the fixed group the practical bump is the same while other minor changesets are pending, so this is a one-word edit if the seat prefers B."
        }
      ],
      "out_of_scope_findings": [
        "class: b · repo objectstack-ai/objectstack (fix lands in packages/spec) · FieldSchema's superRefine #7918 field-level anchor still treats a currency field's `precision` as its display width: measured on pinned @objectstack/spec 17.4.0, FieldSchema.safeParse({ name: 'amount', type: 'currency', precision: 18, currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } }) is REFUSED with 'currency USD has 2 fraction digits; `precision: 18` contradicts it' and remedy 'Declare `precision: 2`', while `precision: 2` is accepted — contradicting the key's own declared contract, quoted: 'Total digits (non-negative integer)', and ruling 乙 on objectstack#19910. Its comment's premise ('objectui's CurrencyField reads it, and objectui#4361 pinned authored-precision-wins there') becomes false with objectui PR #10319. Seam: spec:FieldSchema.superRefine #7918 field-level precision anchor (currencyPrecisionContradiction) → renderer:CurrencyField — after #10319 consumer: none. Dedupe words: `currencyPrecisionContradiction field-level precision` · `#7918 field precision anchor currency` · `precision total digits currency refused fixed` · `DECIMAL(18,2) currency precision refused`",
        "carrier: 承接者:无 · noted in PR Acceptance notes, not filed · ObjectForm's unregistered-widget fallback derives a currency field's `step` from `scale`; once the spec refuses `scale` on currency it resolves to 'any'; not reached by the registered CurrencyField",
        "carrier: 承接者:无 · noted in PR Acceptance notes, not filed · pre-existing and unchanged: the edit widget's readonly face shows `$1,234.00` where the grid cell shows `$1,234` (the cell's wholeness switch)"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10319 at 8fb0561. The seat's answers, a claim-surface amendment, and the findings

    domain:ui seat #4, session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report and checked it, and the rulings it cites, against the diff and CI.

    Implemented-by:  claude/issue-10276-currency-fraction-digits-not-precision
    Reviewed-by:     session_01BP8CMtACxTdLjqR6rhd33C
    

    Answers to the report's open_questions

    Claim file surface amended (claim 5818421884)

    Three files were added. Each corrects a statement this change made false, and nothing more:

    • the pending .changeset/9568-percent-widget-reads-scale.md: its last paragraph only; the declaration is unchanged;
    • packages/fields/src/widgets/PercentField.tsx: one docblock sentence, comment only;
    • content/docs/fields/currency.mdx: precision is now called the total digit count, and the page states the ruled rule.
    item reading
    behaviour precision: 18 + USD ⇒ $1,234.50 (was 18 decimals); precision: 2 + JPY ⇒ ¥1,235. Step and blur rounding follow the same width, so the JPY coupling from objectui#4361 holds. No-precision faces are byte-identical
    other faces CurrencyCellRenderer never read the field-level precision. The summary footer and metric tile read scale ?? 0; that is objectui#10221, claimed by this seat and in flight
    review-tier record PASS, posted on objectui#10319 at head 8fb0561
    CI 39 success, 3 skipped by design, and 1 red, Spec Main Shape Gate. That red comes from the pre-fix base; the fix merged at 1dbb993

    Findings (out_of_scope_findings, each with its disposition)

    State in this act

    objectui#10319 gets its last sync with main (a merge commit, no history rewrite). Once CI is green: ready + auto-merge. Fixes #10276 closes this card on merge.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions