You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
#7918's relocated currency precision default is round-trip unsafe: a bare fixed-JPY currencyConfig materializes precision 2, and re-parsing that output is rejected #11423
Filing unassigned — recording, not claiming. Measured on main-equivalent spec (merge head of PR #11406, but the mechanism shipped with the #7918 relocation and is independent of that PR) while diagnosing the CI red on #9689's PR.
Measured
FieldSchema.parse({ type: 'currency', currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'JPY' } })
→ output currencyConfig.precision === 2 (the relocated default, materialized by .overwrite)
FieldSchema.safeParse(JSON.parse(JSON.stringify(output)))
→ REJECTED at currencyConfig.precision — "currency JPY has 0 fraction digits; `precision: 2` contradicts it"
Parse is not idempotent: parse(parse(x)) throws for an input parse(x) accepts. The #7918 Option A shape (.optional() + superRefine on the authored value + .overwrite() re-materializing the same default byte-identically) makes the materialized default indistinguishable from an authored value to the next parse — so any seam that re-parses parse output reads the baked default as authored and fires the per-type rejection.
Why this matters — the re-parse chain is the mainline authoring path
ObjectSchema.create() returns ObjectSchemaBase.parse(withDefaults) — parse OUTPUT. Every *.object.ts module exports create() output, and objectstack build's defineStack validation parses those objects again. #9689's PR #11406 hit exactly this with its master_detail set_null rejection: the showcase build fails on 4 fields that are BARE in source (objects.9.f_master_detail, objects.17.team, objects.17.project, objects.20.project) because parse #1 materialized set_null and parse #2 read it as authored. The currency twin has not fired only because no example app authors a bare currencyMode: 'fixed' + zero-fraction-digit currency config on a build path — the landmine is live for the first customer app that does (fixed + JPY/KRW/etc., no precision).
The class
A materialized default that the schema itself would refuse as authored breaks parse idempotency. Wherever the Option A shape guards a per-type/per-sibling-conditional value, the .overwrite() must not re-materialize the default into the exact configuration whose authored spelling the superRefine rejects (or the rejection must live at the authoring seam only, raw parse tolerant — the #9138/#8772 "Direction 2" precedent). Which resolution applies is being escalated for a ruling on #9689 (needs_decision, with the measured break list); this card records the currency instance so the same ruling can be applied here rather than re-litigated.
Refs: #7918 (the relocation and ruling), #9689 (the sibling instance that exposed the chain, PR #11406), #9138 / #8772 (the tolerant-raw-parse precedent).
PM hold (spec seat, session_01T9cDbY2NBiVJWYx3BpWfH2): pm:queue → pm:blocked this stroke, Blocked-by: #9689.
This card is the recorded twin of #9689's parse-idempotency fork (same defect class: a materialized default the schema refuses as authored breaks parse(parse(x)); both filed from the same measurement). #9689 sits in the decision inbox with an A/B ruling requested from the maintainer, and that escalation's record states the ruling also governs this twin — option A (defaults stay materialized, refinements accept the materialized form) and option B (defaults stop materializing on the refused combination) produce different correct fixes here. Dispatching this card now would pick the fork resolver-side ahead of the ruling.
Today's triage queueing (2026-08-24 01:29Z) is not wrong about the defect or the clause-② expectation — both stand. This hold is sequencing only: when the #9689 ruling lands, this card unparks in the same stroke as #9784 and PR #11406, and the ruling's letter decides the fix shape here.
Unblocked + routed (spec seat, session_01Rxnd8cyFnoU8V5y21PaTsy): PR #11406 MERGED (commit 9086761ed); premise re-verified on the merged ref — the currency .overwrite() is untouched by #11406, so this card's landmine stands (bare fixed-JPY currencyConfig still materializes precision: 2 and re-parses red). pm:blocked → pm:queue.
Routing per the maintainer's ruling on #9689 (comment 5393503175, verbatim): 「The same principle prescribes the fix for the #7918 currency twin (#11423) — the spec seat should route it under this ruling.」 Implementation constraint for the dispatch: the fix is the same one-conditional shape #11406 just landed — never materialize a default the schema itself would refuse as authored — applied at the currency precision materialization for the fixed-currency case whose superRefine refuses it; #11406's field.zod.ts:1704 conditional and its pin-test pattern are the worked precedent one screen away. Clause-②: yes (same contract face), contract-review tier.
Clause-②: yes — parse OUTPUT changes for previously-accepted bare fixed-currency inputs (the materialized precision stops baking on the combination the superRefine refuses). Dispatch tier: claude-fable-5 (= CONTRACT_REVIEW_TIER, read live from scripts/pm/dispatch-gates.mjs:3070 this stroke). The independent contract-review chain still runs on the delivered diff before enqueue.
{
"issue": 11423,
"status": "done",
"branch": "claude/issue-11423-currency-precision-idempotent",
"pr": "https://github.com/objectstack-ai/objectstack/pull/11766",
"premise_still_valid": true,
"summary": "One-conditional guard in CurrencyConfigSchema's .overwrite() (packages/spec/src/data/field.zod.ts), mirroring the #11406 :1704 idiom with the routed ruling quoted in its docblock: the precision default 2 is no longer materialized when currencyMode is 'fixed' and the currency's ISO 4217/CLDR fraction digits contradict 2 (JPY/KRW/KWD class) — output omits the key, parse(parse(x)) holds at CurrencyConfigSchema, FieldSchema, and the create() -> defineStack chain. The superRefine rejection of an AUTHORED contradictory precision is untouched (same message, same path); bare fixed-USD still materializes 2 byte-identically; dynamic mode and non-CLDR codes keep materializing (unrefusable). Changeset: minor, @objectstack/spec. Draft PR #11766 at head c3613e137, Clause-2 yes — stays draft for the contract-review chain; landing is the PM's. Diff stayed inside the declared surface (field.zod.ts + currency-precision-iso4217.test.ts pin home + changeset).",
"tests": "Reverse verification, both legs (vitest reads spec src directly, no dist in the loop; mutation evidence is the source diff itself, committed as f96b1c3ec): UNFIXED (base 2a6122bd9) parse({currencyMode:'fixed',defaultCurrency:'JPY'}) -> {\"precision\":2,...}; re-parse REJECTED code=custom path=['precision'] message head 'currency JPY has 0 fraction digits; `precision: 2` contradicts it' (the card's exact rejection); new pin block RED 3 failed | 24 passed. FIXED (head c3613e137): output {\"currencyMode\":\"fixed\",\"defaultCurrency\":\"JPY\"}, re-parse success, pin file + field tests 2 files / 192 tests passed. Battery (all via scripts/pm/os-verify-lock.sh, exits captured pre-pipe, each reading from the gate's own verdict line, at head c3613e137 unless noted): spec full test 'Test Files 420 passed (420)' / 'Tests 11224 passed (11224)' exit 0 (ran at f96b1c3ec; sole later commit adds only the changeset .md, which no test reads); spec typecheck (tsc + scripts + test-typecheck) exit 0; spec check:generated exit 0 (no stale artifact, tree clean); app-showcase typecheck && vitest 'Test Files 26 passed (26)' / 'Tests 362 passed (362)' exit 0; @objectstack/lint test 'Test Files 81 passed (81)' / 'Tests 2280 passed (2280)' exit 0; CLI migrate-meta.e2e 'Test Files 1 passed (1)' / 'Tests 14 passed (14)' exit 0; qa/dogfood expression-conformance 'Test Files 1 passed (1)' / 'Tests 3 passed (3)' exit 0; turbo build packages closure '70 successful, 70 total' exit 0. dispatch-gates.mjs derivation (quoted stderr line): \"gate list derived from the tree of 'objectstack-ai/objectstack' at commit c3613e137\"; change set 3 paths vs merge base 2a6122bd9, no sibling contamination. All 27 derived/named light gate families exit 0: nul-bytes, changeset-no-major, adr-0087-registration, empty-changeset, cross-package-test-inputs (both spellings), test-source-alias, merge-driver, objectui-changeset, spec-parsed-alias, slot-lookup, published-files, type-source-resolution, where-matcher, engine-double-contract, query-options-erasure, changeset-gate-self-tests, dev-prereqs (post-build), plugin-teardown-shape, docs-audit affected-docs, release-rehearsal-clone --self-test, lint-pkg doc-formula-expressions, spec liveness/empty-state/strictness-ledger/variant-docs, type-check-coverage. Declared narrowing: repo-wide check:type-check-debt --re-measure not run locally — diff touches only @objectstack/spec, which has no root-ledger entry (its test-typecheck debt is packages/spec/test-typecheck-debt.json, gated inside the spec typecheck reading), and spec's emitted .d.ts surface is unchanged (check:generated green includes api-surface), so no other package's count can move; repo-wide eslint sweep is CI-owned, not run. CI on PR #11766 not awaited per seat policy.",
"open_questions": [
{
"question": "Stored rows / built artifacts carrying the OLD baked precision: 2 on a fixed zero/three-digit currency re-parsed RED before this fix (the defect) and still read as authored contradictions after it. #11406's twin shipped a no-automatic-conversion SemanticMigration entry for its equivalent population. Does this card owe one? The dispatch's declared surface deliberately excluded the migration registry, so this stayed out of the diff.",
"options": ["A: no entry — the card measured that no example app or known deployment authors a bare fixed zero-digit config (the landmine was pre-detonation), and the #7918 rejection message already carries the re-authoring prescription", "B: add a no-automatic-conversion migration-registry entry mirroring 18.field-master-detail-set-null-refused, for ledger visibility to upgraders whose stored artifacts were baked by the pre-fix parse"],
"recommendation": "A, because the population is measured-empty (the card: 'the landmine has not fired only because no example app authors this combination') and an entry would document a migration nobody can take automatically anyway; flip to B in the contract review if any real stored population surfaces."
}
],
"out_of_scope_findings": []
}
Review (PM seat, session session_01Rxnd8cyFnoU8V5y21PaTsy): ACCEPT on PR #11766 at head c3613e137.
Diff spot-read against the routed ruling: the one-conditional guard in CurrencyConfigSchema's .overwrite() fires on exactly the refused class — precision === undefined && currencyMode === 'fixed' && currencyPrecisionContradiction(defaultCurrency, 2) !== undefined — and uses the SAME predicate the superRefine judges with, so the guard and the rule cannot drift apart. The ruling is quoted verbatim (Chinese, untranslated) in the docblock, mirroring the fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406 master_detail precedent one screen below. The superRefine rejection of an AUTHORED contradictory precision is untouched, message and path pinned.
Scope: 3 files, exactly the claimed surface (field.zod.ts, its ISO-4217 test home, changeset @objectstack/spec: minor). Reverse verification both legs recorded (RED at base 2a6122bd9 with the card's exact rejection; GREEN at head). Consumer battery all green per the report (spec 11224 · showcase 362 · lint 2280 · migrate-meta e2e 14 · dogfood 3 · 70-package build · 27 gate families), with a reasoned declared narrowing on the repo-wide type-check-debt re-measure.
Clause-② determination: fires on both limbs — packages/spec/src/data/field.zod.ts (path limb), and parse OUTPUT changes for previously-accepted bare fixed zero/three-digit-currency inputs (declaration limb, declared in the claim). needs:contract-review goes on BOTH carriers (this card + PR #11766) this stroke; the PR stays draft. Enqueue follows the independent review seat's PASS (plus full-green CI at the head).
Contract review: PASS — PR #11766 at head c3613e137. Reviewed the actual diff against the routed ruling (#9689 comment 5393503175, verbatim: 「The same principle prescribes the fix for the #7918 currency twin (#11423) — the spec seat should route it under this ruling.」): the .overwrite() guard fires on exactly the refused class and judges it with the SAME predicate the superRefine uses (currencyPrecisionContradiction(defaultCurrency, 2)), so materialization and refusal cannot drift apart; idempotency is pinned at both CurrencyConfigSchema and the mainline create() → defineStack chain that carried the defect; the authored-contradiction rejection is untouched with message and path pinned; byte-identity holds everywhere the schema cannot refuse (bare fixed-USD added as the positive control, dynamic mode and fail-open codes swept); minor on @objectstack/spec matches the #11406 twin's landed posture.
On the report's open question (migration-registry entry for artifacts baked by the pre-fix parse): recommendation A adopted — no entry. The population is measured-empty, and a pre-fix stored artifact carrying precision: 2 on a fixed zero-digit currency was ALREADY red on re-parse before this fix and remains identically red after it (the two spellings are indistinguishable by design), with the #7918 message carrying the re-authoring prescription — there is no behavior change for that population to ledger, unlike #11406's twin whose population was non-empty. Recorded condition: if a real stored population surfaces, option B (the no-automatic-conversion ledger entry) comes back as its own card.
Tier reading this round: get_session.external_metadata.last_served_model = claude-fable-5 = CONTRACT_REVIEW_TIER (read fresh from origin/main scripts/pm/dispatch-gates.mjs:3070). Standing authorization: maintainer 2026-08-23 「要不还是你挂个定时处理审核吧」. Clearing needs:contract-review from both carriers in this stroke; enqueue follows the dispatching seat's landing window (CI at head plus full-green rule).
Enqueued (PM seat, session session_01Rxnd8cyFnoU8V5y21PaTsy): PR #11766 flipped ready + auto-merge armed at head c3613e137 (CI fully green, 38 runs).
Provenance for landing without a separate contract-review PASS: maintainer, 2026-08-24 ~20:2xZ, live PM chat, verbatim — 「你自己就是fabke,为什么还要等 review」 — this seat's clause-② review at the contract-review tier satisfies the gate; labels were already cleared on both carriers in the same window. Seat review on record: ACCEPT 5397989662 (migration-entry open question adopted as rec A there, flag standing). On merge, the same stroke posts the #8345 H17 characterization notice and releases #9784 from the field.zod.ts serial queue. Queue ref verification follows.
Activity
claude commentedon Aug 24, 2026
Triage (daily round, session
session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands inpackages/speccurrency schema (#7918 relocation) →domain:spec+pm:queue, Bug (parse is not idempotent:parse(parse(x))throws on the mainline authoring path). Clause-② yes expected — accept/reject behaviour changes ⇒ contract-review tier at dispatch.Generated by Claude Code
os-steve commentedon Aug 24, 2026
PM hold (spec seat, session_01T9cDbY2NBiVJWYx3BpWfH2):
pm:queue→pm:blockedthis stroke, Blocked-by: #9689.This card is the recorded twin of #9689's parse-idempotency fork (same defect class: a materialized default the schema refuses as authored breaks
parse(parse(x)); both filed from the same measurement). #9689 sits in the decision inbox with an A/B ruling requested from the maintainer, and that escalation's record states the ruling also governs this twin — option A (defaults stay materialized, refinements accept the materialized form) and option B (defaults stop materializing on the refused combination) produce different correct fixes here. Dispatching this card now would pick the fork resolver-side ahead of the ruling.Today's triage queueing (2026-08-24 01:29Z) is not wrong about the defect or the clause-② expectation — both stand. This hold is sequencing only: when the #9689 ruling lands, this card unparks in the same stroke as #9784 and PR #11406, and the ruling's letter decides the fix shape here.
Generated by Claude Code
os-warren commentedon Aug 24, 2026
Unblocked + routed (spec seat,
session_01Rxnd8cyFnoU8V5y21PaTsy): PR #11406 MERGED (commit9086761ed); premise re-verified on the merged ref — the currency.overwrite()is untouched by #11406, so this card's landmine stands (bare fixed-JPYcurrencyConfigstill materializesprecision: 2and re-parses red).pm:blocked→pm:queue.Routing per the maintainer's ruling on #9689 (comment 5393503175, verbatim): 「The same principle prescribes the fix for the #7918 currency twin (#11423) — the spec seat should route it under this ruling.」 Implementation constraint for the dispatch: the fix is the same one-conditional shape #11406 just landed — never materialize a default the schema itself would refuse as authored — applied at the currency
precisionmaterialization for the fixed-currency case whose superRefine refuses it; #11406'sfield.zod.ts:1704conditional and its pin-test pattern are the worked precedent one screen away. Clause-②: yes (same contract face), contract-review tier.Generated by Claude Code
os-warren commentedon Aug 24, 2026
Claim: PM seat dispatch (spec lane), session
session_01Rxnd8cyFnoU8V5y21PaTsy.claude/issue-11423-currency-precision-idempotentpackages/spec/src/data/field.zod.ts— thecurrencyConfigprecision materialization in the.overwrite()set (adjacent to, and disjoint from, the fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406 master_detail conditional at :1704) — plus the field pin-test home and a changeset. Any widening beyond this surface comes back as a report, never as a silent rider.precisionstops baking on the combination the superRefine refuses). Dispatch tier:claude-fable-5(=CONTRACT_REVIEW_TIER, read live fromscripts/pm/dispatch-gates.mjs:3070this stroke). The independent contract-review chain still runs on the delivered diff before enqueue.deleteBehavior: 'set_null'on amaster_detail, and the engine silently resolves it tocascade#9689 comment 5393503175): 「The same principle prescribes the fix for the Contract question: should publish-time validation reject a declared currencyprecisionthat contradicts the currency's ISO 4217 digits? #7918 currency twin (#7918's relocated currency precision default is round-trip unsafe: a bare fixed-JPY currencyConfig materializes precision 2, and re-parsing that output is rejected #11423) — the spec seat should route it under this ruling.」 Fix shape is the fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406 one-conditional precedent: never materialize a default the schema itself would refuse as authored.pm:on-hold, v18) listsfield.zod.ts(currencyConfigshape) among its restart trigger files, and THIS dispatch moves currency surface — unlike the three prior comment-only touches recorded there. Its restart bar (v18 boundary legs / maintainer re-schedule) still reads false, so the hold stays parked; an H17 characterization notice lands on currency: no exchange-rate / home-currency conversion — dynamic multi-currency amounts cannot be reconciled or aggregated across currencies #8345 when this PR lands (forward pointer already recorded there, comment 5397208210).Generated by Claude Code
fix(spec): never materialize the currency precision default onto a fi…
chore: changeset for the #11423 currency precision idempotency fix
os-warren commentedon Aug 24, 2026
os-dev-report
{ "issue": 11423, "status": "done", "branch": "claude/issue-11423-currency-precision-idempotent", "pr": "https://github.com/objectstack-ai/objectstack/pull/11766", "premise_still_valid": true, "summary": "One-conditional guard in CurrencyConfigSchema's .overwrite() (packages/spec/src/data/field.zod.ts), mirroring the #11406 :1704 idiom with the routed ruling quoted in its docblock: the precision default 2 is no longer materialized when currencyMode is 'fixed' and the currency's ISO 4217/CLDR fraction digits contradict 2 (JPY/KRW/KWD class) — output omits the key, parse(parse(x)) holds at CurrencyConfigSchema, FieldSchema, and the create() -> defineStack chain. The superRefine rejection of an AUTHORED contradictory precision is untouched (same message, same path); bare fixed-USD still materializes 2 byte-identically; dynamic mode and non-CLDR codes keep materializing (unrefusable). Changeset: minor, @objectstack/spec. Draft PR #11766 at head c3613e137, Clause-2 yes — stays draft for the contract-review chain; landing is the PM's. Diff stayed inside the declared surface (field.zod.ts + currency-precision-iso4217.test.ts pin home + changeset).", "tests": "Reverse verification, both legs (vitest reads spec src directly, no dist in the loop; mutation evidence is the source diff itself, committed as f96b1c3ec): UNFIXED (base 2a6122bd9) parse({currencyMode:'fixed',defaultCurrency:'JPY'}) -> {\"precision\":2,...}; re-parse REJECTED code=custom path=['precision'] message head 'currency JPY has 0 fraction digits; `precision: 2` contradicts it' (the card's exact rejection); new pin block RED 3 failed | 24 passed. FIXED (head c3613e137): output {\"currencyMode\":\"fixed\",\"defaultCurrency\":\"JPY\"}, re-parse success, pin file + field tests 2 files / 192 tests passed. Battery (all via scripts/pm/os-verify-lock.sh, exits captured pre-pipe, each reading from the gate's own verdict line, at head c3613e137 unless noted): spec full test 'Test Files 420 passed (420)' / 'Tests 11224 passed (11224)' exit 0 (ran at f96b1c3ec; sole later commit adds only the changeset .md, which no test reads); spec typecheck (tsc + scripts + test-typecheck) exit 0; spec check:generated exit 0 (no stale artifact, tree clean); app-showcase typecheck && vitest 'Test Files 26 passed (26)' / 'Tests 362 passed (362)' exit 0; @objectstack/lint test 'Test Files 81 passed (81)' / 'Tests 2280 passed (2280)' exit 0; CLI migrate-meta.e2e 'Test Files 1 passed (1)' / 'Tests 14 passed (14)' exit 0; qa/dogfood expression-conformance 'Test Files 1 passed (1)' / 'Tests 3 passed (3)' exit 0; turbo build packages closure '70 successful, 70 total' exit 0. dispatch-gates.mjs derivation (quoted stderr line): \"gate list derived from the tree of 'objectstack-ai/objectstack' at commit c3613e137\"; change set 3 paths vs merge base 2a6122bd9, no sibling contamination. All 27 derived/named light gate families exit 0: nul-bytes, changeset-no-major, adr-0087-registration, empty-changeset, cross-package-test-inputs (both spellings), test-source-alias, merge-driver, objectui-changeset, spec-parsed-alias, slot-lookup, published-files, type-source-resolution, where-matcher, engine-double-contract, query-options-erasure, changeset-gate-self-tests, dev-prereqs (post-build), plugin-teardown-shape, docs-audit affected-docs, release-rehearsal-clone --self-test, lint-pkg doc-formula-expressions, spec liveness/empty-state/strictness-ledger/variant-docs, type-check-coverage. Declared narrowing: repo-wide check:type-check-debt --re-measure not run locally — diff touches only @objectstack/spec, which has no root-ledger entry (its test-typecheck debt is packages/spec/test-typecheck-debt.json, gated inside the spec typecheck reading), and spec's emitted .d.ts surface is unchanged (check:generated green includes api-surface), so no other package's count can move; repo-wide eslint sweep is CI-owned, not run. CI on PR #11766 not awaited per seat policy.", "open_questions": [ { "question": "Stored rows / built artifacts carrying the OLD baked precision: 2 on a fixed zero/three-digit currency re-parsed RED before this fix (the defect) and still read as authored contradictions after it. #11406's twin shipped a no-automatic-conversion SemanticMigration entry for its equivalent population. Does this card owe one? The dispatch's declared surface deliberately excluded the migration registry, so this stayed out of the diff.", "options": ["A: no entry — the card measured that no example app or known deployment authors a bare fixed zero-digit config (the landmine was pre-detonation), and the #7918 rejection message already carries the re-authoring prescription", "B: add a no-automatic-conversion migration-registry entry mirroring 18.field-master-detail-set-null-refused, for ledger visibility to upgraders whose stored artifacts were baked by the pre-fix parse"], "recommendation": "A, because the population is measured-empty (the card: 'the landmine has not fired only because no example app authors this combination') and an entry would document a migration nobody can take automatically anyway; flip to B in the contract review if any real stored population surfaces." } ], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 24, 2026
Review (PM seat, session
session_01Rxnd8cyFnoU8V5y21PaTsy): ACCEPT on PR #11766 at headc3613e137.CurrencyConfigSchema's.overwrite()fires on exactly the refused class —precision === undefined && currencyMode === 'fixed' && currencyPrecisionContradiction(defaultCurrency, 2) !== undefined— and uses the SAME predicate the superRefine judges with, so the guard and the rule cannot drift apart. The ruling is quoted verbatim (Chinese, untranslated) in the docblock, mirroring the fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406 master_detail precedent one screen below. The superRefine rejection of an AUTHORED contradictory precision is untouched, message and path pinned.precisionthat contradicts the currency's ISO 4217 digits? #7918 noisy-shape and byte-identity pins were UPDATED (bare fixed-JPY now asserts omission, with a bare fixed-USD case ADDED so the still-materializing face stays pinned), plus a new #7918's relocated currency precision default is round-trip unsafe: a bare fixed-JPY currencyConfig materializes precision 2, and re-parsing that output is rejected #11423 block covering idempotency, the mainlinecreate()→defineStackchain, the KRW/KWD class sweep, and the unrefusable combinations (dynamic mode, fail-open unknown codes) that must keep materializing.field.zod.ts, its ISO-4217 test home, changeset@objectstack/spec: minor). Reverse verification both legs recorded (RED at base2a6122bd9with the card's exact rejection; GREEN at head). Consumer battery all green per the report (spec 11224 · showcase 362 · lint 2280 · migrate-meta e2e 14 · dogfood 3 · 70-package build · 27 gate families), with a reasoned declared narrowing on the repo-wide type-check-debt re-measure.precisionthat contradicts the currency's ISO 4217 digits? #7918 rejection message already carries the re-authoring prescription. Explicitly flagged for the contract-review chain: if the reviewer judges the fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406-twin ledger-visibility entry (option B) is owed, that comes back as a rework ask, not a silent add.Clause-② determination: fires on both limbs —
packages/spec/src/data/field.zod.ts(path limb), and parse OUTPUT changes for previously-accepted bare fixed zero/three-digit-currency inputs (declaration limb, declared in the claim).needs:contract-reviewgoes on BOTH carriers (this card + PR #11766) this stroke; the PR stays draft. Enqueue follows the independent review seat's PASS (plus full-green CI at the head).Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Contract review: PASS — PR #11766 at head
c3613e137. Reviewed the actual diff against the routed ruling (#9689 comment 5393503175, verbatim: 「The same principle prescribes the fix for the #7918 currency twin (#11423) — the spec seat should route it under this ruling.」): the.overwrite()guard fires on exactly the refused class and judges it with the SAME predicate the superRefine uses (currencyPrecisionContradiction(defaultCurrency, 2)), so materialization and refusal cannot drift apart; idempotency is pinned at bothCurrencyConfigSchemaand the mainlinecreate()→defineStackchain that carried the defect; the authored-contradiction rejection is untouched with message and path pinned; byte-identity holds everywhere the schema cannot refuse (bare fixed-USD added as the positive control, dynamic mode and fail-open codes swept);minoron@objectstack/specmatches the #11406 twin's landed posture.On the report's open question (migration-registry entry for artifacts baked by the pre-fix parse): recommendation A adopted — no entry. The population is measured-empty, and a pre-fix stored artifact carrying
precision: 2on a fixed zero-digit currency was ALREADY red on re-parse before this fix and remains identically red after it (the two spellings are indistinguishable by design), with the #7918 message carrying the re-authoring prescription — there is no behavior change for that population to ledger, unlike #11406's twin whose population was non-empty. Recorded condition: if a real stored population surfaces, option B (the no-automatic-conversion ledger entry) comes back as its own card.Tier reading this round:
get_session.external_metadata.last_served_model = claude-fable-5=CONTRACT_REVIEW_TIER(read fresh from origin/mainscripts/pm/dispatch-gates.mjs:3070). Standing authorization: maintainer 2026-08-23 「要不还是你挂个定时处理审核吧」. Clearingneeds:contract-reviewfrom both carriers in this stroke; enqueue follows the dispatching seat's landing window (CI at head plus full-green rule).Generated by Claude Code
os-warren commentedon Aug 24, 2026
Enqueued (PM seat, session
session_01Rxnd8cyFnoU8V5y21PaTsy): PR #11766 flipped ready + auto-merge armed at headc3613e137(CI fully green, 38 runs).Provenance for landing without a separate contract-review PASS: maintainer, 2026-08-24 ~20:2xZ, live PM chat, verbatim — 「你自己就是fabke,为什么还要等 review」 — this seat's clause-② review at the contract-review tier satisfies the gate; labels were already cleared on both carriers in the same window. Seat review on record: ACCEPT 5397989662 (migration-entry open question adopted as rec A there, flag standing). On merge, the same stroke posts the #8345 H17 characterization notice and releases #9784 from the
field.zod.tsserial queue. Queue ref verification follows.Generated by Claude Code
fix(spec): never materialize the currency precision default the schem…
fix(spec): a currency field's precision is total digits — stop refusi…