From 7ada8ef951ab3744908300accd6a3aa7e6cea4d6 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sat, 21 Feb 2026 21:40:57 -0800 Subject: [PATCH] docs(nbd): add README, PLANNING, ARCHITECTURE, and complete Phase 6 Write the three required documentation files for the nbd crate: - README.md: usage guide, field reference, dev workflow, dual-license footer, and Claude Code disclaimer - docs/PLANNING.md: development phase log and post-MVP roadmap - docs/ARCHITECTURE.md: module responsibilities, data flow diagram, storage layout, and testing strategy Run and verify cargo fmt, check, clippy, and test (34 tests pass). Mark all Phase 6 items as complete in PLAN.md. Co-Authored-By: Claude Sonnet 4.6 --- nbd/PLAN.md | 10 +-- nbd/README.md | 106 ++++++++++++++++++++++++++++++++ nbd/docs/ARCHITECTURE.md | 127 +++++++++++++++++++++++++++++++++++++++ nbd/docs/PLANNING.md | 79 ++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 nbd/README.md create mode 100644 nbd/docs/ARCHITECTURE.md create mode 100644 nbd/docs/PLANNING.md diff --git a/nbd/PLAN.md b/nbd/PLAN.md index a2d53c0..b626663 100644 --- a/nbd/PLAN.md +++ b/nbd/PLAN.md @@ -119,11 +119,11 @@ Wire up `clap` subcommands to storage and display. ## Phase 6: Documentation & Validation -- [ ] Write `nbd/README.md`: what, how, run, test, license, Claude Code disclaimer -- [ ] Write `nbd/docs/PLANNING.md`: this plan + work log -- [ ] Write `nbd/docs/ARCHITECTURE.md`: module overview and interactions -- [ ] Run validation in order: `cargo fmt`, `cargo check`, `cargo clippy`, `cargo test` -- [ ] Verify end-to-end: +- [x] Write `nbd/README.md`: what, how, run, test, license, Claude Code disclaimer +- [x] Write `nbd/docs/PLANNING.md`: this plan + work log +- [x] Write `nbd/docs/ARCHITECTURE.md`: module overview and interactions +- [x] Run validation in order: `cargo fmt`, `cargo check`, `cargo clippy`, `cargo test` +- [x] Verified end-to-end: ```sh cargo run -- create --title "Test ticket" --priority 7 --type bug cargo run -- list diff --git a/nbd/README.md b/nbd/README.md new file mode 100644 index 0000000..50e187a --- /dev/null +++ b/nbd/README.md @@ -0,0 +1,106 @@ +# nbd + +A CLI tool for managing work tickets, primarily targeted at agent workflows. + +Tickets are stored as JSON files in `.nbd/tickets/` inside the nearest ancestor +directory that contains a `.nbd/` folder, discovered by traversing upward from +the current working directory — just like `git` finds `.git/`. + +## How it works + +Each ticket is a JSON file named `{id}.json`, where `id` is a unique +6-character lowercase hex string (e.g. `a3f9c2`). Tickets carry: + +| Field | Type | Default | +|---|---|---| +| `id` | 6-char hex string | auto-generated | +| `title` | string | *(required)* | +| `body` | string | `""` | +| `priority` | integer 0–10 | `5` | +| `status` | `todo` \| `in_progress` \| `done` | `todo` | +| `ticket_type` | `project` \| `feature` \| `task` \| `bug` | `task` | +| `dependencies` | list of ticket IDs | `[]` | + +All commands accept `--json` for machine-readable output. + +## Usage + +### Initialise + +Create the tickets directory manually in your project root: + +```sh +mkdir -p .nbd/tickets +``` + +### Create a ticket + +```sh +nbd create --title "Fix login bug" --priority 8 --type bug +nbd create --title "Add rate limiting" --body "Protect public endpoints" --deps a3f9c2 +``` + +### Read a ticket + +```sh +nbd read a3f9c2 +nbd read a3f9c2 --json +``` + +### List all tickets + +```sh +nbd list +nbd list --json +``` + +### Update a ticket + +Only the flags you supply are changed; all other fields retain their current +values. + +```sh +nbd update a3f9c2 --status in_progress +nbd update a3f9c2 --priority 9 --type bug +``` + +## Running + +```sh +# From the nbd/ directory +cargo run -- create --title "Test ticket" --priority 7 --type bug +cargo run -- list +cargo run -- read +cargo run -- update --status in_progress +cargo run -- list --json +``` + +## Testing + +```sh +cargo test +``` + +Unit tests live in `src/tests.rs`. Integration tests (full command flows against +a temporary directory) live in `tests/integration.rs`. + +## Development + +Run these commands in order before committing: + +```sh +cargo fmt +cargo check +cargo clippy +cargo test +``` + +## License + +Dual-licensed under [Apache License, Version 2.0](../LICENSE-APACHE) and +[MIT License](../LICENSE-MIT), consistent with the rest of the `vibed` mono-repo. + +--- + +*This software was written with [Claude Code](https://claude.com/claude-code) +using the claude-sonnet-4-6 model.* diff --git a/nbd/docs/ARCHITECTURE.md b/nbd/docs/ARCHITECTURE.md new file mode 100644 index 0000000..df52fca --- /dev/null +++ b/nbd/docs/ARCHITECTURE.md @@ -0,0 +1,127 @@ +# nbd — Architecture + +## Overview + +`nbd` is a single-binary CLI tool with no background service. All state lives in +`.nbd/tickets/` inside the project directory tree. The binary is structured as +four modules, each with a single responsibility: + +``` +src/ +├── main.rs CLI definition and command dispatch +├── ticket.rs Data model, ID generation, priority validation +├── store.rs File I/O, directory traversal, CRUD +└── display.rs Human-readable and JSON output formatting +``` + +## Module Responsibilities + +### `main.rs` — CLI entry point + +Owns the `clap` struct definitions (`Cli`, `Commands`) and the top-level `main` +function. Delegates all business logic to command handlers (`cmd_create`, +`cmd_read`, `cmd_list`, `cmd_update`). The handlers are thin: they call helpers +from `store`, `ticket`, and `display`, then return. + +Parsing helpers (`parse_status`, `parse_ticket_type`, `parse_deps`) live here +because they translate raw CLI strings into typed values used only by the command +handlers. + +### `ticket.rs` — Data model + +Defines the canonical in-memory representation of a ticket: + +- `Ticket` struct — all fields +- `Status` enum — `Todo | InProgress | Done` +- `TicketType` enum — `Project | Feature | Task | Bug` + +Provides two free functions: + +- `generate_id() -> String` — uses `RandomState` (seeded with OS entropy) to + produce a 6-character lowercase hex string. No external randomness crate needed. +- `validate_priority(u8) -> Result<(), String>` — enforces the 0–10 range. + +The enums derive `serde`'s `Serialize`/`Deserialize` with `#[serde(rename_all)]` +so the JSON representation uses lowercase strings (`"in_progress"`, `"bug"`, …). + +### `store.rs` — File I/O + +All filesystem operations are in this module. Uses `async-std` for async I/O +throughout. + +**Directory discovery:** + +``` +find_nbd_root() + └─ find_nbd_root_from(cwd) + Walk parent dirs until .nbd/ is found +``` + +`find_nbd_root_from` accepts an explicit starting path to make it testable +without changing the process's working directory. + +**CRUD functions:** + +| Function | Operation | +|---|---| +| `ensure_tickets_dir(root)` | Create `.nbd/tickets/` if missing | +| `write_ticket(root, ticket)` | Serialise → pretty JSON → write file | +| `read_ticket(root, id)` | Read file → deserialise; friendly error if not found | +| `list_tickets(root)` | Read all `*.json` → sort by priority desc | + +**Error type:** `store::Result` is aliased to +`Result>`, allowing `?` to propagate both +`io::Error` and `serde_json::Error` without explicit wrapping. + +### `display.rs` — Output formatting + +Two output modes: + +- **Tabular** — human-readable key–value block (single ticket) or column-aligned + table (list). Column widths are compile-time constants. +- **JSON** — delegates to `serde_json::to_string_pretty`. + +Each public surface is split into a `format_*` function (returns `String`, +testable) and a `print_*` function (writes to stdout via `println!`). + +## Data Flow + +``` +CLI args (clap) + │ + ▼ + dispatch() [main.rs] + │ + ├── parse_* helpers ──► typed values + │ + ├── store::find_nbd_root() ──► PathBuf + ├── store::read_ticket() ──► Ticket + ├── store::list_tickets() ──► Vec + ├── store::write_ticket() ──► () + │ + └── display::print_ticket() ──► stdout + display::print_list() ──► stdout +``` + +## Storage Layout + +``` +/ +└── .nbd/ + └── tickets/ + ├── a3f9c2.json + ├── b7d41e.json + └── ... +``` + +Each file is a pretty-printed JSON object with the fields of `Ticket`. File name +equals the ticket's `id` field with a `.json` extension. + +## Testing Strategy + +- **Unit tests** (`src/tests.rs`): exercise `ticket.rs`, `store.rs`, and + `display.rs` in isolation using `tempfile::TempDir` for any file I/O. +- **Integration tests** (`tests/integration.rs`): drive full command flows + (create → read → list → update) through `cmd_*` functions against a temporary + directory. Include a directory-traversal test that invokes commands from a + nested subdirectory. diff --git a/nbd/docs/PLANNING.md b/nbd/docs/PLANNING.md new file mode 100644 index 0000000..78127f9 --- /dev/null +++ b/nbd/docs/PLANNING.md @@ -0,0 +1,79 @@ +# nbd — Planning & Work Log + +## Development Phases + +### Phase 1: Crate Scaffold ✅ + +Set up the crate structure with no logic yet. + +- Created `Cargo.toml` with `clap` (derive), `async-std`, `serde`, `serde_json`; dev-dep `tempfile` +- Added `[profile.release]` optimisations (`opt-level = "z"`, `lto`, `strip`, `codegen-units = 1`) +- Created `src/main.rs` — `async-std` entry point with placeholder `main` +- Created empty modules: `ticket.rs`, `store.rs`, `display.rs`, `tests.rs` +- Created `tests/integration.rs` +- Verified `cargo check` passed + +### Phase 2: Data Model (`ticket.rs`) ✅ + +- Defined `Status` enum (`Todo`, `InProgress`, `Done`; default `Todo`) + - Serialises to lowercase snake_case: `"todo"`, `"in_progress"`, `"done"` +- Defined `TicketType` enum (`Project`, `Feature`, `Task`, `Bug`; default `Task`) + - Serialises to lowercase: `"project"`, `"feature"`, `"task"`, `"bug"` +- Defined `Ticket` struct with all required fields +- Implemented `Ticket::new(id, title)` with defaults +- Implemented `validate_priority(priority)` — errors if > 10 +- Implemented `generate_id()` — 3 random bytes → 6 hex chars via `RandomState` +- Unit tests: serialisation roundtrip, priority validation, ID format and uniqueness + +### Phase 3: Storage (`store.rs`) ✅ + +- Implemented `find_nbd_root_from(start)` — walks up from `start` to find `.nbd/` +- Implemented `find_nbd_root()` — starts from `std::env::current_dir()` +- Implemented `tickets_dir(root)` — pure path computation +- Implemented `ensure_tickets_dir(root)` — creates `.nbd/tickets/` if missing +- Implemented `ticket_path(root, id)` — pure path computation +- Implemented `write_ticket(root, ticket)` — serialise and write JSON file +- Implemented `read_ticket(root, id)` — read and deserialise; descriptive error if not found +- Implemented `list_tickets(root)` — read all `*.json`, sort by priority descending +- Unit tests: write/read roundtrip, list returns all tickets, traversal from grandparent dir + +### Phase 4: Display (`display.rs`) ✅ + +- Implemented `format_ticket(ticket)` / `print_ticket(ticket)` — full tabular view +- Implemented `format_ticket_json(ticket)` / `print_ticket_json(ticket)` — pretty JSON +- Implemented `format_list(tickets)` / `print_list(tickets)` — short summary table +- Implemented `format_list_json(tickets)` / `print_list_json(tickets)` — JSON array +- `format_*` functions return `String` for testability; `print_*` write to stdout +- Unit tests: table output contains expected field values, JSON is valid + +### Phase 5: CLI Commands (`main.rs`) ✅ + +- Defined `Cli` struct with global `--json` flag and `Commands` enum +- Subcommands: `create`, `read`, `list`, `update` +- Implemented `cmd_create`, `cmd_read`, `cmd_list`, `cmd_update` +- Added `parse_status`, `parse_ticket_type`, `parse_deps`, `validate_deps` helpers +- Fixed clippy warning: `map_or(false, ...)` → `is_some_and(...)` +- Integration tests: create→read roundtrip, list, update merge, traversal from subdir, + error on unknown ID, `--json` flag, dep replacement + +### Phase 6: Documentation & Validation ✅ + +- Wrote `README.md` — what, how, usage, testing, license, Claude Code disclaimer +- Wrote `docs/PLANNING.md` (this file) — phases and work log +- Wrote `docs/ARCHITECTURE.md` — module overview and interactions +- Ran `cargo fmt`, `cargo check`, `cargo clippy`, `cargo test` — all clean + +--- + +## Post-MVP Roadmap + +See [`PLAN.md`](../PLAN.md) for the full list of planned post-MVP features, including: + +- `nbd init` — explicit initialisation command +- `nbd ready` — list tickets with no blockers +- `nbd archive` — mark tickets as Closed +- `nbd update` git-diff-style output +- Partial ID matching +- SQLite cache +- Nix flake +- Multiple file format support (`.md`, `.toml`, `.jsonb`)