5.4 KiB
5.4 KiB
Tasks — Timing completion (rateLimit / once / times)
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.
Audit Note (2026-05-02)
Status: DEFERRED. None of rateLimit, once, times, or the RATE_LIMITED sentinel exist in packages/timing/timing/src/throttle/ (currently only contains throttle.ts, throttle.types.ts, throttle.schemas.ts, plus debounce.test.ts). 0% of work is done.
1. Spec authoring
- 1.1 Read
../suaveplan/packages/universal/timing/src/throttle/throttle.ts(rateLimit / once / times sections, L275-L346) - 1.2 Read
openspec/specs/timing/spec.mdand the archived2026-04-29-timing-spec-conformanceproposal - 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/timing/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
TIMING.RATE_LIMIT_INVALID_WINDOWinsrc/errors/ - 2.2 Create
src/throttle/rate-limit/feature module withrate-limit.ts,rate-limit.types.ts,rate-limit.schemas.ts,rate-limit.md,index.ts - 2.3 Implement
rateLimit(fn, options)with sliding-window FIFO and injectableClock; exportRATE_LIMITEDsentinel andRateLimitedSentineltype alias - 2.4 Create
src/throttle/once/feature module withonce.ts,once.md,index.ts - 2.5 Implement
once(fn)with throw-and-retry semantics - 2.6 Create
src/throttle/times/feature module withtimes.ts,times.md,index.ts - 2.7 Implement
times(fn, maxCalls) - 2.8 Wire OTel counters
timing.rate_limit.rejected,timing.once.invocation,timing.times.invocationviasrc/otel/ - 2.9 Add subpath exports
./throttle/rate-limit,./throttle/once,./throttle/timestopackage.json::exports - 2.10 Re-export the three helpers +
RATE_LIMITEDfrom the root barrel
3. Tests
- 3.1 Unit tests at
src/throttle/rate-limit/rate-limit.test.ts— bursts inside window, eviction across window boundary, sentinel returned at cap, injectable clock drives eviction - 3.2 Spec-conformance tests at
src/throttle/rate-limit/rate-limit.spec-conformance.test.ts—RATE_LIMITEDunique symbol pinned viaexpectTypeOf, counter increments on rejection (realMeterProvider), invalid options throwTIMING.RATE_LIMIT_INVALID_WINDOW - 3.3 Unit tests at
src/throttle/once/once.test.ts— runs once, caches return, throw-and-retry flow - 3.4 Spec-conformance tests at
src/throttle/once/once.spec-conformance.test.ts— counter increments on first invocation only,TReturn(notTReturn | undefined) on wrapper - 3.5 Unit tests at
src/throttle/times/times.test.ts— runsntimes, returnsundefinedthereafter - 3.6 Spec-conformance tests at
src/throttle/times/times.spec-conformance.test.ts— counter increments per invocation up tomaxCalls - 3.7 Property-based tests via
fast-checkforrateLimitwindow invariant: at any instant, the count of timestamps in the FIFO is at mostmaxCalls - 3.8 Every
it(...)carries{ timeout: 5000 }
4. Documentation
- 4.1
src/throttle/rate-limit/rate-limit.md— sibling doc (≥200 words, basic + advanced, full API includingRATE_LIMITEDsentinel) - 4.2
src/throttle/once/once.md— sibling doc (≥200 words, document throw-and-retry semantics explicitly, distinguish from legacy) - 4.3
src/throttle/times/times.md— sibling doc (≥200 words) - 4.4 README links all three
.mdfiles under## Modules - 4.5 Update
CLAUDE.md@suaveplan/timingentry to includerateLimit,once,times
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/timing— green - 5.3
bunx turbo test:coverage --filter=@suaveplan/timing— 100/100/100/100 - 5.4
bun scripts/openspec-conformance.ts @suaveplan/timing— exit 0, noerrorfindings - 5.5
bunx turbo build --filter=@suaveplan/timing— green
6. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 6.1 Generics threaded through every wrapper;
expectTypeOfconfirmsRATE_LIMITEDis a unique symbol - 6.2 Zod schemas wired at
rateLimitboundary (validatesmaxCalls >= 1,windowMs > 0) - 6.3
TIMING.RATE_LIMIT_INVALID_WINDOWthrown by at least one production path - 6.4 Strict-mode behaviors tested (e.g.
oncethrow-and-retry vs throw-and-cache contrast) - 6.5 OTel counters emitted from production paths and asserted with a real in-memory
MeterProvider - 6.6 Lifecycle dedup —
onceitself is the dedup primitive; tested directly - 6.7 No retroactive tick — every box above reflects work done in this change
7. Archive
- 7.1 Fold spec delta into
openspec/specs/timing/spec.md - 7.2 Move
openspec/changes/timing-completion/→openspec/archive/<yyyy-mm-dd>-timing-completion/ - 7.3 Refresh
MANIFEST.mdif dep set changed (no expected change)