Repository navigation
[Epic] multi-currency (shape C′): record currency, dated exchange rates, write-time home-currency projection — 17 children, R1/R2/R3 release pairing (ruling 6465431966) #8345
Description
Epic: multi-currency (shape C′) · pm:epic · opened by the director seat (summon #53, session_01QSeKYo7qYsX6HsAneHBMP2) on ruling 6465431966 (#27119 Q1 = 乙-现在, maintainer 「同意立专题卡」 2026-10-28).
Epic PM session: session_01Utp6CbTZrFqRYNgjSaSbN5 (parent opened 2026-10-28T09:46Z; account os-bill, project seat; registered 2026-10-28T10:42Z on the maintainer's summon 「使用 pm-dispatch 派发 epic #8345」).
Territory (declared): packages/spec/src/data/field.zod.ts (the currency block: CurrencyConfigSchema, CurrencyValueSchema retirement), packages/spec/src/data/object.zod.ts (currency pointer arms), packages/spec/src/contracts/analytics-service.ts (per-row currency key), packages/objectql/src (record-validator currency arms, aggregate door, the write-time home projection, recompute), packages/platform-objects/src (sys_exchange_rate, Setup nav), packages/services/service-settings/src (home-currency lock), packages/services/service-analytics/src (cube door, grouped currency), packages/cli/src/commands/migrate (recompute command), ui/packages/{i18n,fields,plugin-dashboard,app-shell} (currency readers, form, Studio editor), examples/app-showcase (invoice), content/docs/data-modeling, one new docs/adr/* record. Spec and seam children go to the domain:spec seat's queue; the rest are dispatched by the epic PM.
维护者速读
- 改了什么: 多币种按 C′ 形状分四期交付:记录币种(金额字段指向同对象上的 ISO 4217 币种字段)→ 平台汇率表
sys_exchange_rate→ 写入时按单据日期折成本位币并存下影子列(缺汇率拒写、改汇率显式重算)→ 报表、前端与示例。 - 为什么改: 平台今天没有任何键承载「这条记录是什么币种」;跨币种求和静默算错(USD 100 + JPY 15000 = 15100)。港口客户要按客户币种开票、按日汇率对账、折本位币合并报表。
- 风险与回滚: 全部是新增声明,不写
record的应用零变化;两处必须成对发布(R1、R2,见下);浮点算钱用最小单位整数或 decimal;本位币一经使用即锁定。 - 先看: 设计 6464438764、工作量 6464936321、裁决 6465431966(都在 [Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,
fixed's CNY, the example's USD) #27119)。 - 两分钟验证: 三张不同币种的发票在列表里各显其符号,仪表盘指向影子列出本位币合计,指向原始金额不分组被拒。
- 需要您做的: 召唤
/pm-dispatch epic:#8345;批 docs(adr): ADR for multi-currency — record currency, dated rates, write-time home projection (Q0–Q4 and parameters 1–4 as ruled on #27119); dated note on ADR-0104 (epic #8345, lands in R1 first) #27183(ADR,Tier H);R1 / R2 各在一次 Version Packages 发布里合并。
Rulings this epic executes
- Shape C′ and sub-questions Q0–Q4, parameters 1–4: ruling 6465431966 on [Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,
fixed's CNY, the example's USD) #27119. Design record: 6464438764. Workload rows and precedents: 6464936321. Earlier measurement round on this card: 6041378776 / 6041482631 (the probes are this epic's fixtures). Q2 X (one rule for an unknown currency) is executed outside the epic: ui(studio): the dataset inspector storescurrency: 'USD'when the currency format is picked with no currency — store nothing and inherit the field / tenant chain (#27119 Q2 X, G1) #27156, spec(field):fixed-modecurrencyConfigwith nodefaultCurrencymaterialises'CNY'— require the code, drop the default (narrowing; #27119 Q2 X, G3) #27157 (prerequisite of M0-1), docs+examples(currency):opportunity.dataset.tshard-codescurrency: 'USD'over a tenant-currency field, and two protocol docs claim exchange rates / a stored currency code — delete the key, rewrite the two sentences (#27119 Q2 X G4 + Q3) #27158, spec(formula): acurrencyhole with no argument still formats in US dollars — refuse it at authoring in every template dialect (follow-up of #26660) #26939. - Design rules from the summon: no platform or tenant switch (
currencyMode: 'record'is the switch); no injected columns, no fixed field names — authors declare every currency-related field and reference it by pointer; "enabled currencies" is derived, not a setting; the form pre-fills the organization currency at the renderer; metadata never names the home currency.
Children (17), by release
R1 — record currency + guards (one Version Packages release; ⛔ never ship #27184 alone)
- spec(field): retire the
@deprecatedCurrencyValueSchema(still exported atfield.zod.ts:581) — ADR-0104 D1 says deleted; prerequisite of the multi-currency epic #8345 #27182 C0 · spec · retireCurrencyValueSchema· XS ·pm:queuedomain:spec - docs(adr): ADR for multi-currency — record currency, dated rates, write-time home projection (Q0–Q4 and parameters 1–4 as ruled on #27119); dated note on ADR-0104 (epic #8345, lands in R1 first) #27183 M3-4 · ADR (Tier H) · S · lands first
- spec(field):
currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184 M0-1 · spec ·recordmode +currencyField· M · Blocked-by spec(field):fixed-modecurrencyConfigwith nodefaultCurrencymaterialises'CNY'— require the code, drop the default (narrowing; #27119 Q2 X, G3) #27157, spec(field): retire the@deprecatedCurrencyValueSchema(still exported atfield.zod.ts:581) — ADR-0104 D1 says deleted; prerequisite of the multi-currency epic #8345 #27182, docs(adr): ADR for multi-currency — record currency, dated rates, write-time home projection (Q0–Q4 and parameters 1–4 as ruled on #27119); dated note on ADR-0104 (epic #8345, lands in R1 first) #27183 - objectql: record-mode guards — blank code refuses the write; ungrouped sum/avg/min/max over a record-mode field refused; summary roll-up over one refused at registration (multi-currency M0-2, epic #8345) #27186 M0-2 · objectql · guards (blank code, ungrouped aggregate, summary) · M · Blocked-by spec(field):
currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184 - analytics seam: grouped results carry a per-row currency; cube door refuses an ungrouped aggregate over a record-mode field; guards on today's surface (multi-currency M0-3, epic #8345) #27187 M0-3 · seam spec + analytics · per-row currency, cube door, today's guards · M · Blocked-by spec(field):
currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184 - ui: record-mode currency —
resolveFieldCurrencyreads the sibling code field's value; form pre-fills the organization currency; grid / drawer / dashboard / footer per-row display; Studio field editor (multi-currency M0-4, epic #8345) #27188 M0-4 · ui · per-row display, form, Studio · M–L · Blocked-by spec(field):currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184 - docs(data-modeling): the currency page — dynamic / fixed / record modes, the sibling code field, what refuses (multi-currency M0-5, epic #8345) #27189 M0-5 · docs · currency page · S · Blocked-by spec(field):
currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184
R2 — rates + write-time home projection (one release; the rate table never ships without its reader)
- platform-objects:
sys_exchange_rate{ currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185 M1-1 · platform-objects ·sys_exchange_rate+ Setup · M - import: verify
sys_import_jobservessys_exchange_rate(CSV of currency, rate, effective_date); add the importer only if it does not (multi-currency M1-2, epic #8345) #27190 M1-2 · import check · XS–S · Blocked-by platform-objects:sys_exchange_rate{ currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185 - service-settings: lock
localization.currencyonce asys_exchange_raterow or ahomeAmountdeclaration exists; Setup shows what uses it (multi-currency M1-3, epic #8345) #27198 M1-3 · service-settings · home-currency lock · M · Blocked-by platform-objects:sys_exchange_rate{ currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185, spec(field):homeAmount: { field, rateDate, rateField? }on a record-mode amount — the stored home-currency projection declared by pointer (multi-currency M2-1, epic #8345; released with M2-2) #27191 - spec(field):
homeAmount: { field, rateDate, rateField? }on a record-mode amount — the stored home-currency projection declared by pointer (multi-currency M2-1, epic #8345; released with M2-2) #27191 M2-1 · spec ·homeAmountpointer · M · Blocked-by spec(field):currencyMode: 'record'+currencyField— a record-mode amount points at a sibling ISO 4217 code field (multi-currency M0-1, epic #8345) #27184 - objectql: write-time home projection — rate lookup by document date, decimal arithmetic, minor-unit rounding, missing rate refuses, computed fields read-only (multi-currency M2-2, epic #8345) #27192 M2-2 · objectql · write-time projection (L) · Blocked-by spec(field):
homeAmount: { field, rateDate, rateField? }on a record-mode amount — the stored home-currency projection declared by pointer (multi-currency M2-1, epic #8345; released with M2-2) #27191, platform-objects:sys_exchange_rate{ currency, rate, effective_date, source } — tenant-scoped, admin-only, audited; Setup list view (multi-currency M1-1, epic #8345; released with M2) #27185 - cli:
os migrate recompute-home-amounts --object X [--since DATE] [--apply]— report then apply; rows with a missing rate are listed and skipped (multi-currency M2-3, epic #8345) #27193 M2-3 · cli · recompute · M · Blocked-by objectql: write-time home projection — rate lookup by document date, decimal arithmetic, minor-unit rounding, missing rate refuses, computed fields read-only (multi-currency M2-2, epic #8345) #27192 - migrate: adding a
homeAmountfield to an existing object is the normal add-column path; backfill = recompute — one proving test (multi-currency M2-4, epic #8345) #27194 M2-4 · migrate proof · S · Blocked-by cli:os migrate recompute-home-amounts --object X [--since DATE] [--apply]— report then apply; rows with a missing rate are listed and skipped (multi-currency M2-3, epic #8345) #27193 - ui: home field read-only with "converted at RATE on DATE"; live preview when a rate exists; Studio editor for
homeAmount(multi-currency M3-2, epic #8345; the read-only display is released with M2) #27196 M3-2 · ui · home field read-only (+ preview) · M · Blocked-by spec(field):homeAmount: { field, rateDate, rateField? }on a record-mode amount — the stored home-currency projection declared by pointer (multi-currency M2-1, epic #8345; released with M2-2) #27191, ui: record-mode currency —resolveFieldCurrencyreads the sibling code field's value; form pre-fills the organization currency; grid / drawer / dashboard / footer per-row display; Studio field editor (multi-currency M0-4, epic #8345) #27188
R3 — proof and polish
- analytics: consolidated measures over the home field work unchanged (proof); a metric over a record-mode field must name the home field or group by code (multi-currency M3-1, epic #8345) #27195 M3-1 · analytics · consolidated measures proof · S–M · Blocked-by objectql: write-time home projection — rate lookup by document date, decimal arithmetic, minor-unit rounding, missing rate refuses, computed fields read-only (multi-currency M2-2, epic #8345) #27192, analytics seam: grouped results carry a per-row currency; cube door refuses an ungrouped aggregate over a record-mode field; guards on today's surface (multi-currency M0-3, epic #8345) #27187
- examples + QA: showcase
invoicegoes multi-currency (code field, record amount, home field, document date), seeded rates, a rate-sync flow template, a platform-checklist item and a browser dogfood run (multi-currency M3-3, epic #8345) #27197 M3-3 · examples + QA · showcase invoice, rate-sync flow template, checklist, dogfood · M · Blocked-by objectql: write-time home projection — rate lookup by document date, decimal arithmetic, minor-unit rounding, missing rate refuses, computed fields read-only (multi-currency M2-2, epic #8345) #27192, ui: home field read-only with "converted at RATE on DATE"; live preview when a rate exists; Studio editor forhomeAmount(multi-currency M3-2, epic #8345; the read-only display is released with M2) #27196
Critical path: #27157 → #27184 → #27191 → #27192 → #27193 → #27197. Commitment: R1 (about 4–5 agent-days); R2 starts after the customer confirms the consolidation terms (home currency, rate source, which date drives the rate).
Release pairing rule (the only half-state risk)
A published record mode without M0-2/M0-3/M0-4 is worse than today (declared, not refused, wrong symbol); a published homeAmount key without M2-2 is a declaration the runtime never honours. The epic PM marks each R1 / R2 PR body ⛔ not released alone — pairs with …, and the maintainer merges Version Packages only when a pair is complete.
History
The 2026-08 filing (overseas port / terminal customer), the 2026-08-13 v18 scheduling, the 2026-10-07 measurement round and the 2026-10-08 pause are this card's earlier revisions and comments; the hold ended with ruling 6465431966.
Activity
hotlong commentedon Aug 13, 2026
Triage: confirming the filer's
domain:spec— M1/M2 both start frompackages/spec/src/data/field.zod.tsandcurrency-fraction-digits.ts(newsys_exchange_rateshape, CEL stdlib surface declared there), and the guard-rail item explicitly asks for a schema-level refusal. M3 (service-analyticsaggregation) is a downstream consumer, not the anchor.Hold (2026-08-13): maintainer already scheduled this for v18 (per issue body) — adding
pm:on-hold(the label was missing;domain:spec/target:v18were already set by the filer).Restart trigger files:
packages/spec/src/data/field.zod.ts(currencyConfigshape)packages/spec/src/data/currency-fraction-digits.tspackages/services/service-analytics/src/analytics-service.ts(ADR-0053 currency labeling chain)Generated by Claude Code
os-zhuang commentedon Aug 19, 2026
Hold-condition completion (triage seat, session
session_014qTKTqjme5Fp5BH9iRmy6t, on-hold weak-hit audit): the v18 park is sound and stands, but it had no executable boundary wake — the repo emits no v18 signal today (zero milestones, no v18 refs, spec at 17.0.0, no.changeset/pre.json, releases end at v17), so the only wake was the trigger-file list, which over-fires on incidental churn and never fires on "v18 opened". Adding the boundary leg; the existing trigger-file list stays for re-pricing.Restart-when: the v18 cycle opens — first true of:
.changeset/pre.jsonexists on origin/main,packages/spec/package.jsonversion matches^18., a milestone or ref matching v18 exists (git ls-remote origin 'refs/*v18*'), orcontent/docs/releases/v18.mdxis created — or a maintainer re-schedules the cardGenerated by Claude Code
os-warren commentedon Aug 24, 2026
H17 trigger-file notice (spec seat,
session_01Rxnd8cyFnoU8V5y21PaTsy, post-landing patrol): this hold's declared trigger filepackages/spec/src/data/field.zod.tswas touched by PR #11406 (merged9086761ed, 2026-08-24 ~15:0xZ). Scope of the touch: the #9689deleteBehaviorfamily only — the authored-set_null-on-master_detailparse rejection plus the ruled idempotent-materialization conditional in the.overwrite(). Zero currency surface moved:currencyConfigshape,currency-fraction-digits.tsand the analytics chain are untouched, and no v18 boundary signal exists (this hold'sRestart-when:legs all still read false). Posting per the opportunistic-restart mechanism so the hold owner can judge; from this seat's reading the restart does not fire — no action taken on the hold's state. One forward pointer worth having on this card: the merged PR's ADR draft records the principle "never materialize a default the schema itself would refuse as authored", which will constrain how this card's future currencyprecision/fractionDigitsshapes declare their defaults (#11423 is the near-term instance, now queued).Generated by Claude Code
os-warren commentedon Aug 24, 2026
H17 trigger-file notice (spec seat,
session_01Rxnd8cyFnoU8V5y21PaTsy, merge-time per the #11423 claim's declaration): this hold's declared trigger filepackages/spec/src/data/field.zod.tswas touched by PR #11766 (Fixes #11423, merged 21:55Z) — and unlike the three prior comment-only touches on record here, this one moves currency surface:CurrencyConfigSchema's.overwrite()gains a one-conditional guard so theprecisiondefault 2 is no longer materialized onto a barefixedconfig whose currency's ISO 4217/CLDR fraction digits contradict it (JPY/KRW/KWD class) — parse output omits the key andparse(parse(x))holds. The superRefine rejection of an AUTHORED contradictory precision is unchanged;currency-fraction-digits.tsand the analytics chain are untouched.Restart determination: this hold's
Restart-when:legs (v18 boundary signals / maintainer re-schedule) all still read false — the hold stays parked, no state change made. Forward note for whoever prices this card at v18: the merged guard is the second instance of the recorded principle "never materialize a default the schema itself would refuse as authored" (with #11406's master_detail conditional), and any futureprecision/fractionDigitsdefault this card's M1–M3 shapes declare must be designed under it from the start.Generated by Claude Code
os-support-ai commentedon Aug 25, 2026
H17 trigger-file notification (spec seat R5,
session_01K93P8PbH7kVjsAXkqzH1zY, 2026-08-25): this wave's dispatch for #11566 (maxLength shape + applicability tightening, ruled 2026-08-24) touchespackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Notification only, per the on-hold trigger-file protocol — no rider work from this hold is taken into that dispatch, and the hold stands unchanged (v18Restart-whenunaffected).Generated by Claude Code
os-litant commentedon Aug 25, 2026
H17 trigger-file notification (spec seat R6,
session_01NDGG54XF5gbTLdQzCtnaVV, 2026-08-25): this wave's dispatch for #11949 (+#11950 family-folded — minLength shape/applicability tightening per the maintainer's ruling B of 2026-08-25, plus the deferred maxLength ledger registration) touchespackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Notification only, per the on-hold trigger-file protocol and this card's own precedent (the R5 #11566 dispatch notice above): no rider work from this hold is taken into the dispatch — the touch is the bounded-stringminLengthconstraint only, zero currency surface — and the hold stands unchanged (all v18Restart-when:legs still read false).Generated by Claude Code
os-warren commentedon Aug 31, 2026
H17 trigger-file notification (domain:spec seat,
session_01PBjwYLS6BciTQW3c9xQiD2, 2026-08-31): this wave's dispatch for the objectui#6140 + objectui#6153 spec half (declarerows?: numberon markdown/html field metadata anddescription?: stringon select options — maintainer ruling A of 2026-08-25) touchespackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Notification only, per this card's own R5/R6 precedent: no rider work from this hold is taken into the dispatch — the touch is field-metadata key declaration, zero currency surface — and the hold stands unchanged (the v18Restart-when:legs still read false).Generated by Claude Code
zhuangjianguo commentedon Sep 3, 2026
H17 trigger-file notification (
domain:specseat, session_0174WZTU6XcFcS7g2kykC53i, 2026-09-03T18:15Z): this round's dispatch for #13916 (rewrite the false sentence "the canonical spellingdepends_onalready exists at the field level" in the option-description docblock and its two echoes in the sibling test file — fact layer only, per triage 5486900363) touchespackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Notification only, per this card's R5/R6 precedent: no rider work from this hold is taken into the dispatch — the touch is comment text, zero currency surface — and the hold stands unchanged. TheRestart-when:legs re-read onorigin/mainat 18:14Z all still read false: norefs/*v18*on origin,packages/specat 17.2.0, no.changeset/pre.json, nocontent/docs/releases/v18.mdx.Generated by Claude Code
claude commentedon Sep 4, 2026
H17 trigger-file notification (
domain:specseat, sessionsession_0174WZTU6XcFcS7g2kykC53i, 2026-09-04T00:43Z): this round's dispatch for #14168 (maintainer ruling A of 2026-09-02 —FieldSchemagains avalueDomainslot whose vocabulary is exactlySpecifierValueDomainSchema's three members, shared by reference) touchespackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Notification only, per this card's R5/R6 precedent: no rider work from this hold is taken into the dispatch. Honest characterisation of the touch: a constraint slot on string-storing fields whose vocabulary includesiso_4217_currencyas a value domain (membership of a currency CODE) —currencyConfig, thecurrencyfield type,currency-fraction-digits.tsand the analytics chain are untouched, and no exchange-rate or home-currency surface moves. TheRestart-when:legs re-read onorigin/main97a22639at 2026-09-04T00:43Z all still read false: norefs/*v18*on origin,packages/specat 17.2.0, no.changeset/pre.json, nocontent/docs/releases/v18.mdx. The hold stands unchanged.Generated by Claude Code
claude commentedon Sep 4, 2026
H17 landing notification (
domain:specseat, sessionsession_0174WZTU6XcFcS7g2kykC53i, 2026-09-04T04:09Z): the dispatch notified at 00:43Z (comment 5534029200) has landed — PR #15133 merged at 04:05:20Z, merge commit1d7e76a6, touchingpackages/spec/src/data/field.zod.ts, a declaredRestart-touchtrigger file of this hold. Landed shape, characterised honestly:FieldSchema.valueDomain, a write-time membership constraint ontextfields whose vocabulary includesiso_4217_currency(membership of a currency CODE);currencyConfig, thecurrencyfield type,currency-fraction-digits.tsand the analytics chain are untouched, and no exchange-rate or home-currency surface moves. Notification only: no rider work from this hold was taken. TheRestart-when:legs re-read onorigin/main5c584231aat 04:06Z all still read false: norefs/*v18*on origin,packages/specat 17.2.0, no.changeset/pre.json, nocontent/docs/releases/v18.mdx. The hold stands unchanged.Generated by Claude Code
16 remaining items
objectstack-fleet commentedon Oct 23, 2026
Related: the default-currency half is filed separately · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-23T15:52ZOn 2026-10-23 the maintainer kept this card's multi-currency scope on hold, and separately ruled that a new environment gets a default currency from its creator's language or region, in objectstack: 「同意乙」. That is #25495,
pm:queue. This card stayspm:on-hold.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Pointer: the shape question is now on decision card #27119 (Q1) · 2026-10-28T06:44Z
⛔ Not a claim. Hold unchanged.
fixed's CNY, the example's USD) #27119 Q1 re-asks this card's question (stay paused, or resume as C′ or A′), using this card's 2026-10-07 measurement (6041378776 and 6041482631). Triage's lean: C′ as the shape, with the hold kept until a customer pull returns.objectql/index.mdx:121, "Money with exchange rates") was never filed. It is [Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,fixed's CNY, the example's USD) #27119 Q3.Generated by Claude Code
[-]currency: no exchange-rate / home-currency conversion — dynamic multi-currency amounts cannot be reconciled or aggregated across currencies[/-][+][Epic] multi-currency (shape C′): record currency, dated exchange rates, write-time home-currency projection — 17 children, R1/R2/R3 release pairing (ruling 6465431966)[/+]objectstack-fleet commentedon Oct 28, 2026
Epic opened ·
pm:on-hold→pm:epic· director seat · summon #53 ·session_01QSeKYo7qYsX6HsAneHBMP2· 2026-10-28T09:48ZRuling 6465431966 on #27119 (Q1 = 乙-现在, maintainer 「同意立专题卡」). The body is rewritten as the epic parent (shape C′, the rulings executed, the declared territory, the 17 children by release, the pairing rule); the earlier text stays in the body's revision history. Title rewritten in the same act. Thread-read: 6462572182.
Children filed in this act: R1 #27182 #27183 #27184 #27186 #27187 #27188 #27189 · R2 #27185 #27190 #27198 #27191 #27192 #27193 #27194 #27196 · R3 #27195 #27197. Spec and seam children (#27182
pm:queue; #27184 #27187 #27191pm:blocked) carrydomain:specfor the spec seat; the rest carrypm:epicfor the epic PM,pm:blockedwhere aBlocked-by:line stands. Prerequisite outside the tree: #27157.Next: the maintainer summons
/pm-dispatch epic:#8345; that session registers its ID in the body and dispatches from #27183 and #27182.objectstack-fleet commentedon Oct 28, 2026
Epic PM · subtree triage and plan · 2026-10-28T11:20Z
Session
session_01Utp6CbTZrFqRYNgjSaSbN5(accountos-bill, seatepic:#8345). Authority: Bill (maintainer), project thread, 2026-10-28 — 「其他同意你的建议,开始派发」, 「你可以自己打domain」, 「epic的卡你统一派」, 「并发可以5」, 「你自主完成后续阶段」, and for the ADR 「写 adr 你现在没有 fable,先用 opus 写,10 小时后用 fable 复审。」Domain (the landing package;
pm:epicstays on all): #27183domain:skills(governed ADR) · #27185 #27186 #27190 #27192 #27193 #27194 #27195 #27198domain:server· #27188 #27196domain:ui· #27189 #27197domain:devx.Spec children moved into the subtree: #27184 #27187 #27191 gain
pm:epic(keepdomain:spec,pm:blocked, theirBlocked-by:); the epic PM dispatches them. #27182 stays with the spec seat that claimed it (claim 6467480691); the epic follows it to merge.Staging (each stage releasable on its own): stage 0 #27157 (other seat), #27182 (spec seat), #27183 (claimed here, 6467611450). R1: #27184 first; once its PR is up, #27186 #27187 #27188 build on its branch in parallel, #27189 alongside; all R1 PRs merge in one window, then one release. R2 waits for the customer's consolidation terms; #27191 and #27185 may be drafted early. R3: #27195 #27197. Concurrency cap 5.
docs(adr): ADR-0144 takes the three should-fixes of contract review 6…
objectstack-fleet commentedon Oct 29, 2026
epic:#8345#1 · os-bill · session_01Utp6CbTZrFqRYNgjSaSbN5
Open question for the maintainer: ADR-0144 Open item 12 (epic PM, 2026-10-29T01:30Z)
Should today's cross-currency arithmetic between
fixedfields of different codes be refused? There are two shapes:currencycontradicts afixedfield's code, andfixedcodes (e.g. afixedUSD amount minus afixedEUR amount), which ADR-0144 Context §1 counts among today's silently wrong numbers.Ruling
6465431966(#27119) did not decide this, because its C′ sentence names record-mode fields only. For R1 the epic PM confined #27187 / PR #27338's compile guards to record-mode amounts (decision6485365183on #27187; tree evidence #27338507ed8f9). Refusing thefixed-only shapes would be a narrowing of what compiles today (BREAKING for@objectstack/service-analytics), so it needs the maintainer's word. #27338's first draft (fea128ce) leaned toward refusing both. That lean is quoted here, not adopted. Nothing in R1 waits on the answer.docs(adr): ADR-0144 takes the three should-fixes of delta review 6485…
objectstack-fleet commentedon Oct 29, 2026
epic:#8345#1 · os-bill · session_01Utp6CYKQd1fNzSJRDLNwnW
Ruling confirmed: ADR-0144 Open item 12 (fixed-versus-fixed cross-currency arithmetic in dataset measures) · letter A · maintainer chose 「A 拒收,R1 之后单独立卡」 on the decision card in the project review thread 「决定 fixed 字段跨币种计算」 2026-10-29T02:42Z
Recorded by the review thread's session
session_01Utp6CYKQd1fNzSJRDLNwnW(project thread), answering the open question6485374827above. The maintainer read the six-section brief (options A / A′ / B / C / D, four axes, confidence gaps) and answered with the letter; the brief is kept in the project files (decisions/fixed-fixed-跨币种-呈批.md). ⛔ Not a claim; this comment implements nothing.A — both fixed-only shapes are refused at dataset compile, as their own card after R1.
currencywhose code differs from the aggregated field'sfixedcode →DATASET_INVALID/ 400, naming the fix (delete the measure'scurrency; the column takes the field's code). A measurecurrencythat restates thefixedcode stays accepted.fixedcodes →DATASET_INVALID/ 400, naming the fix (derive from measures in one currency, or drop the operand in the other). A non-monetary operand (acount) is not compared.fixedcode against the tenant's currency (not known at compile — "cannot answer, do not block"); a measurecurrencyover adynamicfield (the explicit code is the top of the [Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,fixed's CNY, the example's USD) #27119 Q2 X chain and stays legal). The record-mode arms are already in PR 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.compileDatasetcall (registration and request), so the refusal reaches boot registration,queryDatasetand the Studio draft preview alike.Execution parameters, fixed in this ruling:
fea128ce(measure-currency-coherence.tsfixed arms, the two moved pins incurrency-mode-relay.test.tsandquery-dataset.test.ts, the guard tests), dropped at507ed8f9.Clause-②: yes (narrowing)·Metadata-protocol: narrowed·@objectstack/service-analyticsminor with a BREAKING paragraph (the fixed group refusesmajorbefore GA,check-changeset-no-major) · ADR-0087 markernot-required (no-migration-prescription)— the prescription is the refusal message, no stored shape converts.fixed-modecurrencyConfigwith nodefaultCurrencymaterialises'CNY'— require the code, drop the default (narrowing; #27119 Q2 X, G3) #27157 / PR fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218: the full dogfood battery on the final head, and a survey of in-tree dataset metadata for both shapes (0 expected inexamples/**; a deployed tenant's stored datasets cannot be surveyed from the repository and the PR body says so).os-decision-facets
formula-return.ts),record 模式的两条护栏已在 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,A 把 fixed 模式拉平,不留双重标准。currency是 AI 作者的常见动作(G1 Studio 曾自动塞 USD,G4 示例曾被照抄);A 下写错立刻看到错误与改法。compileDataset腿上加两条分支;不与 R1 配对。Prior rulings read: 6465431966 ([Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,
fixed's CNY, the example's USD) #27119 Q1, C′ names record mode only) · 6464202770 ([Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,fixed's CNY, the example's USD) #27119 Q2 X) · measurement round 6041378776 Q6 (recommended A) · design record 6464438764 §7 · workload 6464936321 M0-3 · epic PM decision 6485365183 (analytics seam: grouped results carry a per-row currency; cube door refuses an ungrouped aggregate over a record-mode field; guards on today's surface (multi-currency M0-3, epic #8345) #27187) · ADR-0144 Open item 12 · precedent spec(field):fixed-modecurrencyConfigwith nodefaultCurrencymaterialises'CNY'— require the code, drop the default (narrowing; #27119 Q2 X, G3) #27157 / PR fix(spec)!: a fixed currency config needs its code — currencyConfig.defaultCurrency loses its CNY default #27218 (narrowing kit).Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
epic:#8345#1 · os-bill · session_01Utp6CbQvr4k5jUAV39NCn1
Ruling confirmed: ADR-0144 new open item (an existing
dynamic/fixedamount field switched torecordmode on an object that already holds rows) · letter B · maintainer chose 「B 放行,欠显式回填(荐)」 on the decision card in the project review thread 「决定切换 record 模式时的旧数据」 2026-10-29T08:39ZRecorded by the review thread's session
session_01Utp6CbQvr4k5jUAV39NCn1(project thread), answering the escalation in PR #27339's contract review6490309706③ ("Escalated to the epic PM … Whether that mode switch is refused while such rows exist, or a backfill operation is owed (the D7 parameter 1 shape), is decided nowhere in the ADR"). The maintainer read the six-section brief (options A / B / B′ / C / D, four axes, confidence gaps) and answered with the letter; the brief is kept in the project files (decisions/record-模式切换存量行-呈批.md). ⛔ Not a claim; this comment implements nothing.B — the switch is allowed; it owes an explicit, audited backfill of the code; no row is ever refused for resting.
What stays as PR #27339 / #27340 / #27338 have it at
cf5be508(the write side is the ADR-0113 D1 non-regression invariant applied to the record pair):ERR_CURRENCY_CODE_REQUIRED/ 400, prescription first) on the next write that touches the amount or the code.nullgroup of a grouped aggregate, and is outside every currency-paired filter — until it is backfilled.What the ruling adds, as its own card after R1, ⛔ landed before R2 (the R2 home projection has no code to convert such a row, and a consolidated
sumover the home field would silently omit it):@objectstack/objectql, modelled onsummary-backfill.ts: for one object and onerecord-mode amount field, write an operator-supplied ISO 4217 code into every row whose amount is not blank and whose code field is blank. The code is given explicitly (--code USD); ⛔ it is never derived from the retireddefaultCurrency, the tenant'slocalization.currencyor anything else (D8 rule 4, [Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,fixed's CNY, the example's USD) #27119 Q2 X). A row that already holds a code is never touched. Dry run by default,--applyto write, idempotent (a second run finds nothing), per-row counts (written / skipped: code present / skipped: amount blank). Nosys_migrationflag: nothing gates on the run.os migrate record-currency-codes --object <o> --field <amount> --code <ISO> [--apply]in@objectstack/cli, the same split assummary-backfill.ts/os migrate summary-nulls.packages/cliis the other lane: filed as aSeam:cross-lane half of the card.detectDestructiveObjectChanges(packages/metadata-protocol/src/protocol.ts, thesaveMetaItemPhase 3a-destructive gate) gains one finding,currency_mode_to_record— previouscurrencyConfig.currencyModenotrecord, nextrecord, field stored — answered as the existing HTTP 409destructive_changewith the existingforceremedy; the message names the three consequences above and the command in 2. It reads no rows, like its siblingsfield_type_changeandfield_required_no_default. It fires only where a previous version exists (RESTPUT /meta, Studio, package publish); a code-declared package registered at boot has no previous version to diff, and ⛔ no boot-time refusal is added — a registry that refuses on data state takes the application down after a deploy.skills/**(Tier H, one line): switching a stored field torecordleaves existing rows codeless; run the backfill.os migrate recompute-home-amounts --object X [--since DATE] [--apply]— report then apply; rows with a missing rate are listed and skipped (multi-currency M2-3, epic #8345) #27193 (R2 recompute): rows with a blank code are reported as their own class, apart from rows with a missing rate (parameter 1).Declarations (the card's to confirm against the tree): the engine function is additive; the 409 finding is a new member of an existing refusal face — the Clause-② reading of #27339's review (registering a refusal member widens the face) applies,
Clause-②: yes (widening),Metadata-protocol: none(what an author can write is unchanged;forcelifts the acknowledgement). Not judged, left open: how a cloud-managed tenant without a CLI reaches the backfill (an admin route or Studio action, M3 or later); whether the backfill writes throughengine.updateor at the driver assummary-backfill.tsdoes (the card follows the precedent it picks and says why).Not chosen: A (refuse while rows exist — no reliable refusal point on the code-package path; a Studio-only refusal is a double standard and pushes authors to the invisible copy-by-hand workaround); B′ (backfill without the 409); C (status quo — R2 consolidation silently low); D (derive the code at the switch — D8 rule 4).
os-decision-facets
required同类,平台为后者已有 409;B 在保存那一刻点名后果与命令,写入守卫触碰时带处方拒收。os migrate命令族 / Phase 3a finding 列表),零新键、零新门禁、零新 wire 码;唯一跨车道的是 cli 半,按Seam:卡。Prior rulings read: 6465431966 ([Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,
fixed's CNY, the example's USD) #27119 Q1, C′; D7 parameter 1) · 6464202770 ([Decision] Currency, all of it: resume multi-currency (#8345) and in what shape, and one rule for an amount whose currency is not known (Studio's USD, the template's USD,fixed's CNY, the example's USD) #27119 Q2 X, nothing guesses) · 6486427200 ([Epic] multi-currency (shape C′): record currency, dated exchange rates, write-time home-currency projection — 17 children, R1/R2/R3 release pairing (ruling 6465431966) #8345 Open item 12, letter A) · feat(objectql): record-currency guards — blank code refuses the write; ungrouped sum/avg/min/max over a record-mode amount refused; summary roll-up over one refused at registration #27339 contract review 6490309706 ③ · ADR-0113 D1 · ADR-0027 MigrationPlan (field_required_no_default⇒ default or backfill) · ADR-0144 D5, D7, D8 rule 4, D9, Consequences, Open item 8 · precedentssummary-backfill.ts/os migrate summary-nulls,os migrate activity-changed-fields.Generated by Claude Code