docs(objectql): the overview and the types page say a currency field carries "exchange rates" and stores "amount + currency code"; the shipped field is a bare number, with no record currency and no conversion #23739
Description
Filing gate ③: a task a ruling left. The hold record 6051408208 on #8345 (2026-10-08T03:13Z) says: "the two published sentences the measurement found false today … are a shipped statement that is wrong, which North Star rule 4 makes a finding for the triage seat to file as a documentation correction, independent of this hold." This card is that filing. It is a week late; the triage seat's record R428 notes the miss.
Filed by the triage seat · session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim.
Reader who acts: the domain:devx seat (docs).
What the docs say (on origin/main 534ca13eb5)
content/docs/protocol/objectql/index.mdx:121: the field-type table row iscurrency| "Money with exchange rates" |{amount: 1000, currency: 'USD'}.content/docs/protocol/objectql/types.mdx:30: therevenueexample's comment says the field "Automatically knows: Store amount + currency code".
What ships
packages/spec/src/data/field.zod.ts:511, the currencyMode describe:
dynamic(the default): the field has no currency of its own — amounts display in the tenant default currency … Neither mode is a per-record choice: the value is a bare number either way.
- The platform has no exchange-rate object and no conversion. The design round on currency: no exchange-rate / home-currency conversion — dynamic multi-currency amounts cannot be reconciled or aggregated across currencies #8345 (
6041378776) found zero implementation hits forexchange rate,fx_rateandcurrency_conversionunderpackages/. currency: no exchange-rate / home-currency conversion — dynamic multi-currency amounts cannot be reconciled or aggregated across currencies #8345 is paused by the maintainer. - A
currencyvalue is a number, not{amount, currency}.
The change
- Rewrite both sentences to what ships: a number; one currency per field (
fixed) or the tenant default (dynamic); precision per ISO 4217. - ⛔ Promise no conversion. Do not cite currency: no exchange-rate / home-currency conversion — dynamic multi-currency amounts cannot be reconciled or aggregated across currencies #8345 as coming; it is on hold.
- ⛔ Don't touch
field.zod.ts. The spec is the source the docs follow.
Executable criterion
git grep -n -E "exchange rates|amount \+ currency code" -- content/docsgives 0 hits.- The docs gates the PR touches pass:
check:doc-*.
Dedupe
MCP search_issues on this repo, closed included, at this filing: docs currency "Money with exchange rates" over-claim objectql index.mdx gives 5 hits. They are #8345 (the paused feature), #20196 and #20090 (the skills / field-types currency teaching, both closed and both other files), #22727 (formula currency result, closed) and #7244 (schema.mdx samples, closed). None is these two sentences.
Dedupe words: currency docs exchange rates; amount + currency code; index.mdx field type table currency.
Activity
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 5 ·
domain:devxseat 2 · 2026-10-16T00:12Z · filed and graded by the triage seat (session_01AavokzJ5DndAwitDXvKy4U)Session:
session_016PVjjuVPte5XEJvaQx1E4tAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23739-currency-docsWorktree:
objectstack-issue-23739Domain:
domain:devxSeat:
domain:devx#2File surface:
content/docs/protocol/objectql/index.mdx;content/docs/protocol/objectql/types.mdx. Stop on breach and explain it in the report. ⛔ Nopackages/spec/src/**(the card: the spec is the source the docs follow), ⛔ nocontent/docs/references/**(generated).Container & model:
S,mode:subagent,model: TIER_DEFAULT(dispatch-gates --tieron the two paths: "no path-derived mandate")Clause-②: no
Responsibility: n/a — not a defect card (a documentation correction,
documentation)Thread-read: none
Serial constraints cleared:
pm:dispatchedclaim names them.origin/main140238a47b:index.mdx:121andtypes.mdx:30still carry the two sentences:git grep -n -E "exchange rates|amount \+ currency code" -- content/docsgives exactly 2 hits.field.zod.ts:511still says the value is a bare number.pm:on-hold.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23739,
"status": "done",
"branch": "claude/issue-23739-currency-docs",
"pr": "#23762",
"session": "session_016PVjjuVPte5XEJvaQx1E4t",
"premise_still_valid": true,
"summary": "Premise re-read on origin/main 140238a. The criterion grep still gave exactly the 2 hits (index.mdx:121, types.mdx:30), and field.zod.ts:511 still says the value is a bare number. Rewrote both sentences to what field.zod.ts ships. index.mdx:121 is now
currency| 'Money, in the field's fixed currency or the tenant default' |1000(a bare number), resting on :511, :528 and field-value.zod.ts:63-64. types.mdx:30 is now a 3-line comment: a bare number with no per-record currency code, shown in the tenant default currency (plain if none is set) or one fixed on the field, at that currency's ISO 4217 decimal places; it rests on :511, :512, :442/:473 and :1310. Item 3: the other three comment lines are each true and unchanged. Symbol: objectui@f96f771140f1 CurrencyField.tsx:15/:159 with resolveFieldCurrency (i18n/src/currency.ts:46-64), symbol only when a currency resolves, plain number otherwise (number-format.ts:130), and the rewritten line now states that condition. Precision: record-validator.ts:786 + :1358-1380 give max_precision on currency writes. $1,234.56: formatCurrency number-format.ts:158 at currencyDisplayWidth :120-121, which is 2 for USD. precision: 18 is kept; per :1285 it is total digits. No conversion is promised, and the on-hold feature card is not cited. Changeset: none; skip-changeset per AGENTS.md (measured, see gates). Per the dispatch, the label was NOT applied by me; the seat applies it. Worktree and its node_modules removed after the PR opened.","tests": "No test-pinned behaviour changed (docs-only), so no ablation. (1) Card criterion:
git grep -n -E \"exchange rates|amount \\+ currency code\" -- content/docsgives 0 hits, exit 1. Two control legs: the same command at 140238a gives the 2 original hits (exit 0); the pattern widened with the two new phrases hits the 2 new lines (exit 0). (2) The types.mdxrevenueYAML block, parsed with yaml and run through FieldSchema.safeParse from the freshly built spec dist: success true, keys type,label,precision. Control with scale: 2 added: refused ('scaleis not valid on acurrencyfield'). (3) Builds under os-verify-lock:pnpm --filter @objectstack/spec buildVERDICT command-exit 0;turbo run build --filter=@objectstack/lint... --filter=@objectstack/formula... --filter=@objectstack/client-react^...34/34 tasks, VERDICT command-exit 0;pnpm --filter @objectstack/client-react buildVERDICT command-exit 0. git status was clean after each build. (4) Control-byte self-scan of both files, grep -naP: none (exit 1).","gates": "Local, on final head 2733ac2.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 40 commands; all 40 ran, each exit captured before any pipe; final exit 0 for all 40. Four first exited 3 (PREREQUISITE NOT MET: @objectstack/lint, formula, client-react unbuilt) and re-ran exit 0 after the builds: lint check:doc-formula-expressions ('15 predicate(s) ... judged clean'), lint check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift ('4 declared transcript value(s) across 415 page(s) ... equal'). Verdict lines of note: check:doc-frontmatter ('content/docs 415'), check:doc-anchors ('518 internal #fragment link(s) ... all resolve'), check:doc-authoring (clean), check:docs-single-h1 (415 pages), check:docs-spec-enumerations OK, spec check:docs ('227 generated files in sync'), spec check:yaml-examples ('19 tagged YAML example(s) across 1 file(s)'; the types.mdx block is untagged, hence measured in tests (2)), check:nul-bytes OK (11676 files).--ranwithcmd :: exit Nrecords: 'Run reconciliation — 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero), exit 0. check-governed-merges --branch claude/issue-23739-currency-docs: NOT governed, 0 of 2 paths, 6 changed lines, exit 0. Not run locally: check:dev-prereqs and check:skill-frame-freshness, because the diff touches neither the packages/spec build nor the four-axis frame files. PR check-runs at head 2733ac2, read once: 37 total, 11 success, 6 skipped, 20 in_progress. Check Changeset in_progress (label-settling wait; it reds unless the seat applies skip-changeset). Governed Surface Queue Guard in_progress. Test Core 1/6 to 6/6 in_progress. Build Core skipped. All Lint and Type Check lanes in_progress except 'Lint · scoped · tooling self-tests' success. Build Docs in_progress. Success: Spec property liveness, Dogfood Regression Gate, Check Documentation Links, Check PR Size, the claim/closing-keyword guards. Every in_progress CI family is NOT MEASURED here, reason: still running at report time, and convergence is the PM's.","mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, each executed as objectstack-fleet[bot]). Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/23762/assignees (os-sales); read-back 'identical', assignee listed. Stroke 2: POST /repos//issues/23739/comments (this report). Not REST: 2 git push. Label writes: 0.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: none measured at a public door and no named producer — noted, not filed · content/docs/protocol/objectql/types.mdx:289, the currency section's opening line 'Money amount with currency code.' contradicts field.zod.ts:511 ('Neither mode is a per-record choice: the value is a bare number either way'); the same section corrects it a few lines later (Storage: bare number, warn callout). Seam: spec:CurrencyConfigSchema.currencyMode → consumer: none (docs prose). The bounded in-place exemption's four conditions hold (same class, mechanical, the file is in this claim's surface, same gate family), but the dispatch said fix only the two sentences plus the item-3 example, so it was left. Recommend a patch round on #23762 that widens the surface to this line and the two below, one landing for the family · dedupe words: currency docs 'Money amount with currency code'; types.mdx currency section; currency code per value",
"class: b · reach: none measured at a public door and no named producer — noted, not filed · content/docs/data-modeling/field-type-decision-tree.mdx:146, Number Types row:
currency| 'Money amounts with currency code' — same over-claim as above. Seam: spec:CurrencyConfigSchema.currencyMode → consumer: none (docs prose) · dedupe words: field-type-decision-tree currency 'Money amounts with currency code'","class: b (borderline) · reach: none measured — noted, not filed · content/docs/data-modeling/field-type-decision-tree.mdx:228: 'Product price | currency | Money needs currency code and decimal handling' is true under
fixedand not underdynamic, the default, where the field has no currency of its own · dedupe words: decision tree product price currency code decimal handling","carrier: none (承接者:无) · noted, not filed ·
git grep -n -i -E \"exchange.rate|currency conversion|multi-currency\" -- content/docsgave exactly one hit at 140238a, the card's own index.mdx:121, which this PR removes. content/docs/data-modeling/seed-data.mdx:109defineSeed(ExchangeRateCache, …)is a user-defined cache object in a seed-mode example, not a platform exchange-rate claim."],
"files_changed": [
"content/docs/protocol/objectql/index.mdx",
"content/docs/protocol/objectql/types.mdx"
],
"changed_lines": "6 (+4 / -2) over 2 files, one commit 2733ac2",
"line_budget": "n/a",
"deviations": [
"Attribution: the commit carries AGENTS.md's model-free trailer pair (Claude-Session + Co-authored-by: Claude) and the PR body ends with AGENTS.md's session-URL footer, not the harness reminder's model-named Co-Authored-By and emoji footer. AGENTS.md/os-dev.md outrank that reminder.",
"check-governed-merges.mjs --branch needs a ref; run as
--branch claude/issue-23739-currency-docs.","dispatch-gates.mjs refuses
--commandswith--ran(exit 2, 'Pass one'); the reconciliation ran as--repo objectstack-ai/objectstack --ran FILE. The first ran file had no exit codes ('a CLAIMED zero'); it was rebuilt ascmd :: exit Nrecords, giving a derived zero.","The report comment was posted with the shared checkout's scripts/pm/post-stamped.mjs (read-only use, run after my worktree was removed). Its import closure is byte-identical to BASE:
git diff 9bb93706f 140238a47b -- scripts/pm scripts/invoked-as.mjstouches only check-skill-line-ratchet.mjs."]
}
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 5, file surface revised for a patch round ·
domain:devxseat 2 · 2026-10-16T00:44Z · supersedes6204998381Session:
session_016PVjjuVPte5XEJvaQx1E4tAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23739-currency-docsWorktree:
objectstack-issue-23739Domain:
domain:devxSeat:
domain:devx#2File surface:
content/docs/protocol/objectql/index.mdx;content/docs/protocol/objectql/types.mdx;content/docs/data-modeling/field-type-decision-tree.mdx(added for the patch round: the same over-claim at:146, reported by the dev in6205370007). Stop on breach and explain it in the report. ⛔ Nopackages/spec/src/**(the card: the spec is the source the docs follow), ⛔ nocontent/docs/references/**(generated).Container & model:
S,mode:subagent,model: TIER_DEFAULT(dispatch-gates --tieron the two paths: "no path-derived mandate")Clause-②: no
Responsibility: n/a — not a defect card (a documentation correction,
documentation)Thread-read: 6205370007
Serial constraints cleared:
field-type-decision-tree.mdx(all open PRs' file lists were read at this act), and nopm:dispatchedclaim names it.origin/main140238a47b:index.mdx:121andtypes.mdx:30still carry the two sentences:git grep -n -E "exchange rates|amount \+ currency code" -- content/docsgives exactly 2 hits.field.zod.ts:511still says the value is a bare number.pm:on-hold.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23739,
"status": "done",
"branch": "claude/issue-23739-currency-docs",
"pr": "#23762",
"session": "session_016PVjjuVPte5XEJvaQx1E4t",
"premise_still_valid": true,
"summary": "Patch round, per verdict 6205423626 under revised claim 6205407760. The newest Claim: on the card is 6205407760; it names this branch and adds field-type-decision-tree.mdx to the surface. GitHub read the PR as mergeable (head 2733ac2), so main was not merged. Worktree recreated from origin/claude/issue-23739-currency-docs at 2733ac2; one commit bd86042, pushed to the same branch. (1) types.mdx currency section first line, :289 at origin/main and :291 at 2733ac2: 'Money amount with currency code.' is now 'Money amount, stored as a bare number. The currency is field configuration, never stored per value:
defaultCurrencyundercurrencyMode: fixed, or the tenant default currency underdynamic, the default.' This matches the warn callout below it and rests on field.zod.ts:511, :512, :528. (2) field-type-decision-tree.mdx:146 Use When cell: 'Money amounts with currency code' is now 'Money amounts, stored as a bare number (currency: fixed on the field, or the tenant default)'. It follows the percent row's 'stored as' style, promises no conversion, and rests on :511, :512, :528 and field-value.zod.ts:63-64. :228 untouched, per the verdict. Labels untouched (skip-changeset is on the PR, applied by the seat). The PR body was NOT updated: see deviations[0]. The replacement body is in pr_body_replacement, for the seat to write. Worktree and node_modules removed after the push.","tests": "Docs-only, no test-pinned behaviour changed, so no ablation. Widened card criterion
git grep -n -E \"exchange rates|amount \\+ currency code|Money amount with currency code|Money amounts with currency code\" -- content/docs: 0 hits, exit 1, on the bd86042 tree. Control legs with the same pattern: 4 hits at 140238a, and 2 hits at 2733ac2 (field-type-decision-tree.mdx:146, types.mdx:291). Positive control on the new phrases: 2 hits, exit 0. Repo-widegit grep -Fof the two old phrases at 140238a: only those 2 content/docs lines. Control-byte self-scan of both edited files: none (exit 1).","gates": "Local, on final head bd86042 (3 paths vs merge base 140238a, 12 changed lines).
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 40 commands, the same set as on 2733ac2. Prerequisites built first under os-verify-lock:turbo run build --filter=@objectstack/spec --filter=@objectstack/lint... --filter=@objectstack/formula... --filter=@objectstack/client-react..., 35/35 tasks (34 cached), VERDICT command-exit 0; tree clean afterwards. All 40 ran with exits captured before any pipe, and all 40 exited 0 on the first run (including check:doc-frontmatter, check:doc-anchors, check:doc-authoring, check:docs-single-h1, check:docs-spec-enumerations, check:docs-transcript-drift, lint check:doc-formula-expressions and check:doc-security-posture, spec check:docs and check:yaml-examples, check:nul-bytes).--repo objectstack-ai/objectstack --ran FILE(cmd :: exit N records): 'Run reconciliation — 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero), exit 0. check-governed-merges --branch claude/issue-23739-currency-docs: NOT governed, 0 of 3 paths, 12 changed lines, exit 0. PR check-runs at bd86042, read once: 39 total, 30 success, 7 skipped, 2 in_progress. Success includes Lint & Repo Gates, TypeScript Type Check, Governed Surface Queue Guard, Build Docs, every Lint and Type Check lane, and Test Core 3/6 to 6/6. In progress: Test Core 1/6 and 2/6, NOT MEASURED here (still running at report time; convergence is the PM's). Skipped: Build Core, Check Changeset (label present), Console Pin Gate, Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance.","mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "This round: 1 relay stroke, POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/23739/comments (this report); plus 1 git push (not REST). No PR body write, no label write. Cumulative on this card: 3 relay strokes (pr_create with its assignee leg, round-1 report, this report) and 3 git push.",
"open_questions": [],
"out_of_scope_findings": [],
"files_changed": [
"content/docs/protocol/objectql/types.mdx",
"content/docs/data-modeling/field-type-decision-tree.mdx"
],
"files_changed_pr_total": [
"content/docs/protocol/objectql/index.mdx",
"content/docs/protocol/objectql/types.mdx",
"content/docs/data-modeling/field-type-decision-tree.mdx"
],
"changed_lines": "This round 6 (+4 / -2) over 2 files, commit bd86042. PR total 12 (+8 / -4) over 3 files.",
"line_budget": "n/a",
"pr_body_change": "Replace the whole body of PR #23762 with pr_body_replacement (9460 bytes, sha256 af3bca5e1f49cbaec2c13608659661cb811324f3444a03d618bd5f8ee4893b7b). It is the stored round-1 body (verified unchanged at this act) with: a new '## Patch round' section before '## Changeset: none' (before/after of both lines, with the field.zod.ts lines each rests on, and :228 standing per the verdict); the changeset paragraph updated (the old phrases occur only in content/docs; the seat applied the label); the widened criterion added to Gates; the head, path count and changed lines updated to bd86042, 3 paths, 12 lines; Acceptance notes trimmed to what remains. Lines 1-2 (Fixes #23739 / Clause-②: no) and the session-URL footer are unchanged. Relay route:
issue_patchwith issue 23762 and body (PATCH /repos//issues/23762). The relay has nopr_updaterow.","pr_body_replacement": "Fixes #23739\nClause-②: no\n\n## What changed\n\nTwo shipped sentences said a
currencyfield carries exchange rates and stores a currency code beside the amount. Neither is what ships. Both now say whatpackages/spec/src/data/field.zod.tsdescribes. This is doc text only: no spec, runtime or renderer change.\n\n### 1.content/docs/protocol/objectql/index.mdx:121, the field-type table row\n\n| | Business Meaning | Example |\n|---|---|---|\n| before | Money with exchange rates |{amount: 1000, currency: 'USD'}|\n| after | Money, in the field's fixed currency or the tenant default |1000(a bare number) |\n\nWhat the new cells rest on (all read atorigin/main140238a47b):\n\n- "the field's fixed currency or the tenant default":field.zod.ts:511, thecurrencyModedescribe.fixedgives the field one currency,defaultCurrency.dynamic, the default, gives the field no currency of its own: amounts display in the tenant default currency (localization.currency). There is no exchange-rate object and no conversion, so the cell promises none.\n- "1000(a bare number)": three sources agree.\n -field.zod.ts:511: "the value is a bare number either way".\n -field.zod.ts:528: the deprecatedCurrencyValueSchema's note says acurrencyvalue is a bare number everywhere, and the code lives in field config.\n -field-value.zod.ts:63-64:NUMERIC_VALUE_TYPESholdscurrency.\n\n### 2.content/docs/protocol/objectql/types.mdx:30, therevenueexample's comment\n\nBefore:\n\nyaml\n # - Store amount + currency code\n\n\nAfter:\n\nyaml\n # - Store a bare number, no per-record currency code: it shows in the\n # tenant default currency (plain if none is set) or one fixed on the\n # field, at that currency's ISO 4217 decimal places\n\n\nWhat it rests on:\n\n- "a bare number, no per-record currency code":field.zod.ts:511("Neither mode is a per-record choice: the value is a bare number either way") andfield.zod.ts:528.\n- "tenant default currency (plain if none is set) or one fixed on the field":field.zod.ts:511and:512.defaultCurrencyis read only underfixed.\n- "at that currency's ISO 4217 decimal places":field.zod.ts:442(CURRENCY_DECIMAL_PLACES_ARE_THE_CURRENCYS, whichCURRENCY_CONFIG_DECIMAL_PLACES_GUIDANCEat:473uses) andfield.zod.ts:1310(scaleis refused on acurrencyfield).\n\nThe example'sprecision: 18stays.field.zod.ts:1285defines it as the total digit count, enforced oncurrencywrites, and:442uses the sameprecision: 18for a DECIMAL(18,2) amount.\n\nThe YAML block was parsed and run throughFieldSchema.safeParsefrom the freshly built spec dist. It is green, with keystype,label,precision. A control that addsscale: 2is refused: "scaleis not valid on acurrencyfield".\n\n## The example's other three comment lines\n\nI checked each against the code that does it. All three are true, so none was edited.\n\n-# - UI shows currency symbol: true when a currency resolves. In objectui at the console pinf96f771140f1:\n -packages/fields/src/widgets/CurrencyField.tsx:15resolves the currency withresolveFieldCurrency(packages/i18n/src/currency.ts:46-64): the field's fixed currency, else the tenant default.\n -CurrencyField.tsx:159renders the symbol only when a currency resolved.\n - With none, the amount is a plain number with no symbol (packages/fields/src/widgets/number-format.ts:130).\n\n The rewritten line above now states that condition ("plain if none is set"), so this line reads correctly beneath it.\n-# - Validate numeric precision: true.\n -packages/objectql/src/validation/record-validator.ts:786admitscurrencyto the numeric branch throughNUMERIC_VALUE_TYPES.\n -:1358-1380refuses a write whose total digit count exceeds the field'sprecision, with field codemax_precision.\n - Only thescalebranch skipscurrency(:1290).\n-# - Format for display ($1,234.56): true for a field whose currency resolves to USD.formatCurrency(number-format.ts:158) uses the currency style atcurrencyDisplayWidth(:120-121). That width is the ISO 4217 minor unit, which is 2 for USD.\n\n## Patch round\n\nThe seat's verdict6205423626widened the claim (6205407760) to two more lines of the same over-claim. Both are fixed in commitbd8604213.\n\n###content/docs/protocol/objectql/types.mdx:289(now:291), thecurrencysection's first line\n\nBefore:\n\ntext\nMoney amount with currency code.\n\n\nAfter:\n\ntext\nMoney amount, stored as a bare number. The currency is field configuration,\nnever stored per value: `defaultCurrency` under `currencyMode: fixed`, or the\ntenant default currency under `dynamic`, the default.\n\n\nIt now agrees with the warn callout below it. It rests on:\n\n-field.zod.ts:511: "Neither mode is a per-record choice: the value is a bare number either way".dynamicis the default and displays in the tenant default currency.\n-field.zod.ts:512:defaultCurrencyis afixed-mode field's one currency, and is not read underdynamic.\n-field.zod.ts:528: the currency code lives in field config, not per value.\n\n###content/docs/data-modeling/field-type-decision-tree.mdx:146, the Number Types row's "Use When" cell\n\n| | Use When |\n|---|---|\n| before | Money amounts with currency code |\n| after | Money amounts, stored as a bare number (currency: fixed on the field, or the tenant default) |\n\nIt follows thepercentrow's "stored as ..." style and promises no conversion. It rests onfield.zod.ts:511,:512and:528, and onfield-value.zod.ts:63-64(NUMERIC_VALUE_TYPESholdscurrency).\n\nPer the verdict,field-type-decision-tree.mdx:228("Money needs currency code and decimal handling") stands as written: it is a reason to choose the type, not a storage claim.\n\n## Changeset: none, and this PR takesskip-changeset\n\nMeasured, not assumed:\n\n-content/docs/**lives in no package directory. It is read byapps/docs(@objectstack/docs,private: true) through../../content/docs, and no package'sfiles[]names it.\n- The two old sentences occur nowhere else in the tree. A repo-widegit grep -Fat140238a47bhits only these two lines; as a control, the sibling rows of the same table hit with the same command. The spec's shippedllms.txtis hand-kept and is not derived from these pages.\n-.changeset/config.jsonsetsprivatePackages.version: true, but no pending changeset names@objectstack/docs(0 of 130; the control,@objectstack/spec, is named by 35).\n\nSo the diff publishes nothing from any released package, which is AGENTS.md's criterion forskip-changeset. The patch round's two old phrases are likewise found only incontent/docs(a repo-widegit grep -Fat140238a47b). The seat applied the label on its own check of the diff (6205423626); the author does not write labels.\n\n## Gates\n\n- Card criterion:git grep -n -E \"exchange rates|amount \\+ currency code\" -- content/docsgives 0 hits (exit 1). The same command at140238a47bgives the 2 original hits. With the new phrases added to the pattern, it finds the 2 new lines, so the zero is not a blind grep.\n- Widened criterion (patch round):git grep -n -E \"exchange rates|amount \\+ currency code|Money amount with currency code|Money amounts with currency code\" -- content/docsgives 0 hits (exit 1). The same pattern gives 4 hits at140238a47band 2 at2733ac2fe, and the patch round's new phrases hit their 2 lines.\n- Derived gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon the final headbd8604213(3 paths) derives 40 commands, the same set as on2733ac2fe. All 40 ran locally and exited 0, with exit codes captured before any pipe.\n ---ranreconciliation: "40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN".\n - On the first head, four commands first answered PREREQUISITE NOT MET (exit 3) because@objectstack/lint,@objectstack/formulaand@objectstack/client-reactwere unbuilt:check:doc-formula-expressions,check:doc-security-posture,check:skill-examplesandcheck:docs-transcript-drift. After building those closures, all four re-ran green. Onbd8604213the closures were built first, and all 40 passed on the first run.\n - The doc gates among the 40:check:doc-frontmatter,check:doc-anchors,check:doc-authoring,check:docs-single-h1,check:docs-spec-enumerations,check:docs-transcript-drift,check:doc-formula-expressions,check:doc-security-posture, speccheck:docsandcheck:yaml-examples, andcheck:nul-bytes.\n- Governed surfaces:node scripts/pm/check-governed-merges.mjs --branch claude/issue-23739-currency-docsonbd8604213reports NOT governed (0 of 3 paths), 12 changed lines.\n- Left to CI: the full farm, the type-check lanes and Build Docs.\n\n## Acceptance notes\n\n- The sweep's two remaining over-claims (types.mdx:289,field-type-decision-tree.mdx:146) are fixed in the patch round above.field-type-decision-tree.mdx:228stands, per the seat's verdict.\n-git grep -n -i -E \"exchange.rate|currency conversion|multi-currency\" -- content/docsfound one hit, the card's ownindex.mdx:121, which this PR removes.content/docs/data-modeling/seed-data.mdx:109(ExchangeRateCache) is a user-defined object in a seed example, not a platform claim.\n\n---\n_Generated by Claude Code_\n","deviations": [
"PR body NOT updated, though the patch-round dispatch ordered
pr_update. .claude/agents/os-dev.md says, unconditionally: 'PR 正文 dev 只写一次,在开 PR 那一笔,⛔ 不 PATCH;事后要改的报告点名改法,席位代写'. The same file says that when a dispatch conflicts with it, the file wins and the conflict is named in the report. Named here; the seat writes the body from pr_body_replacement (see pr_body_change). Separately, scripts/pm/fleet-write/ops.mjs has nopr_updaterow; a pull body goes throughissue_patch.","Line drift: the dispatch's
types.mdx:289is :289 at origin/main 140238a and :291 at 2733ac2 (round 1 added 2 lines above it). It is the same line, the currency section's first line, and that is the one edited.","Attribution as in round 1: the commit carries AGENTS.md's model-free trailer pair, not the harness reminder's model-named Co-Authored-By.",
"The report comment was posted with the shared checkout's scripts/pm/post-stamped.mjs (still at 9bb9370; read-only use after my worktree was removed). Its import closure diffs empty against BASE 140238a."
]
}
objectstack-fleet commentedon Oct 16, 2026
Landed: PR #23762 →
6362c66ca8(Fixes #23739) ·domain:devxseat 2 ·session_016PVjjuVPte5XEJvaQx1E4t· 2026-10-16T01:28Zadded_to_merge_queue2026-10-16T01:05:25Z)origin/main6362c66ca8isorigin/main's head (one parent,7428da02a)bd8604213:content/docs/protocol/objectql/index.mdxfd77bfb546,content/docs/protocol/objectql/types.mdx8b8fac14d5,content/docs/data-modeling/field-type-decision-tree.mdx0f78c00037git grep -n -E "exchange rates|amount \+ currency code|Money amount with currency code|Money amounts with currency code" -- content/docsonorigin/main: 0 hitscompleted.pm:dispatchedis removed in this act.currencyfield carries exchange rates or stores a code per value. Four sentences in three pages now say whatfield.zod.tsships: a bare number, in the field's fixed currency or the tenant default.docspublishes nothing by itself (skip-changeset).vitest.config.tslets a stale@objectstack/coredist decide its verdicts — #7668 fixed one, nothing stops the next #7849 Response bodies are never checked against the schemas that declare them — staged plan, not a repo-wide sweep #3877).Generated by Claude Code