Uh oh!
There was an error while loading. Please reload this page.
fix(spec): never materialize the currency precision default the schema itself refuses — bare fixed-JPY parse is idempotent (#11423) - #11766
Conversation
…xed currency whose fraction digits refuse it (#11423) The CurrencyConfigSchema .overwrite() baked precision 2 onto a bare fixed-JPY/KRW/KWD-class config; the superRefine refuses that spelling as authored, and the two are indistinguishable by design - so parse output rejected itself on the mainline ObjectSchema.create() -> defineStack re-parse. One conditional in the .overwrite() (the #9689 master_detail precedent one screen away): the refused combination parses to output that OMITS precision; every other combination keeps byte-identity. parse(parse(x)) pinned idempotent at CurrencyConfigSchema, FieldSchema and the create() -> defineStack chain; authored contradictions stay rejected with the same message; bare fixed-USD still materializes 2 byte-identically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 126 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 005d5f65b7d4bdd958b83b95ee28c51972df64aa && git checkout 005d5f65b7d4bdd958b83b95ee28c51972df64aa
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6d0cccc7271f295cac1d46423285ee0e8c4cb78d c3613e13715d3a71d27000481e24196bd30915ab && git checkout -B drift-repro 6d0cccc7271f295cac1d46423285ee0e8c4cb78d && git merge --no-ff c3613e13715d3a71d27000481e24196bd30915ab
node scripts/docs-audit/affected-docs.mjs --json 6d0cccc7271f295cac1d46423285ee0e8c4cb78d |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11423
What
CurrencyConfigSchema's.overwrite()no longer materializes the relocatedprecisiondefault (2) onto the one combination whose authored spelling its ownsuperRefinerefuses:currencyMode: 'fixed'with adefaultCurrencywhose ISO 4217 / CLDR fraction digits contradict2(the JPY/KRW/KWD class). A bare fixed-JPY config now parses to output that OMITSprecision, soparse(parse(x))holds on the mainlineObjectSchema.create()→defineStackre-parse chain. One conditional, mirroring the #11406 master_detail guard one screen below in the same file; the superRefine rejection of an AUTHORED contradictory precision is untouched, message and path included.Governing ruling
Routed by the spec seat under the maintainer's ruling on #9689 (comment 5393503175), verbatim:
The principle, as landed by PR #11406 (commit
9086761ed): never materialize a default the schema itself would refuse as authored.Reverse verification (both legs, measured)
Unfixed source (branch base
2a6122bd9):CurrencyConfigSchema.parse({ currencyMode: 'fixed', defaultCurrency: 'JPY' })→{"precision":2,"currencyMode":"fixed","defaultCurrency":"JPY"}code: custom,path: ['precision'], message head:currency JPY has 0 fraction digits; `precision: 2` contradicts it— exactly the card's measured rejection3 failed | 24 passed(the three #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 idempotency pins), failing on the materialized2and the false re-parse, not on an unrelated errorFixed source (this branch, head
c3613e137):parse({ currencyMode: 'fixed', defaultCurrency: 'JPY' })→{"currencyMode":"fixed","defaultCurrency":"JPY"}; re-parse → success, no issues2 files / 192 tests passedBehavior matrix (pinned)
precision: 2; re-parse REJECTEDprecision; re-parse green (idempotent)precision: 2+ fixed JPYprecision: 2precision: 2, byte-identicalprecision: 2precision: 2— the superRefine cannot refuse theseContract note
Clause-②: yes — parse output changes for previously-accepted bare fixed-currency inputs; the PR stays draft, the contract-review chain runs before enqueue.
Stored rows / built artifacts that carry the OLD baked
precision: 2on a fixed zero/three-digit currency re-parsed RED before this change (that is the defect) and still read as authored contradictions after it; same population shape #11406 handled with a no-automatic-conversion migration entry on its twin. Whether this card owes its own registry entry is left to the contract review — the dispatch's declared file surface deliberately did not include the migration registry, and no consumer outsidepackages/specreadscurrencyConfig.precision(measured: analytics/dogfood read onlydefaultCurrency).Verification — real readings at head
c3613e137Gate derivation line (quoted per dispatch):
dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit c3613e137 (/home/user/objectstack-11423).— change set3 path(s) vs merge base 2a6122bd9(field.zod.ts, currency-precision-iso4217.test.ts, the changeset).Heavy runs all through
scripts/pm/os-verify-lock.sh; every exit captured before any pipe; each reading quotes the gate's own verdict line:pnpm --filter @objectstack/spec test(full)Test Files 420 passed (420)·Tests 11224 passed (11224)· VERDICT command-exit 0pnpm --filter @objectstack/spec typecheck(tsc + scripts + test-typecheck)pnpm --filter @objectstack/spec check:generatedexamples/app-showcasetypecheck && vitest runTest Files 26 passed (26)·Tests 362 passed (362)· VERDICT command-exit 0pnpm --filter @objectstack/lint testTest Files 81 passed (81)·Tests 2280 passed (2280)· VERDICT command-exit 0test/migrate-meta.e2e.test.tsTest Files 1 passed (1)·Tests 14 passed (14)· VERDICT command-exit 0qa/dogfoodtest/expression-conformance.test.tsTest Files 1 passed (1)·Tests 3 passed (3)· VERDICT command-exit 0turbo run build --filter=./packages/* --filter=./packages/*/*70 successful, 70 total· VERDICT command-exit 0Derived gate families, each exit 0 (captured pre-pipe): 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 check:liveness / check:empty-state / check:strictness-ledger / check:variant-docs · check:type-check-coverage.
One run predates the head commit by content-irrelevant bytes: the full spec suite started at
f96b1c3ec(the fix commit); the only later commitc3613e137adds.changeset/thin-yen-keeps-no-cents.md, which no test reads. Every other reading above was taken atc3613e137.Declared narrowing (CI runs the full farm regardless): repo-wide
check:type-check-debt --re-measurewas not run locally — the diff touches only@objectstack/spec, which has no entry in the root debt ledger (its test-typecheck debt lives inpackages/spec/test-typecheck-debt.json, gated by spec's owncheck:test-typecheck, executed inside the spectypecheckreading above), and spec's emitted.d.tssurface is unchanged (check:generatedgreen includescheck:api-surface), so no other package's ledger count can move from this diff. Repo-widepnpm lint(eslint sweep) is CI-owned and was not run.Generated by Claude Code