Repository navigation
Commit 180ef90
docs(skills): currency is a bare number in both modes; the resolution order is mode-conditioned (field-types.md) (#20118)
Part of #20090
Clause-②: no
Skill half of the card (the `skills/**` governed surface, Tier H). The
two `content/docs` pages land separately on
`claude/issue-20090-currency-mode-docs` so the docs correction does not
wait on this approval; #20090 stays open until both have landed and the
seat closes it.
## What changed
One file: `skills/objectstack-data/rules/field-types.md`, the "Currency
with Precision" example and the paragraph under it. Text only; no spec
change, no example shape change (the block stays a valid `Field` literal
— it carries no `os:check` marker, so `check:skill-examples` type-checks
nothing in this file; the block is a fragment like every other block
there).
- The example comment taught `'dynamic'` as a per-record `{ value,
currency }`. It now reads: `'fixed'` = the column is pinned to
`defaultCurrency`; `'dynamic'` = tenant default.
- The paragraph "Currency resolution (ADR-0053)" read
`currencyConfig.defaultCurrency` unconditionally, then the tenant
default. It now says: the value is a bare number in both modes (ADR-0104
D1), never `{ value, currency }`; the displayed symbol is
`currencyConfig.defaultCurrency` only under `'fixed'`; under `'dynamic'`
(the default) it is the tenant `localization.currency` setting, not
`defaultCurrency`; with neither, a plain grouped number; a measure's own
`currency` wins.
Every sentence is re-derived from the spec, not from any renderer:
- `packages/spec/src/data/field-value.zod.ts` — `NUMERIC_VALUE_TYPES`
carries `currency` with the comment "`currency` IS a bare number", and
the module header explains `CurrencyValueSchema` was "never-consumed".
- `packages/spec/src/data/field.zod.ts` — `currencyMode:
z.enum(['dynamic','fixed']).default('dynamic')`; `defaultCurrency`
"Default or fixed currency code" with default `'CNY'`; the `FieldSchema`
guidance "a fixed currency is declared as `currencyConfig: {
currencyMode: 'fixed', defaultCurrency: 'JPY' }`. A field without one
uses the tenant default at runtime."; and the precision-check comment
"The currency is statically known only under
`currencyConfig.currencyMode: 'fixed'` (`dynamic` is out of reach BY
DESIGN; a field with no `currencyConfig` has only the runtime tenant
default, which is not static)".
- The tenant default's producer:
`packages/services/service-settings/src/manifests/localization.manifest.ts`,
key `currency` — "ISO 4217 code applied when a currency field omits its
own. Leave unset to render code-less amounts as plain numbers."
## The ADR citation (mechanism assumption b, measured)
`docs/adr/0053-*` on `origin/main` is
`0053-date-and-datetime-semantics.md`. It introduces the tenant
`localization` settings manifest for the timezone ladder and never names
currency (`git grep -ic currency` on it: 0). `git grep -il currency
origin/main -- docs/adr/` returns 14 files; the only one that governs a
currency rule is ADR-0104 (D1: "`currency` is a scalar number —
`CurrencyValueSchema` is deleted"). No ADR in this registry, and no
`cloud ADR-NNNN` cited from this repo, governs the display-resolution
order. So the paragraph now cites ADR-0104 D1 for the value shape and
drops the ADR-0053 citation rather than keep a wrong one; the tenant
default is named by its setting, `localization.currency`, which is its
actual producer.
## "dynamic (user selectable)" (mechanism assumption c, measured)
The spec describe reads "dynamic (user selectable)". ADR-0104 says the
currency code "lives in field config, not per value"; ADR-0053 is
silent; no ADR, schema key or runtime seam names a place where a
per-record currency is selected (`resolveExecutionContext` reads
`localization.currency` onto the context; the analytics chain reads
field config and the context). The skill therefore teaches only what the
spec guarantees — bare number; tenant default — and names no selection
mechanism. The gap is recorded below.
## Budget readings (the seat's `skills/**` budget: net at most +2 lines
in this file)
| reading | before (`fa00ebf4`) | after (`7bf1503b`) | delta |
|:---|:---|:---|:---|
| `skills/objectstack-data/rules/field-types.md` lines | 328 | 328 | 0 |
| `skills/objectstack-data/**/*.md` package lines (15 files) | 3717 |
3717 | 0 |
| `field-types.md` tokens, `check-skills-token-ratchet.mjs` (ceiling
3158) | 3158 (headroom 0) | 3157 (headroom 1) | −1 |
| bundle total tokens (whole shipped tree) | 153973 | 153972 | −1 |
| `field-types.md` bytes (the ratchet counts ceil(bytes / 4)) | 12629 |
12625 | −4 |
The file sat at zero token headroom, so the rewrite is paid for inside
the same sentences (no re-wrap credit claimed): 7 lines replaced by 7
lines.
## Changeset
`skip-changeset` (measured, not assumed): no package's `files[]` names a
`skills` path (positive control: `packages/spec` `files[]` names
`dist`), and `create-objectstack` installs the catalog at scaffold time
with `npx skills add objectstack-ai/objectstack/skills`, from the
repository, never from its tarball. Nothing published moves.
## Verification (head `7bf1503b`)
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` in this worktree, no paths, derives 23
families; every one run in the foreground with the exit captured before
any pipe, then reconciled: `--ran` verdict "23 derived, 23 run, 0
NOT-MEASURED, 0 UNRUN", exit 0. All 23 exit 0:
`check-ci-filter-parity` · `check-closing-keyword-parity` (+
`--self-test`) · `check-comment-mask-corpus` · `check-doc-route-spelling
--advisory` (+ `--self-test`) · `check-skills-token-ratchet` (+
`--self-test`) · `@objectstack/lint check:doc-formula-expressions` ·
`check:agent-test-spelling` · `check:corpus-claim-drift` ·
`check:cross-package-test-inputs` · `check:doc-authoring` ·
`check:driver-memory-census` · `check:gitlink-declared` ·
`check:nul-bytes` · `check:pm-governed-merges` ·
`check:refd-timer-probe` · `check:role-word` ·
`check:skill-compatibility` · `check:skill-frame-sync` ·
`check:skill-identifier-liveness` · `check:watch-hint-literal`.
Two prerequisite refusals (exit 3, "nothing was measured") were cleared
by building the named packages under the verify lock and re-running:
`check:doc-formula-expressions` (needed `@objectstack/formula` +
`@objectstack/lint` built) and `check:skill-examples` (needed
`@objectstack/client-react` built). `check:skill-examples` is outside
this diff's derivation and was run anyway because it reads `skills/**`:
"259 prose examples type-check across 3 surface(s)", exit 0.
No reverse verification or ablation applies: the change is prose with no
consumer that parses it.
## Acceptance notes
- The spec describe "dynamic (user selectable)" (`field.zod.ts`
`currencyMode`) surfaces verbatim in the generated reference
`content/docs/references/data/field.mdx` (2 rows). No mechanism selects
a per-record currency anywhere in the tree, so that word over-claims
against ADR-0104 D1's own sentence that the code lives in field config.
Out of scope here (this card forbids a spec change; the reference page
is auto-generated). carrier: spec lane (a one-word describe change plus
`gen:docs`); reported in the os-dev report as a class-b candidate with
dedupe words.
- The same unconditioned chain with the same wrong ADR number appears in
`skills/objectstack-ui/rules/dashboards.md` (the measure comment,
"resolves measure `currency` → the aggregated field's
`currencyConfig.defaultCurrency` → the tenant `localization.currency`
default (ADR-0053)"), in `content/docs/api/data-api.mdx` ("the ADR-0053
currency chain") and in
`packages/services/service-analytics/src/plugin.ts` and
`analytics-service.ts` comments ("ADR-0053 currency chain"). Those are
#20091's runtime half and its teaching surfaces; #20091 remains open and
is not touched here. carrier: #20091.
## 维护者速读(草稿)
**改了什么** — 只改一份对外发布的技能文件 `skills/objectstack-data/rules/field-types.md`
里关于货币字段的两处教学文字:示例注释和其下的"货币解析"段落。行数 328 → 328,token 3158 → 3157(上限
3158),整包 3717 行 → 3717 行。不改 spec,不改任何代码。
**为什么改** — 原文教 AI 把 `dynamic` 模式的货币值写成 `{ value, currency }`
对象,而运行时(校验器、SQL 驱动的数值列、导入)只接受裸数字;照抄的作者会把对象写进数值列。原文还把 `defaultCurrency`
当成无条件的显示币种,而它默认是 `'CNY'`,这样每个 `dynamic` 字段都会显示人民币;按 spec,它只在 `fixed`
模式下是该列的币种,否则显示租户的默认币种。段落引用的 ADR-0053 其实是日期语义的记录,与货币无关,改为引用 ADR-0104
D1(值形状)并去掉错误引用。
**风险与代价(含回滚)** — 纯文字改动,零运行时影响;技能包 token 净减 1,不触及棘轮上限。回滚即 revert 这一个
commit。相关的运行时半边(analytics 无条件读 `defaultCurrency`)归 #20091,不在本 PR。
**席位意见** —
**你要做的** — 这是 Tier H 受管面(`skills/**`),需要您(或授权审批账号)的一次 APPROVED
review;审批后由席位落地。请顺带看一眼"dynamic (user selectable)"这个 spec 描述词是否要另立一张卡改掉。
---
_Generated by [Claude
Code](https://claude.ai/code/session_0148fenvVvyQV9HYxgVDQ33q)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 536f2d5 commit 180ef90
1 file changed
Lines changed: 7 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
179 | 179 | | |
180 | 180 | | |
181 | 181 | | |
182 | | - | |
183 | | - | |
| 182 | + | |
| 183 | + | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| |||
0 commit comments