You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vibed/quotesdb/docs/plans/dreamy-marinating-corbato.md

347 lines
17 KiB
Markdown

# quotesdb Implementation Dispatch Plan
<!-- Save to: quotesdb/docs/plans/2026-03-02-implementation-dispatch.md -->
> **For Claude:** REQUIRED SUB-SKILL: Use `superpowers:executing-plans` to implement this plan task-by-task.
**Goal:** Commit all pending triage work, advance `ec118c` to `in_progress`, then dispatch parallel domain orchestrators to implement all 69 open implementation tickets.
**Architecture:** A sequential foundation agent resolves shared-file conflicts first (Cargo.toml touched by both API and UI setup tickets). Then 3 parallel domain orchestrators — API+QA, UI, Infra — each pick up tickets via `nbd ready --json`, create per-ticket git worktrees, dispatch domain-expert sub-agents, validate, merge, and loop.
**Tech Stack:** Rust/Axum/Tokio (API), Yew/Trunk/Wasm (UI), OpenTofu/Cloudflare (Infra), reqwest/tokio-test (QA), nbd (ticket tracking), git worktrees (agent isolation)
**Critical files:**
- `quotesdb/Cargo.toml` — single crate, shared by API + UI; modified by foundation tickets only
- `quotesdb/.nbd/tickets/` — per-ticket markdown files; no cross-ticket conflicts
- `quotesdb/docs/ARCHITECTURE.md` — already updated with WASM dep versions
- `quotesdb/api/openapi.yaml` — OpenAPI spec (source of truth, do not generate)
---
## Task 1: Commit all pending changes
**Run from:** `/run/media/pop/4e1ca4f1-37f1-4bd3-bad2-603166a773db/Projects/src/gitea.elijah.run/vibed/worktrees/quotesdb/` (the worktree git root, one level above `quotesdb/`)
**What is in the working tree:**
- 80 modified/untracked files: mostly nbd ticket updates (triage resolutions, new implementation tickets, XML tag additions from previous session)
- `quotesdb/CLAUDE.md` — updated
- `quotesdb/docs/ARCHITECTURE.md` — updated with WASM dependency version table
- `flake.lock` — updated
**Step 1: Stage all changes**
```bash
git add quotesdb/.nbd/tickets/ \
quotesdb/CLAUDE.md \
quotesdb/docs/ARCHITECTURE.md \
flake.lock
```
**Step 2: Verify staged files look right**
```bash
git status
git diff --cached --stat
```
**Step 3: Commit**
```bash
git commit -m "$(cat <<'EOF'
chore(quotesdb): resolve all triage tickets and create implementation tickets
- All 21 TRIAGE decision tickets resolved with chosen approaches documented
- This session: e2bd9b (SPA routing → _redirects), 2ec8b1 (OpenAPI → build.rs),
0d84fa (HTTP client → reqwest), 0bc655 (auth code → session storage)
- New implementation tickets created: 9ef703, 8892d5, 5379eb
- Downstream tickets updated with resolved approaches and correct dependencies
- ARCHITECTURE.md updated with pinned WASM dependency versions (yew 0.22,
yew-router 0.19, wasm-bindgen 0.2)
- XML tags added to all tickets for improved LLM guidance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EOF
)"
```
---
## Task 2: Update root ticket ec118c to in_progress
**Run from:** `quotesdb/` directory
Phase 0 (design) and Phase 1 (planning / ticket creation) are both complete. All 21 triages are resolved. Moving to Phase 2 (implementation).
```bash
nbd update ec118c --status in_progress --json | jq '{id, status}'
```
**Expected output:**
```json
{ "id": "ec118c", "status": "in_progress" }
```
---
## Task 3: Foundation agent — shared Cargo.toml setup (sequential)
**Why sequential:** Tickets `1f5bb5`, `93515e`, and `8892d5` all write to `Cargo.toml`. Running them in parallel would cause merge conflicts. This one-shot foundation agent does them in order before parallel agents start.
**Dispatch a single `general-purpose` agent with this prompt:**
> You are a senior Rust engineer setting up the foundation of the `quotesdb` project — a single Cargo crate with `api` and `ui` binaries.
>
> **Working directory:** `quotesdb/` inside the worktree at `/run/media/pop/4e1ca4f1-37f1-4bd3-bad2-603166a773db/Projects/src/gitea.elijah.run/vibed/worktrees/quotesdb/`
>
> **Task tracking:** Always run `nbd` from `quotesdb/`. Use `nbd update <id> --status in_progress --json` before starting a ticket, `nbd update <id> --status done --json` when finished.
>
> **Worktree convention (per ticket):**
> ```bash
> git worktree add .claude/worktrees/quotesdb-shared-<id> -b quotesdb/shared/<id>
> # do all work inside quotesdb/ root within the worktree
> cargo fmt && cargo check && cargo clippy && cargo test
> git checkout quotesdb
> git merge quotesdb/shared/<id>
> git worktree remove .claude/worktrees/quotesdb-shared-<id>
> ```
>
> **Do these 4 tickets in order — do not start the next until the previous is merged:**
>
> 1. **`7a0d9f`** — Implement `generate_id()` in `src/lib.rs` (UUID v4, WASM-compatible). Read the ticket first: `nbd read 7a0d9f --json`. Validation: `cargo check`. Branch: `quotesdb/shared/7a0d9f`.
>
> 2. **`1f5bb5`** — Set up `Cargo.toml` with all crate dependencies (axum, tokio, workers-rs, rusqlite, uuid, rand, serde, getrandom, plus `[build-dependencies]` serde_json + serde_yaml). Read: `nbd read 1f5bb5 --json`. Validation: `cargo check`. Branch: `quotesdb/shared/1f5bb5`.
>
> 3. **`93515e`** — Add Yew/Wasm UI dependencies to `Cargo.toml` (yew 0.22, yew-router 0.19, wasm-bindgen, gloo, web-sys). Read: `nbd read 93515e --json`. Validation: `cargo check`. Branch: `quotesdb/shared/93515e`.
>
> 4. **`8892d5`** — Create `build.rs` that converts `api/openapi.yaml` to JSON at compile time (writes to `$OUT_DIR/openapi.json`). Read: `nbd read 8892d5 --json`. Validation: `cargo check` (confirms build.rs runs). Branch: `quotesdb/shared/8892d5`.
>
> **After all 4 are merged**, run `cargo fmt && cargo check && cargo clippy && cargo test` from `quotesdb/` to confirm the foundation is clean.
>
> Return: which tickets were completed, the final `cargo check` output, and any issues encountered.
**Proceed to Task 4 only after this agent returns successfully.**
---
## Task 4: Dispatch 3 parallel domain orchestrators
**Invoke all 3 agents in a single message (parallel).** Each orchestrator runs `nbd ready --json`, filters to its domain, picks the highest-priority unblocked ticket, works it in a worktree, merges, and loops until its domain's sub-project ticket can be closed.
---
### Orchestrator A: API + QA
**Domain:** `src/bin/api/`, `src/lib.rs` (non-Cargo parts), `tests/`
**Sub-project ticket:** `f3dc74` (quotesdb/api), `ce1e4f` (quotesdb/qa)
**Worktree prefix:** `quotesdb/api/<id>` and `quotesdb/qa/<id>`
**Ready tickets to start with (priority order):**
- `00aff0` — QuoteRepository trait + cfg-split D1/rusqlite implementations
- `5f5ba0` — Add integration test dev-dependencies (reqwest, tokio, tempfile, serde_json)
- `a5049d` — DB connection module + SQLx migrations (after `00aff0`)
- `9c9546``.env.example` documenting `DATABASE_URL`
- `af56a7``docs/LOCAL_DEV.md` quickstart guide
- `6e829e` — API server `main.rs` (router setup, Axum app)
- `28e7d9` — GET /api/ handler (serves compiled openapi.json)
- `a6bce1` — GET /api/quotes (list, pagination, filters)
- `1a274d` — GET /api/quotes/random
- `b20b5a` — GET /api/quotes/:id
- `05f8ae` — PUT /api/quotes (create)
- `2c5a57` — Auth middleware (X-Auth-Code header validation)
- `5f1112` — POST /api/quotes/:id (update)
- `5cdbd9` — DELETE /api/quotes/:id
- Then QA tickets: `9b581f`, `789d0f`, `aa0eab`, `93f1b6`, `f9f448`, `4a4c26`, `fae330`, `8c87db`, `893eba`, `e8f5cf`
**Agent prompt:**
> You are a senior Rust backend engineer implementing the `quotesdb` API (Axum + Tokio) and its integration test suite.
>
> **Working directory:** Always work from `quotesdb/` at `/run/media/pop/4e1ca4f1-37f1-4bd3-bad2-603166a773db/Projects/src/gitea.elijah.run/vibed/worktrees/quotesdb/quotesdb/`
>
> **Your domains:** `src/bin/api/`, `src/lib.rs` (non-Cargo parts), `tests/`
>
> **Ticket tracking:** `nbd` commands always from `quotesdb/`. Mark `in_progress` before starting, `done` after merging.
>
> **Worktree loop (repeat until `f3dc74` and `ce1e4f` can close):**
> 1. `nbd ready --json | jq '[.[] | select(.title | test("api|lib|test|DB|QuoteRepo|router|handler|migration|local|env|auth|GET|PUT|POST|DELETE|random|list|quotes|Test suite|integration"; "i"))]'`
> 2. Pick highest priority unblocked ticket. Read it: `nbd read <id> --json`
> 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-api-<id> -b quotesdb/api/<id>`
> 4. Dispatch a Bash sub-agent to implement the ticket inside `quotesdb/` within the worktree
> 5. Validate: `cargo fmt && cargo check && cargo clippy && cargo test`
> 6. Merge: `git checkout quotesdb && git merge quotesdb/api/<id>`
> 7. Remove: `git worktree remove .claude/worktrees/quotesdb-api-<id>`
> 8. Mark ticket done: `nbd update <id> --status done --json`
> 9. Loop
>
> **Key architectural facts:**
> - API target is Cloudflare Workers (`wasm32`) in prod; native Axum/Tokio for local dev and tests
> - DB layer uses `QuoteRepository` trait with two impls: `D1Repository` (wasm32) and `RusqliteRepository` (native)
> - Auth: `X-Auth-Code` header, stored plaintext in `quotes.auth_code`, 403 on mismatch
> - IDs: UUID v4 via `generate_id()` in `src/lib.rs` (done in foundation phase)
> - Auth codes: 4-word passphrase, generated via `rand 0.10` with `OsRng`
> - Router order: `GET /api/quotes/random` MUST be registered before `GET /api/quotes/:id`
> - OpenAPI spec: served via `include_str!(concat!(env!("OUT_DIR"), "/openapi.json"))` (done in foundation)
>
> **Validation before closing any ticket:**
> ```sh
> cargo fmt && cargo check && cargo clippy && cargo test
> ```
>
> **Close `f3dc74` when all API tickets are done. Close `ce1e4f` when all QA tickets are done.**
>
> Return a summary: tickets completed, any blockers encountered, final test run output.
---
### Orchestrator B: UI
**Domain:** `src/bin/ui/`, `index.html`, `Trunk.toml`, `_redirects`
**Sub-project ticket:** `c3503b` (quotesdb/ui)
**Worktree prefix:** `quotesdb/ui/<id>`
**Ready tickets to start with (priority order):**
- `9ef703` — Create `_redirects` file + add Trunk copy-file directive to `index.html`
- `00d6d7` — Add Trunk proxy `[[proxy]]` block for `/api/*``localhost:3000`
- `5379eb` — Auth code session storage utility (`src/bin/ui/storage.rs`) + AuthModal pre-fill
- `dc3d2b``Trunk.toml` + `index.html` final setup (depends on `9ef703`)
- `04f865` — Yew app shell, BrowserRouter, route definitions for 5 pages
- `0fbdd5``src/bin/ui/style.css` full stylesheet (BEM naming)
- `bb1514` — CSS content implementation
- Page components: HomPage, BrowsePage, QuotePage, AuthorPage, SubmitPage
- `f850c6` — AuthModal component
- `fc2f51` — Error display component
- `00d6d7` — Pagination component
- CI/CD: `5137d7` (deploy-ui workflow)
- Docs: `372790`
**Agent prompt:**
> You are a senior Rust frontend engineer implementing the `quotesdb` UI (Yew + Wasm compiled by Trunk, hosted on Cloudflare Pages).
>
> **Working directory:** Always work from `quotesdb/` at `/run/media/pop/4e1ca4f1-37f1-4bd3-bad2-603166a773db/Projects/src/gitea.elijah.run/vibed/worktrees/quotesdb/quotesdb/`
>
> **Your domains:** `src/bin/ui/`, `index.html`, `Trunk.toml`, `_redirects`, `src/bin/ui/style.css`
>
> **Ticket tracking:** `nbd` from `quotesdb/`. Mark `in_progress` before starting, `done` after merging.
>
> **Worktree loop (repeat until `c3503b` can close):**
> 1. `nbd ready --json | jq '[.[] | select(.title | test("ui|Trunk|Yew|CSS|style|page|component|modal|redirect|auth|BrowserRouter|browse|submit|author|home|wasm|SPA"; "i"))]'`
> 2. Pick highest priority. Read: `nbd read <id> --json`
> 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-ui-<id> -b quotesdb/ui/<id>`
> 4. Implement inside `quotesdb/` within the worktree
> 5. Validate: `trunk build` (from `quotesdb/` root)
> 6. Merge + mark done, loop
>
> **Key architectural facts:**
> - Compile target: `wasm32-unknown-unknown`. No `std::thread`, `std::fs`, or threading primitives.
> - Yew 0.22 + yew-router 0.19 (only correct compatible versions).
> - API calls via `gloo::net::http::Request` (not reqwest — incompatible with wasm32).
> - Auth code storage: `sessionStorage` via `web_sys::window().session_storage()` (NOT localStorage). Key pattern: `auth_code_{quote_id}`.
> - `_redirects` file content: `/* /index.html 200` — must be at project root, copied to dist/ by Trunk.
> - CSS: plain CSS, BEM naming (`quote-card`, `quote-card__text`, `page-browse`). No Tailwind.
> - Trunk proxy: `[[proxy]] rewrite = "/api" backend = "http://localhost:3000"` — local dev only.
> - In production, Cloudflare routes `/api/*` to the Worker before Pages sees the request.
>
> **Frontend routes (5 pages):**
> - `/` → Home (random quote + Browse link)
> - `/browse` → Paginated list + author/tag filters
> - `/quotes/:id` → View/edit/delete single quote
> - `/author/:name` → All quotes by author
> - `/submit` → New quote form
>
> **Validation before closing any ticket:**
> ```sh
> trunk build
> ```
>
> **Close `c3503b` when all UI tickets are done.**
>
> Return a summary: tickets completed, any blockers, final `trunk build` output.
---
### Orchestrator C: Infra
**Domain:** `infra/`, `.gitea/workflows/`
**Sub-project ticket:** `25c413` (quotesdb/infra)
**Worktree prefix:** `quotesdb/infra/<id>`
**Ready tickets to start with (priority order):**
- `2d1371` — Bootstrap OpenTofu project (`providers.tf`, `terraform.tf`, `infra/.gitignore`)
- Then: D1 database resource, Cloudflare Worker resource, Pages project, domain/routes, variables
- CI/CD: `57fe5e` (deploy-api workflow), `3781c9` (deploy-ui workflow listed under infra)
- Docs: `d5839a` (infra README)
**Agent prompt:**
> You are a DevOps/infrastructure engineer provisioning the `quotesdb` Cloudflare infrastructure with OpenTofu.
>
> **Working directory:** `infra/` at `/run/media/pop/4e1ca4f1-37f1-4bd3-bad2-603166a773db/Projects/src/gitea.elijah.run/vibed/worktrees/quotesdb/quotesdb/infra/`
>
> **Your domains:** `infra/` (all `.tf` files), `.gitea/workflows/` (CI/CD YAML files)
>
> **Ticket tracking:** `nbd` from `quotesdb/`. Mark `in_progress` before starting, `done` after merging.
>
> **Worktree loop (repeat until `25c413` can close):**
> 1. `nbd ready --json | jq '[.[] | select(.title | test("infra|OpenTofu|tofu|Cloudflare|Worker|D1|Pages|provider|deploy|workflow|gitea|action|migration|wrangler|domain"; "i"))]'`
> 2. Pick highest priority. Read: `nbd read <id> --json`
> 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-infra-<id> -b quotesdb/infra/<id>`
> 4. Implement. Validate from `infra/`: `tofu validate && tofu plan`
> 5. Merge + mark done, loop
>
> **Key architectural facts:**
> - OpenTofu state backend: local file (`terraform.tfstate`), gitignored.
> - Resource for the Worker: `cloudflare_workers_script` (check resolved triage `efee79` for the exact resource name in the current provider version).
> - D1 binding: chicken-and-egg resolved in triage `07cafb` — create D1 first (`tofu apply -target`), then bind its ID to the Worker.
> - Pages project: direct upload (no git source block). Deploy via `wrangler pages deploy dist/`.
> - D1 migrations: separate `wrangler d1 execute` step, not in OpenTofu (resolved in triage `5c0c64`).
> - Every `resource` and `data` block must have a comment explaining its purpose.
> - Do NOT commit `.terraform/`, `*.tfstate`, `*.tfstate.backup`, or `.terraform.lock.hcl`.
>
> **Validation before closing any ticket (from `infra/`):**
> ```sh
> tofu validate
> tofu plan
> ```
>
> **Close `25c413` when all infra tickets are done.**
>
> Return a summary: tickets completed, `tofu plan` output summary, any blockers.
---
## Execution Notes
### Ordering
```
Task 1 (commit) → Task 2 (ec118c update) → Task 3 (foundation agent)
Task 4: 3 parallel orchestrators
(single message, 3 Task tool calls)
```
### Coordination between orchestrators
| Concern | Resolution |
|---|---|
| `Cargo.toml` conflicts | Foundation agent handles all Cargo.toml tickets first |
| `.nbd/tickets/` conflicts | Each ticket is a separate file — no cross-agent conflicts |
| `src/lib.rs` | Foundation agent owns this; orchestrators only add to `src/bin/` |
| UI calling API | UI uses API endpoint paths (not shared code) — no conflict |
| QA calling API | QA tests against a running API server in tests — no shared files |
### When orchestrators finish
After all 3 return:
1. Run full validation from `quotesdb/`: `cargo fmt && cargo check && cargo clippy && cargo test`
2. Run `trunk build` to verify UI
3. Run `nbd list --json | jq '[.[] | select(.status != "done")]'` — should be empty or near-empty
4. Close sub-project tickets: `f3dc74`, `c3503b`, `25c413`, `ce1e4f`
5. Close root ticket: `nbd update ec118c --status done --json`
6. Use `superpowers:finishing-a-development-branch` to merge `quotesdb` branch into `main`
### Ticket filtering tips for orchestrators
```bash
# Find the next unblocked ticket in your domain
nbd ready --json | jq 'sort_by(-.priority) | .[0] | {id, priority, title}'
# Check if a sub-project ticket can now close (all deps done)
nbd read f3dc74 --json | jq '.dependencies'
# Then for each dep: nbd read <id> --json | jq .status
```