# quotesdb Implementation Dispatch Plan > **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 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 --status in_progress --json` before starting a ticket, `nbd update --status done --json` when finished. > > **Worktree convention (per ticket):** > ```bash > git worktree add .claude/worktrees/quotesdb-shared- -b quotesdb/shared/ > # do all work inside quotesdb/ root within the worktree > cargo fmt && cargo check && cargo clippy && cargo test > git checkout quotesdb > git merge quotesdb/shared/ > git worktree remove .claude/worktrees/quotesdb-shared- > ``` > > **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/` and `quotesdb/qa/` **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 --json` > 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-api- -b quotesdb/api/` > 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/` > 7. Remove: `git worktree remove .claude/worktrees/quotesdb-api-` > 8. Mark ticket done: `nbd update --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/` **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 --json` > 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-ui- -b quotesdb/ui/` > 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/` **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 --json` > 3. Create worktree: `git worktree add .claude/worktrees/quotesdb-infra- -b quotesdb/infra/` > 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 --json | jq .status ```