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).
Generated by Claude Code
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
Parse is not idempotent:
parse(parse(x))throws for an inputparse(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()returnsObjectSchemaBase.parse(withDefaults)— parse OUTPUT. Every*.object.tsmodule exports create() output, andobjectstack build's defineStack validation parses those objects again. #9689's PR #11406 hit exactly this with its master_detailset_nullrejection: 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 materializedset_nulland parse #2 read it as authored. The currency twin has not fired only because no example app authors a barecurrencyMode: '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., noprecision).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).
Generated by Claude Code