102 lines
7.6 KiB
Markdown
102 lines
7.6 KiB
Markdown
# Tasks — Sitemap completion
|
|
|
|
> Every checkbox MUST reflect work actually done in this change. Do not retroactively tick.
|
|
|
|
## 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)
|
|
|
|
Status: **DEFERRED**. `packages/api/sitemap/src/` only contains `core`, `errors`, `schemas`, `utils` — no `extensions/`, `incremental/`, or `compression/` directories. SitemapImageExtension, SitemapVideoExtension, IncrementalSitemapBuilder, and compressSitemap helpers are all absent. 0% of work is done.
|
|
|
|
## 1. Spec authoring
|
|
|
|
- [ ] 1.1 Read `../suaveplan/enterprise/sitemap.md` end-to-end
|
|
- [ ] 1.2 Read `openspec/specs/sitemap/spec.md` (if it exists) — currently absent; this change introduces the canonical capability spec for the four new surfaces
|
|
- [ ] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14
|
|
- [ ] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
|
|
- [ ] 1.5 Fill `## Source mapping` table with upstream line ranges
|
|
- [ ] 1.6 Draft `specs/sitemap/spec.md` from `openspec/templates/spec-delta.md`
|
|
- [ ] 1.7 Every requirement carries a `**Verified by:**` line
|
|
- [ ] 1.8 `openspec change validate sitemap-completion` exits with no errors
|
|
|
|
## 2. Implementation — image extension
|
|
|
|
- [ ] 2.1 Port `SitemapImageExtension` from `packages/universal/sitemap/src/extensions/image-sitemap.ts` (L1-L111) into `packages/api/sitemap/src/extensions/image-sitemap/image-sitemap.ts`
|
|
- [ ] 2.2 Reuse existing `SitemapImageSchema` from `packages/api/sitemap/src/schemas/sitemap.schema.ts` for input validation
|
|
- [ ] 2.3 Throw `SitemapError` with code `IMAGE_VALIDATION_FAILED` on Zod failure
|
|
- [ ] 2.4 Sibling `.md` (≥200 words, basic + advanced examples)
|
|
|
|
## 3. Implementation — video extension
|
|
|
|
- [ ] 3.1 Port `SitemapVideoExtension` from `packages/universal/sitemap/src/extensions/video-sitemap.ts` (L1-L182) into `packages/api/sitemap/src/extensions/video-sitemap/video-sitemap.ts`
|
|
- [ ] 3.2 Reuse existing `SitemapVideoSchema` for input validation
|
|
- [ ] 3.3 Throw `SitemapError` with code `VIDEO_VALIDATION_FAILED` on Zod failure
|
|
- [ ] 3.4 Sibling `.md`
|
|
|
|
## 4. Implementation — incremental builder
|
|
|
|
- [ ] 4.1 Port `IncrementalSitemapBuilder` from `packages/universal/sitemap/src/incremental/incremental-builder.ts` (L1-L213) into `packages/api/sitemap/src/incremental/incremental-builder/incremental-builder.ts`
|
|
- [ ] 4.2 Replace direct `fs.promises.writeFile` with injectable `WritableStorageLike` from `@suaveplan/types`
|
|
- [ ] 4.3 Replace `Date.now()` with injectable `Clock`
|
|
- [ ] 4.4 Implement rollover at `urlsPerSitemap` (default 50_000) OR `maxBytes` (default 50 *1024* 1024) — whichever first
|
|
- [ ] 4.5 Emit one `SitemapIndex` entry per rollover; carry final index assembly through `finalize()`
|
|
- [ ] 4.6 When `compress: true`, pipe each rollover output through `compressSitemap` and append `.gz` extension
|
|
- [ ] 4.7 Add `INCREMENTAL_ROLLOVER_FAILED` to `SITEMAP_CODES` via existing `defineCodes` call
|
|
- [ ] 4.8 Sibling `.md`
|
|
|
|
## 5. Implementation — gzip compressor
|
|
|
|
- [ ] 5.1 Port `compressSitemap` / `compressSitemapIndex` / `decompressGzipSitemap` from `packages/universal/sitemap/src/compression/sitemap-compressor.ts` (L1-L91) into `packages/api/sitemap/src/compression/sitemap-compressor/sitemap-compressor.ts`
|
|
- [ ] 5.2 Use `fflate` (already in catalog) for gzip; verify gzip magic bytes `1f 8b` in output
|
|
- [ ] 5.3 Stamp the gzip header `mtime` field from injectable `Clock` (defaults to `() => 0` for deterministic test fixtures)
|
|
- [ ] 5.4 Add `COMPRESSION_FAILED` to `SITEMAP_CODES`
|
|
- [ ] 5.5 Sibling `.md`
|
|
|
|
## 6. Wiring
|
|
|
|
- [ ] 6.1 Update `packages/api/sitemap/src/index.ts` to re-export the four new modules and their types
|
|
- [ ] 6.2 Add subpath exports `./extensions/image-sitemap/image-sitemap`, `./extensions/video-sitemap/video-sitemap`, `./incremental/incremental-builder/incremental-builder`, `./compression/sitemap-compressor/sitemap-compressor` to `packages/api/sitemap/package.json`
|
|
- [ ] 6.3 Update root `README.md` `## Modules` section to link the four new co-located `.md`s
|
|
|
|
## 7. Tests
|
|
|
|
- [ ] 7.1 `packages/api/sitemap/src/extensions/image-sitemap/image-sitemap.test.ts` — adds image, multiple images, all optional fields, Zod rejection
|
|
- [ ] 7.2 `packages/api/sitemap/src/extensions/video-sitemap/video-sitemap.test.ts` — adds video, all optional fields, contentLoc / playerLoc mutual exclusion handling, Zod rejection
|
|
- [ ] 7.3 `packages/api/sitemap/src/incremental/incremental-builder/incremental-builder.test.ts` — append URLs in batches; rollover at 50k URLs; rollover at 50 MiB; final `finalize()` produces a valid sitemap index XML
|
|
- [ ] 7.4 `packages/api/sitemap/src/incremental/incremental-builder/incremental-builder.spec-conformance.test.ts` — injected `WritableStorageLike` receives the right paths; injected `Clock` controls mtime; `compress: true` produces gzip-magic output
|
|
- [ ] 7.5 `packages/api/sitemap/src/compression/sitemap-compressor/sitemap-compressor.test.ts` — gzip round-trip; magic bytes `1f 8b` present; mtime deterministic; `decompressGzipSitemap` recovers the original XML byte-for-byte
|
|
- [ ] 7.6 Every `it(...)` carries `{ timeout: 5000 }` (30000 for the rollover-at-50k test, which appends 50_000 URLs)
|
|
|
|
## 8. Documentation
|
|
|
|
- [ ] 8.1 Sibling `.md` for every new implementation `.ts` (≥200 words, basic + advanced examples, full API reference)
|
|
- [ ] 8.2 README links the four new `.md`s under `## Modules`
|
|
|
|
## 9. Pre-archive verification (the 5-gate triple)
|
|
|
|
- [ ] 9.1 `bun run check` — Biome 0/0/0 across the monorepo
|
|
- [ ] 9.2 `bunx turbo lint typecheck test --filter=@suaveplan/sitemap` — green
|
|
- [ ] 9.3 `bunx turbo test:coverage --filter=@suaveplan/sitemap` — 100/100/100/100
|
|
- [ ] 9.4 `bun scripts/openspec-conformance.ts @suaveplan/sitemap` — exit 0, no `error` findings
|
|
- [ ] 9.5 `bunx turbo build --filter=@suaveplan/sitemap` — green
|
|
|
|
## 10. Pre-archive 7-row checklist (AGENTS.md Section 13)
|
|
|
|
- [ ] 10.1 Generics threaded; `expectTypeOf` assertions on `IncrementalSitemapBuilderOptions` literal-type fields
|
|
- [ ] 10.2 Zod schemas wired at every public boundary (`SitemapImageSchema`, `SitemapVideoSchema`, `IncrementalSitemapBuilderOptionsSchema`)
|
|
- [ ] 10.3 New error codes thrown from at least one production path (`IMAGE_VALIDATION_FAILED`, `VIDEO_VALIDATION_FAILED`, `INCREMENTAL_ROLLOVER_FAILED`, `COMPRESSION_FAILED`)
|
|
- [ ] 10.4 Strict-mode tested: rollover at exactly 50,000 URLs vs 49,999; rollover at exactly 50 MiB vs 50 MiB - 1 byte
|
|
- [ ] 10.5 OTel counters / histograms emitted (`sitemap.incremental.rollover_count`, `sitemap.compression.bytes_in`, `sitemap.compression.bytes_out`); asserted with a real in-memory `MeterProvider`
|
|
- [ ] 10.6 Lifecycle dedup: `IncrementalSitemapBuilder.finalize()` is idempotent — calling it twice produces the same index XML and does not double-emit storage writes
|
|
- [ ] 10.7 No retroactive tick — every box reflects work done in this change
|
|
|
|
## 11. Archive
|
|
|
|
- [ ] 11.1 Fold spec delta into `openspec/specs/sitemap/spec.md` (creating it if absent)
|
|
- [ ] 11.2 Move `openspec/changes/sitemap-completion/` → `openspec/archive/<yyyy-mm-dd>-sitemap-completion/`
|
|
- [ ] 11.3 Refresh `MANIFEST.md`
|