# Single-Crate Refactor Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Collapse `api/`, `ui/`, and `tests/` sub-crates into a single `quotesdb` Cargo crate with two binaries (`api`, `ui`) and standard integration tests. **Architecture:** One `Cargo.toml` at `quotesdb/` root; api code lives in `src/bin/api/main.rs`; ui code in `src/bin/ui/main.rs`; shared types in `src/lib.rs`; integration tests in `tests/` as plain `.rs` files. All source files are currently stubs so migration is purely structural. **Tech Stack:** Rust, Cargo multi-binary layout, Trunk (for Wasm/ui), nbd (ticket tracking) --- ## Pre-flight All source files are stubs (`fn main() {}`). No logic to preserve. Validation commands run from `quotesdb/` root: ```sh cargo fmt && cargo check && cargo clippy && cargo test ``` Trunk validation (requires wasm32 toolchain): ```sh trunk build ``` --- ### Task 1: Mark ticket in progress **Step 1: Update ticket status** ```sh nbd update b38032 --status in_progress --json ``` Expected: JSON with `"status": "in_progress"`. --- ### Task 2: Create directory structure **Step 1: Create binary directories** ```sh mkdir -p src/bin/api src/bin/ui ``` No output expected. --- ### Task 3: Create root `Cargo.toml` **Files:** - Create: `Cargo.toml` **Step 1: Write `Cargo.toml`** ```toml [package] name = "quotesdb" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" default-run = "api" [[bin]] name = "api" path = "src/bin/api/main.rs" [[bin]] name = "ui" path = "src/bin/ui/main.rs" [dependencies] common = { path = "../common" } [dev-dependencies] [profile.release] opt-level = "z" lto = true strip = true codegen-units = 1 ``` Note: Dependencies stay minimal — just what the stubs need (nothing). Additional deps are added in implementation tickets. **Step 2: Verify it's parseable** ```sh cargo metadata --no-deps --manifest-path Cargo.toml ``` Expected: JSON output without errors. --- ### Task 4: Create `src/lib.rs` **Files:** - Create: `src/lib.rs` **Step 1: Write `src/lib.rs`** ```rust //! Shared types and utilities for the `quotesdb` crate. //! //! This module is used by both the `api` and `ui` binaries. //! Code placed here must compile for both the host target (api) //! and `wasm32-unknown-unknown` (ui). //! //! Use `#[cfg(not(target_arch = "wasm32"))]` for host-only items //! and `#[cfg(target_arch = "wasm32")]` for wasm-only items. ``` --- ### Task 5: Create `src/bin/api/main.rs` **Files:** - Create: `src/bin/api/main.rs` **Step 1: Write the api binary entrypoint** ```rust //! API server binary entrypoint. //! //! Runs the quotesdb REST API. In production this targets Cloudflare Workers //! via workers-rs. For local development it runs a plain Axum/Tokio server. fn main() {} #[cfg(test)] mod tests {} ``` Note: The old `api/src/tests.rs` was a separate file imported as a module. Here we inline the empty test module directly — simpler for a stub. --- ### Task 6: Create `src/bin/ui/main.rs` **Files:** - Create: `src/bin/ui/main.rs` **Step 1: Write the ui binary entrypoint** ```rust //! UI binary entrypoint. //! //! Compiled to WebAssembly via Trunk targeting `wasm32-unknown-unknown`. //! Runs the Yew frontend application. fn main() {} #[cfg(test)] mod tests {} ``` --- ### Task 7: Verify `cargo check` passes **Step 1: Run check** ```sh cargo check ``` Expected: No errors. If the `common` path dependency causes issues, verify `../common` is correct relative to `quotesdb/`. **Step 2: Run full validation** ```sh cargo fmt && cargo check && cargo clippy && cargo test ``` Expected: All pass. `cargo test` will report 0 tests but exit 0. --- ### Task 8: Move Trunk files **Files:** - Create: `index.html` (moved from `ui/index.html`) - Create: `Trunk.toml` (moved from `ui/Trunk.toml`, updated) **Step 1: Write `index.html`** Content is identical to `ui/index.html`: ```html QuotesDB ``` Note: Added `data-bin="ui"` to the `` tag so Trunk knows which binary to compile. **Step 2: Write `Trunk.toml`** ```toml [build] target = "index.html" ``` No `[build.cargo]` block needed — the `data-bin="ui"` attribute in `index.html` is sufficient to tell Trunk which binary to build. --- ### Task 9: Consolidate docs **Files:** - Modify: `docs/PLANNING.md` - Modify: `docs/ARCHITECTURE.md` **Step 1: Update `docs/PLANNING.md`** The sub-crate planning docs are all stubs with the same placeholder text. Replace the project-level `docs/PLANNING.md` with a consolidated version: ```markdown # quotesdb — Planning ## Development Phases ### Phase 0: Design (complete) Finalized database schema, API endpoints, request/response shapes, auth model, and frontend routes. ### Phase 1: Structure refactor (complete) Collapsed `api/`, `ui/`, and `tests/` sub-crates into a single `quotesdb` Cargo crate. - Single `Cargo.toml` at project root with two binaries: `api` and `ui` - Shared code lives in `src/lib.rs` - Integration tests live in `tests/` as standard Cargo integration tests - Trunk moved to project root ### Phase 2: Ticket Planning (pending) Planning agents create nbd tickets for api, ui, and infra implementation work. ### Phase 3: Implementation (pending) Implementation agents work through domain tickets. ## Work Log - **2026-02-27** — Phase 0 complete. Project skeleton bootstrapped. Design doc written. - **2026-02-27** — Phase 1 complete. Refactored to single-crate layout (ticket b38032). ``` **Step 2: Update `docs/ARCHITECTURE.md`** ```markdown # quotesdb — Architecture ## Component Overview | Component | Path | Description | |-----------|------|-------------| | API binary | `src/bin/api/main.rs` | Rust/Axum backend on Cloudflare Workers. Handles all data operations via SQLx and Cloudflare D1. | | UI binary | `src/bin/ui/main.rs` | Yew (Rust/Wasm) frontend on Cloudflare Pages. Communicates with the API over HTTP. | | Shared library | `src/lib.rs` | Types and utilities shared between api and ui binaries. Must compile for both host and wasm32. | | Integration tests | `tests/` | Standard Cargo integration tests. Spin up the API against in-memory SQLite and make real HTTP requests. | | Infra | `infra/` | OpenTofu configuration for Cloudflare Worker, D1 database, and Pages project. | ## Component Interactions ``` Browser └──> Cloudflare Pages (ui binary compiled to Wasm) └──> Cloudflare Workers (api binary) └──> Cloudflare D1 (SQLite) ``` The UI is a static Wasm bundle served from Cloudflare Pages. It makes fetch requests to the Worker API, which reads and writes to a D1 database bound to the Worker. Integration tests bypass the UI and talk directly to the API over HTTP, using a local in-memory SQLite database. ## Build Targets | Artifact | Command | Compile target | |----------|---------|----------------| | API server | `cargo run` or `cargo build` | host (native) | | UI Wasm bundle | `trunk serve` or `trunk build` | `wasm32-unknown-unknown` | | Tests | `cargo test` | host (native) | ## Shared Code Constraints `src/lib.rs` must compile for **both** `wasm32-unknown-unknown` (ui) and the host target (api). Avoid: - Threading primitives (`std::thread`, `std::sync::Mutex`) - Filesystem access (`std::fs`) - Any API that is not available in a Wasm environment Use `#[cfg(not(target_arch = "wasm32"))]` and `#[cfg(target_arch = "wasm32")]` guards where needed. ``` --- ### Task 10: Update project `README.md` **Files:** - Modify: (check if `quotesdb/README.md` exists; if not, create it) **Step 1: Check for existing README** ```sh ls README.md ``` **Step 2: Write consolidated `README.md`** ```markdown # quotesdb A quotes web application — browse, submit, and manage memorable quotes. ## What quotesdb is a full-stack web application with: - A JSON REST API (`api` binary) backed by Cloudflare Workers + D1 (SQLite) - A Yew/Wasm frontend (`ui` binary) hosted on Cloudflare Pages - NanoID-identified quotes protected by a 4-word passphrase auth code ## How Single Cargo crate with two binaries sharing common types via `src/lib.rs`: - `api`: Axum on Tokio, targeting Cloudflare Workers via workers-rs, SQLx + D1 - `ui`: Yew compiled to `wasm32-unknown-unknown` via Trunk ## Run ```sh # Start API server (local dev) cargo run # Start UI dev server (requires wasm32 toolchain + trunk) trunk serve ``` ## Test ```sh cargo fmt && cargo check && cargo clippy && cargo test ``` ## License Licensed under either of [Apache License, Version 2.0](../LICENSE-APACHE) or [MIT License](../LICENSE-MIT) at your option. ## Disclaimer This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6). ``` --- ### Task 11: Delete old sub-crate directories **Step 1: Remove `api/`, `ui/`, `tests/` directories** ```sh rm -rf api/ ui/ tests/ ``` > **Warning:** Verify Task 5, 6, 7, and 8 are complete before running this. Double-check with `ls src/bin/api/main.rs src/bin/ui/main.rs index.html Trunk.toml`. **Step 2: Verify the correct files remain** ```sh find . -name "*.rs" | grep -v target | sort ``` Expected output: ``` ./src/bin/api/main.rs ./src/bin/ui/main.rs ./src/lib.rs ``` --- ### Task 12: Run full validation **Step 1: Format** ```sh cargo fmt ``` Expected: exits 0. **Step 2: Check** ```sh cargo check ``` Expected: exits 0, no errors. **Step 3: Clippy** ```sh cargo clippy ``` Expected: exits 0, no warnings (or only acceptable ones). **Step 4: Test** ```sh cargo test ``` Expected: exits 0. Output: `running 0 tests` (stubs have no tests yet). --- ### Task 13: Update `CLAUDE.md` **Files:** - Modify: `CLAUDE.md` Update the `CLAUDE.md` to reflect the new single-crate structure. Key changes: 1. Directory structure diagram — remove `api/`, `ui/`, `tests/` as sub-crates 2. nbd note: tickets are scoped to the `quotesdb/` directory — always run `nbd` from there, not from parent directories 3. Validation — commands run from `quotesdb/` root (no sub-directory `cd` needed) 4. Agent dispatch — agents work in `src/bin/api/` or `src/bin/ui/`, not separate crates See Task 14 for specific edits. --- ### Task 14: Commit **Step 1: Stage all changes** ```sh git add Cargo.toml src/ index.html Trunk.toml docs/PLANNING.md docs/ARCHITECTURE.md README.md CLAUDE.md git status ``` Verify no leftover `api/`, `ui/`, or `tests/` files appear. **Step 2: Commit** ```sh git commit -m "refactor(quotesdb): collapse to single crate with api and ui binaries" ``` --- ### Task 15: Close ticket **Step 1: Mark ticket done** ```sh nbd update b38032 --status done --json ``` Expected: JSON with `"status": "done"`.