52 Commits (7d0df10d7550f7f1832cb2d5d88ddfeb51738739)

Author SHA1 Message Date
Elijah Voigt 7d0df10d75 feat(quotesdb): show locked banner on /submit when submissions are closed
Check GET /api/status on mount; if submissions_locked is true, hide the
form and show a .submissions-closed-banner instead. Fail-open: on error,
treat as unlocked and display the form normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 0be1193759 fix(quotesdb): fix admin_reset_auth_code call, handle status fetch error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 2d1634c815 fix(quotesdb): fix admin_reset_auth_code call, handle status fetch error
Pass "" for the unused `current` param in admin_reset_auth_code (the
server only checks X-Admin-Code). Handle get_status() failure in the
on-mount effect with fail-open behaviour: set submissions_locked=Some(false)
and display "Could not fetch status." so the UI never hangs on
"Loading status...".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt d9659a1c00 feat(quotesdb): /admin page component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 37cc5ce443 fix(quotesdb): atomic update_admin_auth_code, fix handler docstring
Replace the two-step read-check-write in update_admin_auth_code with a
single atomic UPDATE … WHERE key = 'admin_auth_code' AND value = ?current
in both NativeRepository and D1Repository. Rows-affected count is checked:
zero means the code was absent or mismatched → DbError::Forbidden; one
means success.

Also remove the now-unnecessary replacement2 clone binding in native.rs.

Fix the reset_auth_code handler doc comment to accurately describe that a
missing X-Admin-Code header is caught by the handler itself (before any DB
call), while a wrong-but-present code reaches the DB layer which returns
DbError::Forbidden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 8b301d23bb feat(quotesdb): POST /api/admin/reset-auth-code endpoint
Adds handler, route registration, request/response types, and five unit
tests for the admin auth-code rotation endpoint. Updates openapi.yaml
with the new path and a ResetAuthCodeResponse component schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 7619391d43 feat(quotesdb): enforce submission lock on PUT /api/quotes
Add a pre-flight check at the top of create_handler that calls
get_submissions_locked() before processing the request. Returns
423 Locked with {"error": "submissions are closed"} when locked.

Update openapi.yaml to document the 423 response on PUT /api/quotes.

Add three unit tests: locked → 423, unlocked → 201, unlock-then-create → 201.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 64cd35ce1e fix(quotesdb): fix verify_admin_code docstring, add 500 to OpenAPI, make handlers private
- Clarify verify_admin_code docstring to say "standard string equality"
  instead of leaving comparison method implicit
- Add missing "500" response entries to /api/admin/lock and
  /api/admin/unlock in openapi.yaml
- Remove pub from lock_submissions and unlock_submissions to match all
  other handlers in the file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt d693ce18cb feat(quotesdb): POST /api/admin/lock and /api/admin/unlock endpoints
Add two admin-protected endpoints that toggle the global submissions lock:
- POST /api/admin/lock  — sets submissions_locked = true
- POST /api/admin/unlock — sets submissions_locked = false

Both require the X-Admin-Code header and return { "submissions_locked": bool }
on success, or 403 on missing/wrong code. Operation is idempotent.

Shared helper verify_admin_code() fetches and compares the stored admin code.
Routes registered in the router() function. Five unit tests added covering
correct code, wrong code, missing header, and idempotent lock behaviour.

