Skip to content

fix(service-analytics): a derived sum or difference over one fixed currency code carries that code on its column - #28122

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-uoae54
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-uoae54

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

Fixes #28049

domain:server#3 · os-elon-musk · session_016WV4d1NUiRegpdMBrWq7FZ

Clause-②: no (the response comes to say what content/docs/data-modeling/currency.mdx L180–181 already declares: "A derived sum or difference over one fixed code carries that code")
Metadata-protocol: none
Skills-docs: none. git grep -n -i currency skills/ hits skills/objectstack-ui/rules/dashboards.md L298–306 and skills/objectstack-data/rules/field-types.md L180–188; both state the measure / field chain and the refusals, and neither says a derived measure carries no code, so neither becomes false. content/docs/api/data-api.mdx L497–509 (the currency chain) likewise says nothing about derived measures and stays true.

维护者速读

  • 改了什么:数据集里一个衍生度量(derived),如果是 sum / difference,且其所有金额操作数都固定为同一个币种(例如两个固定 EUR 的度量相加),查询结果的这一列现在带上该币种(currency: 'EUR')。线上查询和 Studio 草稿预览一致。
  • 为什么改:文档早已写明“同一固定币种上的衍生求和/差带该币种”,但实测修复前该列在线上是不带币种的裸数字,前端只能不带货币符号显示欧元。编译期守卫早已算出这个币种,本次让结果列直接读同一个解析结果,不再另算一遍。
  • 风险与回滚:只给原来缺币种的这类衍生列补上标签;比率、乘积、混合租户币种、记录币种、动态字段上的显式币种等其余衍生列都保持原答案(测试逐一钉住)。不改 spec、不改作者能写什么。回滚即 revert 本 PR。
  • 先看:packages/services/service-analytics/src/measure-currency-coherence.ts(解析抽成 measureCurrencyResolver),analytics-service.ts 的 enrichResultColumns 币种链(derivedCode)。
  • 两分钟验证:pnpm --filter @objectstack/service-analytics exec vitest run src/__tests__/record-currency-analytics.test.ts,预期 52 passed(新增 3 条:两种策略与草稿预览下 eur_twice / eur_net 列为 EUR,6 个对照列无币种)。
  • 需要你做的:确认本 PR 对 analytics(dataset): a derived measure that declares its own currency over operands resolving to one fixed code compiles and prints that code's amounts labelled as the declared currency #28019 尚未发布的 changeset 的更正(见 Acceptance notes 第一条;check-empty-changeset 因此按设计保持红,等人工确认)。

What changes

  • measure-currency-coherence.ts. The compile guard's per-measure currency resolution (resolvedOf, monetaryOperands) moves out of assertMeasureCurrenciesCoherent into measureCurrencyResolver(dataset, declaredCurrency), unchanged, with one reader added: fixedCodeOf(m), the one fixed code a measure resolves to, else undefined. The guard now destructures the same resolver. Module-internal; not exported from the package entry.
  • The refusal of a derived currency contradicting its operands' fixed code (PR fix(service-analytics): refuse a derived measure currency that contradicts its operands' fixed code #28043) said "restate the operands' EUR as the measure's currency, or delete it (the column then carries no currency code)". After this change that parenthetical is false, so it now reads "delete the measure's currency (the column takes the operands' EUR), or restate EUR." The field-level refusal beside it already spells its fix this way.
  • analytics-service.ts. The declaredCurrency adapter over sourceFieldMeta that compile() handed the compiler becomes one private member, and enrichResultColumns builds a measureCurrencyResolver over the same adapter. A derived measure's column is monetary when fixedCodeOf answers, and the chain is explicit measure currency → field fixed code → derived fixed code → tenant. The guard already refuses a derived currency that contradicts the resolved code, so the first and third links cannot disagree.
  • record-currency-analytics.test.ts. New block "a derived measure column takes the currency its operands resolve to": native and ObjectQL strategies plus the draft preview. Pins EUR on a sum over a field-fixed and a restated EUR measure, a difference, and a restated derived; pins no currency and no currencyField on a fixed+tenant sum, a tenant+tenant sum, a sum of explicit JPY over a dynamic field, a ratio, a product, and a record-mode sum. Values checked too ({ USD: 20, EUR: 180 }). The existing message assertion moves to the new wording.
  • .changeset/28049-derived-sum-fixed-currency-column.md (new, patch) and .changeset/28019-derived-measure-fixed-currency.md (one sentence corrected, see below).

