5.5 KiB
5.5 KiB
Tasks: enterprise-currency
Change ID: enterprise-currency
Package: @suaveplan/currency
Target path: finance/currency/currency
Prerequisites on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/retry, @suaveplan/logger
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree per
proposal.md. package.jsonat0.1.0,"sideEffects": false, exports"."+ engine + provider subpaths.- Deps: workspace + catalog as listed.
- DevDeps:
vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,fast-check catalog:testing,@suaveplan/testing workspace:^,@suaveplan/money workspace:^. tsconfig.jsonextendinglibrary.json;vitest.config.tswithcreateTestConfig().- Scripts:
lint,typecheck,test,test:coverage,build,docs:check.
2. Types and errors
- Define all interfaces and Zod schemas (
ConverterConfigSchema,ConvertOptionsSchema) insrc/types/currency.types.ts. - Write
currency.types.md(≥200 words, both examples, API reference). src/error-codes.ts: five codes;registerCurrencyErrorCodes(); eager call.
3. Rate cache
- Implement
RateCacheinsrc/engine/rate-cache/rate-cache.ts:get(from, to)/set(from, to, rate)/prune()/invalidate(from?, to?).- TTL check on
get; prune on set when cache exceeds 1000 entries. - OTel counter
currency.cache_hits_total/currency.cache_misses_total.
- Write
rate-cache.test.ts: TTL expiry, prune, invalidate, hit/miss tracking. - Write
rate-cache.md(≥200 words).
4. Triangulation
- Implement BFS triangulation in
src/engine/triangulate/triangulate.ts:findPath(cache, from, to): string[] | null— BFS over cached pairs to find shortest path; max 3 hops.triangulateRate(cache, path): number— multiply rates along path.
- Write
triangulate.test.ts: direct found; one-hop; two-hop; no path returns null; cycle avoidance. - Write
triangulate.md(≥200 words).
5. Circuit breaker
- Implement per-provider
ProviderCircuitBreakerinsrc/engine/circuit-breaker/circuit-breaker.ts:- States: closed → open (after N consecutive failures) → half-open (after cooldown) → closed.
wrap(fn): Promise<T>— skips call when open; re-tries in half-open.- Uses
@suaveplan/retryfor backoff calculation.
- Write
circuit-breaker.test.ts: consecutive failures open circuit; cooldown half-open; success resets. - Write
circuit-breaker.md(≥200 words).
6. Providers
- Implement
InMemoryProviderandcreateStaticProviderin respective feature dirs. - Write unit tests +
.mdfor each.
7. CurrencyConverter
- Implement
CurrencyConverterinsrc/engine/converter/converter.ts:- Constructor validates config via
ConverterConfigSchema; callsregisterCurrencyErrorCodes()eagerly. convert(from, to, amount, options?):- Check cache; if hit, return without provider call.
- For each provider (skip open circuit): call
fetchRates({ symbols: [to], base: from }); on success cache result. - If no direct rate found: BFS triangulate from cache.
- If still not found: throw
CURRENCY_TRIANGULATION_IMPOSSIBLE. - Honour
AbortSignalbetween each provider attempt. - Return
ConversionResultwithproviderName,triangulatedThrough. - OTel span
currency.convert; countercurrency.converts_totalwith{direct, triangulated}.
convertBatch(requests): group by provider calls, execute, map results.setVirtualRate(symbol, rate): update cache; emitcurrency:rate:updatedevent via@suaveplan/events.invalidateCache(from?, to?): delegate toRateCache.
- Constructor validates config via
- Write
converter.types.ts. - Write
converter.test.ts(≤500 lines): direct convert; cache hit; triangulation; AbortSignal; circuit breaker open; virtual rate; batch; all providers fail throws. - Write
converter.md(≥200 words, simple + advanced, API reference).
8. Integration + property tests
tests/integration.test.ts: three-provider fallback chain; circuit breaker in play; virtual rate update.tests/property.test.ts(fast-check):convert(from, to)thenconvert(to, from)yields approximately inverse;convertBatchresults equal individual converts.
9. Docs + verify + publish
- All
.mdper layout; rootREADME.mdwith quick-start, provider guide, BFS triangulation explanation, API table. bunx turbo lint --filter=@suaveplan/currency— zero errors.bunx turbo typecheck --filter=@suaveplan/currency— zero errors.bunx turbo test --filter=@suaveplan/currency— all pass.bunx turbo test:coverage --filter=@suaveplan/currency— 100%.bun scripts/publish.ts --package @suaveplan/currency.- Update
CLAUDE.md; archive toopenspec/archive/2026-04-24-enterprise-currency/.