# nbd — Implementation Plan ## Overview `nbd` is a CLI tool for managing work tickets, primarily targeted at agent workflows. It stores tickets as JSON files in `.nbd/tickets/` in the project root (found by traversing up from cwd, like git). **Crate location:** `vibed/nbd/` **Language:** Rust (edition 2021) **Key crates:** `clap`, `async-std`, `serde` / `serde_json`, `tempfile` (tests) --- ## Phase 1: Crate Scaffold Set up the crate structure with no logic yet. - [x] Create `nbd/Cargo.toml` with dependencies: `clap` (derive feature), `async-std`, `serde`, `serde_json`; dev-dependencies: `tempfile` - [x] Add `[profile.release]` optimizations (`opt-level = "z"`, `lto = true`, `strip = true`, `codegen-units = 1`) - [x] Create `nbd/src/main.rs` — `async-std` entry point, placeholder `fn main()` - [x] Create `nbd/src/ticket.rs` — empty module - [x] Create `nbd/src/store.rs` — empty module - [x] Create `nbd/src/display.rs` — empty module - [x] Create `nbd/src/tests.rs` — empty test module - [x] Create `nbd/tests/integration.rs` — empty integration test file - [x] Verify: `cargo check` passes --- ## Phase 2: Data Model (`ticket.rs`) Define the core types. - [x] Define `Status` enum: `Todo`, `InProgress`, `Done` (default: `Todo`) - Derive: `Serialize`, `Deserialize`, `Debug`, `Clone`, `PartialEq` - Serialize to/from lowercase strings (`"todo"`, `"in_progress"`, `"done"`) - [x] Define `TicketType` enum: `Project`, `Feature`, `Task`, `Bug` (default: `Task`) - Derive same traits - Serialize to/from lowercase strings - [x] Define `Ticket` struct: ``` id: String // 6-char hex, e.g. "a3f9c2" title: String body: String priority: u8 // 0..=10, default 5 status: Status dependencies: Vec // Vec of ticket IDs ticket_type: TicketType // field name avoids keyword collision ``` - Derive: `Serialize`, `Deserialize`, `Debug`, `Clone` - [x] Implement `Ticket::new(id, title)` constructor with all defaults - [x] Implement priority validation (error if > 10) - [x] Implement ID generation: 3 random bytes → 6 hex chars (use `std::collections::hash_map::RandomState` or similar; no external crate needed for MVP) - [x] Unit tests: serialization roundtrip, priority validation, ID format (6 hex chars, unique across N calls) --- ## Phase 3: Storage (`store.rs`) File I/O and directory traversal using `async-std`. - [x] Implement `find_nbd_root() -> Result`: walk from cwd upward until `.nbd/` is found; error with helpful message if not found - [x] Implement `find_nbd_root_from(start: &Path) -> Result`: testable variant that accepts a starting path - [x] Implement `tickets_dir(root: &Path) -> PathBuf`: returns `root/.nbd/tickets/` - [x] Implement `ensure_tickets_dir(root: &Path) -> Result<()>`: creates `.nbd/tickets/` if missing (used only by `create`) - [x] Implement `ticket_path(root: &Path, id: &str) -> PathBuf`: returns `.nbd/tickets/{id}.json` - [x] Implement `write_ticket(root: &Path, ticket: &Ticket) -> Result<()>`: serialize to JSON, write file - [x] Implement `read_ticket(root: &Path, id: &str) -> Result`: read file, deserialize; error if not found - [x] Implement `list_tickets(root: &Path) -> Result>`: read all `*.json` from tickets dir, deserialize all, sort by priority descending - [x] Unit tests: roundtrip write/read with tempdir, list returns all tickets, traversal finds `.nbd/` in grandparent dir --- ## Phase 4: Display (`display.rs`) Output formatting. - [x] Implement `print_ticket(ticket: &Ticket)`: full tabular display ``` ID: a3f9c2 Title: Fix login bug Body: Users cannot log in with email addresses containing + Priority: 8 Status: in_progress Type: bug Dependencies: b7d41e, c9e823 ``` - [x] Implement `print_ticket_json(ticket: &Ticket)`: pretty-printed JSON to stdout - [x] Implement `print_list(tickets: &[Ticket])`: short table ``` ID PRI TYPE STATUS TITLE a3f9c2 8 bug in_progress Fix login bug b7d41e 5 task todo Add rate limiting ``` - [x] Implement `print_list_json(tickets: &[Ticket])`: JSON array to stdout - [x] Added `format_ticket`, `format_ticket_json`, `format_list`, `format_list_json` internal functions for testability - [x] Unit tests: table output contains expected field values, JSON output is valid --- ## Phase 5: CLI Commands (`main.rs`) Wire up `clap` subcommands to storage and display. - [x] Define CLI structure with `clap` derive: - Global flag: `--json` (all commands) - Subcommand `create`: `--title` (required), `--body`, `--priority`, `--status`, `--type`, `--deps` (comma-separated IDs) - Subcommand `read`: positional `` - Subcommand `list`: no args - Subcommand `update`: positional ``, same optional flags as `create` - [x] Implement `cmd_create`: generate ID, validate deps exist, write ticket, print - [x] Implement `cmd_read`: find ticket by ID, print - [x] Implement `cmd_list`: list all tickets, print - [x] Implement `cmd_update`: read existing ticket, merge only provided flags, write, print - [x] Added `parse_status`, `parse_ticket_type`, `parse_deps`, `validate_deps` helpers - [x] Fixed clippy warning in `store.rs`: `map_or(false, ...)` → `is_some_and(...)` - [x] Integration tests (tempdir): create → read roundtrip, list shows created tickets, update merges correctly, traversal test (run from subdir), error on unknown ID, JSON flag tests, dep replacement test --- ## Phase 6: Documentation & Validation - [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 cargo run -- read cargo run -- update --status in_progress cargo run -- list --json ``` --- ## Post-MVP The following features are planned but excluded from the MVP: - `nbd init` — explicit initialization command - `nbd ready` — list tickets with no blockers - `nbd archive` — mark tickets as Closed - `nbd update` git-diff style +/- output - Partial ID matching (`nbd read a3f` finding `a3f9c2`) - SQLite cache for performance - Nix flake for depending on `nbd` in other projects ### Multiple File Format Support `nbd` will support multiple ticket file formats selectable via `--ftype=[json|md|toml|jsonb]` on `create` and `update`. **Format detection** is by file extension: `.json`, `.md`, `.toml`, `.jsonb`. The store layer detects format from the file extension when reading; no format metadata is stored separately. **Markdown format** (`.md`): The file body is the ticket `body` field. Metadata is stored as frontmatter: - `---` delimiter → YAML frontmatter - `+++` delimiter → TOML frontmatter Example (YAML frontmatter): ``` --- id: a3f9c2 title: Fix login bug priority: 8 status: in_progress ticket_type: bug dependencies: [b7d41e] --- Long-form body text goes here. Supports full markdown. ``` **TOML format** (`.toml`): All fields in a flat TOML file; `body` is a multi-line string. **Binary JSON** (`.jsonb`): Compact binary-encoded JSON (e.g. via CBOR or BSON; crate TBD at implementation time). **Conversion via `nbd update --ftype`**: Reads the ticket in its current format, writes it in the new format, and deletes the old file. The ID and all field values are preserved. **Crates to evaluate:** `serde_yaml`, `toml`, `ciborium` (CBOR) or `bson`.