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.

252 lines
7.1 KiB
Markdown

@../CLAUDE.md
# CLAUDE.md — quotesdb
<working-directory>
Run all commands from the relevant sub-directory within `quotesdb/` (e.g., `api/`, `ui/`, `infra/`) — not from the repository root or the `quotesdb/` root.
</working-directory>
<project-overview>
## 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`
</project-overview>
<ticket-hierarchy>
## 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 <id>
beans update --json <id> --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/<bean-id> (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 <sub-epic-id> --blocked-by <new-bean-id>`.
- 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).
</ticket-hierarchy>
<branch-and-worktree-workflow>
## Branch & Worktree Workflow
All work happens in **git worktrees** on named branches.
### Branch naming
```
quotesdb/<domain>/<ticket-id>
```
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-<id> -b quotesdb/api/<id>
# ... 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/<id>
# Remove worktree
git worktree remove .claude/worktrees/quotesdb-api-<id>
```
</branch-and-worktree-workflow>
<domain-expert-agents>
## 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 <id>`)
3. The validation commands to run before closing the ticket
4. The conventional commit scope to use (e.g., `feat(quotesdb): ...`)
</domain-expert-agents>
<design-reference>
## Design Reference
<database-schema>
### 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)
);
```
</database-schema>
<api-endpoints>
### 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`.
</api-endpoints>
<auth>
### 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.
</auth>
<frontend-routes>
### 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 |
</frontend-routes>
</design-reference>
<api-specification>
## 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
```
</api-specification>