OpenAPI spec updated with AdminCode security scheme, LockResponse schema,
/api/admin/lock and /api/admin/unlock path entries, and an admin tag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt f0b10d506c docs(quotesdb): add /api/status to OpenAPI spec
Add GET /api/status path and StatusResponse schema. The endpoint
returns { "submissions_locked": bool } with 200 or 500, requires
no auth, and is tagged under the existing `meta` group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt feac146403 feat(quotesdb): GET /api/status public endpoint
Adds the GET /api/status handler that returns {"submissions_locked": bool}.
Registers the route in the router before the quotes routes.
Adds three unit tests covering unlocked state, locked state, and default false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 177a892d94 feat(quotesdb): admin API client functions in UI
Add ApiError::Forbidden variant, StatusResponse / ResetAuthCodeResponse /
LockResponse / ResetAuthCodeBody types, and four new async functions to
src/bin/ui/api.rs:
- get_status()           → GET /api/status
- admin_reset_auth_code() → POST /api/admin/reset-auth-code (X-Admin-Code + X-Auth-Code)
- admin_lock()           → POST /api/admin/lock (X-Admin-Code)
- admin_unlock()         → POST /api/admin/unlock (X-Admin-Code)

HTTP 403 responses map to ApiError::Forbidden in all three admin functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt a7bd635b59 test(quotesdb): add handler-level tests for DB admin methods
Make MockRepo stateful for admin_auth_code and submissions_locked so
the new QuoteRepository methods can be exercised without a real DB.

Add four tests to src/bin/api/handlers/mod.rs:
- get_submissions_locked returns false by default
- set_submissions_locked(true) then get_submissions_locked returns true
- update_admin_auth_code with correct current succeeds and returns new code
- update_admin_auth_code with wrong current returns Forbidden

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 67d247afd1 feat(quotesdb): DB layer — add submissions_locked + update_admin_auth_code
Add three new QuoteRepository trait methods and a seed helper:
- update_admin_auth_code(current, new_code): replaces the admin code if
  `current` matches; generates a fresh passphrase when new_code is None;
  returns DbError::Forbidden on mismatch.
- get_submissions_locked(): reads the submissions_locked key from
  admin_config; returns false when the key is absent.
- set_submissions_locked(locked): upserts "1"/"0" into admin_config.
- seed_submissions_locked(): INSERT OR IGNORE "0" — safe to call on every
  startup without clobbering an active lock.

Implemented in both NativeRepository (rusqlite) and D1Repository (wasm32).
Updated startup seeding in main.rs (native and wasm32 paths) to call
seed_submissions_locked after the existing admin auth code seeding.

Added 7 unit tests in db/native.rs covering all four specified scenarios:
default false, set-then-get, seed does not overwrite, correct code succeeds,
None new_code generates passphrase, wrong code returns Forbidden, stored
code unchanged after Forbidden.

