tickets: triage TODO files → nbd tickets

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>
main
Elijah Voigt 3 months ago
parent ba6425793a
commit aa4e9fdbfe

@ -0,0 +1,64 @@
+++
title = "Add contact footer to all service front-ends"
priority = 3
status = "todo"
ticket_type = "task"
dependencies = []
+++
## 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:
```html
<footer style="text-align:center;padding:1em;opacity:0.6">
Contact: <a href="mailto:vibedbooks@elijah.run">vibedbooks@elijah.run</a>
</footer>
```
## Files involved
- `edu/theme/index.hbs` (new — copy from mdbook default, add footer)
- `edu/book.toml` (may need `[output.html] theme = "theme"` entry)
- `quotesdb/ui/src/components/footer.rs` (new) or similar

@ -0,0 +1,66 @@
+++
title = "Add justfiles for all projects with lint, check, build, build-release, and release recipes"
priority = 5
status = "todo"
ticket_type = "task"
dependencies = []
+++
## 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.
## Recipes to add per project
| Recipe | What it runs |
|---|---|
| `lint` | Auto-format: `cargo fmt`, `tofu fmt`, `mdbook build` (edu), etc. |
| `check` | Static checks: `cargo check && cargo clippy`, `tofu validate && tofu plan`, etc. |
| `build` | Dev build: `cargo build`, `trunk build`, `mdbook build` |
| `build-release` | Optimised binary or bundle: `cargo build --release`, `trunk build --release` |
| `release` | Deploy/publish: `tofu apply`, `wrangler deploy` (where applicable) |
Not every project needs every recipe; stub missing ones with an error or a no-op comment.
## Per-project breakdown
### `nbd/justfile`
- `lint`: `cargo fmt`
- `check`: `cargo check && cargo clippy && cargo test`
- `build`: `cargo build`
- `build-release`: `cargo build --release`
- `release`: n/a (CLI tool, no deployment)
### `edu/justfile`
- `lint`: `mdbook build` (build validates the content)
- `check`: `mdbook build`
- `build`: `mdbook build`
- `build-release`: same as `build` (mdbook has no separate release mode)
- `release`: TBD — CF Pages auto-deploys from main branch once infra is configured (see edu deployment ticket)
### `quotesdb/api/justfile`
- `lint`: `cargo fmt`
- `check`: `cargo check && cargo clippy`
- `build`: `cargo build`
- `build-release`: `cargo build --release`
- `release`: `wrangler deploy` (or `tofu apply` from `infra/`)
### `quotesdb/ui/justfile`
- `lint`: `cargo fmt`
- `check`: `cargo check && cargo clippy`
- `build`: `trunk build`
- `build-release`: `trunk build --release`
- `release`: CF Pages auto-deploys from main branch once infra is configured
## Files to create
- `nbd/justfile`
- `edu/justfile`
- `quotesdb/api/justfile`
- `quotesdb/ui/justfile`
## Conventions
- Recipe names must be consistent across all projects (`lint`, `check`, `build`, `build-release`, `release`).
- Commands are run from the directory containing the `justfile`, so paths are relative to the project directory.
- Add a comment above each recipe explaining what it does.
- Check whether the `just` package is already in each project's Nix dev shell (`flake.nix`); add it if missing.

7
Cargo.lock generated

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "vibed"
version = "0.1.0"

@ -1,6 +0,0 @@
[package]
name = "vibed"
version = "0.1.0"
edition = "2024"
[dependencies]

@ -0,0 +1 @@
# TODO

7
common/Cargo.lock generated

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "common"
version = "0.1.0"

@ -1,8 +0,0 @@
[package]
name = "common"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Shared types, utilities, and definitions for Vibesville services"
[dependencies]

@ -1,12 +0,0 @@
//! Shared types, utilities, and definitions for Vibesville services.
//!
//! This crate provides common building blocks used across multiple services
//! in the Vibesville mono-repo. Services depend on it via a path dependency:
//!
//! ```toml
//! [dependencies]
//! common = { path = "../common" }
//! ```
#[cfg(test)]
mod tests;

@ -1 +0,0 @@
//! Unit tests for the common crate.

@ -0,0 +1,49 @@
+++
title = "edu: write chapter on shader programming"
priority = 3
status = "todo"
ticket_type = "task"
dependencies = []
+++
## Background
From `edu/TODO.md`: Hands-on: Shader programming.
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
### Part 4 — Textures and Samplers
11. Texture coordinates (UVs), texture creation, sampler config
12. Exercise 4: Render a textured quad
### Part 5 — Compute Shaders
13. Compute pipelines: dispatching work groups
14. Storage buffers and read/write access from WGSL
15. Exercise 5: GPU-accelerate a particle simulation
### Part 6 — Going Further
16. Post-processing effects (bloom, blur) — conceptual overview
17. Signed Distance Fields for font rendering
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

