Archive self-play beans, update shader/LLM parent beans to completed,
and add self-play chapter content to ml-self-play.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Markov epic (edu-svom) incorrectly used blocked-by relationships to
reference its 10 section tasks. Replaced with proper parent-child
hierarchy: removed blocked-by entries from the epic and set parent on
each section task.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds llm-from-scratch.md stub with 14 sections (GPT-1 style: character
tokenisation, self-attention, transformer block, candle model, training
loop, sampling). Creates beans edu-32xl through edu-9sb7 for each section.
Add edu/src/ml-self-play.md with 14 stubbed sections across 5 parts:
foundations (RL, MCTS, self-play), game engine (Tic-Tac-Toe), MCTS
implementation, neural network integration, and the full self-play loop.
Create one beans ticket per section (edu-wobk through edu-brtk).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add shaders.md with 18 stub sections across 6 parts (GPU model,
wgpu setup, vertex/fragment shaders, textures, compute shaders,
going further). Create beans tickets edu-5g0l through edu-7m8d
for each section. Add # Graphics section to SUMMARY.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nbd has been superseded by beans for issue tracking. All tickets were
migrated to .beans/ in the previous commits. Remove the nbd/ project
directory, its flake input, and all references.
- Remove nbd/ (source, tests, docs, beans, skills, flake)
- Remove nbd flake input and nbd package from devShell in flake.nix
- Update flake.lock to drop nbd and its transitive inputs
- Remove nbd entry from PROJECTS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Simplify all issue-tracking documentation to defer to `beans prime`
rather than maintaining a parallel description of beans commands.
- CLAUDE.md: collapse task-tracking section to just `beans prime` callout
- edu/.claude/skills/work: nbd next → beans list --ready
- nbd/.claude/skills/work: nbd next → beans list --ready
- nbd/.claude/skills/triage: nbd tickets/.nbd → beans beans/.beans
- nbd/src/claude_md_snippet.md: replace nbd snippet with beans prime stub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds src/introduction.md explaining the project (LLM-generated learning
resources, Claude Code + Opus 4.6/Sonnet 4.6, CC0 public domain, contact
email) and links it as the first entry in SUMMARY.md.
- OpenTofu config in infra/ for Pages project, DNS CNAME, and custom
domain at vibebooks.elijah.run
- justfile with build, serve, deploy, infra-init, infra-plan, infra-apply
targets
Closes 59c122
- Add GET /api/admin/verify — side-effect-free code check used by the
admin unlock flow; registered before reset-auth-code in the router
- Remove "Reset auth code" section from admin panel (UI + dead API code);
rotation is now CLI-only via `wrangler secret put ADMIN_AUTH_CODE`
- Add rotate-admin-code justfile recipe using pwgen for local key rotation
- Add pwgen to Nix dev shell
- Update OpenAPI spec with /api/admin/verify definition
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AppState to handlers, read ADMIN_AUTH_CODE from Worker env, gate
reset-auth-code with 409 when secret is active, update tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Yew's Renderer::new() renders into <body>, not #app, so the flex
column layout on #app had no effect. Move display:flex and
flex-direction:column to body so .main-content{flex:1} correctly
pushes .site-footer to the bottom of the viewport.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move src/bin/api/db/ and src/bin/api/handlers/ to src/db/ and
src/handlers/ so they compile as library modules accessible to both
the native binary and the Cloudflare Workers entry point
- Upgrade worker crate 0.5 → 0.7; add workers-api feature flag and
cdylib/rlib crate-type to Cargo.toml
- Update flake.nix: add worker-build and just to the dev shell; bump
flake.lock (nixpkgs + rust-overlay)
- Consolidate rate limit rules to one (Free plan allows only 1 rule
per zone in the http_ratelimit phase)
- Update infra/worker.tf to deploy via wrangler rather than Terraform
(Cloudflare provider v4 can't upload ES module + wasm bundles)
- Extend .gitignore to exclude *.wasm build artifacts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
D1's exec() treats newlines as statement separators, causing multiline
CREATE TABLE statements to be truncated after the first line and return
"incomplete input: SQLITE_ERROR" on every request.
Fix: switch run_migrations() in D1Repository to use prepare(sql).run()
instead of exec(sql), which treats the full string as a single statement.
Also moves db and handlers modules from src/bin/api/ to src/ (library
modules), adds justfile with build/deploy/migrate recipes, adds
migrations/schema.sql for direct wrangler d1 execute usage, and adds
wrangler.toml for worker deployment configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove captcha-gated disabled state on report submit button — Turnstile
does not fire a DOM input event so captcha_solved never became true; rate
limiting is handled at the WAF layer so the gate was incorrect
- Token is still read from the hidden Turnstile input at submit time if present
- Add explanatory hint below "Enter Auth Code" heading in AuthModal explaining
the code was provided or generated when the quote was created
- Add .auth-modal__hint CSS class for the hint text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Derive PartialEq on ReportSummary, ReportListResponse, ReportRow, and
QuoteReports so they can be held in Yew state enums that require PartialEq
- Remove unused ApiError import from moderation_tab.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Moderation tab to the admin page (visible after unlock) showing a
paginated list of reported quotes. Clicking a row opens a detail modal with
the full quote, all individual reports, and action buttons to delete the
quote, hide it, or dismiss the reports.
- api.rs: add ReportSummary, ReportListResponse, ReportRow, QuoteReports types
and five admin_* async functions for the reports endpoints
- components/moderation_tab.rs: new ModerationTab function_component with
paginated list, row-click loading state, and an inline detail modal
- components/mod.rs: expose moderation_tab module
- pages/admin.rs: introduce AdminTab enum and tab bar; wrap existing settings
content in the Settings tab; add Moderation tab rendering ModerationTab
- style.css: add styles for admin tabs, moderation list table, and detail modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add HideAuth action variant to the Action enum
- Show yellow badge when quote.hidden is true
- Add Hide/Make Public toggle button in the actions bar
- Reuse AuthModal for the hide/unhide auth prompt
- Call POST /api/quotes/:id with { hidden: !current } and X-Auth-Code
- Update quote state on success; re-prompt on 403
- Add .page-quote__hidden-badge CSS styles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Report button to /quotes/:id that opens a modal containing:
- Optional reason textarea (max 256 chars) with live character counter
- Cloudflare Turnstile CAPTCHA widget (always-passes test sitekey as const)
- Submit button disabled until CAPTCHA token is non-empty
- Cancel button
On submit POSTs { reason?, captcha_token } to POST /api/quotes/:id/report.
Shows a success banner on 201 or an error via ErrorDisplay on failure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a toggle button ("Filters ▼ / ▲") above the quote list on the
browse page. Filter controls are hidden by default and expand when
clicked. Each filter (Author, Tag, Date range) is on its own labelled
row with consistent styling. Existing API query logic is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mark admin moderation endpoints ticket as done following successful
implementation and test pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements ticket 6c5904 — five admin-authenticated endpoints for
moderation workflows:
GET /api/admin/reports paginated list of reported quotes
GET /api/admin/reports/:id full quote + all report rows
DELETE /api/admin/reports/:id/quote unconditionally delete a quote
POST /api/admin/reports/:id/hide set hidden=1 on a quote
DELETE /api/admin/reports/:id/reports clear all reports for a quote
All endpoints require X-Admin-Code header; 403 on missing/wrong code.
DB layer additions:
- QuoteRepository trait gains list_reports, get_reports_for_quote,
admin_delete_quote, hide_quote, and clear_reports methods
- New ReportRow, ReportSummary, ReportListResult, and QuoteReports
types added to db/mod.rs
- Implementations in native.rs (rusqlite) and d1.rs (Cloudflare D1)
Tests added:
- 14 unit handler tests using MockRepo (3 per endpoint covering
success, 404, and 403 cases)
- 5 integration tests using real SQLite via NativeRepository
- 10 DB-layer unit tests in native.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds infra/rate-limits.tf with a cloudflare_ruleset (phase: http_ratelimit)
implementing per-IP rate limits on all mutating API endpoints:
- PUT /api/quotes: 5 requests per 10 minutes (quote creation)
- POST /api/quotes/:id/report: 3 requests per hour (abuse reports)
- POST /api/quotes/🆔 10 requests per minute (quote updates)
- DELETE /api/quotes/🆔 10 requests per minute (quote deletes)
The report rule is ordered before the general update rule to ensure the
more-specific /report path matches before the broader /api/quotes/:id
pattern. Documents the approach, plan requirements, and layered protection
rationale in docs/ARCHITECTURE.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove the Admin link from the top navigation bar; /admin remains
reachable by direct URL but is no longer discoverable from normal
browsing.
- Rework /admin to an auth-first flow: on load the page shows only a
password input and an Unlock button. On success, the admin controls
(submission lock/unlock, auth code reset) are revealed; on failure a
clear error message is shown and the page stays locked. Refreshing
always resets to locked state (code is in component state only).
- Add api::verify_admin_code() — calls POST /api/admin/reset-auth-code
with new_code equal to the entered code, making the call idempotent
on success (code unchanged) while still returning 403 on mismatch.
- Fix pre-existing wasm build breakage in quote.rs: UpdateQuoteInput
gained a hidden field in an earlier ticket but quote.rs was never
updated. Added hidden: None to the struct literal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CREATE_REPORTS migration constant (was unused — now wired in)
- Wire CREATE_REPORTS into run_migrations for both NativeRepository and D1Repository
- Add create_report to QuoteRepository trait with NotFound semantics
- Implement create_report in NativeRepository (two-step: existence check then insert)
- Implement create_report in D1Repository (two-step: COUNT check then insert)
- Add report_handler: POST /api/quotes/{id}/report, 201/400/404/500
- Register route before /{id} in router so static /report suffix wins
- Add create_report to MockRepo in handler tests
- Add handler tests: test_report_success, test_report_quote_not_found, test_report_reason_too_long
- Add DB tests: test_create_report_success, test_create_report_not_found
- Add ReportInput schema and /api/quotes/{id}/report path to openapi.yaml
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add three tests verifying hidden-quote filtering behaviour in
NativeRepository:
- list_quotes_excludes_hidden: hidden quotes do not appear in paginated
listing results.
- get_random_quote_excludes_hidden: get_random_quote returns None when
the only quote is hidden.
- get_quote_returns_hidden_quote: get_quote (direct ID lookup) still
returns the quote when it is hidden.
Also refactor the inline row-mapping closure in list_quotes to use the
existing row_to_quote helper, eliminating duplicated column mapping
logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `hidden` (boolean, required) to the Quote response schema so all
GET responses reflect the field. Add `hidden` (boolean, optional) to
QuoteUpdateRequest so callers can toggle visibility via POST /api/quotes/:id.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `hidden: bool` to the `Quote` struct and `hidden: Option<bool>` to
`UpdateQuoteInput` in `src/lib.rs`
- Add `ALTER_QUOTES_ADD_HIDDEN` migration constant in `db/migrations.rs`
- Apply the ALTER TABLE migration in `NativeRepository::run_migrations` and
`D1Repository::run_migrations` with try/ignore for idempotency
- Exclude hidden quotes from `list_quotes` (WHERE hidden = 0) and
`get_random_quote` in both native and D1 implementations
- Update all SELECT queries to include the `hidden` column
- Handle `hidden` field in `update_quote` SET clause for both implementations
- Update `MockRepo` and `sample_quote` in handler tests to include `hidden`
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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
- 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>
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>
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>
- 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>
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>
- 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>
- 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
- 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
- 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>
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>
Fill in every stub section with full educational content covering
foundations, parsing with nom, semantic analysis, code generation,
and integration testing. Close all associated nbd tickets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root TODO.md:
- 82df74: Add justfiles for all projects (lint/check/build/release)
- 09cda0: Add contact footer to all service front-ends
nbd/TODO.md:
- 39b2cf: Add --xml output format to nbd
edu/TODO.md:
- 59c122: Deploy edu mdbook to Cloudflare Pages (vibebooks.elijah.run)
- 8618e4: Write chapter on co-op worker-owned business structure
- 8f14c6: Write Machine Learning chapter (self-play game AI)
- 389d8d: Write chapter on creating and training a simple LLM
- 0fbe1a: Write chapter on shader programming
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
320 changed files with 20038 additions and 14908 deletions
title: Add justfiles for all projects with lint, check, build, build-release, and release recipes
status: todo
type: task
priority: normal
created_at: 2026-03-10T23:29:17Z
updated_at: 2026-03-10T23:29:17Z
---
## Background
The root `TODO.md` calls for each project in the mono-repo to have a `justfile` with standard recipe names so that any contributor can run the same commands regardless of project type. `just` is a command runner similar to Make but with cleaner syntax.
title: Add contact footer to all service front-ends
status: todo
type: task
priority: low
created_at: 2026-03-10T23:29:17Z
updated_at: 2026-03-10T23:29:17Z
---
## Background
The root `TODO.md` calls for a footer on every service front-end with contact info:
> Contact: `<site>@elijah.run`
Where `<site>` depends on the service.
## Target services and email addresses
| Service | Front-end type | Contact email |
|---|---|---|
| `quotesdb` | Yew (Rust/Wasm) | `quotes@elijah.run` |
| `edu` | mdbook static site | `vibedbooks@elijah.run` |
| Blog editor | Yew (not yet built) | `editor@elijah.run` |
## Implementation
### quotesdb/ui
Add a `Footer` component to the Yew app:
1. Create `quotesdb/ui/src/components/footer.rs` (or add inline to `app.rs`).
2. Render a `<footer>` element at the bottom of every page with the text:
`Contact: quotes@elijah.run`
3. Use an `<a href="mailto:quotes@elijah.run">` tag so it is clickable.
4. Include the footer in the top-level layout so every route shows it.
### edu (mdbook)
mdbook supports injecting HTML before/after the content via the `[output.html]` table in `book.toml`:
```toml
[output.html]
additional-js = []
additional-css = []
```
Or use the `before-content` / `after-content` HTML templates. More precisely, create `edu/theme/footer.hbs` with the footer markup and register it in `book.toml`.
Alternatively (simpler): add a `theme/` directory with an `index.hbs` that extends the default template and appends the footer. The mdbook docs for custom theming describe this approach.
Simplest option: add `footer.html` fragment via `[output.html] additional-js` hack, or just add a sentence at the bottom of each page — but that is not scalable.
**Recommended approach for mdbook:** Use mdbook's `theme/index.hbs` override (copy the default from the mdbook source) and add the footer HTML before the closing `</body>` tag:
**IMPORTANT**: before you do anything else, run the `beans prime` command and heed its output.
`nbd` is a CLI tool for managing work tickets, designed for agent workflows.
### Initialisation
This project uses **beans** for issue tracking. `beans prime` outputs the full usage guide for the current project — always follow its instructions for creating, updating, and tracking work.
```sh
nbd init
```
Run once in the project root. Creates `.nbd/tickets/`. Safe to run multiple times.
# Initialise (run once per project directory)
beans init
### Core commands
```sh
# Create a new ticket (use --ftype md for a human-readable body)
title = "Markov exercise: Simulating a Random Walk (Rust)"
priority = 7
status = "archived"
ticket_type = "task"
dependencies = []
+++
Write Section 5 of edu/markov.md: Exercise 2 — Simulating a Random Walk\n\nLearning objectives:\n- Model a countably-finite state space (bounded integers) in Rust\n- Implement reflecting boundary conditions\n- Aggregate results from many trials into a histogram\n\nContent to produce:\n- Setup instructions (reuse or extend Exercise 1 project)\n- Step-by-step hints:\n 1. Define RandomWalk with min, max, prob_right fields\n 2. Implement step with boundary reflection (clamp or reverse)\n 3. Implement histogram by running simulate() many times\n 4. Print histogram as ASCII bar chart\n 5. Observe convergence toward uniform (symmetric walk) or skewed (asymmetric)\n- Full reference solution\n\nTarget: replace the stub in edu/markov.md §5
---
# edu-0w1v
title: 'Markov exercise: Simulating a Random Walk (Rust)'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:25Z
parent: edu-svom
---
Write Section 5 of edu/markov.md: Exercise 2 — Simulating a Random Walk\n\nLearning objectives:\n- Model a countably-finite state space (bounded integers) in Rust\n- Implement reflecting boundary conditions\n- Aggregate results from many trials into a histogram\n\nContent to produce:\n- Setup instructions (reuse or extend Exercise 1 project)\n- Step-by-step hints:\n 1. Define RandomWalk with min, max, prob_right fields\n 2. Implement step with boundary reflection (clamp or reverse)\n 3. Implement histogram by running simulate() many times\n 4. Print histogram as ASCII bar chart\n 5. Observe convergence toward uniform (symmetric walk) or skewed (asymmetric)\n- Full reference solution\n\nTarget: replace the stub in edu/markov.md §5
Write Section 1 of edu/markov.md: 'What Is a Markov Chain?'\n\nLearning objectives:\n- Define the Markov property (memorylessness)\n- Give 3–4 concrete real-world examples (weather, board games, web surfing, genetics)\n- Explain why the Markov property is a useful modelling assumption\n- Introduce the notation P(Xₙ₊₁ = s | X₀, …, Xₙ) = P(Xₙ₊₁ = s | Xₙ)\n\nContent to produce:\n- 3–5 paragraphs of prose\n- At least one illustrative example worked through informally\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §1
---
# edu-18qe
title: 'Markov lesson: What Is a Markov Chain?'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:02Z
updated_at: 2026-03-13T22:37:25Z
parent: edu-svom
---
Write Section 1 of edu/markov.md: 'What Is a Markov Chain?'\n\nLearning objectives:\n- Define the Markov property (memorylessness)\n- Give 3–4 concrete real-world examples (weather, board games, web surfing, genetics)\n- Explain why the Markov property is a useful modelling assumption\n- Introduce the notation P(Xₙ₊₁ = s | X₀, …, Xₙ) = P(Xₙ₊₁ = s | Xₙ)\n\nContent to produce:\n- 3–5 paragraphs of prose\n- At least one illustrative example worked through informally\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §1
@ -87,4 +90,4 @@ Print the retrieved passages first (so the reader can see what context was used)
## Reference solution
Full `main.rs` inside `<details>`. Keep `retrieve`, `build_prompt`, and `call_llm` as clearly named separate functions. The `main` function should be a thin orchestrator.
Full `main.rs` inside `<details>`. Keep `retrieve`, `build_prompt`, and `call_llm` as clearly named separate functions. The `main` function should be a thin orchestrator.
title = "Markov lesson: Applications and Further Reading"
priority = 7
status = "done"
ticket_type = "task"
dependencies = []
+++
Write Section 10 of edu/markov.md: 'Applications and Further Reading'\n\nLearning objectives:\n- Survey real-world applications: PageRank, MCMC, HMMs, RL, bioinformatics\n- Give a 2–3 sentence description of each application and how Markov chains appear\n- Point to concrete next steps for learners who want to go deeper\n\nContent to produce:\n- Application survey (5–7 topics, each 2–3 sentences)\n- Annotated reading list:\n * 'Introduction to Probability' (Blitzstein & Hwang) — free PDF\n * 'Markov Chains' (Norris) — rigorous treatment\n * Sutton & Barto 'Reinforcement Learning' — RL connection\n * The Metropolis-Hastings algorithm article on Wikipedia\n- Rust ecosystem pointers (crates for probabilistic modelling)\n\nTarget: replace the stub in edu/markov.md §10
---
# edu-34co
title: 'Markov lesson: Applications and Further Reading'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:26Z
parent: edu-svom
---
Write Section 10 of edu/markov.md: 'Applications and Further Reading'\n\nLearning objectives:\n- Survey real-world applications: PageRank, MCMC, HMMs, RL, bioinformatics\n- Give a 2–3 sentence description of each application and how Markov chains appear\n- Point to concrete next steps for learners who want to go deeper\n\nContent to produce:\n- Application survey (5–7 topics, each 2–3 sentences)\n- Annotated reading list:\n * 'Introduction to Probability' (Blitzstein & Hwang) — free PDF\n * 'Markov Chains' (Norris) — rigorous treatment\n * Sutton & Barto 'Reinforcement Learning' — RL connection\n * The Metropolis-Hastings algorithm article on Wikipedia\n- Rust ecosystem pointers (crates for probabilistic modelling)\n\nTarget: replace the stub in edu/markov.md §10
title: 'Write §2: Monte Carlo Tree Search — algorithm explained'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:48:46Z
parent: edu-coqp
---
Step-by-step walkthrough of MCTS: selection (UCB1), expansion, simulation/rollout, backpropagation. Include a worked example on a small game tree.
## Summary of Changes\n\nWrote full content for §2 covering MCTS algorithm: the four phases (selection, expansion, simulation, backpropagation), UCB1/UCT formula, worked ASCII tree example, and strengths/limitations.
title: 'Write §13: The full AlphaGo Zero training loop'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-16T01:35:06Z
parent: edu-coqp
---
Reading lesson: generate → train → evaluate → promote. Discuss the ELO-based model selection step and why it matters.
## Summary of Changes\n\nWrote full content for §13 covering the complete AlphaGo Zero training loop: self-play with temperature, training, evaluation gate, and the iterative improvement cycle with complete Rust implementation.
title = "Markov exercise: N-gram Generalization (Rust)"
priority = 7
status = "done"
ticket_type = "task"
dependencies = []
+++
Write Section 8 of edu/markov.md: Exercise 4 — N-gram Generalization\n\nLearning objectives:\n- Generalize from bigrams to arbitrary-order n-gram chains\n- Use Vec<String> as a HashMap key (or a joined string)\n- Empirically compare output quality for n = 1, 2, 3, 4\n\nContent to produce:\n- Setup instructions (extend Exercise 3 project)\n- Step-by-step hints:\n 1. Modify train to use a sliding window of n words as the key\n 2. Modify generate to maintain a deque/window of the last n words\n 3. Run on the same corpus with n = 1, 2, 3, 4 and print 50 words each\n 4. Discuss observations: when does it start memorising the corpus?\n- Full reference solution\n- Stretch goal: implement character-level n-grams instead of word-level\n\nTarget: replace the stub in edu/markov.md §8
Write Section 8 of edu/markov.md: Exercise 4 — N-gram Generalization\n\nLearning objectives:\n- Generalize from bigrams to arbitrary-order n-gram chains\n- Use Vec<String> as a HashMap key (or a joined string)\n- Empirically compare output quality for n = 1, 2, 3, 4\n\nContent to produce:\n- Setup instructions (extend Exercise 3 project)\n- Step-by-step hints:\n 1. Modify train to use a sliding window of n words as the key\n 2. Modify generate to maintain a deque/window of the last n words\n 3. Run on the same corpus with n = 1, 2, 3, 4 and print 50 words each\n 4. Discuss observations: when does it start memorising the corpus?\n- Full reference solution\n- Stretch goal: implement character-level n-grams instead of word-level\n\nTarget: replace the stub in edu/markov.md §8
title: 'Write §8: Exercise 2 — play Tic-Tac-Toe with pure MCTS'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T23:04:14Z
parent: edu-coqp
---
Exercise: wire MCTS to the game logic from Exercise 1 and run a match. Show sample output, discuss iteration count vs strength.
## Summary of Changes
Wrote full content for §8: Exercise 2 with MCTS vs MCTS tournament (100 games, iteration count experiments), human vs MCTS CLI game, experimentation prompts, and readiness checklist.
title: 'Write §3: Why self-play? The AlphaGo Zero insight'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:51:31Z
parent: edu-coqp
---
Explain the key insight: a capable engine can be its own teacher. Historical context (AlphaGo vs AlphaGo Zero) and why the approach generalises.
## Summary of Changes\n\nWrote full content for §3 covering the evolution from Deep Blue to AlphaGo Zero, the self-play insight, the virtuous training cycle, and how we'll adapt the approach for Tic-Tac-Toe.
title = "Markov exercise: Bigram Text Generator (Rust)"
priority = 7
status = "done"
ticket_type = "task"
dependencies = []
+++
Write Section 7 of edu/markov.md: Exercise 3 — Bigram Text Generator\n\nLearning objectives:\n- Build a HashMap-based transition table from a text corpus\n- Implement weighted random sampling over successor words\n- Generate and print novel word sequences from a seed\n\nContent to produce:\n- Setup instructions (new Cargo project or extend previous, add rand crate)\n- Step-by-step hints:\n 1. Tokenize corpus into words (split_whitespace)\n 2. Build BigramModel::train by iterating consecutive word pairs\n 3. Implement weighted sampling in generate (accumulate weights, compare to rng draw)\n 4. Handle end-of-chain gracefully (seed word not in model)\n 5. Try with a public-domain text (e.g., Project Gutenberg excerpt)\n- Full reference solution\n\nTarget: replace the stub in edu/markov.md §7
---
# edu-6r70
title: 'Markov exercise: Bigram Text Generator (Rust)'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:26Z
parent: edu-svom
---
Write Section 7 of edu/markov.md: Exercise 3 — Bigram Text Generator\n\nLearning objectives:\n- Build a HashMap-based transition table from a text corpus\n- Implement weighted random sampling over successor words\n- Generate and print novel word sequences from a seed\n\nContent to produce:\n- Setup instructions (new Cargo project or extend previous, add rand crate)\n- Step-by-step hints:\n 1. Tokenize corpus into words (split_whitespace)\n 2. Build BigramModel::train by iterating consecutive word pairs\n 3. Implement weighted sampling in generate (accumulate weights, compare to rng draw)\n 4. Handle end-of-chain gracefully (seed word not in model)\n 5. Try with a public-domain text (e.g., Project Gutenberg excerpt)\n- Full reference solution\n\nTarget: replace the stub in edu/markov.md §7
Write Section 4 of edu/markov.md: Exercise 1 — Weather Model\n\nLearning objectives:\n- Translate a transition matrix into a Rust struct\n- Use a weighted random draw to implement a single Markov step\n- Run a simulation and print or collect the resulting sequence\n\nContent to produce:\n- Setup instructions (new Cargo project, add rand crate)\n- Step-by-step hints:\n 1. Define the Weather enum and index conversion\n 2. Implement WeatherChain::step using rand::Rng::gen::<f64>()\n 3. Implement WeatherChain::simulate as a loop collecting states\n 4. Run with transition matrix [[0.8, 0.2], [0.4, 0.6]] from Sunny\n 5. Count sunny vs rainy days and compare to stationary distribution\n- Full reference solution (collapsed or at end)\n\nTarget: replace the stub in edu/markov.md §4
---
# edu-7cp2
title: 'Markov exercise: Weather Model (Rust)'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:00Z
updated_at: 2026-03-13T22:37:25Z
parent: edu-svom
---
Write Section 4 of edu/markov.md: Exercise 1 — Weather Model\n\nLearning objectives:\n- Translate a transition matrix into a Rust struct\n- Use a weighted random draw to implement a single Markov step\n- Run a simulation and print or collect the resulting sequence\n\nContent to produce:\n- Setup instructions (new Cargo project, add rand crate)\n- Step-by-step hints:\n 1. Define the Weather enum and index conversion\n 2. Implement WeatherChain::step using rand::Rng::gen::<f64>()\n 3. Implement WeatherChain::simulate as a loop collecting states\n 4. Run with transition matrix [[0.8, 0.2], [0.4, 0.6]] from Sunny\n 5. Count sunny vs rainy days and compare to stationary distribution\n- Full reference solution (collapsed or at end)\n\nTarget: replace the stub in edu/markov.md §4
title: 'Write §12: Exercise 4 — replace rollout with the value network'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-16T01:31:54Z
parent: edu-coqp
---
Exercise: substitute random rollout in MCTS with a neural-network value estimate; compare strength before and after.
## Summary of Changes
Wrote full content for §12: Exercise 4 covering PUCT formula, replacing random rollouts with value network evaluation, adding policy priors to MCTS nodes, modified MCTS code, and pure vs network-guided MCTS comparison.
This is the top-level project ticket for `edu/src/vector-db.md` — a self-guided mdbook course on vector databases in the **Vibed Learning** site (`edu/`).
@ -47,4 +63,4 @@ The course is modelled on `edu/src/markov.md`. It teaches vector databases throu
- **KNN query:**`vector_top_k('table', vector('[...]'), k)` table-valued function
title = "Markov lesson: Text Generation with Markov Chains"
priority = 7
status = "done"
ticket_type = "task"
dependencies = []
+++
Write Section 6 of edu/markov.md: 'Text Generation with Markov Chains'\n\nLearning objectives:\n- Explain how words (or characters) become states in a text Markov chain\n- Define bigrams and how they form a transition table from a corpus\n- Discuss why generated text sounds locally plausible but globally incoherent\n- Introduce the concept of order-n chains and the tradeoff between coherence and novelty\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A short worked bigram example from a 2-sentence sample text (show the table)\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §6
---
# edu-9kuk
title: 'Markov lesson: Text Generation with Markov Chains'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:26Z
parent: edu-svom
---
Write Section 6 of edu/markov.md: 'Text Generation with Markov Chains'\n\nLearning objectives:\n- Explain how words (or characters) become states in a text Markov chain\n- Define bigrams and how they form a transition table from a corpus\n- Discuss why generated text sounds locally plausible but globally incoherent\n- Introduce the concept of order-n chains and the tradeoff between coherence and novelty\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A short worked bigram example from a 2-sentence sample text (show the table)\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §6
title = "Markov lesson: Transition Probabilities and Matrices"
priority = 7
status = "done"
ticket_type = "task"
dependencies = []
+++
Write Section 3 of edu/markov.md: 'Transition Probabilities and Matrices'\n\nLearning objectives:\n- Formally define the transition matrix P where P[i][j] = P(next=j | current=i)\n- Explain the stochastic matrix constraint: all rows sum to 1, all entries ≥ 0\n- Show how to compute the distribution after k steps: π₀ Pᵏ\n- Work through a 2×2 weather-model example by hand\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A worked numeric example (2-state weather chain)\n- LaTeX-style or plain-text matrix notation\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §3
---
# edu-a1al
title: 'Markov lesson: Transition Probabilities and Matrices'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:00Z
updated_at: 2026-03-13T22:37:25Z
parent: edu-svom
---
Write Section 3 of edu/markov.md: 'Transition Probabilities and Matrices'\n\nLearning objectives:\n- Formally define the transition matrix P where P[i][j] = P(next=j | current=i)\n- Explain the stochastic matrix constraint: all rows sum to 1, all entries ≥ 0\n- Show how to compute the distribution after k steps: π₀ Pᵏ\n- Work through a 2×2 weather-model example by hand\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A worked numeric example (2-state weather chain)\n- LaTeX-style or plain-text matrix notation\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §3
Capstone exercise: run the full self-play loop for 1000 games; plot win-rate over iterations; discuss what worked and what didn't.
## Summary of Changes\n\nWrote full content for §14: Exercise 5 — the capstone exercise running 1000 self-play games, tracking diagnostics, validating convergence to perfect play, and providing next steps for further learning.
## §9 Generating Embeddings in Rust — Stub to fill
File: `edu/src/vector-db.md`, section `### 9. Generating Embeddings in Rust`
@ -60,4 +63,4 @@ Show a minimal async function that POSTs to the embeddings endpoint and returns
**Choosing between them.** Recommend fastembed for §10–§12 because: no API key, no network dependency, deterministic results (important for exercises), sub-100ms per batch on CPU. Use the HTTP approach when you need a specific production-grade model or have one already deployed.
**Dimensionality note.** The `F32_BLOB(d)` column type must match the model's output dimension exactly — you cannot mix dimensions. Change the DDL from the toy `F32_BLOB(3)` used in §6–§8 to `F32_BLOB(384)` for BGE-Small, `F32_BLOB(768)` for all-MiniLM-L6-v2, or `F32_BLOB(1536)` for OpenAI text-embedding-3-small. The index must also be dropped and recreated if you change the dimension.
**Dimensionality note.** The `F32_BLOB(d)` column type must match the model's output dimension exactly — you cannot mix dimensions. Change the DDL from the toy `F32_BLOB(3)` used in §6–§8 to `F32_BLOB(384)` for BGE-Small, `F32_BLOB(768)` for all-MiniLM-L6-v2, or `F32_BLOB(1536)` for OpenAI text-embedding-3-small. The index must also be dropped and recreated if you change the dimension.
title: 'edu: write Machine Learning chapter (self-play game AI, Alpha Go Zero style)'
status: completed
type: feature
priority: low
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-16T01:40:56Z
---
## Background
From `edu/TODO.md`: Hands-on: Machine Learning; training a computer to play a game by playing against itself (a-la Alpha Go Zero).
A self-play reinforcement learning course. The practical focus is implementing a simplified version of the MCTS + neural network self-play loop in Rust, targeting a simple deterministic two-player game (e.g., Tic-Tac-Toe or Connect Four).
## Content outline (suggested)
### Part 1 — Foundations
1. What is reinforcement learning? (state, action, reward, policy, value)
2. Monte Carlo Tree Search (MCTS) — algorithm explained step by step
3. Why self-play? The AlphaGo Zero insight
### Part 2 — The Game
4. Choosing a simple game: Tic-Tac-Toe as the learning vehicle
5. Representing game state in Rust
6. Exercise 1: Implement the game logic (move generation, win detection, terminal states)
### Part 3 — MCTS
7. Implementing MCTS in Rust (selection, expansion, simulation, backpropagation)
8. Exercise 2: Play Tic-Tac-Toe with pure MCTS (no neural network)
### Part 4 — Neural Network Policy/Value Head
9. Overview of the network architecture (shared trunk + policy head + value head)
10. Integrating a neural network crate (e.g., `tch-rs` or `candle`)
11. Exercise 3: Train the network on MCTS-generated data
12. Exercise 4: Replace MCTS simulation with the learned value function
### Part 5 — Self-Play Loop
13. The full Alpha Go Zero training loop: generate data → train → evaluate → repeat
14. Exercise 5: Run 1000 self-play games and observe the policy improving
## File to create
- `edu/src/ml-self-play.md`
- Add to `edu/src/SUMMARY.md` under a `# Machine Learning` section
## Summary of Changes\n\nAll 14 sections written (7,622 lines total). The chapter covers:\n- Part 1 (§1-3): RL fundamentals, MCTS algorithm, AlphaGo Zero insight\n- Part 2 (§4-6): Tic-Tac-Toe as learning vehicle, Rust game state, Exercise 1\n- Part 3 (§7-8): MCTS implementation in Rust, Exercise 2 (pure MCTS play)\n- Part 4 (§9-12): NN from scratch, training on MCTS data, PUCT-guided MCTS\n- Part 5 (§13-14): Full self-play training loop, capstone exercise
@ -68,4 +71,4 @@ Output format: `"Customers who liked Laptop also liked: Mechanical Keyboard (0.0
## Reference solution
Full `main.rs` inside `<details>`. The `recommend` function should be clearly separated from the setup boilerplate. The recommendation query pattern (SELECT embedding → feed as query to vector_top_k) is the key technique to highlight.
Full `main.rs` inside `<details>`. The `recommend` function should be clearly separated from the setup boilerplate. The recommendation query pattern (SELECT embedding → feed as query to vector_top_k) is the key technique to highlight.
title: 'Write §5: Representing game state in Rust'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:56:30Z
parent: edu-coqp
---
Reading lesson: design of Board, Player, Move types. Discuss representation trade-offs (bitboard vs array). Show the full type definitions.
## Summary of Changes
Wrote full content for §5 covering Rust game state representation: Player enum, GameState struct, board indexing, Display impl, move generation, winner checking, and immutable apply_move design.
@ -34,4 +37,4 @@ This is a **reading lesson** — no Rust code. Target 400–700 words. Follow th
**Practical dimensionalities.** 384 (MiniLM, fast, ~130MB), 768 (BERT-base, sentence-transformers default), 1536 (OpenAI text-embedding-3-small), 3072 (text-embedding-3-large). Larger is not always better — depends on task and dataset.
**Embeddings for non-text data.** Brief mention: CLIP produces image embeddings comparable to text embeddings in the same space (enabling text-to-image search). Product embeddings can be learned from purchase co-occurrence. The vector database stores float arrays regardless of modality.
**Embeddings for non-text data.** Brief mention: CLIP produces image embeddings comparable to text embeddings in the same space (enabling text-to-image search). Product embeddings can be learned from purchase co-occurrence. The vector database stores float arrays regardless of modality.
File: `edu/src/vector-db.md`, section `### 4. What Is a Vector Database?`
@ -43,4 +46,4 @@ This is a **reading lesson** — no Rust code. Target 500–700 words. Bold lead
- Index build time: one-time cost paid before serving queries.
- Memory footprint: HNSW stores graph edges in RAM; this limits how large the index can grow on a single machine.
**Where sqlite-vec / Turso fits.** sqlite-vec is appropriate for embedded applications, local development, and small-to-medium corpora (up to a few million vectors). Dedicated cloud vector databases (Pinecone, Qdrant, Weaviate) handle larger scale and add features like multi-tenancy, filtering, and distributed search.
**Where sqlite-vec / Turso fits.** sqlite-vec is appropriate for embedded applications, local development, and small-to-medium corpora (up to a few million vectors). Dedicated cloud vector databases (Pinecone, Qdrant, Weaviate) handle larger scale and add features like multi-tenancy, filtering, and distributed search.
## §6 Setting Up Turso + sqlite-vec — ALREADY COMPLETE
This section is fully written in `edu/src/vector-db.md` under `### 6. Setting Up`. No further content work is needed. Mark this ticket done.
@ -18,4 +21,4 @@ This section is fully written in `edu/src/vector-db.md` under `### 6. Setting Up
- Reference table of all sqlite-vec constructs used in later exercises: `F32_BLOB(d)`, `vector()`, `vector_extract()`, `vector_distance_cos()`, `libsql_vector_idx`, `vector_top_k()`
- Creating a `F32_BLOB(3)` vector table
- Creating an HNSW index with `USING libsql_vector_idx(embedding)`
- Complete working `main.rs` listing — produces "SQLite version: x.y.z" and "Database ready."
- Complete working `main.rs` listing — produces "SQLite version: x.y.z" and "Database ready."
Conceptual lesson: shared convolutional trunk, policy head (move probabilities), value head (win probability). Diagrams encouraged. No code yet.
## Summary of Changes
Wrote full content for §9 covering neural network fundamentals from scratch: neurons/weights/biases, layers, forward pass, training intuition, and the dual-headed policy+value architecture for game AI with concrete TTT dimensions.
title: 'Write §4: Choosing a simple game — Tic-Tac-Toe'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:53:54Z
parent: edu-coqp
---
Explain why Tic-Tac-Toe is ideal: small state space, deterministic, zero-sum, easily verifiable. Foreshadow how the same approach scales to Go/Chess.
## Summary of Changes
Wrote full content for §4 covering why Tic-Tac-Toe is the ideal learning vehicle: suitable game properties, game tree size, known optimal solution as validation target, and comparison with alternatives.
title: 'Write §11: Exercise 3 — train the network on MCTS data'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-16T00:32:55Z
parent: edu-coqp
---
Exercise: generate training examples (state, policy vector, value) from pure MCTS self-play; run one training epoch; log loss.
## Summary of Changes\n\nWrote full content for §11: Exercise 3 covering MCTS data generation pipeline, TrainingExample struct, mini-batch SGD training loop, loss tracking, network evaluation, and experimentation prompts.
This section is fully written in `edu/src/vector-db.md` under `### 1. What Is a Vector?`. No further content work is needed. Mark this ticket done.
@ -15,4 +18,4 @@ This section is fully written in `edu/src/vector-db.md` under `### 1. What Is a
- Geometric intuition in 2D and 3D: magnitude (`‖v‖ = √(Σ vᵢ²)`), direction, orthogonality via dot product
- High-dimensional spaces: the curse of dimensionality, normalisation onto the unit hypersphere, dimensions are not individually interpretable features
- Vectors as representations: the key insight that similarity in meaning corresponds to proximity in vector space — the basis of everything that follows
- Notation used throughout the course: **v**, `v[i]`, `‖v‖`, *d* for dimension, *n* for number of stored vectors
- Notation used throughout the course: **v**, `v[i]`, `‖v‖`, *d* for dimension, *n* for number of stored vectors
Walk through selection (UCB1 formula), expansion, simulation (random rollout), backpropagation. Show Rust code for the node structure and the four phases.
## Summary of Changes\n\nWrote full content for §7 covering MCTS implementation in Rust: arena-allocated node structure, all four phases implemented and explained, UCT calculation, main loop, and move selection.
Full `main.rs` inside `<details><summary>Show full solution</summary>`. The solution should re-run setup from §7 (create table, insert data) then run the three KNN queries.
Full `main.rs` inside `<details><summary>Show full solution</summary>`. The solution should re-run setup from §7 (create table, insert data) then run the three KNN queries.
Self-guided Markov chain course in edu/markov.md.\n\nThe course outline lives in edu/markov.md. Each of the 10 section tickets must be completed to flesh out all stubs before this project is done.\n\nSections:\n1. fbf323 — What Is a Markov Chain?\n2. 738be2 — States and Transitions\n3. 44ebe7 — Transition Probabilities and Matrices\n4. 257a2a — Exercise 1: Weather Model (Rust)\n5. 64826a — Exercise 2: Random Walk (Rust)\n6. 92a829 — Text Generation with Markov Chains\n7. 74be50 — Exercise 3: Bigram Text Generator (Rust)\n8. 1f995a — Exercise 4: N-gram Generalization (Rust)\n9. 68ee16 — Stationary Distributions\n10. 5994a6 — Applications and Further Reading
---
# edu-svom
title: markov
status: completed
type: epic
priority: high
created_at: 2026-03-10T23:30:02Z
updated_at: 2026-03-13T22:33:43Z
---
Self-guided Markov chain course in edu/markov.md.\n\nThe course outline lives in edu/markov.md. Each of the 10 section tickets must be completed to flesh out all stubs before this project is done.\n\nSections:\n1. fbf323 — What Is a Markov Chain?\n2. 738be2 — States and Transitions\n3. 44ebe7 — Transition Probabilities and Matrices\n4. 257a2a — Exercise 1: Weather Model (Rust)\n5. 64826a — Exercise 2: Random Walk (Rust)\n6. 92a829 — Text Generation with Markov Chains\n7. 74be50 — Exercise 3: Bigram Text Generator (Rust)\n8. 1f995a — Exercise 4: N-gram Generalization (Rust)\n9. 68ee16 — Stationary Distributions\n10. 5994a6 — Applications and Further Reading
@ -34,4 +37,4 @@ This is a **reading lesson with inline math** — no Rust code. Target 400–600
**When to use each.** Text and sentence embeddings: cosine (or dot product if model outputs unit vectors, which many do). Follow the model card's recommendation when specified. Low-dimensional geometric features: L2.
**Worked example.** Use vectors a = [1, 0, 1] and b = [1, 1, 0]. Compute all three by hand and show the arithmetic step by step. Cosine similarity = 0.5, L2 distance ≈ 1.414, dot product = 1. This concretises the formulas before the reader sees them in SQL queries.
**Worked example.** Use vectors a = [1, 0, 1] and b = [1, 1, 0]. Compute all three by hand and show the arithmetic step by step. Cosine similarity = 0.5, L2 distance ≈ 1.414, dot product = 1. This concretises the formulas before the reader sees them in SQL queries.
title: Deploy edu mdbook to Cloudflare Pages at vibebooks.elijah.run
status: completed
type: epic
priority: normal
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-10T23:30:01Z
---
## Background
From `edu/TODO.md`:
- Host the mdbook on Cloudflare Pages
- Host on vibebooks.elijah.run
- Create an `infra/` directory containing opentofu configs for the above
- Add a big disclaimer about the content being AI-generated
## Sub-tasks
### 1. Add AI-generated disclaimer
Add a prominent disclaimer page or preface to the mdbook. The disclaimer should say (in the user's words):
> these [chapters] are AI generated and not intended to be definitive, trustworthy, or even good, just an experiment in generating tailored educational content about topics I am interested in but not sure where to start, and with a practical focus on exercises with Rust since that is the language I use most often
Placement options:
- A dedicated `src/disclaimer.md` page added first in `src/SUMMARY.md`
- Or a callout block at the top of every chapter (requires mdbook preprocessor)
- Recommended: `src/disclaimer.md` as the first page, with a short note in each chapter's preamble
### 2. Create `edu/infra/` with OpenTofu configs
Follow the pattern from `quotesdb/infra/` (Cloudflare provider, tofu modules).
Minimal resources:
- `cloudflare_pages_project` — create the Pages project named `vibedbooks`
- `cloudflare_pages_domain` — bind `vibebooks.elijah.run` to the Pages project
- `cloudflare_record` — CNAME DNS record pointing `vibebooks` at the Pages subdomain
Write Section 9 of edu/markov.md: 'Stationary Distributions'\n\nLearning objectives:\n- Define stationary distribution π: πP = π, Σπᵢ = 1\n- Explain existence and uniqueness conditions: irreducibility and aperiodicity\n- Show how to compute π analytically for a 2-state chain\n- Introduce power iteration as a numerical method\n- Connect to the long-run frequency interpretation from the simulation in Exercise 1\n\nContent to produce:\n- 4–6 paragraphs of prose\n- Worked 2×2 example: solve πP = π by hand\n- Power iteration pseudocode or brief Rust sketch\n\nTarget: replace the stub in edu/markov.md §9
---
# edu-urpp
title: 'Markov lesson: Stationary Distributions'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:26Z
parent: edu-svom
---
Write Section 9 of edu/markov.md: 'Stationary Distributions'\n\nLearning objectives:\n- Define stationary distribution π: πP = π, Σπᵢ = 1\n- Explain existence and uniqueness conditions: irreducibility and aperiodicity\n- Show how to compute π analytically for a 2-state chain\n- Introduce power iteration as a numerical method\n- Connect to the long-run frequency interpretation from the simulation in Exercise 1\n\nContent to produce:\n- 4–6 paragraphs of prose\n- Worked 2×2 example: solve πP = π by hand\n- Power iteration pseudocode or brief Rust sketch\n\nTarget: replace the stub in edu/markov.md §9
title: 'Write §1: What is reinforcement learning?'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:46:18Z
parent: edu-coqp
---
Cover: state, action, reward, policy, value function. Intuitive explanation with a game-playing example. No code.
## Summary of Changes\n\nWrote full content for §1 covering RL fundamentals: agent/environment loop, state/action/reward/policy/value concepts, contrast with supervised/unsupervised learning, and why RL fits games.
title: 'Write §6: Exercise 1 — implement Tic-Tac-Toe game logic'
status: completed
type: task
priority: normal
created_at: 2026-03-13T20:03:17Z
updated_at: 2026-03-13T22:58:06Z
parent: edu-coqp
---
Hands-on exercise: move generation, win detection, terminal-state check, displaying the board. Include starter code and expected test output.
## Summary of Changes\n\nWrote full content for §6: Exercise 1 with project setup instructions, 8 unit test specifications with collapsible solutions, a random-game main function, and a readiness checklist.
Write Section 2 of edu/markov.md: 'States and Transitions'\n\nLearning objectives:\n- Define state space (finite vs countably infinite)\n- Define a transition as a directed edge between states with an associated probability\n- Introduce state-transition diagrams and how to draw them\n- Distinguish absorbing states, transient states, and recurrent states at an intuitive level\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A hand-drawn-style ASCII or described state-transition diagram for the weather example\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §2
---
# edu-zjy1
title: 'Markov lesson: States and Transitions'
status: completed
type: task
priority: high
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-13T22:37:25Z
parent: edu-svom
---
Write Section 2 of edu/markov.md: 'States and Transitions'\n\nLearning objectives:\n- Define state space (finite vs countably infinite)\n- Define a transition as a directed edge between states with an associated probability\n- Introduce state-transition diagrams and how to draw them\n- Distinguish absorbing states, transient states, and recurrent states at an intuitive level\n\nContent to produce:\n- 3–5 paragraphs of prose\n- A hand-drawn-style ASCII or described state-transition diagram for the weather example\n- No code in this section\n\nTarget: replace the stub in edu/markov.md §2
Write the exercise to draw a hard-coded coloured triangle: define three vertices in a vertex buffer, write a vertex shader that passes colour through, write a fragment shader that outputs the interpolated colour. First rendered geometry. Part 3 of the Shader Programming chapter.
title: §17 Signed Distance Fields for font rendering
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:55:11Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the conceptual section on SDFs for font rendering: what an SDF encodes, why it scales better than bitmaps, how to threshold and anti-alias in the fragment shader. Brief — reading and intuition only. Part 6 of the Shader Programming chapter.
Write the WGSL syntax overview: types (scalars, vectors, matrices), functions, entry points, built-in variables (@vertex, @fragment, @builtin), uniforms and bindings. Show annotated snippets of a minimal vertex+fragment shader pair. Part 1 of the Shader Programming chapter.
title: '§13 Compute pipelines: dispatching work groups'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:55:02Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the lesson on compute shaders: ComputePipeline vs RenderPipeline, dispatch_workgroups, workgroup size declarations in WGSL, global_invocation_id and how threads are indexed. Part 5 of the Shader Programming chapter.
title: §7 Vertices, buffers, and the vertex shader
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:54:49Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the lesson on vertex data: vertex structs in Rust, VertexBufferLayout, uploading a buffer to the GPU with wgpu::util::DeviceExt, and what the vertex shader receives (position, colour, normals). Part 3 of the Shader Programming chapter.
A practical introduction to GPU shaders, written with Rust as the host language. The course covers the graphics pipeline conceptually and has hands-on exercises writing WGSL shaders driven by `wgpu`.
## Content outline (suggested)
### Part 1 — The GPU and the Graphics Pipeline
1. CPU vs GPU: parallel execution model
2. The programmable pipeline: vertex shaders, fragment shaders, compute shaders
3. What is WGSL? (WebGPU Shading Language) — syntax overview
### Part 2 — Setting Up with wgpu
4. What is `wgpu`? Cross-platform graphics API in Rust
5. Exercise 1: Create a window and clear it to a colour (the GPU 'hello world')
6. The render loop: swap chains, frames, command encoders
### Part 3 — Vertex and Fragment Shaders
7. Vertices, buffers, and the vertex shader
8. Interpolation and the fragment shader
9. Exercise 2: Draw a coloured triangle
10. Exercise 3: Animate the triangle using a time uniform
18. Resources: Learn WGPU tutorial, Shadertoy, The Book of Shaders
## File to create
- `edu/src/shaders.md`
- Add to `edu/src/SUMMARY.md` under a `# Graphics` section
## Summary of Changes
All 18 sections written with full educational content covering the GPU execution model, wgpu setup, vertex/fragment shaders, textures, compute shaders, and advanced topics. ~4400 lines of content including full Rust+WGSL code examples, ASCII diagrams, and 5 hands-on exercises.
Write the lesson on the CPU vs GPU execution model: SIMD vs SIMT, thread hierarchies (threads, warps/waves, workgroups), why GPUs excel at data-parallel work and struggle with branching. Part 1 of the Shader Programming chapter.
title: '§5 Exercise 1: create a window and clear it to a colour'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:54:44Z
updated_at: 2026-03-16T02:30:27Z
parent: edu-4u7w
---
Write the first hands-on exercise: create a winit window, initialise a wgpu surface, and clear it to a solid colour each frame. Walk through Instance → Adapter → Device → Surface → RenderPass with annotated Rust code. The GPU hello world. Part 2 of the Shader Programming chapter.
Explain BPE vs byte-level vs character-level. Motivate character-level as the simplest choice for a from-scratch exercise. Show vocabulary construction.
title: '§18 Resources: Learn WGPU, Shadertoy, The Book of Shaders'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:55:12Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the resources and next-steps section: links and descriptions for the Learn WGPU tutorial, Shadertoy for experimentation, The Book of Shaders for mathematical foundations, and any other curated pointers. Part 6 of the Shader Programming chapter.
Write the conceptual overview of post-processing: render to texture (off-screen framebuffer), Gaussian blur as a separable two-pass compute shader, bloom as blur applied to bright regions. No full exercise — reading only. Part 6 of the Shader Programming chapter.
title: 'Write §10: Cross-entropy loss and the training loop'
status: completed
type: task
priority: normal
created_at: 2026-03-13T22:02:02Z
updated_at: 2026-03-16T02:30:26Z
parent: edu-u2w7
---
Next-token prediction loss: cross-entropy over the vocab. Adam optimiser. Training loop structure: batch → forward → loss → backward → step. No bells and whistles.
title: 'edu: write chapter on co-op worker-owned business structure'
status: todo
type: task
priority: low
created_at: 2026-03-10T23:30:01Z
updated_at: 2026-03-10T23:30:01Z
---
## Background
From `edu/TODO.md`: write a chapter about how to structure a co-op profit-sharing worker-owned business.
This is a conceptual/informational chapter, not a Rust hands-on. It fits naturally in the Vibed Learning book as a practical guide for engineers who might want to start or join a worker cooperative.
## Content outline (suggested)
1. **What is a worker cooperative?** — definition, historical examples, how they differ from traditional businesses
2. **Legal structures** — co-op incorporation options by jurisdiction (US LCA/LLC, UK LLP, etc.), relevant legislation
Write the lesson on textures: UV coordinates, wgpu texture creation from image data, TextureView, Sampler configuration (filter modes, address modes), and bind group layout for textures. Part 4 of the Shader Programming chapter.
title: '§10 Exercise 3: animate the triangle using a time uniform'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:54:54Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the exercise to animate the coloured triangle: introduce uniform buffers, upload a time value each frame, rotate or pulse the triangle in the vertex shader using the time uniform. Introduces the uniform/bind-group pattern. Part 3 of the Shader Programming chapter.
title: '§15 Exercise 5: GPU-accelerate a particle simulation'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:55:05Z
updated_at: 2026-03-16T02:30:28Z
parent: edu-4u7w
---
Write the compute shader exercise: implement an N-body or simple gravity particle simulation. Store particle positions and velocities in storage buffers, update them in a compute shader each frame, render the result as points. Part 5 of the Shader Programming chapter.
Write the lesson on rasterisation and interpolation: how values output from the vertex shader are interpolated across fragments, what the fragment shader receives and outputs (colour), and how the pipeline assembles a final image. Part 3 of the Shader Programming chapter.
title: '§6 The render loop: swap chains, frames, command encoders'
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:54:44Z
updated_at: 2026-03-16T02:30:27Z
parent: edu-4u7w
---
Write the lesson on the wgpu render loop: SurfaceTexture acquisition, CommandEncoder, RenderPass, queue submission, present. Explain double/triple buffering conceptually. Part 2 of the Shader Programming chapter.
title: 'Write §12: Exercise 5 — sample from the model'
status: completed
type: task
priority: normal
created_at: 2026-03-13T22:02:05Z
updated_at: 2026-03-16T02:30:26Z
parent: edu-u2w7
---
Temperature sampling and greedy decoding. Prompt the trained model and decode character-by-character. Compare output at different training checkpoints.
title: §4 What is wgpu? Cross-platform graphics API in Rust
status: completed
type: task
priority: normal
created_at: 2026-03-13T19:54:43Z
updated_at: 2026-03-16T02:30:27Z
parent: edu-4u7w
---
Write the wgpu introduction: what it is, how it maps to WebGPU/Vulkan/Metal/D3D12, the key types (Instance, Adapter, Device, Queue, Surface), and how it differs from OpenGL. Part 2 of the Shader Programming chapter.
title: 'Write §11: Exercise 4 — train on a small text corpus'
status: completed
type: task
priority: normal
created_at: 2026-03-13T22:02:04Z
updated_at: 2026-03-16T02:30:26Z
parent: edu-u2w7
---
Use a small public-domain text (e.g. Shakespeare's sonnets or a children's book). Show data loading, batching with random windows, training loop, loss curve. Reader runs training and watches loss fall.