6.3 KiB
6.3 KiB
Tasks — Retry withCircuitBreaker convenience wrapper
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened.
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.
Audit Note (2026-05-02)
Minimal progress — DEFERRED, do not archive.
packages/flow/retry/src/hascircuit-breaker/andretry/modules but nocompose/directory.grep -rln "withCircuitBreaker\|with-circuit-breaker\|compose/" packages/flow/retry/src/returns zero hits.bunx turbo lint typecheck test --filter=@suaveplan/retryis GREEN — but the composition wrapper is not implemented.- All 38 tasks remain: register
RETRY.CIRCUIT_OPEN_AFTER_RETRYcode,with-circuit-breaker.tsfeature module withWithCircuitBreakerOptions+WithCircuitBreakerHandle<TReturn>,cause-chainedRetryErrorwrapping ofCircuitBreakerError, OTel span + counter, subpath export, sibling.md, conformance + property tests, dependent sweep across all 8 retry consumers.
1. Spec authoring
- 1.1 Read
../suaveplan/packages/universal/retry/src/retry/retry.ts(withCircuitBreakersection, L222-L260) - 1.2 Read
openspec/specs/retry/spec.mdand the existingcircuit-breaker.spec-conformance.test.ts - 1.3 Read
openspec/AGENTS.mdSections 13 and 14 - 1.4 Draft
proposal.mdfromopenspec/templates/proposal.md - 1.5 Fill in
## Source mappingtable — every requirement cites a line range OR appears in the "no source line" table with justification - 1.6 Draft
specs/retry/spec.mdfromopenspec/templates/spec-delta.md - 1.7 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles
2. Implementation
- 2.1 Register error code
RETRY.CIRCUIT_OPEN_AFTER_RETRYinsrc/errors/ - 2.2 Create
src/compose/with-circuit-breaker/feature module withwith-circuit-breaker.ts,with-circuit-breaker.types.ts,with-circuit-breaker.schemas.ts,with-circuit-breaker.md,index.ts - 2.3 Define
WithCircuitBreakerOptionsandWithCircuitBreakerHandle<TReturn>interfaces inwith-circuit-breaker.types.ts - 2.4 Implement
withCircuitBreaker(options)— composeretry(() => breaker.execute(fn), retryConfig)withresult.valueunwrap; retry-only path whenretryConfigomitted - 2.5 Wrap
CircuitBreakerErrorraised inside the retry pass withRetryError(RETRY.CIRCUIT_OPEN_AFTER_RETRY)carryingcause - 2.6 Wire OTel span
retry.with-circuit-breaker.executeand counterretry.with-circuit-breaker.invocation - 2.7 Add subpath export
./compose/with-circuit-breakertopackage.json::exports - 2.8 Re-export
withCircuitBreakerfrom the root barrelsrc/index.ts
3. Tests
- 3.1 Unit tests at
src/compose/with-circuit-breaker/with-circuit-breaker.test.ts— composition runs retries inside breaker; breaker opens mid-retry and short-circuits remaining attempts; retry-only path whenretryConfigomitted;getCircuitBreaker()returns live handle - 3.2 Spec-conformance tests at
src/compose/with-circuit-breaker/with-circuit-breaker.spec-conformance.test.ts—RetryError(RETRY.CIRCUIT_OPEN_AFTER_RETRY)thrown withcauseset toCircuitBreakerError; OTel span emitted; counter increments perexecute; subpath export resolves - 3.3 Property test (fast-check): for any
maxAttempts∈ [1, 10] and any pre-failed attempts ∈ [0, maxAttempts], the wrapper either returns success OR throwsRetryErrorwith the breaker state observable viagetCircuitBreaker() - 3.4 Every
it(...)carries{ timeout: 5000 }
4. Documentation
- 4.1
src/compose/with-circuit-breaker/with-circuit-breaker.md— sibling doc (≥200 words, basic + advanced examples, full API reference) - 4.2 README links
./compose/with-circuit-breaker/with-circuit-breaker.mdunder## Modules - 4.3 Update
CLAUDE.md@suaveplan/retryentry to mentionwithCircuitBreaker
5. Pre-archive verification (the 5-gate triple)
- 5.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 5.2
bunx turbo lint typecheck test --filter=@suaveplan/retry— green - 5.3
bunx turbo test:coverage --filter=@suaveplan/retry— 100/100/100/100 - 5.4
bun scripts/openspec-conformance.ts @suaveplan/retry— exit 0, noerrorfindings - 5.5
bunx turbo build --filter=@suaveplan/retry— green - 5.6
bunx turbo test --filter=...@suaveplan/retry— every dependent (auth, jwt-tenant, db-sqlite, surrealdb, network-websockets, webhook-client, storage-s3, elevenlabs) passes
6. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 6.1 Generics threaded through
WithCircuitBreakerHandle<TReturn>andexecute<T>;expectTypeOfconfirmsexecutedoes not narrow tounknown - 6.2 Zod schema wired at
WithCircuitBreakerOptionsboundary - 6.3
RETRY.CIRCUIT_OPEN_AFTER_RETRYthrown by at least one production path - 6.4 Strict-mode behaviors tested (with retry vs without retry; breaker open during first attempt vs after multiple attempts)
- 6.5 OTel span and counter emitted from production paths and asserted with a real in-memory
TracerProvider+MeterProvider - 6.6 Lifecycle dedup — N/A (factory returns a fresh handle per call)
- 6.7 No retroactive tick — every box above reflects work done in this change
7. Archive
- 7.1 Fold spec delta into
openspec/specs/retry/spec.md - 7.2 Move
openspec/changes/retry-circuit-breaker-wrapper/→openspec/archive/<yyyy-mm-dd>-retry-circuit-breaker-wrapper/ - 7.3 Refresh
MANIFEST.mdif dep set changed (no expected change)