Repository navigation
fix(service-analytics): a dataset refuses a measure currency that contradicts its field's fixed code, and a derived measure across two different fixed codes - #27991
Conversation
…cting its field's fixed code, and a derived measure across two different fixed codes Both compile-time guards in measure-currency-coherence.ts gain their fixed arm (ADR-0144 open item 12, ruled letter A). The two chain pins move off the contradicting fixed shape: currency-mode-relay restates the fixed code, and query-dataset's chain pin moves to a dynamic field. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyL63KrU8pu6fycLHW
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 10 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 d937ded5d5f41b036e4ae14b521762e6d643a3b4 && git checkout d937ded5d5f41b036e4ae14b521762e6d643a3b4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5e1342a6e07b287e0b829332ac565872f568666f c053f4cabaa4929a80aabba4320f406846a74d12 && git checkout -B drift-repro 5e1342a6e07b287e0b829332ac565872f568666f && git merge --no-ff c053f4cabaa4929a80aabba4320f406846a74d12
node scripts/docs-audit/affected-docs.mjs --json 5e1342a6e07b287e0b829332ac565872f568666f |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract reviewServed-tier: Face: narrowing ( ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing holds against the published spec on Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Seat ACCEPT · PR #27991 · head
|
Requested by Elon · project thread
Fixes #27579
Clause-②: yes (narrowing)
Metadata-protocol: narrowed
Skills-docs: follow-up owed — skills/objectstack-data/rules/field-types.md, skills/objectstack-ui/rules/dashboards.md, content/docs/data-modeling/currency.mdx, content/docs/api/data-api.mdx
维护者速读
fixed币种的拒收。度量写的currency和被汇总字段的fixed币种不同,拒收;派生度量(任何运算)的两个金额操作数落在两个不同的fixed币种,拒收。都返回DATASET_INVALID/ 400,并给出改法。packages/services/service-analytics/src/measure-currency-coherence.ts(两个fixed分支与fixed标记),packages/services/service-analytics/src/__tests__/record-currency-analytics.test.ts(三道门的 pin 与对照)。pnpm --filter @objectstack/service-analytics exec vitest run src/__tests__/record-currency-analytics.test.ts,预期 42 个用例全绿。What changes
assertMeasureCurrenciesCoherent(measure-currency-coherence.ts), called once fromcompileDataset, gains thefixedarm of each guard. The record-mode arms from #27338 are unchanged.currencydiffers from its field'sfixedcode refuses withDATASET_INVALID/ 400. The message names the measure, the field, the object and both codes, and the fix: delete the measure'scurrency, and the column takes the field's code. Acurrencythat restates thefixedcode is accepted.sum,difference,ratio,product), whose monetary operands resolve to two differentfixedcodes refuses withDATASET_INVALID/ 400, naming both operands, their codes and the fix (derive it from measures in one currency, or drop the operand in the other). A derivedsum/differenceover onefixedcode resolves to that code, so a derived measure built on it is judged at the next level. Acountoperand is not compared.fixedcode is. A resolved code now carriesfixed: true when it is the field'sfixedcode, restated by the measure or not. A derivedsum/differenceisfixedwhen every operand is. Shape 2 compares onlyfixedcodes.fixedcode against the tenant currency; a measurecurrencyover adynamicfield. Because the ruling lists the second as legal, an operand whose code is such an explicit one is not afixedcode, and Shape 2 does not compare it (see Acceptance notes).80b98fb8, dropped at507ed8f9):currency-mode-relay.test.ts'sm_explicit_fixedrestates the fixed field'sEURinstead of contradicting it withJPY, andquery-dataset.test.ts's chain pin moves to adynamicfield.@objectstack/service-analyticsminor, with a BREAKING paragraph, both declaration lines, and the ADR-0087 markernot-required (no-migration-prescription).Tests
record-currency-analytics.test.ts, guard block (real engine over SQLite, throughAnalyticsServicePlugin):currencyover adynamicfield not judged (control).sumof onefixedcode; afixedoperand restated by its measure code compares by that one code (control); an explicit code over adynamicfield against afixedone is not judged (control); amount per invoice over eachfixedcode, and afixedamount with the tenant currency, accepted (controls).queryDataset, with and withoutpreviewDrafts, refuses both shapes withDATASET_INVALID/ 400 naming the measure; a restated code is served withcurrency: 'EUR'on both (control).fixedshapes are replaced by the pins above.Acceptance notes
examples/**,packages/platform-objects/**,packages/runtime/src/seed-datasets.ts,ui/examples/**and the skills evals. 16 files declaremeasures:, and none sets a measurecurrency. The two derived measures in the tree (chart-gallery.dataset.ts,revenue-pulse.dataset.ts) are both ratios of two counts. A deployed tenant's stored datasets cannot be surveyed from the repository: a stored dataset with either shape now refuses at registration and at query, and its message names the edit.fixedcodes", and lists "a measurecurrencyover adynamicfield" as legal. feat(spec,service-analytics): record-currency analytics — grouped answers name a per-row currency, the cube door refuses an ungrouped amount aggregate, compile guards on measure currencies #27338's first draft compared any two resolved codes, so it also refused a derived measure over an explicitJPYon adynamicfield and afixedUSD. This PR follows the ruling's words: onlyfixedcodes are compared, and a CONTROL pins that pair as accepted. Refusing that pair too would narrow further, and would need its own ruling.skills/objectstack-data/rules/field-types.mdsays "Under'fixed'/'dynamic'a measure's owncurrencywins", which this PR makes false for a contradictingfixedcode.skills/objectstack-ui/rules/dashboards.md(the currency chain comment),content/docs/data-modeling/currency.mdx(Roll-ups and datasets) andcontent/docs/api/data-api.mdx(the currency chain) describe the record-mode refusals only. None is in this card's file surface. Grep:git grep -n -iE "measure.{0,40}currency|currency.{0,40}measure" -- 'skills/**' 'content/docs/**'.dataset-compiler.ts. The comment above theassertMeasureCurrenciesCoherentcall names only the record-mode arms. It is outside the file surface and is incomplete rather than false, so this PR leaves it.Verification
Head
c053f4ca(basebacb3e6a).Red on the base, ablations, restore
measure-currency-coherence.tsofbacb3e6a: 10 red, 83 green over the three files. Red: Shape 1 at registration, Shape 2 for each of the four ops, Shape 2 one level up, and both shapes atqueryDatasetand at the draft preview. Every control was green (restated code,dynamicfield,countoperand, tenant currency, explicit code over adynamicfield).scripts/ablation-replace.mjs(anchor hit 1 → 0, restored blob == HEAD,git diff HEADempty after each). The tests import the source, so no build was needed between legs.queryDataset, preview), 39 green.fixedpair disabled: 7 red (four ops, one level up,queryDataset, preview), 35 green.dynamicfield markedfixed: 1 red (the CONTROL that pins it as not judged), 41 green.Package suite, gates, dogfood
pnpm --filter @objectstack/service-analytics test: 196 files, 4752 passed, 282 skipped.typecheckexit 0 (itsincludeissrc, tests included).node scripts/pm/dispatch-gates.mjs --commands: 59 derived families, all run.--ranreconciliation: "59 derived, 58 run, 1 NOT-MEASURED, 0 UNRUN". 58 exited 0. NOT MEASURED:check:dual-build-cjs-loads, exit 3 (prerequisite: a whole-tree build; this run built only the dogfood closure and@objectstack/service-analytics). CI runs it.pnpm lint: exit 0.(narrowing)arm),pnpm --filter @objectstack/dogfood testunder the verify lock atc053f4ca: VERDICT command-exit 0, Test Files 357 passed and 3 skipped (360), Tests 2875 passed and 20 skipped (2895), no failures.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyL63KrU8pu6fycLHW