From dc73fc8f692243dfa78ede17c49849d3c76f9eca Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Mon, 2 Mar 2026 21:36:29 -0800 Subject: [PATCH] plan to implement the current batch of tickets --- .../docs/plans/dreamy-marinating-corbato.md | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 quotesdb/docs/plans/dreamy-marinating-corbato.md diff --git a/quotesdb/docs/plans/dreamy-marinating-corbato.md b/quotesdb/docs/plans/dreamy-marinating-corbato.md new file mode 100644 index 0000000..5db87cd --- /dev/null +++ b/quotesdb/docs/plans/dreamy-marinating-corbato.md @@ -0,0 +1,346 @@ +# 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 +```