Tests

Red on main, green after, ablation
  • New block on the unfixed tree (origin/main 8cb8c7f4, test only): 3 failed / 49 skipped (filtered). The draft preview failed AssertionError: eur_twice: expected undefined to be 'EUR'. That first draft also selected a derived-over-derived measure, which failed both live strategies earlier, in the executor (second acceptance note), so it was dropped from the block. The ablation below is the clean red for the final block on all three paths.
  • Fixed head 670a7790: record-currency-analytics.test.ts 52 passed.
  • Ablation on the committed head, scripts/ablation-replace.mjs on analytics-service.ts (const derivedCode = m.derived ? currencies.fixedCodeOf(m) : undefined; → const derivedCode = undefined as string | undefined; // ABLATION; anchor 1 → 0, blob 98e0c551 → 885a2a1f): 3 failed / 49 passed. Restored: blob back to 98e0c551 = HEAD, git diff HEAD empty. The suite imports the service from src (relative imports), so no dist rebuild was in the path.
  • pnpm --filter @objectstack/service-analytics test: 196 files passed, 4762 passed / 282 skipped. pnpm --filter @objectstack/service-analytics typecheck (includes src/__tests__): exit 0.

Verification

Gates at head 670a779
  • dispatch-gates --commands derived 62 families; all run with exit codes recorded, after turbo run build --filter='@objectstack/dogfood^...' --filter='@objectstack/runtime^...'.
  • 60 exit 0. check:dual-build-cjs-loads exit 3 (no dist for seven unrelated packages, e.g. @objectstack/studio): NOT MEASURED locally, CI runs it. check-empty-changeset --base origin/main exit 1: the deliberate correction below, red by design.
  • --ran: 62 derived, 61 run, 1 NOT-MEASURED (derived from the recorded exit 3), 0 unrun.
  • pnpm lint: exit 0.
  • The head is 6 commits behind origin/main; none touches service-analytics or the corrected changeset.

Acceptance notes

  • A pending release note corrected (needs a person's confirmation). .changeset/28019-derived-measure-fixed-currency.md (PR fix(service-analytics): refuse a derived measure currency that contradicts its operands' fixed code #28043, not yet released) told readers the refusal's fix was "restate the operands' code as the measure's currency, or delete it (the column then carries no currency code; a derived column takes no code from its operands on the wire)". This PR makes that parenthetical false, so the sentence now reads "delete the measure's currency (the column takes the operands' code), or restate that code." Nothing else in that note changed. check-empty-changeset refuses any edit to another PR's changeset and stays red until a person confirms; restoring the old sentence would publish a false statement in the same release as this fix.
  • Derived-over-derived on the live query. The compile guard resolves a derived measure built on another derived one, and the currency docs describe that shape. A live queryDataset selecting such a measure fails on both strategies: "Measure 'eur_twice' on cube … aggregates field 'eur_twice', which object … does not have". The draft preview serves it. That is the executor, outside this card, and is reported to the seat. The new block does not use the shape.
  • The column type stays number, as for a base sum over a currency field. currency is what makes a column monetary on the wire.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (Not touched.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the repo's own Chinese reader section).
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_016WV4d1NUiRegpdMBrWq7FZ


Generated by Claude Code

…rrency code carries that code on its column

A derived measure has no field to lend a currency code, so
enrichResultColumns minted its column monetary only when the measure
restated `currency`. A sum of two EUR-fixed measures reached the wire as a
bare number, though the currency docs say it carries EUR.

The compile guard's resolution of each measure's currency is lifted into
measureCurrencyResolver, which both the guard and the column chain read: a
derived measure that resolves to one `fixed` code now carries it, on the
live query and the draft preview. Every other derived measure keeps its
answer. The guard's refusal of a contradicting derived `currency` no longer
says the column would carry no code once the `currency` is deleted.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4d1NUiRegpdMBrWq7FZ
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-analytics, touching 6 documentable anchor(s).

⛔ 3 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17/17-5.mdx (via AnalyticsService (symbol, a top-level class))
  • content/docs/releases/v17/17-6.mdx (via AnalyticsService (symbol, a top-level class))
  • content/docs/releases/v17/17-7.mdx (via AnalyticsService (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 91a423ef6b94a86be8874f1ac6449b406a8971c8 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c369804aa6ef15d92e7d4221fd6c7e9a4ee176b3 — the merge of head 670a779093bce77b73cb2fd370e68510c8f69e62 into base 91a423ef6b94a86be8874f1ac6449b406a8971c8, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c369804aa6ef15d92e7d4221fd6c7e9a4ee176b3 && git checkout c369804aa6ef15d92e7d4221fd6c7e9a4ee176b3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 91a423ef6b94a86be8874f1ac6449b406a8971c8 670a779093bce77b73cb2fd370e68510c8f69e62 && git checkout -B drift-repro 91a423ef6b94a86be8874f1ac6449b406a8971c8 && git merge --no-ff 670a779093bce77b73cb2fd370e68510c8f69e62

node scripts/docs-audit/affected-docs.mjs --json 91a423ef6b94a86be8874f1ac6449b406a8971c8

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 91a423ef6b94a86be8874f1ac6449b406a8971c8 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract-review-claim: session_016WV4czuTWNgKR22fKv7jia · head 670a779093bce77b73cb2fd370e68510c8f69e62 · 2026-10-30T13:22Z

The lane seat writes the TIER_DEFAULT same-head review record that confirms this PR's DELIBERATE CORRECTION of the pending note .changeset/28019-derived-measure-fixed-currency.md (the diff changes code too, so the confirmation is a review record that judges each rewritten sentence; references/landing-operations.md). Clause-②: no; no packages/spec/src/** or governed path in the diff.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract review · DELIBERATE CORRECTION confirmation

Served-tier: TIER_DEFAULT
Head-sha: 670a779093bce77b73cb2fd370e68510c8f69e62
Local-runs: none
Scope: packages/services/service-analytics/src/analytics-service.ts, packages/services/service-analytics/src/measure-currency-coherence.ts, packages/services/service-analytics/src/tests/record-currency-analytics.test.ts, .changeset/28049-derived-sum-fixed-currency-column.md, and the rewritten pending note .changeset/28019-derived-measure-fixed-currency.md (from #28043, merged, not yet released)

Owed because this PR rewrites a pending release note it made false and also changes code: the confirmation is a same-head review record that judges each rewritten sentence. Clause-②: no; the diff touches no packages/spec/src/** and no governed path, so the tier is TIER_DEFAULT, written by the lane seat.

① Rewritten sentences, judged one by one

Note: .changeset/28019-derived-measure-fixed-currency.md, the first bullet's last sentence (the only line changed in that file).

  • Before: "… and the fix: restate the operands' code as the measure's currency, or delete it (the column then carries no currency code; a derived column takes no code from its operands on the wire)."
  • After: "… and the fix: delete the measure's currency (the column takes the operands' code), or restate that code."
  • Judgment: the old sentence is false at this head, the new one is true. enrichResultColumns (analytics-service.ts) now sets fc.currency from currencies.fixedCodeOf(m) when a derived measure declares no currency and its operands resolve to one fixed code, so "takes no code from its operands on the wire" no longer holds. With the measure's currency deleted the column carries the operands' code (pinned: eur_twice / eur_net → EUR on every strategy and on the draft-preview path). Restating the code still compiles (eur_twice_restated → EUR, and the note's next bullet, unchanged, says so). The reorder puts the no-config fix first, matching the field-level sentence in content/docs/data-modeling/currency.mdx ("Delete the measure's currency, and the column takes the field's code"). Nothing else in the note moved: the BREAKING banner, the refusal's scope, its code DATASET_INVALID / 400 and the "Not judged" paragraph are untouched and still true.

Refusal message: measure-currency-coherence.ts, guard 1 derived branch (what the note quotes).

  • Before: "… so the column would print them as {declared}: restate the operands' {code} as the measure's currency, or delete it (the column then carries no currency code)."
  • After: "… so the column would print them as {declared}: delete the measure's currency (the column takes the operands' {code}), or restate {code}."
  • Judgment: true, and it matches the note. Same refusal, same code and status, same trigger; only the remedy text changes, and it now says what the column does. The pin moved with it (record-currency-analytics.test.ts, the toContain on the new wording).

New note: .changeset/28049-derived-sum-fixed-currency-column.md. Each sentence matches the diff: the column carries the one fixed code on the live query and the draft preview; the code is read from the compile guard's own resolution (measureCurrencyResolver, shared with assertMeasureCurrenciesCoherent); the "Unchanged" list is the test's six controls (mixed fixed + tenant, two tenant amounts, an explicit code over a dynamic field, a record-mode amount, a ratio, a product).

② Derived judgments

  • One derivation. assertMeasureCurrenciesCoherent and the column now read one resolver (measureCurrencyResolver); the refactor moves the existing memoised resolution out unchanged and adds fixedCodeOf. The column cannot claim a code compile did not judge.
  • Order of precedence. mc.currency ?? meta?.defaultCurrency ?? derivedCode ?? context?.currency: a declared measure currency still wins (and a contradicting one is refused at compile); the tenant currency is reached only when nothing else resolves, as before.
  • Fails safe. fixedCodeOf answers only for a fixed code; tenant-currency, record-mode, mixed and ratio / product measures stay unlabelled, as before.
  • Clause-② no stands. currency is an existing optional member of a result column; no member, error code or accept set is added. measureCurrencyResolver is not re-exported from the package entry (src/index.ts). The column now says what the currency page and the field-level rule already say.
  • Wrong: none found.

③ Semver level

  • @objectstack/service-analytics patch, Clause-②: no, Metadata-protocol: none, line-initial. The LEVEL AXIS step of Check Changeset is green at this head.

④ Designed red: gate and cause

  • Gate: Check Changeset (pr-automation.yml, job changeset-check), step check-empty-changeset, run 41061995413 / job 124083774429.
  • Cause: this PR changes .changeset/28019-derived-measure-fixed-currency.md, a pending note it did not add. That is the DELIBERATE CORRECTION class: the code change made the note's sentence false, so it was rewritten in the same stroke and must not be restored. The script says so itself: it is red by design on the pushed branch and "stays red either way". This record is the confirmation the remedy asks for. The other steps of that job (ADR-0087 registration, the live allow-major read, the major guard) ran and are green.
  • pr-automation.yml triggers on pull_request only, so the gate does not run on merge_group.

⑤ Design judgment

Implemented-by: claude/project-thread-uoae54
Reviewed-by: session_016WV4czuTWNgKR22fKv7jia (lane seat)

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Seat ACCEPT · PR #28122 · head 670a779093bce77b73cb2fd370e68510c8f69e62 · 2026-10-30T13:55Z

维护者速读

  • 改了什么: 数据集派生度量(sum / difference)的各操作数若都固定为同一币种(如都是 EUR),查询结果列现在带上该币种代码 EUR;以前是不带币种的裸数字,前端会把欧元当无币种数字打印(analytics: a derived sum over one fixed-currency code reaches the wire with no currency code, though the docs say it carries that code #28049)。实时查询与 Studio 草稿预览一致。
  • 为什么改: 币种文档早已写明「同一 fixed 币种上的派生 sum/difference 带该币种」,回答却没有做到;编译期守卫已经算出了这个币种,只是结果列没用上。
  • 风险与回滚: 结果列直接读编译守卫同一个解析结果(measureCurrencyResolver),列上写的就是编译判过的;度量自己声明的 currency 仍优先;租户币种、记录币种、混合币种、ratio / product 仍不带。回滚即还原 service-analytics 两个源文件。
  • 先看: analytics-service.ts 的 enrichResultColumns 中 derivedCode 一段,和 measure-currency-coherence.ts 抽出的 measureCurrencyResolver。
  • 两分钟验证: 跑 record-currency-analytics.test.ts 新增的 describe(三种策略 + 草稿预览,3 个应带币种、6 个对照不带)。
  • 需要你做的: 无。本 PR 改写了尚未发布的说明 .changeset/28019-derived-measure-fixed-currency.md 的一句(旧句在本修复后为假),Check Changeset 按设计红;同 head 复核记录 6523892478 已逐句判定并确认,按本期落地授权带红入队。

席位核对

  • Shape: draft → main, Fixes #28049. 5 files (+133/−20): analytics-service.ts, measure-currency-coherence.ts, one test file, a new changeset, and the one-sentence correction of the pending 28019 note. check-governed-merges --pr 28122: NOT governed.
  • Premise: re-measured on main by the thread: a derived sum over two EUR-fixed measures reached POST /analytics/dataset/query with no currency on its column.
  • File surface: within the claim (service-analytics source, tests, .changeset/*).
  • Declarations: Clause-②: no (currency is an existing optional column member; no new member, error code or export from the package entry), Metadata-protocol: none, @objectstack/service-analytics patch. Check Changeset's LEVEL AXIS step agrees.
  • DELIBERATE CORRECTION: note .changeset/28019-derived-measure-fixed-currency.md (fix(service-analytics): refuse a derived measure currency that contradicts its operands' fixed code #28043, merged, unreleased). Before: "…or delete it (the column then carries no currency code; a derived column takes no code from its operands on the wire)." After: "…delete the measure's currency (the column takes the operands' code), or restate that code." Confirmed sentence by sentence in the same-head TIER_DEFAULT record 6523892478 (PASS), which also judges the refusal message rewritten in measure-currency-coherence.ts.
  • Carrying the red (all three conditions hold): (1) scripts/check-empty-changeset.mjs describes this class as red by design on the pushed branch ("this gate stays red either way"); (2) pr-automation.yml runs on pull_request only, so Check Changeset does not run on merge_group; (3) record 6523892478 names the gate (job 124083774429) and the cause. The job's other steps (ADR-0087 registration, allow-major read, major guard) are green.
  • Gate check-runs on this head: 48 total, 0 pending; all success or path-filter skipped except Check Changeset (the designed red above). mergeable_state: unstable from that red only.
  • Out of scope: analytics: a live dataset query selecting a derived measure whose of names another derived measure is refused ("aggregates field … which object does not have"), though the draft preview serves it #28127 (a nested derived measure refused on the live query), carded by the seat.
  • Review request: the card thread requested os-elon-musk before the seat's instruction reached it; Tier S needs no human review, and the relay cannot withdraw a request. It is not a merge condition.
  • Landing: Tier S, under the maintainer's landing grant for this term (2026-10-29 22:05Z, seat thread). Ready + auto-merge through the relay.

ACCEPT

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 13:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 96367f1 Oct 30, 2026
48 of 49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-uoae54 branch October 30, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

analytics: a derived sum over one fixed-currency code reaches the wire with no currency code, though the docs say it carries that code

1 participant