@ -0,0 +1,47 @@
+++
title = "edu: write chapter on creating and training a simple LLM"
priority = 3
status = "todo"
ticket_type = "task"
dependencies = []
+++
## Background
From `edu/TODO.md`: Hands-on: Creating and training a simple LLM.
A practical course on building a small language model from scratch in Rust, covering tokenisation, the Transformer architecture, and a training loop. The goal is deep understanding rather than production scale.
## Content outline (suggested)
### Part 1 — What is a Language Model?
1. Predicting the next token: the core task
2. Tokenisation: BPE, byte-level, character-level — pick character-level for simplicity
3. Exercise 1: Build a character-level tokeniser in Rust
### Part 2 — The Transformer Architecture
4. Embeddings and positional encoding
5. Self-attention: queries, keys, values — the attention formula
6. Multi-head attention
7. Feed-forward sublayers, residual connections, layer norm
8. Exercise 2: Implement a single attention head in Rust (no ML framework)
### Part 3 — Assembling the Model
9. Stacking Transformer blocks into a decoder-only LM
10. Using `candle` (Hugging Face's Rust ML framework) for tensor ops and autodiff
11. Exercise 3: Define a small GPT-like model in `candle`
### Part 4 — Training
12. Cross-entropy loss for next-token prediction
13. The training loop: forward pass, loss, backward pass, optimizer step
14. Exercise 4: Train on a small text corpus (e.g., Shakespeare or a short book)
15. Exercise 5: Sample from the model and observe output quality vs. training steps
### Part 5 — Reflection
16. What limits this model? Scale, data, compute
17. Pointers to real LLM training (GPT-2, LLaMA, Mistral)
18. Further reading
## File to create
- `edu/src/llm-from-scratch.md`
- Add to `edu/src/SUMMARY.md` under the `# Machine Learning` section

@ -0,0 +1,74 @@
+++
title = "Deploy edu mdbook to Cloudflare Pages at vibebooks.elijah.run"
priority = 5
status = "todo"
ticket_type = "project"
dependencies = []
+++
## 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
File layout:
```
edu/infra/
├── main.tf # provider config, cloudflare_pages_project
├── dns.tf # cloudflare_record and cloudflare_pages_domain
├── variables.tf # cloudflare_account_id, cloudflare_zone_id, etc.
├── outputs.tf # pages subdomain URL
└── .gitignore # *.tfstate, .terraform/
```
All `resource` and `data` blocks must have a comment explaining their purpose (per CLAUDE.md conventions).
### 3. Configure mdbook build for CF Pages
Cloudflare Pages can auto-build mdbook sites if given the right build command and output directory. Set in the Pages project config:
- Build command: `mdbook build`
- Build output directory: `book`
- Root directory: `edu/`
Alternatively, use a CI/CD pipeline (GitHub Actions / Gitea Actions) to build and push to the Pages project via `wrangler pages deploy`.
### 4. Add edu/justfile
Add a `release` recipe to `edu/justfile` (see justfiles ticket) that triggers the Pages deployment once infra is configured.
## Relevant files
- `edu/book.toml` — mdbook configuration
- `edu/src/SUMMARY.md` — add disclaimer page
- `edu/src/disclaimer.md` (new)
- `edu/infra/` (new directory)
- Cloudflare dashboard: Pages project, DNS zone `elijah.run`
## Validation
- `tofu validate && tofu plan` from `edu/infra/`
- `mdbook build` from `edu/` — builds without errors
- After deploy: `curl -I https://vibebooks.elijah.run` returns 200

@ -0,0 +1,31 @@
+++
title = "edu: write chapter on co-op worker-owned business structure"
priority = 3
status = "todo"
ticket_type = "task"
dependencies = []
+++
## 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
3. **Profit sharing models** — patronage dividends, equal shares, labor-hour weighted, hybrid approaches
4. **Governance** — one-member-one-vote, board structure, decision-making processes
5. **Practical startup steps** — founding documents, initial capital, operating agreements
6. **Case studies** — Mondragon, REI, Cooperative Home Care Associates, tech co-ops (Loomio, etc.)
7. **Resources and further reading**
## File to create
- `edu/src/co-op.md`
- Add to `edu/src/SUMMARY.md` under a new `# Business` section (or `# Other Topics`)
## Notes
This chapter is explicitly not intended to be authoritative legal or financial advice — add the standard AI-generated disclaimer.

@ -0,0 +1,43 @@
+++
title = "edu: write Machine Learning chapter (self-play game AI, Alpha Go Zero style)"
priority = 3
status = "todo"
ticket_type = "task"
dependencies = []
+++
## 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

@ -1,17 +1,5 @@
# TODO # TODO
- [ ] Host the mdbook on cloudflare pages
- [ ] Host on vibebooks.elijah.run
- [ ] Create an `infra` directory containing opentofu configs for the above
- [ ] Add a big ol' disclaimer about these being AI generated and not intended to be difinitive, trustworthy, or even good, just an experiment in generating tailored educational content about topics I am intersted 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
## Interactive Learning and Education ## Interactive Learning and Education
- [x] Git worktrees, how do use please - [x] Git worktrees, how do use please
- [ ] How to structure a co-op profit sharing worker owned business
- [x] Hands-on: Markov Chains - [x] Hands-on: Markov Chains
- [~] Hands-on: Vector Databases
- [ ] Hands-on: Machine Learning; training a computer to play a game by playing against itself (a-la alpha go zero)
- [ ] Hands-on: Creating and training a simple LLM
- [~] Hands-on: Writing your own language (lisp, interpreted, compiled to C)
- [ ] Hands-on: Shader programming

@ -0,0 +1,104 @@
+++
title = "Add --xml output format that wraps each ticket section in XML tags"
priority = 4
status = "todo"
ticket_type = "feature"
dependencies = []
+++
## Background
From `nbd/TODO.md`:
> Add a `--xml` output format that prints tickets with XML around each section to make it easier to parse metadata.
Unlike `--json` (which is a complete, structured parse), the XML format adds lightweight tags around each metadata field in the human-readable output. This makes it trivial for scripts to extract specific fields using simple text tools (e.g., `grep`, `sed`, XPath) without pulling in a full JSON parser.
## Intended XML format
For a single ticket (`nbd read <id> --xml`):
```xml
<ticket>
<id>a3f9c2</id>
<title>Fix login bug</title>
<priority>8</priority>
<status>in_progress</status>
<type>bug</type>
<dependencies>
<dep>b7d41e</dep>
<dep>c9e823</dep>
</dependencies>
<body>Users cannot log in with email addresses containing +</body>
</ticket>
```
For a list (`nbd list --xml`):
```xml
<tickets>
<ticket>...</ticket>
<ticket>...</ticket>
</tickets>
```
## Implementation
### `src/main.rs`
Add a global `--xml` flag to the `Cli` struct, parallel to `--json`:
```rust
/// Output XML instead of a human-readable table.
#[arg(long, global = true)]
xml: bool,
```
Precedence: if both `--json` and `--xml` are supplied, `--json` wins (or return an error — TBD, but JSON-first is simpler).
Pass `cli.xml` through `dispatch` to every command handler, alongside `cli.json`.
Each command handler signature gains an `xml: bool` parameter. When `xml` is true and `json` is false, call the new `display::print_ticket_xml` / `display::print_list_xml` functions.
### `src/display.rs`
Add:
- `format_ticket_xml(ticket: &Ticket) -> String` — serialises a single ticket as XML
- `print_ticket_xml(ticket: &Ticket)` — wraps `format_ticket_xml` + `println!`
- `format_list_xml(tickets: &[Ticket]) -> String` — wraps list in `<tickets>`
- `print_list_xml(tickets: &[Ticket])`
XML escaping: at minimum, escape `&`, `<`, `>`, `"`, `'` in field values. Use a small helper `xml_escape(s: &str) -> String` rather than pulling in an XML crate.
For `dependencies`: render each as a `<dep>` child element.
### `src/tests.rs`
Add unit tests:
- `format_ticket_xml` with a ticket that has special characters in the title and body (`&`, `<`, `>`)
- `format_ticket_xml` with empty dependencies
- `format_ticket_xml` with multiple dependencies
- `format_list_xml` with zero and multiple tickets
### `tests/integration.rs`
Add integration tests:
- `nbd read <id> --xml` returns valid XML containing the ticket's fields
- `nbd list --xml` returns valid XML wrapping multiple tickets
- `nbd create ... --xml` returns the created ticket as XML
- `nbd update ... --xml` returns the updated ticket as XML
## Scope
All commands that support `--json` should also support `--xml`:
- `nbd create`
- `nbd read`
- `nbd list`
- `nbd update`
- `nbd ready`
- `nbd next`
- `nbd archive`
- `nbd migrate`
- `nbd graph` (the JSON graph format has a defined structure; XML should mirror it)
- `nbd claude-md` (wrap snippet in `<snippet>` tag)
- `nbd init` (wrap root path in `<init>`)

@ -1,2 +1 @@
# Work to be ticketed # Work to be ticketed

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}
Loading…
Cancel
Save