MockRepo in handlers/mod.rs updated with stub implementations of all four
new trait methods to satisfy the trait bound.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 95cd0a8183 docs(quotesdb): admin features design doc 3 months ago
Elijah Voigt 2ccad33921 feat(quotesdb): Cloudflare Turnstile CAPTCHA on submit
- infra/turnstile.tf: provision Turnstile widget (managed mode, quotes.elijah.run domain) with site_key and secret_key outputs
- infra/variables.tf: add var.domain (default: quotes.elijah.run)
- src/lib.rs: add cf_turnstile_token: Option<String> (#[serde(default)]) to CreateQuoteInput; update doctest
- Cargo.toml: add reqwest (0.12, rustls-tls) under native-only dependencies
- src/bin/api/handlers/mod.rs: add verify_turnstile() and CAPTCHA gate in create_handler, both gated on #[cfg(not(target_arch = "wasm32"))]
- src/bin/api/db/native.rs: add cf_turnstile_token: None to all CreateQuoteInput struct literals in tests
- api/openapi.yaml: document cf_turnstile_token field in QuoteCreateRequest schema
- index.html: add Turnstile JS script tag
- src/bin/ui/pages/submit.rs: add turnstile_token state, use_effect_with callback registration, widget div, token included in CreateQuoteInput
- docs/LOCAL_DEV.md: add Cloudflare Turnstile CAPTCHA section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt fb93483f5c feat(quotesdb): date range filter for quotes list
Add 6 optional query parameters to GET /api/quotes:
  date_after_year/month/day and date_before_year/month/day

Changes:
- QuoteRepository::list_quotes gains date_after and date_before params
- NativeRepository and D1Repository build ISO date prefix WHERE clauses;
  quotes with NULL date are excluded when any bound is set
- list_handler validates component ordering (month requires year, etc.)
  and returns 400 on invalid combinations
- build_date_bound helper converts y/m/d components to ISO prefix strings
- UI api::list_quotes and browse page gain From/To year filter inputs
- author page call updated to pass None for the new date params
- openapi.yaml extended with 6 new query parameter entries
- 6 new integration tests covering after, before, range, and 400 cases
- 1 new native DB unit test covering all filter combinations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt caf2246bff feat(quotesdb): admin super auth code for quote moderation
Add an admin_config table storing a single admin auth code that
bypasses per-quote auth checks for update and delete operations.
The code is auto-generated on first startup and printed to stderr.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt c9e4d10934 feat(quotesdb): add workers-rs WASM entry point to api binary
- Gate native Tokio/Axum main() with #[cfg(not(target_arch = "wasm32"))]
- Add #![cfg_attr(target_arch = "wasm32", no_main)] to suppress missing-main error
- Add #[worker::event(fetch)] entry point using worker::HttpRequest / http::Response<axum::body::Body>
- Enable `http` feature on worker dep so fetch handler uses standard http types
- Add axum (json+query features), tower-service, and http to wasm32 deps
- Move async-trait to shared [dependencies] so both targets have it
- Make db::d1 module pub so main.rs can access D1Repository on wasm32
- Fix worker::d1::Database → D1Database and PreparedStatement → D1PreparedStatement
- Add #[cfg_attr(target_arch = "wasm32", worker::send)] to all 7 handler fns
  so their futures satisfy Axum's Handler<Send> bound on single-threaded wasm32
3 months ago
Elijah Voigt 84088ef45b fix(quotesdb): resolve compiler warnings in api and ui
- Remove redundant #![cfg(target_arch="wasm32")] from d1.rs (module
  declaration in mod.rs already gates it)
- Remove unused D1Repository re-export from db/mod.rs
- Drop unused page/total_count fields from UI ListResponse struct
  (only total_pages is consumed by the browse page)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt c52eb820d2 feat(quotesdb): implement D1Repository for Cloudflare Workers
Replace all 7 stub methods in src/bin/api/db/d1.rs with full working
implementations using the Cloudflare D1 API from workers-rs 0.5.

Implements:
- run_migrations: executes four DDL statements via db.exec()
- list_quotes: dynamic WHERE clause with positional params, COUNT query,
  paginated SELECT, per-quote tag fetch
- get_quote: prepared statement with first::<QuoteRow>()
- get_random_quote: ORDER BY RANDOM() LIMIT 1
- create_quote: INSERT + batch tag insert + read-back for timestamps
- update_quote: auth check, dynamic SET clause, optional tag replacement,
  read-back of updated row
- delete_quote: auth check, DELETE, returns DeleteResult enum

Also adds helper structs (QuoteRow, AuthRow, TagRow, CountRow),
fetch_tags() helper method, and unsafe Send/Sync impls required for
Arc<dyn QuoteRepository + Send + Sync> on single-threaded wasm32.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt cd3afa1920 fix(quotesdb): home page friendly empty state when no quotes
Detect 404 from random quote endpoint and render a friendly
'Nothing here yet — Submit a quote!' message with a link to
/submit instead of the generic error display.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 8086ca06a1 fix(quotesdb): submit form UX fixes (6a4c61, dfd185, 5b3475)
- Replace 'Submit another' link with 'Browse all quotes' -> Route::Browse
- Change date input from type=text to type=date, remove placeholder
- Make author optional (defaults to Anonymous), update label
- Clarify auth code label to 'Edit/delete passphrase'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 59859c14b0 fix frontend proxy issue 3 months ago
Elijah Voigt 159956e527 chore(quotesdb): close all tickets — implementation complete
All 69 implementation tickets done. Root ticket ec118c closed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 739e6b8476 test(quotesdb): add integration test suite for all API endpoints
Adds 26 integration tests in handlers::integration_tests using a real
NativeRepository backed by a NamedTempFile SQLite database. Each test
gets an isolated database via test_router(). tempfile added to
[dev-dependencies]. Closes tickets: 5f5ba0, 9b581f, 789d0f, aa0eab,
f9f448, 4a4c26, 93f1b6, fae330, 8c87db, 893eba, e8f5cf, ce1e4f.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt d4be4653d8 fix(quotesdb): gate UI binary on wasm32, update UI ticket statuses
- main.rs: add #[cfg(target_arch = "wasm32")] guards on all wasm32-only
  code; add stub fn main() for native targets so cargo check passes
