8.9 KiB
8.9 KiB
Tasks — Pagination streaming
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.
- No
packages/data/pagination/src/streaming/directory exists. Existing dirs:cursor,keyset,offset,helpers,adapters,tokens,errors,otel,schemas,types. grep -rln "streamPaginated\|stream-pagination\|collectStream\|countStream\|takeFromStream\|filterStream\|mapStream" packages/data/pagination/src/returns zero hits.bunx turbo lint typecheck test --filter=@suaveplan/paginationis GREEN — but the streaming module is not implemented.- All 57 tasks remain:
streamPaginated<T>AsyncIterable, five operator helpers (collectStream,countStream,takeFromStream,filterStream,mapStream),DEFAULT_STREAM_PAGE_SIZE, AbortSignal contract, subpath export, sibling.md, full test matrix.
1. Spec authoring
- 1.1 Read
../suaveplan/enterprise/pagination.mdend-to-end - 1.2 Read
openspec/specs/pagination/spec.md(already exists — confirm PG-12 AbortSignal contract still aligned) - 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/pagination/spec.mdfromopenspec/templates/spec-delta.md— note: this is a delta against the existing canonical spec, so add as ADDED Requirements (PG-14, PG-15) numbered after the current PG-13 - 1.7 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles
2. Implementation — streaming/stream-pagination
- 2.1 Create
packages/data/pagination/src/streaming/stream-pagination.tsexportingstreamPaginated<T>(adapter, options?): AsyncIterable<T> - 2.2 Default
pageSizeto 50 (constantDEFAULT_STREAM_PAGE_SIZE); export the constant fromconstants/index.ts - 2.3 Honour the one-page-in-memory invariant — lazy-init on first
next(); fetch next page only when current page exhausted; mark exhausted whencurrentPage.length < pageSize - 2.4 Forward
options.filterandoptions.sorttoadapter.fetchPage(offset, pageSize, sort, filter)byte-for-byte with upstream - 2.5 Honour
options.signal— checksignal.abortedbefore eachfetchPageAND before yielding each item; on abort throwPaginationError(PAGINATION.ABORTED) - 2.6 Add
StreamPaginationOptionstype (pageSize,filter,sort,signal) totypes/pagination.types.ts - 2.7 Add
streaming/stream-pagination.mdsibling doc (≥200 words, basic + advanced examples)
3. Implementation — operator helpers
- 3.1 Add
collectStream<T>(iterable): Promise<T[]>— drains; verify it does NOT short-circuit - 3.2 Add
countStream<T>(iterable): Promise<number>— counts without retaining - 3.3 Add
takeFromStream<T>(iterable, count): Promise<T[]>— short-circuits onceitems.length >= count - 3.4 Add
filterStream<T>(iterable, predicate): AsyncIterable<T>— lazy filter passthrough - 3.5 Add
mapStream<T, U>(iterable, mapper): AsyncIterable<U>— lazy map passthrough - 3.6 All five helpers honour
AbortSignalindirectly via the upstream iterable (no new signal parameter; cancellation propagates through thefor awaitchain)
4. Subpath exports + barrel
- 4.1 Add
"./streaming/stream-pagination"topackages/data/pagination/package.jsonexports map (matching existing per-feature subpath pattern) - 4.2 Re-export
streamPaginated,collectStream,countStream,takeFromStream,filterStream,mapStream,StreamPaginationOptions,DEFAULT_STREAM_PAGE_SIZEfromsrc/index.ts
5. Tests
- 5.1
streaming/stream-pagination.test.ts::"streams items from MemoryPaginationAdapter"— basic round-trip over 100 items withpageSize=10 - 5.2
streaming/stream-pagination.test.ts::"defaults pageSize to 50 when omitted"— instrumentfetchPageand assert called withpageSize=50 - 5.3
streaming/stream-pagination.test.ts::"holds at most one page in memory"— instrument iterator state; verifycurrentPagesize ≤pageSizeat every step - 5.4
streaming/stream-pagination.test.ts::"forwards filter and sort to adapter.fetchPage"— assert call args - 5.5
streaming/stream-pagination.test.ts::"is lazy — no fetch before first next()"— instrument; verify zero calls before firstnext() - 5.6
streaming/stream-pagination.test.ts::"stops when partial page returned"— verify exhausted-on-partial behavior - 5.7
streaming/stream-pagination.spec-conformance.test.ts::"throws PAGINATION.ABORTED before first fetchPage when signal already aborted"— verifyfetchPageis NOT called - 5.8
streaming/stream-pagination.spec-conformance.test.ts::"throws PAGINATION.ABORTED on next() after signal abort between yields"— abort mid-iteration - 5.9
streaming/stream-pagination.spec-conformance.test.ts::"propagates rejection when signal aborts in-flight fetchPage"— adapter aware of signal rejects naturally - 5.10
streaming/stream-pagination.spec-conformance.test.ts::"pins StreamPaginationOptions shape via expectTypeOf"— type-level assertion - 5.11
streaming/stream-pagination.test.ts::"collectStream drains all items"— happy path - 5.12
streaming/stream-pagination.test.ts::"countStream returns total without retention"— verify result is correct count - 5.13
streaming/stream-pagination.test.ts::"takeFromStream short-circuits at count"— instrument fetches; verify only enough pages fetched to satisfy take - 5.14
streaming/stream-pagination.test.ts::"filterStream is lazy and passthrough"— verify items skipped don't pass through - 5.15
streaming/stream-pagination.test.ts::"mapStream is lazy and passthrough"— verify mapper called once per yielded item - 5.16 Every
it(...)in 5.1–5.15 carries{ timeout: 5000 }
6. Documentation
- 6.1 Sibling
.mdforstream-pagination.ts(≥200 words, basic + advanced examples) - 6.2 Update
packages/data/pagination/README.mdModules section — add link to streaming module; add Quick-start snippet showingstreamPaginated+for await - 6.3 Update
CLAUDE.mdpackage listing line for@suaveplan/paginationto mention streaming
7. Pre-archive verification (the 5-gate triple)
- 7.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 7.2
bunx turbo lint typecheck test --filter=@suaveplan/pagination— green - 7.3
bunx turbo test:coverage --filter=@suaveplan/pagination— 100/100/100/100 - 7.4
bun scripts/openspec-conformance.ts @suaveplan/pagination— exit 0, noerrorfindings - 7.5
bunx turbo build --filter=@suaveplan/pagination— green - 7.6 Dependent sweep:
bunx turbo test --filter=...@suaveplan/pagination(covers@suaveplan/search)
8. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 8.1 Generics threaded through
streamPaginated<T>,mapStream<T, U>,filterStream<T>, etc. - 8.2 Zod schemas wired at the
StreamPaginationOptionsboundary if exposed across a network edge (likely no — purely in-memory contract) - 8.3
PAGINATION.ABORTEDthrown by at least one production path (the streaming helper) - 8.4 Strict-mode behaviors tested both on and off (signal supplied vs absent)
- 8.5 OTel instruments emitted (page-fetch counter, page-fetch duration histogram) and asserted with a real in-memory
MeterProvider - 8.6 Lifecycle dedup not applicable here — streaming is single-iterator instances, no shared registry
- 8.7 No retroactive tick — every box above reflects work done in this change
9. Archive
- 9.1 Fold spec delta into
openspec/specs/pagination/spec.md— add as PG-14 (streaming pagination contract) and PG-15 (streaming operator helpers) - 9.2 Move
openspec/changes/pagination-streaming/→openspec/archive/<yyyy-mm-dd>-pagination-streaming/ - 9.3 Refresh
MANIFEST.mdif package set or deps changed (no expected change here)