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.
227 lines
8.4 KiB
Markdown
227 lines
8.4 KiB
Markdown
+++
|
|
title = "quotesdb"
|
|
priority = 8
|
|
status = "in_progress"
|
|
ticket_type = "project"
|
|
dependencies = ["ce1e4f", "f3dc74", "c3503b", "25c413"]
|
|
+++
|
|
|
|
<skills>
|
|
REQUIRED: Use `superpowers:executing-plans` to implement this plan task-by-task.
|
|
SUGGESTED: Use `superpowers:dispatching-parallel-agents` for parallel agent dispatch.
|
|
SUGGESTED: Use `superpowers:subagent-driven-development` for agent dispatch within a session.
|
|
SUGGESTED: Use `superpowers:verification-before-completion` before marking any phase complete.
|
|
SUGGESTED: Use `superpowers:finishing-a-development-branch` when ready to merge.
|
|
SUGGESTED: Use `superpowers:using-git-worktrees` for parallel work in isolated branches.
|
|
SUGGESTED: Use `superpowers:test-driven-development` during development.
|
|
</skills>
|
|
|
|
<goal>
|
|
Bootstrap the `quotesdb` service (Rust/Axum backend on Cloudflare Workers + Yew frontend on Cloudflare Pages), then orchestrate parallel agents to plan and implement the work.
|
|
|
|
All four sub-project tickets (`f3dc74` api, `c3503b` ui, `ce1e4f` qa, `25c413` infra) must be completed and closed before this ticket can close.
|
|
</goal>
|
|
|
|
<context>
|
|
A new `quotesdb` service is being added to the vibed mono-repo. The design was finalized in Phase 0. This plan covers three phases:
|
|
|
|
1. **Phase 0 (done):** Design finalized — see `docs/plans/2026-02-27-quotesdb-design.md`
|
|
2. **Phase 1:** Dispatch 4 parallel planning agents (each in a git worktree) to create nbd tickets per domain
|
|
3. **Phase 2:** Dispatch 4 parallel implementation orchestrators per domain
|
|
|
|
**Tech Stack:** Rust, Axum, Tokio, workers-rs, SQLx, Turso (local) / Cloudflare D1 (prod), Yew, Trunk, OpenTofu, Cloudflare Workers + Pages
|
|
</context>
|
|
|
|
<design>
|
|
### 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 |
|
|
|
|
**Note:** `GET /api/quotes/random` must be registered BEFORE `GET /api/quotes/:id` in the router.
|
|
|
|
### Request/Response Shapes
|
|
|
|
**PUT /api/quotes** — Create
|
|
```json
|
|
// Request body (auth_code optional — generated if omitted)
|
|
{
|
|
"text": "...",
|
|
"author": "...",
|
|
"source": "Stanford Commencement 2005",
|
|
"tags": ["motivation"],
|
|
"date": "2005-06-12",
|
|
"auth_code": "ocean-table-purple-storm"
|
|
}
|
|
|
|
// Response 201
|
|
{
|
|
"quote": { "id": "V1StG...", "text": "...", "author": "...", ... },
|
|
"auth_code": "ocean-table-purple-storm"
|
|
}
|
|
```
|
|
|
|
**GET /api/quotes** — List
|
|
```json
|
|
{
|
|
"quotes": [...],
|
|
"page": 1,
|
|
"total_pages": 4,
|
|
"total_count": 38
|
|
}
|
|
```
|
|
|
|
**Error responses:** `{ "error": "message" }` with appropriate HTTP status.
|
|
|
|
### Auth
|
|
|
|
- No user accounts. Each quote has an `auth_code` (4-word passphrase).
|
|
- Auth codes are 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 Pages (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 form |
|
|
</design>
|
|
|
|
<phases>
|
|
## Phase 1 — Parallel Planning Agents
|
|
|
|
> **Required skill:** Use `superpowers:dispatching-parallel-agents` before dispatching.
|
|
|
|
Dispatch 4 agents IN PARALLEL (single message, 4 Task tool calls). Each works in a dedicated git worktree on its own branch. Each agent's job:
|
|
|
|
1. Read the design doc at `docs/plans/2026-02-27-quotesdb-design.md`
|
|
2. Plan their domain thoroughly
|
|
3. Create nbd tickets for every piece of work (using `nbd create --ftype md --json`)
|
|
4. Commit their tickets on their branch
|
|
|
|
### Agent 1: api-planner
|
|
|
|
**Branch:** `quotesdb/api` | **Directory:** `src/bin/api/`
|
|
|
|
> You are a senior Rust backend engineer. Plan and ticket all backend API work for `quotesdb`.
|
|
>
|
|
> The backend is Rust + Axum + Tokio targeting Cloudflare Workers (workers-rs), with SQLx/Turso for dev and Cloudflare D1 in production. An auto-generated OpenAPI spec is required.
|
|
>
|
|
> Create one ticket per: Cargo.toml setup, database migrations, each endpoint handler, auth code generator, NanoID generator, pagination logic, tag filtering, OpenAPI spec generation, unit tests, and documentation files.
|
|
|
|
### Agent 2: ui-planner
|
|
|
|
**Branch:** `quotesdb/ui` | **Directory:** `src/bin/ui/`
|
|
|
|
> You are a senior Rust frontend engineer. Plan and ticket all frontend UI work for `quotesdb`.
|
|
>
|
|
> The frontend is Rust + Yew compiled to wasm32-unknown-unknown, built by Trunk, hosted on Cloudflare Pages.
|
|
>
|
|
> Create one ticket per: Cargo.toml + Trunk.toml setup, Yew app shell + routing, each page component (Home, Browse, Quote Detail, Author, Submit), API client module, auth code modal, pagination component, and documentation files.
|
|
|
|
### Agent 3: qa-planner
|
|
|
|
**Branch:** `quotesdb/qa` | **Directory:** `tests/`
|
|
|
|
> You are a senior QA engineer. Plan and ticket all integration test work for `quotesdb`.
|
|
>
|
|
> Tests live in `tests/` as Cargo integration tests, using a real HTTP client against a locally-spawned API server with a temporary SQLite database.
|
|
>
|
|
> Create one ticket per: dev-dependencies setup, test server harness, and one test suite per endpoint (list, get, random, create, update, delete, tags, router ordering).
|
|
|
|
### Agent 4: infra-planner
|
|
|
|
**Branch:** `quotesdb/infra` | **Directory:** `infra/`
|
|
|
|
> You are a Terraform/OpenTofu expert. Plan and ticket all infrastructure work for `quotesdb`.
|
|
>
|
|
> Resources needed: Cloudflare Worker (API), Cloudflare D1 database, Cloudflare Pages project, custom domain `quotes.elijah.run`. Use OpenTofu with the Cloudflare provider.
|
|
>
|
|
> Create one ticket per: OpenTofu project init, Workers script resource, D1 database resource, Pages project, custom domain, secrets docs, migration workflow docs, and infra README.
|
|
|
|
---
|
|
|
|
## Phase 2 — Parallel Implementation Orchestrators
|
|
|
|
> **Required skill:** Use `superpowers:dispatching-parallel-agents` before dispatching.
|
|
|
|
After all planning branches are merged, dispatch 4 orchestrator agents IN PARALLEL. Each orchestrator:
|
|
1. Lists all tickets for their domain (`nbd ready --json`)
|
|
2. Dispatches a sub-agent per ticket (one at a time — complete one before starting the next)
|
|
3. Reviews the sub-agent's work after each ticket (validate: `cargo fmt && cargo check && cargo clippy && cargo test`)
|
|
4. Continues to the next ticket
|
|
|
|
**Domain orchestrators:**
|
|
- **api-orchestrator** — works in `src/bin/api/`, dispatches 1 sub-agent per api ticket
|
|
- **ui-orchestrator** — works in `src/bin/ui/`, dispatches 1 sub-agent per ui ticket
|
|
- **qa-orchestrator** — works in `tests/`, dispatches 1 sub-agent per qa ticket
|
|
- **infra-orchestrator** — works in `infra/`, dispatches 1 sub-agent per infra ticket
|
|
|
|
Each sub-agent receives: design doc context, the specific ticket body, validation commands, and the conventional commit scope (`feat(quotesdb): ...`).
|
|
</phases>
|
|
|
|
<verification>
|
|
After Phase 2 completes, verify the full stack:
|
|
|
|
```sh
|
|
# From quotesdb/
|
|
cargo fmt && cargo check && cargo clippy && cargo test
|
|
|
|
# Start API server locally
|
|
cargo run
|
|
|
|
# Build UI to Wasm
|
|
trunk build
|
|
|
|
# Dry-run infra (from infra/)
|
|
tofu plan
|
|
```
|
|
|
|
Manual smoke test:
|
|
```sh
|
|
# Create a quote
|
|
curl -X PUT http://localhost:8787/api/quotes \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"text":"Hello world","author":"Test"}'
|
|
|
|
# List quotes
|
|
curl http://localhost:8787/api/quotes
|
|
|
|
# Random quote
|
|
curl http://localhost:8787/api/quotes/random
|
|
```
|
|
</verification>
|