- Ticket statuses updated for all completed UI page/component tickets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 92e0f691cf feat(quotesdb): add comprehensive BEM stylesheet for all UI components
Covers all component and page classes:
- Navigation bar (nav, nav__brand, nav__links, nav__link)
- QuoteCard (quote-card, quote-card__text, footer, author, source, tags, tag, link)
- ErrorDisplay (error-display, error-display__message)
- Pagination (pagination, pagination__btn, pagination__info)
- AuthModal (auth-modal__overlay, auth-modal, auth-modal__title/input/actions)
- All page containers (page-home, page-browse, page-quote, page-author, page-submit)
- Edit form, submit form, success state with auth code box
- Base styles: reset, typography, buttons, form inputs
- Responsive breakpoint at 640px

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 5aa31b59f0 feat(quotesdb): implement all UI page components (Home, Browse, QuoteDetail, Author, Submit)
- HomePage: fetches random quote on mount, displays with QuoteCard and browse/submit links
- BrowsePage: paginated list with author and tag filter inputs, Pagination component
- QuotePage: view/edit/delete with AuthModal gating, 403/404 handling, sessionStorage auth
- AuthorPage: lists quotes by author with tag filter and pagination
- SubmitPage: full form with all fields, success state showing auth code prominently
- Tag filter (d3d502) integrated into Browse and Author pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt a5b7c8d856 feat(quotesdb): implement API DB layer and all HTTP handlers
DB layer (src/bin/api/db/):
- native.rs: NativeRepository (tokio-rusqlite) implementing all CRUD ops,
  dynamic WHERE for filters, two-phase auth check for update, 13 unit tests
- d1.rs: D1Repository wasm32 stub (all methods return Internal error)
- connection.rs: open() helper — WAL + foreign_keys pragmas
- mod.rs: cfg-gate async_trait (Send on native, ?Send on wasm32)

Handlers (src/bin/api/handlers/mod.rs):
- All 7 routes: GET /api/, random, {id}, list, PUT create, POST update, DELETE
- Router order: random BEFORE {id} (prevents "random" matching as id)
- Auth: X-Auth-Code header validation → 403 on mismatch
- 13 handler unit tests with MockRepo

main.rs: opens DB, runs migrations, wraps in Arc<dyn Repo + Send + Sync>,
  binds on $PORT (default 3000)

Cargo.toml: tower dev-dep for ServiceExt::oneshot in tests

All 32 tests pass (26 api + 6 lib)

