Repository navigation
docs(spec): name a currency amount's decimal places as its CLDR fraction digits, not its ISO 4217 minor unit - #30755
Conversation
…s a currency amount's decimal places The currency `scale` refusal, the inline grid column refusal, the `currencyConfig` decimal-places prescription, the `precision` and `scale` describes, the semantic upgrade-guide entries, the docs pages and the ui mirror strings and docblocks said a currency amount's decimal places are its ISO 4217 minor unit. The platform's digit source is CLDR's fraction digits (`CURRENCY_FRACTION_DIGITS`, and `Intl` on every display face), which differ from ISO 4217 for sixteen codes (IQD 0 against 3, and fifteen codes 0 against 2). Words only: no table value, refusal arm, rule id or path moves. Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
…erences from the reworded describes Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
…orm on the CLDR wording Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
…ck and a test title Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
…encyFractionDigits declaration changes words Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
…s the currency's minor unit now say its CLDR fraction digits Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
📓 Docs Drift CheckThis PR changes 2 package(s): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 172 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 8a46f583a580ae312e3ee0521fda8851355061c2 && git checkout 8a46f583a580ae312e3ee0521fda8851355061c2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3214615d7ac3b7b61cfabbae06fbc44de6819ed8 b15d64c6539df5bfcc035c031b0a97771241befd && git checkout -B drift-repro 3214615d7ac3b7b61cfabbae06fbc44de6819ed8 && git merge --no-ff b15d64c6539df5bfcc035c031b0a97771241befd
node scripts/docs-audit/affected-docs.mjs --json 3214615d7ac3b7b61cfabbae06fbc44de6819ed8
|
|
domain:spec#2 · os-sales · session_01L6bhgVugtCCwBs98FeR27P Contract reviewServed-tier: Inputs: card #30648 (body; comments 6669128063, 6670054953, 6670528106, 6671521736, 6671610302, 6671693654, 6673544989), PR #30755 (body, 51-file list, net diff ① Derived judgmentsAccept set — unchanged, judged on the tree, each named RIGHT:
Public-surface changes implied — named:
Truth of the wording, read against the two sources the card names:
② Semver level
③ Boundary flags
④ Design judgmentEach
Must-fix: none. Should-fix: S1. Not reviewed here, the lane seat's ACCEPT at Implemented-by: VERDICT: PASS Generated by Claude Code |
…ion-digits clause The docblock above INLINE_GRID_CURRENCY_SCALE_REFUSAL still said the pin holds the two refusals to "the same minor-unit clause"; the pin's constant is FRACTION_DIGITS_CLAUSE now and its own docblock says "fraction-digits clause". Comment only. Claude-Session: https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU Co-authored-by: Claude <[email protected]>
|
domain:spec#2 · os-sales · session_01L6bhgVugtCCwBs98FeR27P Contract reviewServed-tier: Delta review over round 1 (record 6673665638, PASS on ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Must-fix: none. Should-fix: none open (S1 closed). Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:spec#2 · os-sales · session_01L6bhgVugtCCwBs98FeR27P 维护者速读
席位核对ACCEPT · head
Generated by Claude Code |
Requested by Sales · project thread
domain:spec#2 · os-sales · session_01L6bhgWFZUMBKFD9Lcwt4DU
Fixes #30648
Clause-②: no
Metadata-protocol: none
维护者速读
scale拒收文案、currencyConfig小数位提示、precision/scale字段说明、升级指南条目、四个文档页,以及 ui 侧逐字镜像的拒收串、控制台警告和注释。CURRENCY_FRACTION_DIGITS和各显示面的Intl),有 16 个币种与 ISO 4217 不同(如 IQD:CLDR 0 位,ISO 3 位)。旧文案让伊拉克第纳尔、匈牙利福林等币种的作者以为会显示小数,平台却按整数显示。packages/spec/src/data/field.zod.ts的 currencyscale拒收(L3161)与INLINE_GRID_CURRENCY_SCALE_REFUSAL(L1084);②ui/packages/types/src/zod/form.zod.ts的CURRENCY_FIELD_SCALE_REFUSAL(L1089,与 spec 逐字相等);③packages/spec/src/data/field-currency-scale-refused.test.ts末尾新增的 describe(L258)。pnpm --filter @objectstack/spec exec vitest run src/data/field-currency-scale-refused.test.ts,预期 13 条全过,其中新 pin 断言拒收消息含 “CLDR fraction digits”、不含 “ISO 4217 minor unit”。What changes
The refusal sentence, FROM → TO (spec
FieldSchemacurrencyscalerefusal, held word for word by uiCURRENCY_FIELD_SCALE_REFUSAL):The inline grid column refusal (
INLINE_GRID_CURRENCY_SCALE_REFUSAL, and itsconsole.warncopy inui/packages/plugin-form/src/deriveMasterDetail.ts) moves the same way: "…the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the cell displays the amount and the width a computed amount is rounded to." The first sentence and the remedy ("delete the key") are unchanged everywhere. The examples stay: USD 2, JPY 0 and KWD 3 are the same in both systems.The wording follows PR #30644 (
currency-fraction-digits.ts,currency.mdx): "CLDR fraction digits, not ISO 4217's minor units". Where a docs page explains the difference, it gives IQD (0 in CLDR, 3 in ISO 4217) as the example of the sixteen codes and links the currency page that lists them.field.zod.ts): thecurrencyConfigdecimal-places reasonCURRENCY_DECIMAL_PLACES_ARE_THE_CURRENCYS(it feeds theprecisiontombstone and thedecimals/scaleprescriptions), the inline grid refusal, the field refusal, and theprecision/scale/ inline columnscaledescribes. Comments in the same file at L504, L711, L3139, L3145 and L3171.currency-config-precision-removed, the semantic entriescurrency-config-precision-retired,field-currency-scale-refused,inline-grid-column-currency-scale-refused,inline-grid-column-identity-only-currency-scale-refused,ui-object-master-detail-form-details-closed,ui-page-panel-children-cross-references-judged,ui-record-line-items-props-closed, andregistry.ts.template. The generated upgrade guide (in memory and--out) now carries "CLDR fraction digits" 10 times; its only "ISO 4217 minor unit" left are the three quoted rulings.field-scale.ts,field.form.ts,retired-keys/18.data__CurrencyConfig__precision.ts,liveness/field.json(thecurrencyConfigrow's note).data-modeling/field-types.mdx,data-modeling/validation-rules.mdx,protocol/objectql/schema.mdx,protocol/objectql/types.mdx.content/docs/references/**(field, object, migration, component) regenerated withgen:docs, not hand-edited.validation/record-validator.ts.CURRENCY_FIELD_SCALE_REFUSAL, thederiveMasterDetailwarning, and the docblocks and comments intypes/src/form.ts,fields/src/{currency.ts,index.tsx},fields/src/widgets/{CurrencyField.tsx,GridField.tsx,PercentField.tsx,number-format.ts},plugin-dashboard/src/ObjectMetricWidget.tsx,plugin-form/src/{MasterDetailForm.tsx,deriveMasterDetail.ts},plugin-grid/src/useColumnSummary.ts,app-shell/.../ObjectFieldInspector.tsx, plus ui test docblocks and two test titles that repeated the sentence.patch:@objectstack/spec,@object-ui/types,@object-ui/fields,@object-ui/plugin-form, one per package whose published bytes change (measured below). No changeset for@objectstack/objectql,@object-ui/plugin-gridand@object-ui/plugin-dashboard(comments only, absent from their builtdist/) or@object-ui/app-shell(private).The branch is the harness-assigned
claude/project-thread-437a6c, notclaude/issue-30648-*.Hit census and dispositions
Base
4bf9a2d7. The card's one-linegit grep -n -i "ISO 4217 minor unit"misses line-wrapped spellings, so the census is a multi-line search over every tracked file mentioning4217(CHANGELOGs,content/docs/releases/**and translation packs excluded):ISO, then4217, then one of minor / decimal / fraction / width / digit, with whitespace, comment stars and string-concatenation characters allowed between them. 116 lines at base (13 of them in the generatedcontent/docs/references/**), plusui/packages/fields/src/currency.tsL41 ("fraction digits ISO 4217 gives") and the test titleformatCurrency.minorUnits.test.tsxL85 ("as ISO 4217 says"), found by a second search for "ISO 4217 gives / says".Every hit, with its disposition
Reworded (the file surface of claim 6671521736, widened by amendment 6671693654):
packages/spec/src/data/field.zod.tsL492, L504-505, L711-712, L1086-1087, L1159, L1463, L1488, L3139, L3145, L3162, L3171packages/spec/src/data/field-scale.tsL107, L109 ·field.form.tsL81 ·conversions/entries/18/currency-config-precision-removed.tsL22, L53 ·migrations/entries/retired-keys/18.data__CurrencyConfig__precision.tsL9 ·migrations/registry.ts.templateL1630 ·liveness/field.jsonL43218.currency-config-precision-retiredL19, L29-30 ·18.field-currency-scale-refusedL12, L21, L43 ·18.inline-grid-column-currency-scale-refusedL14 ·18.inline-grid-column-identity-only-currency-scale-refusedL22 ·18.ui-object-master-detail-form-details-closedL32 ·18.ui-page-panel-children-cross-references-judgedL25 ·18.ui-record-line-items-props-closedL35currency-precision-iso4217.test.tsL11-12, L60, L108, L227 ·field-currency-scale-refused.test.tsL8, L52, L141 ·inline-grid-column-currency-scale-refused.test.tsL63 ·field-scale.test.tsL58field-types.mdxL237, plus L250 and L598, which said a currency cell uses "the currency's minor unit" on the same page ·validation-rules.mdxL160 ·protocol/objectql/schema.mdxL293 ·protocol/objectql/types.mdxL33, L343 · regeneratedreferences/data/field.mdx(4),references/data/object.mdx(4),references/system/migration.mdx(4),references/ui/component.mdx(1)packages/objectql/src/validation/record-validator.tsL1528types/src/zod/form.zod.tsL1091 ·types/src/form.tsL2074 ·plugin-form/src/deriveMasterDetail.tsL255, L301 ·plugin-form/src/MasterDetailForm.tsxL490 ·plugin-grid/src/useColumnSummary.tsL417 ·plugin-dashboard/src/ObjectMetricWidget.tsxL605 ·app-shell/.../ObjectFieldInspector.tsxL562 ·fields/src/currency.tsL25, L41 ·fields/src/index.tsxL582-583, L915-916 ·fields/src/widgets/CurrencyField.tsxL22-23 ·GridField.tsxL127-128, L327 ·PercentField.tsxL90 ·number-format.tsL105-106ObjectFieldInspector.currencyScale-10221L9 ·CurrencyField.minorUnitsL16 ·PercentCurrencyField.readonlyDisplay-11444L26-27, L130 (title) ·formatCurrency.centsDigitL29 ·formatCurrency.minorUnitsL10-11, L30-31, L85 (title) ·GridField.currencyMinorUnit-10355L11 ·GridField.currencyScaleNotRead-10783L10-11Quoted ruling, kept as written (ruling 乙 「a currency's ISO 4217 minor unit decides its display」, quoted, not paraphrased):
field.zod.tsL1077 ·inline-grid-column-currency-scale-refused.test.tsL7 ·18.inline-grid-column-currency-scale-refusedL19 ·18.ui-object-master-detail-form-details-closedL40 ·18.form-view-subform-columns-closedL29.Already correct, not touched (they name CLDR and use ISO 4217 only as the contrast):
currency-fraction-digits.ts(5) ·content/docs/data-modeling/currency.mdxL303 ·packages/spec/src/data/index.tsL199 ·packages/objectql/src/home-amount-projection.tsL36 · ADR-0144 (4, a governed surface) ·.changeset/30103-home-amount-rate-field-scale-floor.md(2).Out of scope:
ui/packages/i18n/src/__tests__/machineLocaleCensus-9909.test.tsL255 (open PR #30552 touches the file; see Acceptance notes) · everyCHANGELOG.md.After, at this PR's head: the same search answers 33 lines, and none of them says ISO 4217 is where a currency's decimal places come from:
machineLocaleCensus-9909.test.tsL255 (kept out, see Acceptance notes);not.toContain('ISO 4217 minor unit')and its comment and titles), and 3 docs lines ("CLDR fraction digits (not ISO 4217's minor units)":field-types.mdxL238,validation-rules.mdxL160,types.mdxL343).Tests
currency-precision-iso4217.test.ts(theprecisiontombstone and thedecimals/scaleprescriptions),field-currency-scale-refused.test.ts(the ruled-remedy helper and thescaledescribe),inline-grid-column-currency-scale-refused.test.ts(MINOR_UNIT_CLAUSErenamedFRACTION_DIGITS_CLAUSE), and the comment infield-scale.test.ts.describein the existing harnessfield-currency-scale-refused.test.ts): theFieldSchemarefusal forscaleon acurrencyfield iscode: 'custom', contains "CLDR fraction digits" and does not contain "ISO 4217 minor unit"; thescaledescribe says the same.ui/packages/types/src/__tests__/strict-face-read-keys-11070.test.tscomparesCURRENCY_FIELD_SCALE_REFUSALwith the spec's issue message for the same entry.Acceptance notes
ui/packages/i18n/src/__tests__/machineLocaleCensus-9909.test.tsL255 still says "an ICU metadata PROBE for the ISO-4217 fraction-digit count". It is a census reason string in a test, not shown to authors. Open PR feat(console): an administrator's first-run checklist on Home replaces the currency and time zone dialog #30552 touches the same file, so the seat kept it out of this PR (amendment 6671693654). It can be reworded after feat(console): an administrator's first-run checklist on Home replaces the currency and time zone dialog #30552 lands.precisionthat contradicts the currency's ISO 4217 digits? #7918 check as an "ISO 4217" check ("declared and validated against ISO 4217", "its own Contract question: should publish-time validation reject a declared currencyprecisionthat contradicts the currency's ISO 4217 digits? #7918 ISO 4217 contradiction check") are left as written: they name what that check was called, not where a currency's decimal places come from (registry.ts.templateL1629,currency-config-precision-removed.tsL23 and L54,field.zod.tsL510,currency-precision-iso4217.test.tsL9,retired-keys/18.data__CurrencyConfig__precision.tsL5-6).field-types.mdx, the author-facing page this PR already rewords. It is the generic name for a currency's sub-unit, and CLDR's digits are that count too.Verification
Gates (local subset, head b15d64c)
Exit codes captured before any pipe.
Review round 1 (seat contract review 6673665638, S1):
b15d64c6changes one comment only, the docblock aboveINLINE_GRID_CURRENCY_SCALE_REFUSALinpackages/spec/src/data/field.zod.ts(L1081), from "the same minor-unit clause" to "the same fraction-digits clause", the name the pin now uses (FRACTION_DIGITS_CLAUSE). Atb15d64c6:pnpm --filter @objectstack/spec build0; specvitest run(all projects, a wider run than the packagetestscript's--project local817 files) 907/907 files, 27366 passed, 1 todo; spectsc --noEmit0; eslint--no-inline-configonfield.zod.ts0;rg -n -U -i "minor[\s*-]*unit[\s*]*clause"overfield.zod.tsand the inline-grid pin finds nothing; the multi-line census still reads 33 lines, the same set as atfbb6e7fb.dispatch-gates --commandsderives the same 112 families as atfbb6e7fb, all 112 exit 0;--ran: "112 derived famil(ies) accounted for — 112 run, 0 NOT-MEASURED (a DERIVED zero …)". No ablation re-run (comment only). The figures below are fromfbb6e7fb.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatfbb6e7fb: 112 derived families, all 112 exit 0.--ran(with:: exit Nper line): "112 derived, 112 run, 0 NOT-MEASURED, 0 UNRUN … a DERIVED zero — all 112 recorded an exit code and none of them is 3".The same 112 ran first at
82aa23e3(before the main merge): 108 exit 0 and 4 exit 3 PREREQUISITE NOT MET on a fresh clone (check-plugin-teardown-shape --self-testneeded its pinned fixture commit fetched; speccheck:skill-examples,check:dual-build-cjs-loadsandcheck:i18nneeded unbuilt dists). Aftergit fetch --depth=1of the fixture and one turbo build of the 39 missing packages, all four re-ran exit 0; the run atfbb6e7fbneeded no re-run.pnpm --filter @objectstack/spec build0;check:generated0 ("All 14 generated artifacts are up to date"), aftergen:docsregenerated the four reference pages;check:spec-changes0,check:upgrade-guide0,node scripts/check-adr-0087-registration.mjs --base origin/main0 ("adds no declared-breaking changeset").@objectstack/spectest: 817/817 files, 25827 passed, 1 todo (lock VERDICT command-exit 0, after the main merge; 25793 before it). typecheck 0.@object-ui/typestest 387/387 files (10464 tests);@object-ui/fields298 passed, 1 skipped (4467 tests);@object-ui/plugin-form223/223 (2454 tests);@object-ui/plugin-grid278/278 (2392);@object-ui/plugin-dashboard203/203 (1957);@object-ui/app-shellObjectFieldInspector.currencyScale-10221.test.tsx5/5. After the main merge: the parity file 94/94 and the app-shell file 5/5 again. The first ui run failed to resolve@objectstack/formula/@objectstack/client(unbuilt dists on a fresh clone, no test ran); the recorded numbers are the run after building them.type-check0 for@object-ui/types,fields,plugin-form,plugin-grid,plugin-dashboard(after building the ui dependency closure; before it, TS2307 on unbuilt@object-ui/*dists).@object-ui/app-shelltype-check not run: its two files change comments only.eslint
--no-inline-configon the 37 changed.ts/.tsxfiles atfbb6e7fb: root config 0 errors,ui/config 0 errors (279 warnings, allno-explicit-anyon lines this PR does not touch).Full dogfood battery: not owed (no
!in the title, no(narrowing)arm).PR check-runs at
fbb6e7fb: every check suite completed with none failed (check_suite.completed, 2026-11-06T01:10Z). Atb15d64c6: not read at the time of writing.Ablation: the old refusal clause back in the spec only
Run with
node scripts/ablation-replace.mjs(run mode: mutate, run the leg, restore with proof) underos-verify-lock.sh, on committed head82aa23e3. The anchor is the TO clause of the field refusal inpackages/spec/src/data/field.zod.ts; the replacement is the FROM clause. Nothing underui/was changed.Restore leg:
git diff HEADon the file 0 bytes, blobe7f40f20…equalsHEAD:packages/spec/src/data/field.zod.ts; spec rebuilt;ablation-dist-preflight.mjs … --absentexit 0 ("marker absent from all 592 built files", "working tree clean against HEAD"); the four spec pin files 59/59 and the ui parity file 94/94 green again;git status --porcelainempty.Published bytes: which packages need a changeset
Each package built at this head, then
grepover itsdist/for the new words, with a control.dist/@objectstack/specdist/*.js;src/**/*.zod.tsships toopatch@object-ui/typesdist/zod/form.zod.js,dist/zod/index.zod.js,dist/form.d.tspatch@object-ui/plugin-formdist/index.js,dist/index.umd.cjs(the warning)patch@object-ui/fieldsdist/currency.d.ts(the exportedcurrencyFractionDigitsdocblock)patch@object-ui/plugin-gridformatCurrencypresent@object-ui/plugin-dashboard@objectstack/objectqlmax_scalepresent indist/core.js@object-ui/app-shellprivate: true, publishes nothingThe docs pages and
content/docs/references/**are not a published package.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU