Files
2026-05-27 23:29:53 +01:00

5.1 KiB

Tasks: enterprise-money

Change ID: enterprise-money Package: @suaveplan/money Target path: finance/currency/money Prerequisites on Verdaccio: @suaveplan/error, zod


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create full directory tree per proposal.md.
  • package.json at 0.1.0, "sideEffects": false, exports "." + core/money + core/currency-registry + core/currencies + core/money-range + formatting + allocation + schemas + serialization.
  • Deps: @suaveplan/error workspace:^, zod catalog:forms, tslib catalog:build.
  • DevDeps: vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, fast-check catalog:testing, @suaveplan/testing workspace:^.
  • tsconfig.json extending library.json; vitest.config.ts with createTestConfig().
  • Scripts: lint, typecheck, test, test:coverage, build, docs:check.

2. Error hierarchy

  • src/error-codes.ts: define MoneyErrorCode union; implement MoneyError, CurrencyMismatchError, UnknownCurrencyError, AllocationError, ParseError extending SuaveplanError; registerMoneyErrorCodes(); eager call.

3. Currency registry + ISO data

  • Implement CurrencyRegistry in src/core/currency-registry/currency-registry.ts: internal Map<string, CurrencyInfo>; all methods per API surface.
  • src/core/currencies/currencies.ts: ISO 4217 data (≥50 common currencies minimum, full list preferred); seed the global CurrencyRegistry.global() on module load.
  • Write tests: register/get/virtual/multiplier; getCurrencyOrThrow throws UnknownCurrencyError; virtual vs ISO separation.

4. Money class

  • Implement Money in src/core/money/money.ts:
    • fromMajor(value, currency, registry?): use BigInt(Math.round(Number(value) * Number(multiplier))) for number; use exact decimal-string parse for string inputs (no float intermediate).
    • All arithmetic: BigInt operations; throw CurrencyMismatchError on mismatched currencies.
    • toDecimalString(): format as exact decimal (e.g. "12.34" for USD with 2 minor units).
    • toJSON() / Money.withRegistry(registry).
    • Negative-zero guard: ensure 0n never becomes -0n.
  • Write money.test.ts (≤500 lines): all operators, edge cases, currency mismatch, negative-zero, decimal-string round-trip.

5. Formatting + parse

  • Implement formatMoney, formatMoneyCompact, parseMoney in src/formatting/formatter/formatter.ts.
  • parseMoney: handle comma decimals, grouped separators, currency symbol removal via Intl.NumberFormat.
  • Write formatter.test.ts: multiple locales, compact, parseMoney round-trip.

6. Allocation

  • Implement all five allocators + sumMoney + minMoney + maxMoney + applyRounding in src/allocation/allocator/allocator.ts.
  • Remainder redistribution: no minor unit lost or invented.
  • Write allocator.test.ts: even/ratio/percentage edge cases, remainder, sum invariant.

7. MoneyRange

  • Implement MoneyRange in src/core/money-range/money-range.ts: constructor validates min <= max; all methods per API surface.
  • Write money-range.test.ts: contains, intersect (null on no overlap), union, toArray.

8. Zod schemas + serialization

  • Implement MoneyLikeSchema, CurrencyCodeSchema, FormatOptionsSchema in src/schemas/money-schemas/money-schemas.ts.
  • Implement moneyToJSON / moneyFromJSON in src/serialization/json/json.ts.
  • Write tests for each.

9. Property-based tests

  • tests/property.test.ts (fast-check, { timeout: 30000 }):
    • a + b - b = a for arbitrary same-currency Money pairs.
    • allocateEvenly(m, n) sums to m.
    • allocateByRatios(m, rs) sums to m.
    • toDecimalString round-trip identity.
    • parseMoney(formatMoney(m, locale), m.currency, locale) equals m for common locales.
    • MoneyRange intersect is commutative and associative on overlapping ranges.

10. Docs + verify + publish

  • All .md files per layout; root README.md with arithmetic quick-start, allocation example, MoneyRange, API table.
  • bunx turbo lint --filter=@suaveplan/money — zero errors.
  • bunx turbo typecheck --filter=@suaveplan/money — zero errors.
  • bunx turbo test --filter=@suaveplan/money — all pass.
  • bunx turbo test:coverage --filter=@suaveplan/money — 100%.
  • bun scripts/publish.ts --package @suaveplan/money.
  • Update CLAUDE.md; archive to openspec/archive/2026-04-24-enterprise-money/.