Tickets closed: 00aff0 a5049d 6e829e 28e7d9 886bfd 2ce22e 5dbb7d 05f8ae
                d792e2 5d9f5a b20b5a 175382 03bb91

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 682d15b40d feat(quotesdb): implement UI app shell, API client, storage, and base components
- Add BrowserRouter with all 5 routes (Home, Browse, QuoteDetail, Author, Submit)
- Implement typed API client (list, get, random, create, update, delete)
- Implement sessionStorage auth code helpers (set/get/clear)
- Add ErrorDisplay, QuoteCard, AuthModal, Pagination components
- Add stub page components for initial compilation
- Fix Cargo.toml: uuid js feature for wasm32, getrandom 0.3 wasm_js for rand dep,
  js-sys and Storage web-sys feature for API client and storage module

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt ea6fa981fc feat(quotesdb): add QuoteRepository trait, migrations, and thiserror dependency
- db/mod.rs: QuoteRepository async trait + ListResult/DeleteResult/DbError types
- db/migrations.rs: SQL DDL strings for quotes, quote_tags, and indexes
- lib.rs: fix rand 0.9 trait import (SliceRandom → IndexedRandom)
- Cargo.toml: add thiserror = "2" for DbError derive

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt efa23f1c7c feat(quotesdb): set up Trunk.toml and index.html for UI build
- Trunk.toml: target index.html, proxy /api/* to localhost:3000
- index.html: link CSS, Rust wasm binary (ui), copy _redirects

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt cb59ccb716 chore(quotesdb): add _redirects SPA fallback for Cloudflare Pages routing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt b629b1541c feat(quotesdb): add build.rs to convert api/openapi.yaml to JSON at compile time
- Create build.rs at crate root using serde_yaml (build-dep only) to parse
  api/openapi.yaml and write compact JSON to $OUT_DIR/openapi.json
- cargo:rerun-if-changed ensures re-conversion only when spec changes
- serde_yaml never enters the Workers or native binary (build-dep only)
- Downstream GET /api/ handler consumes via include_str!(concat!(env!("OUT_DIR"), "/openapi.json"))

Closes ticket 8892d5
3 months ago
Elijah Voigt 894ef980c0 feat(quotesdb): add Yew/Wasm UI dependencies to Cargo.toml
- Add yew 0.22 with csr feature for Rust/Wasm SPA frontend
- Add yew-router 0.19 for client-side routing
- Add gloo 0.11 for Web API utilities (timers, fetch, events)
- Add wasm-bindgen 0.2 matching Nix wasm-bindgen-cli 0.2.108
- Add wasm-bindgen-futures 0.4 for async fetch support
- Add web-sys 0.3 for raw DOM/browser API bindings
All UI deps scoped to wasm32 cfg target to avoid host build pollution

Closes ticket 93515e
3 months ago
Elijah Voigt 2993149da1 chore(quotesdb): set up Cargo.toml with api and ui dependencies
- Add axum 0.8 + tokio 1 (native-only) for the HTTP API server
- Add rusqlite 0.32 + tokio-rusqlite 0.6 (native-only) for local SQLite
- Add async-trait 0.1 (native-only) for QuoteRepository abstraction
- Add worker 0.5 with d1 feature (wasm32-only) for Cloudflare Workers
- Add rand 0.9 + serde + serde_json (shared) for passphrase/serialisation
- Add build-dependencies: serde_json 1 + serde_yaml 0.9 for build.rs
- Pin rusqlite to 0.32 to match tokio-rusqlite 0.6 transitive dependency

Closes ticket 1f5bb5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 66e5302d9b feat(quotesdb): add generate_id() using UUID v4 — WASM-compatible ID generation
- Add uuid = { version = "1", features = ["v4", "serde"] } to [dependencies]
- Add getrandom = { version = "0.4", features = ["wasm_js"] } under wasm32 cfg
- Implement generate_id() in src/lib.rs with rustdoc and doctest
- Add unit tests for length, hyphen count, and uniqueness

Closes ticket 7a0d9f

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 57322d865b plan to implement the current batch of tickets 3 months ago
Elijah Voigt fc89180b82 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 <noreply@anthropic.com>
3 months ago
Elijah Voigt 895b63a77c docs(quotesdb): add XML tags to all tickets for improved LLM guidance
All 80 nbd tickets updated with structured XML tags:
- Task tickets: <context>, <goal>, <constraints>, <skills>, <validation>, <commit>
- TRIAGE tickets: <context>, <question>, <options>, <resolution>, <commit>
- Project tickets: <context>, <goal>, <skills>, <validation>
- ec118c (root): <skills>, <goal>, <context>, <design>, <phases>, <verification>
- b38032 (done): <goal>, <current-state>, <target-state>, <changes>, <constraints>, <validation>, <summary>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 7fb2f8cac7 some tickets marked in progress 3 months ago
Elijah Voigt bd0c2af085 refactor(quotesdb): collapse to single crate with api and ui binaries
Replaces the three separate sub-crates (api/, ui/, tests/) with a single
Cargo crate at the quotesdb/ root. Shared code lives in src/lib.rs; the
api and ui are multi-binary targets; integration tests use the standard
Cargo tests/ layout. Trunk files moved to project root with data-bin="ui".

Closes ticket b38032.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 775761929c chore(quotesdb): bootstrap project skeleton and design doc
- Scaffold api/, ui/, tests/, infra/, docs/ directories
- Stub Cargo.toml for api, ui, and tests crates
- Write finalized design doc to docs/plans/2026-02-27-quotesdb-design.md
- Add placeholder PLANNING.md, ARCHITECTURE.md, README.md per domain
- Add stub main.rs and tests.rs for api and ui
- Add index.html and Trunk.toml for ui
- Add placeholder infra/main.tf with Cloudflare provider stub

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt cfcddefc80 flake and quotesdb misc change 3 months ago
Elijah Voigt 7e0ba78240 docs: improve CLAUDE.md structure and clarity
- Add working-directory rule to root and all project docs
- Generalize root from "HTTP web services" to "independent projects"
- Add @../CLAUDE.md inheritance to nbd, edu, quotesdb project docs
- Remove sections duplicated from root in project-level docs
- Wrap all sections in semantic XML tags for clearer agent parsing
- Rename Tech Stack → Common Patterns (framed as defaults, not requirements)
- Rename Running Services Locally → Running Projects Locally
3 months ago
Elijah Voigt 75876988e0 claude generated openapi spec 3 months ago
Elijah Voigt 1c90c0fac1 tickets(quotesdb): assign all tickets to domain sub-projects with dependency graph
Evaluated all quotesdb tickets and assigned each to the correct sub-project
dependency (api/f3dc74, ui/c3503b, qa/ce1e4f, infra/25c413). Also wired
inter-ticket dependencies to enforce correct implementation order within each
domain and cross-domain (e.g. QA test suites depend on the API handlers they
exercise). Validated with `nbd graph --json` — 78 nodes, 200 edges, no cycles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt bc17a9ae07 plan(quotesdb): create domain planning tickets for api, ui, qa, and infra
Adds 84 tickets across 4 sub-domains with a full dependency graph:

- quotesdb/api (f3dc74): 15 work tickets + 6 TRIAGE tickets
  Covers Cargo.toml, Workers entry point, DB migrations, all 7 endpoints,
  auth code gen, tag handling, error envelope, unit tests, and docs.

- quotesdb/ui (c3503b): 15 work tickets + 5 TRIAGE tickets
  Covers Yew/Wasm setup, Trunk config, app shell, API client, 5 pages,
  shared components (QuoteCard, auth modal, pagination, tag filter), and docs.

- quotesdb/qa (ce1e4f): 12 work tickets + 4 TRIAGE tickets
  Covers test harness, one test suite per endpoint, router ordering,
  tag operations, and README.

- quotesdb/infra (25c413): 10 work tickets + 6 TRIAGE tickets
  Covers OpenTofu setup, D1 database, Worker script + route, Pages project,
  custom domain (quotes.elijah.run), migration workflow, secrets, and README.

Dependency graph:
- Root ticket (ec118c) blocked on all 4 sub-project tickets
- Sub-project tickets blocked on their leaf work tickets
- Work tickets have intra-domain sequential deps (e.g. Cargo.toml → DB → handlers)
- QA test harness has a cross-domain dep on the completed api sub-project
- All 21 TRIAGE tickets are immediately unblocked as the highest-priority starting work

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
Elijah Voigt 04b659ee7c quotesdb scaffolding 3 months ago