99 lines
5.7 KiB
Markdown
99 lines
5.7 KiB
Markdown
# Tasks — `@suaveplan/api-plugin-campaigns`
|
|
|
|
## §0 Pre-flight (every dispatched agent, every chunk)
|
|
|
|
- [x] `pwd && git worktree list && git status` — confirm we are inside
|
|
`.claude/worktrees/agent-*` and NOT in `/home/paul/web/genesis/` directly.
|
|
- [x] §0a Sync — fast-forward worktree to the orchestrator-supplied
|
|
`EXPECTED_BASE` SHA; STOP if `git merge-base --is-ancestor` returns
|
|
non-zero.
|
|
|
|
## §A Chunk A — foundation (this dispatch)
|
|
|
|
- [x] A.1 Package shell — `package.json` (`@suaveplan/api-plugin-campaigns@0.1.0`,
|
|
`"sideEffects": false`, subpath exports per feature folder), `vite.config.ts`,
|
|
`vitest.config.ts`, `tsconfig.json` + `tsconfig.lib.json`, `tests/setup/polyfills.ts`,
|
|
`README.md`.
|
|
- [x] A.2 `src/error-codes/` — `CAMPAIGNS_API_CODES` (Chunk-A subset of the registered
|
|
codes; B/C extend) + `CampaignsApiError` with explicit public constructor.
|
|
- [x] A.3 `src/otel/` — meter binding + counter (`campaigns_api.requests_total`) +
|
|
histogram (`campaigns_api.request_duration_ms`); cached + resettable.
|
|
- [x] A.4 `src/types/` — `CampaignsPluginOptions`, `CampaignsPluginConfig`.
|
|
- [x] A.5 `src/schemas/` — `CampaignsPluginOptionsSchema` Zod validator.
|
|
- [x] A.6 `src/context/` + `src/services/` — Hono `ContextVariableMap` and
|
|
`ServiceRegistry` declaration-merge augmentations (Chunk-A baseline; B will
|
|
add repo slots).
|
|
- [x] A.7 `src/plugin/` — `createCampaignsPlugin` via
|
|
`definePlugin` from `@suaveplan/api-plugin`. `onInit` is a no-op
|
|
Chunk-A-correct placeholder; documented in `plugin.md`.
|
|
- [x] A.8 `src/index.ts` — explicit named re-exports for Chunk-A surface.
|
|
- [x] A.9 Per-file `.md` siblings with the six required sections.
|
|
- [x] A.10 Per-file `.test.ts` siblings; runner imports from
|
|
`@suaveplan/testing/runner` only.
|
|
- [x] A.11 Verification gate green:
|
|
`bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-campaigns`,
|
|
`bunx turbo test:coverage --filter=...` 100/100/100/100,
|
|
`bunx turbo build --filter=...`, feature-layout check empty.
|
|
- [x] A.12 `bun scripts/folder-reorg/generate-manifest.ts` re-runs cleanly.
|
|
|
|
## §B Chunk B — repositories (follow-up)
|
|
|
|
- [x] B.1 `src/repositories/campaign/` — `SurrealCampaignRepository`
|
|
implementing `CampaignStore` from `@suaveplan/campaigns`; uses positional
|
|
`$p0` placeholders against the genesis `SurrealAdapter`. The package
|
|
also re-exports `InMemoryCampaignStore` from `@suaveplan/campaigns`
|
|
as the in-memory reference implementation.
|
|
- [x] B.2 `src/repositories/suppression/` — local `CampaignSuppressionStore`
|
|
interface + `SurrealCampaignSuppressionRepository` + reference
|
|
`InMemoryCampaignSuppressionStore` (tenant + channel shape; NOT a
|
|
wrapper over `@suaveplan/suppression`, which is email-only).
|
|
- [ ] B.3 `src/repositories/delivery-events/` — wrapper that consumes the
|
|
already-shipped `SurrealDeliveryEventStore` from
|
|
`@suaveplan/delivery-tracking`. (Out of scope for this dispatch per
|
|
orchestrator's Chunk-B scope; rolls into a follow-up.)
|
|
- [x] B.4 Extended `CAMPAIGNS_API_CODES` registry — added six codes:
|
|
`CAMPAIGN_NOT_FOUND`, `SUPPRESSION_NOT_FOUND`, `INVALID_INPUT`,
|
|
`DB_UNAVAILABLE`, `DUPLICATE_SUPPRESSION`, `TENANT_REQUIRED`.
|
|
- [ ] B.5 Bundled SurrealQL DDL strings. (Deferred to Chunk C; plugin's
|
|
`applySchemaOnInit` is wired there.)
|
|
- [ ] B.6 Plugin `onInit` registers the repositories on the
|
|
`ServiceRegistry` and Hono `ContextVariableMap`. (Deferred to Chunk C
|
|
which owns the lifecycle; service-registry slots are reserved here
|
|
as optional fields.)
|
|
- [x] B.7 100/100/100/100 coverage on the full package (Chunk A + Chunk B
|
|
modules); verified via `bunx turbo test:coverage`.
|
|
|
|
## §C Chunk C — routes (this dispatch)
|
|
|
|
- [x] C.1 `src/routes/campaigns/` — 12 routes total: list/create/get/update/delete
|
|
plus send/pause/cancel/duplicate/test action routes and stats/events
|
|
reporting routes. State guard uses `CampaignStateMachine` from
|
|
`@suaveplan/campaigns`; invalid transitions raise
|
|
`CAMPAIGNS_API.INVALID_STATE_TRANSITION` (HTTP 409).
|
|
- [x] C.2 `src/routes/suppression/` — 4 routes: list/check/add/remove with
|
|
tenant scoping and channel-keyed dedup.
|
|
- [x] C.3 `src/routes/webhooks/` — 2 routes: provider delivery-event ingest
|
|
(auto-suppress on unsubscribed/bounced/complained) and base64-JSON
|
|
one-click unsubscribe with `INVALID_UNSUBSCRIBE_TOKEN` mapping.
|
|
- [x] C.4 Plugin `onInit` resolves `app` + `db` + `openAPIRegistry` from the
|
|
`ServiceRegistry`, constructs `SurrealCampaignRepository`,
|
|
`SurrealCampaignSuppressionRepository`, and `SurrealDeliveryEventStore`,
|
|
injects them on the Hono `ContextVariableMap`, and mounts every route
|
|
via `definePluginRoutes`. Collision guard raises
|
|
`CAMPAIGNS_API.ROUTE_PREFIX_COLLISION`. Missing-service guard raises
|
|
`CAMPAIGNS_API.SERVICE_NOT_AVAILABLE`.
|
|
- [x] C.5 Integration tests in `tests/integration/plugin-integration.test.ts`
|
|
against a real `OpenAPIHono` host with in-memory repositories; 8
|
|
happy-path round-trips covering every route group.
|
|
- [x] C.6 100/100/100/100 coverage on the full package (Chunks A + B + C).
|
|
- [x] C.7 Extended `CAMPAIGNS_API_CODES` with five Chunk-C codes:
|
|
`INVALID_STATE_TRANSITION`, `INVALID_UNSUBSCRIBE_TOKEN`,
|
|
`ROUTE_PREFIX_COLLISION`, `WEBHOOK_REJECTED`, `SERVICE_NOT_AVAILABLE`.
|
|
|
|
## Post-C orchestrator steps (NOT a chunk)
|
|
|
|
- [ ] Fold the spec delta into `openspec/specs/api-plugin-capaigns/spec.md`.
|
|
- [ ] Move `openspec/changes/api-plugin-campaigns/` to
|
|
`openspec/archive/<yyyy-mm-dd>-api-plugin-campaigns/`.
|
|
- [ ] Update `MANIFEST.md` and `CLAUDE.md` package roster.
|