Initial Commit
This commit is contained in:
77
openspec/changes/retry-circuit-breaker-wrapper/tasks.md
Normal file
77
openspec/changes/retry-circuit-breaker-wrapper/tasks.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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 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.
|
||||
|
||||
## Audit Note (2026-05-02)
|
||||
|
||||
**Minimal progress** — DEFERRED, do not archive.
|
||||
|
||||
- `packages/flow/retry/src/` has `circuit-breaker/` and `retry/` modules but no `compose/` directory.
|
||||
- `grep -rln "withCircuitBreaker\|with-circuit-breaker\|compose/" packages/flow/retry/src/` returns zero hits.
|
||||
- `bunx turbo lint typecheck test --filter=@suaveplan/retry` is GREEN — but the composition wrapper is not implemented.
|
||||
- All 38 tasks remain: register `RETRY.CIRCUIT_OPEN_AFTER_RETRY` code, `with-circuit-breaker.ts` feature module with `WithCircuitBreakerOptions` + `WithCircuitBreakerHandle<TReturn>`, `cause`-chained `RetryError` wrapping of `CircuitBreakerError`, 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` (`withCircuitBreaker` section, L222-L260)
|
||||
- [ ] 1.2 Read `openspec/specs/retry/spec.md` and the existing `circuit-breaker.spec-conformance.test.ts`
|
||||
- [ ] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14
|
||||
- [ ] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
|
||||
- [ ] 1.5 Fill in `## Source mapping` table — every requirement cites a line range OR appears in the "no source line" table with justification
|
||||
- [ ] 1.6 Draft `specs/retry/spec.md` from `openspec/templates/spec-delta.md`
|
||||
- [ ] 1.7 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [ ] 2.1 Register error code `RETRY.CIRCUIT_OPEN_AFTER_RETRY` in `src/errors/`
|
||||
- [ ] 2.2 Create `src/compose/with-circuit-breaker/` feature module with `with-circuit-breaker.ts`, `with-circuit-breaker.types.ts`, `with-circuit-breaker.schemas.ts`, `with-circuit-breaker.md`, `index.ts`
|
||||
- [ ] 2.3 Define `WithCircuitBreakerOptions` and `WithCircuitBreakerHandle<TReturn>` interfaces in `with-circuit-breaker.types.ts`
|
||||
- [ ] 2.4 Implement `withCircuitBreaker(options)` — compose `retry(() => breaker.execute(fn), retryConfig)` with `result.value` unwrap; retry-only path when `retryConfig` omitted
|
||||
- [ ] 2.5 Wrap `CircuitBreakerError` raised inside the retry pass with `RetryError(RETRY.CIRCUIT_OPEN_AFTER_RETRY)` carrying `cause`
|
||||
- [ ] 2.6 Wire OTel span `retry.with-circuit-breaker.execute` and counter `retry.with-circuit-breaker.invocation`
|
||||
- [ ] 2.7 Add subpath export `./compose/with-circuit-breaker` to `package.json::exports`
|
||||
- [ ] 2.8 Re-export `withCircuitBreaker` from the root barrel `src/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 when `retryConfig` omitted; `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 with `cause` set to `CircuitBreakerError`; OTel span emitted; counter increments per `execute`; 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 throws `RetryError` with the breaker state observable via `getCircuitBreaker()`
|
||||
- [ ] 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.md` under `## Modules`
|
||||
- [ ] 4.3 Update `CLAUDE.md` `@suaveplan/retry` entry to mention `withCircuitBreaker`
|
||||
|
||||
## 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, no `error` findings
|
||||
- [ ] 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>` and `execute<T>`; `expectTypeOf` confirms `execute` does not narrow to `unknown`
|
||||
- [ ] 6.2 Zod schema wired at `WithCircuitBreakerOptions` boundary
|
||||
- [ ] 6.3 `RETRY.CIRCUIT_OPEN_AFTER_RETRY` thrown 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.md` if dep set changed (no expected change)
|
||||
Reference in New Issue
Block a user