@../CLAUDE.md # CLAUDE.md — quotesdb Run all commands from the relevant sub-directory within `quotesdb/` (e.g., `api/`, `ui/`, `infra/`) — not from the repository root or the `quotesdb/` root. ## Project Overview `quotesdb` is a quotes web application in the vibed mono-repo. It is a **single Cargo crate** with two binaries and shared library code: ``` quotesdb/ ├── Cargo.toml # single crate, default-run = "api" ├── src/ │ ├── lib.rs # shared types/utilities (compiles for host + wasm32) │ └── bin/ │ ├── api/ │ │ └── main.rs # API server binary │ └── ui/ │ └── main.rs # Yew frontend binary (compiled by Trunk) ├── tests/ # Cargo integration tests (cargo test runs these) ├── index.html # Trunk HTML entry ├── Trunk.toml # Trunk config ├── infra/ # OpenTofu infrastructure └── docs/ # Planning, architecture, and design docs └── plans/ └── 2026-02-27-quotesdb-design.md ``` Design reference: `docs/plans/2026-02-27-quotesdb-design.md` ## beans Ticket Tracking **Always run `beans` commands from the `quotesdb/` directory.** Beans are scoped to the directory where `beans init` was run. Running `beans` from a parent directory will not find these beans. ```sh # Correct — from quotesdb/ beans list --json beans show --json beans update --json --status completed # Wrong — from vibed/ cd quotesdb && beans list --json # must be in quotesdb/ ``` ### Bean Hierarchy ``` quotesdb (root epic bean) ├── quotesdb/api (sub-epic bean) │ ├── quotesdb/api/ (task/feature/bug beans) │ └── ... ├── quotesdb/ui │ └── ... ├── quotesdb/qa │ └── ... └── quotesdb/infra └── ... ``` Rules: - Create a bean **before** starting any non-trivial work. - Work beans block their sub-epic bean — wire with `beans update --blocked-by `. - Sub-epic beans block the root epic bean. - The dependency flows upward: `work → sub-epic → root`. Each level can only close after all beans below it are done. - **Only close a bean after its work has been validated** (all `cargo fmt/check/clippy/test` pass, or equivalent for infra). ## Branch & Worktree Workflow All work happens in **git worktrees** on named branches. ### Branch naming ``` quotesdb// ``` Examples: - `quotesdb/api/a1b2c3` - `quotesdb/ui/d4e5f6` - `quotesdb/qa/g7h8i9` - `quotesdb/infra/j0k1l2` ### Merge target All completed work branches merge into the **`quotesdb`** branch (not `main`). ### Typical flow ```bash # Create worktree for a ticket git worktree add .claude/worktrees/quotesdb-api- -b quotesdb/api/ # ... do the work inside quotesdb/ root ... # Validate before merging (always from quotesdb/ root) cargo fmt && cargo check && cargo clippy && cargo test # Merge into quotesdb branch git checkout quotesdb git merge quotesdb/api/ # Remove worktree git worktree remove .claude/worktrees/quotesdb-api- ``` ## Domain Expert Sub-Agents Dispatch all implementation work to a domain expert sub-agent. Match the domain to the agent: | Domain | Path | Agent role | |--------|------|-----------| | API | `src/bin/api/` | Senior Rust backend engineer — expert in API design, Axum, workers-rs, SQLx, and unit testing | | UI | `src/bin/ui/` | Senior Rust frontend engineer — expert in Yew, Wasm, Trunk, and web design | | Shared | `src/lib.rs` | Software architect — must keep code compatible with both host and wasm32 targets | | Infra | `infra/` | DevOps/infrastructure engineer — expert in OpenTofu, Terraform, and Cloudflare | | Anything else | — | Software architect — expert in cloud services, Rust software patterns, and system design | Each sub-agent should receive: 1. The relevant section of `docs/plans/2026-02-27-quotesdb-design.md` 2. The specific bean body (`beans show --json `) 3. The validation commands to run before closing the ticket 4. The conventional commit scope to use (e.g., `feat(quotesdb): ...`) ## Design Reference ### Database Schema ```sql CREATE TABLE quotes ( id TEXT PRIMARY KEY, -- NanoID (~21 chars) text TEXT NOT NULL, author TEXT NOT NULL, source TEXT, -- optional: book, speech, etc. date TEXT, -- optional: ISO date YYYY-MM-DD auth_code TEXT NOT NULL, -- 4-word passphrase e.g. ocean-table-purple-storm created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE quote_tags ( quote_id TEXT NOT NULL REFERENCES quotes(id) ON DELETE CASCADE, tag TEXT NOT NULL, PRIMARY KEY (quote_id, tag) ); ``` ### API Endpoints | Method | Path | Description | Auth | |--------|------|-------------|------| | GET | `/api/` | OpenAPI spec (JSON) | None | | GET | `/api/quotes` | List quotes, 10/page. Query: `?page=N&author=X&tag=Y` | None | | GET | `/api/quotes/random` | Random quote | None | | GET | `/api/quotes/:id` | Get quote by NanoID | None | | PUT | `/api/quotes` | Create a quote | None (auth_code optional in body) | | POST | `/api/quotes/:id` | Update a quote | `X-Auth-Code` header | | DELETE | `/api/quotes/:id` | Delete a quote | `X-Auth-Code` header | > **Router order:** `GET /api/quotes/random` must be registered **before** `GET /api/quotes/:id`. ### Auth - No user accounts. Each quote has an `auth_code` (4-word passphrase), stored plaintext. - Provided via `X-Auth-Code` header for update/delete. On mismatch: `403 Forbidden`. - Auth code is always returned in the create response. ### Frontend Routes (Yew) | Route | Page | |-------|------| | `/` | Home — random quote + "Browse all" link | | `/browse` | Paginated list with author/tag filter controls | | `/quotes/:id` | Single quote — view, edit (auth prompt), delete (auth prompt) | | `/author/:name` | All quotes by an author | | `/submit` | New quote submission form | ## API Specification The API spec lives at `api/openapi.yaml` (OpenAPI 3.1.0, YAML format). - The spec is the source of truth for all endpoint contracts. - Validate with Redocly CLI after any changes (from the `quotesdb/` directory): ```sh redocly lint api/openapi.yaml ``` ## Validation Run in order from the **`quotesdb/` root** before closing any ticket: ```sh cargo fmt # 1. formatting cargo check # 2. syntax cargo clippy # 3. best practices cargo test # 4. correctness ``` For API spec changes: ```sh redocly lint api/openapi.yaml ``` For infra changes, run from the `infra/` directory: ```sh tofu validate tofu plan ```