From 3ce8b16581d70e393b1e8c14e969f450dd084600 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Mon, 23 Feb 2026 22:31:11 -0800 Subject: [PATCH] chore(nbd): triage TODO into 9 tickets - d9713b: open/start/complete/close convenience sub-commands - 53fdbe: list positional arg shortcuts - 1c686c: next positional arg shortcuts - 4aceeb: nbd init adds cache.db to .nbd/.gitignore - 1c5783: VERSION file + nbd version subcommand + /work skill bump - 3ba7f9: .nbd/config.toml per-project defaults - e222cd: triage status (new default, excluded from ready/next/list) - 67209c: [backlog] investigate project-based ticket filtering - 0f577a: [backlog] investigate user-configurable type/status strings Co-Authored-By: Claude Sonnet 4.6 --- nbd/.nbd/tickets/0f577a.md | 48 ++++++++++++++++++++++ nbd/.nbd/tickets/1c5783.md | 76 +++++++++++++++++++++++++++++++++++ nbd/.nbd/tickets/1c686c.md | 44 ++++++++++++++++++++ nbd/.nbd/tickets/3ba7f9.md | 73 +++++++++++++++++++++++++++++++++ nbd/.nbd/tickets/4aceeb.md | 33 +++++++++++++++ nbd/.nbd/tickets/53fdbe.md | 49 +++++++++++++++++++++++ nbd/.nbd/tickets/67209c.md | 31 ++++++++++++++ nbd/.nbd/tickets/d9713b.md | 48 ++++++++++++++++++++++ nbd/.nbd/tickets/e222cd.md | 82 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 484 insertions(+) create mode 100644 nbd/.nbd/tickets/0f577a.md create mode 100644 nbd/.nbd/tickets/1c5783.md create mode 100644 nbd/.nbd/tickets/1c686c.md create mode 100644 nbd/.nbd/tickets/3ba7f9.md create mode 100644 nbd/.nbd/tickets/4aceeb.md create mode 100644 nbd/.nbd/tickets/53fdbe.md create mode 100644 nbd/.nbd/tickets/67209c.md create mode 100644 nbd/.nbd/tickets/d9713b.md create mode 100644 nbd/.nbd/tickets/e222cd.md diff --git a/nbd/.nbd/tickets/0f577a.md b/nbd/.nbd/tickets/0f577a.md new file mode 100644 index 0000000..96c7a1f --- /dev/null +++ b/nbd/.nbd/tickets/0f577a.md @@ -0,0 +1,48 @@ ++++ +title = "Investigate: user-configurable type and status strings with lifecycle phases" +priority = 3 +status = "backlog" +ticket_type = "task" +dependencies = [] ++++ +## Problem + +`type` and `status` are currently hard-coded Rust enums. Users cannot add custom values (e.g., `status = "review"` or `type = "spike"`) without modifying the codebase. But certain status values (`done`, `archived`, `closed`) have special semantics (excluded from `list`/`ready`/`next`; counted as resolved for deps). Making these extensible requires a design that lets users declare which values are "pre-work", "in-work", and "post-work". + +## Design questions + +### Lifecycle phases (proposed) + +Instead of hard-coding which statuses are excluded, introduce a three-phase model: + +| Phase | Examples | Behaviour | +|---|---|---| +| `pre` | `triage`, `backlog` | Excluded from `list`, `ready`, `next`. Not resolved. | +| `during` | `todo`, `in_progress`, `review` | Included in `list`, `ready`, `next`. Not resolved. | +| `post` | `done`, `archived`, `closed` | Excluded from `list`, `ready`, `next`. Counted as resolved. | + +Users would configure phases in `.nbd/config.toml`: + +```toml +[nbd.status] +pre = ["triage", "backlog"] +during = ["todo", "in_progress", "review", "qa"] +post = ["done", "archived", "closed"] +default = "triage" +``` + +### Type extensibility + +Allow `type` to be any string. Keep built-in types (`project`, `feature`, `task`, `bug`) but do not reject unknown values; validate only that the string is non-empty. + +## Questions to answer + +1. Is TOML config the right place for lifecycle phase definitions, or should there be a separate schema file? +2. How does serialisation/deserialisation change? `Status` can no longer be an enum if it is user-defined. +3. What is the migration path for existing tickets serialised with the current enum values? +4. Are there cases where a single status should belong to multiple phases (e.g., `review` might be both `during` and gating some post-processing)? +5. What is the minimum viable change? Could we start by making status a `String` internally while keeping the built-in values and their semantics, then layer config-driven phases on top? + +## Expected output + +Create one or more follow-up tickets with a concrete implementation plan and migration strategy. \ No newline at end of file diff --git a/nbd/.nbd/tickets/1c5783.md b/nbd/.nbd/tickets/1c5783.md new file mode 100644 index 0000000..4cc414b --- /dev/null +++ b/nbd/.nbd/tickets/1c5783.md @@ -0,0 +1,76 @@ ++++ +title = 'VERSION file: compile into binary via include_str\! and add nbd version subcommand' +priority = 5 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +The current version string is assembled in `main.rs` from `CARGO_PKG_VERSION` (Cargo.toml) and `GIT_SHORT_SHA` (build.rs). There is no `nbd version` subcommand — only `--version`. And the `/work` skill does not bump any version on completion. + +## Changes required + +### 1. Create `VERSION` file + +Create a `VERSION` file at the crate root (`nbd/VERSION`) containing the initial version: + +``` +0.1.0 +``` + +### 2. Use `VERSION` in the binary + +In `src/main.rs`, replace `CARGO_PKG_VERSION` with an `include_str\!` of the `VERSION` file (trimmed): + +```rust +const VERSION: &str = concat\!( + env\!("CARGO_PKG_VERSION"), // keep for crate metadata + "+", + env\!("GIT_SHORT_SHA"), +); +``` + +Actually, use `include_str\!("../VERSION")` trimmed and concatenated with the git SHA: + +```rust +const VERSION_FILE: &str = include_str\!("../VERSION"); +const VERSION: &str = /* VERSION_FILE.trim() + "+" + GIT_SHORT_SHA at runtime or via concat */ ; +``` + +Note: `include_str\!` and `concat\!` cannot trim at compile time. Use a `build.rs` approach: read `VERSION` in `build.rs`, trim it, and emit it via `cargo:rustc-env=NBD_VERSION=...`. Then: + +```rust +const VERSION: &str = concat\!(env\!("NBD_VERSION"), "+", env\!("GIT_SHORT_SHA")); +``` + +Update `build.rs` to read `VERSION` and emit `NBD_VERSION`. + +### 3. Add `nbd version` subcommand + +Add a `Version` variant to the `Commands` enum: + +```rust +/// Print the nbd version string and exit. +Version, +``` + +Handler prints the same string as `--version`. With `--json`, output `{"version": "X.Y.Z+sha"}`. + +### 4. Update `/work` skill + +Edit `.claude/skills/work/SKILL.md` to add a version-bump step after marking a ticket complete: + +- Breaking changes (major rework, API change) → bump major: `X.0.0` +- Features (`ticket_type == feature`) → bump minor: `X.Y.0` +- Tasks and bugs (`ticket_type == task | bug`) → bump patch: `X.Y.Z` + +The skill should read `VERSION`, parse the semver, increment the appropriate component, and write it back. Also update `Cargo.toml` `version` to match (keep in sync). + +## Relevant files + +- `nbd/VERSION` (new) +- `nbd/build.rs` (update: emit `NBD_VERSION`) +- `nbd/src/main.rs` (update: use `NBD_VERSION`, add `Version` subcommand) +- `nbd/Cargo.toml` (keep version in sync with `VERSION` file) +- `.claude/skills/work/SKILL.md` (update: add version-bump step) \ No newline at end of file diff --git a/nbd/.nbd/tickets/1c686c.md b/nbd/.nbd/tickets/1c686c.md new file mode 100644 index 0000000..10b3914 --- /dev/null +++ b/nbd/.nbd/tickets/1c686c.md @@ -0,0 +1,44 @@ ++++ +title = "Add next filtered sub-commands (next bug, next feature, next task)" +priority = 4 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +`nbd next --filter type=bug` is verbose. When an engineer wants the highest-priority ready bug, they should be able to say `nbd next bug`. + +## Sub-commands to add (positional arg to `next`) + +Accept an optional positional `` argument to `nbd next`: + +```sh +nbd next feature # equivalent to: nbd next --filter type=feature +nbd next task # equivalent to: nbd next --filter type=task +nbd next bug # equivalent to: nbd next --filter type=bug +nbd next project # equivalent to: nbd next --filter type=project +``` + +## Implementation + +**`src/main.rs`** — `Commands::Next` + +Add an optional positional argument `ticket_type` to the `Next` variant: + +```rust +Next { + ticket_type: Option, // new: positional shorthand + filter: Vec, +} +``` + +In `cmd_next`, when `ticket_type` is `Some(t)`: +- Validate it is one of `project`, `feature`, `task`, `bug` (return an error otherwise) +- Prepend `format!("type={t}")` to the effective filter list before calling `parse_filters` + +Explicit `--filter type=...` values are ORed with the positional shorthand, consistent with `TicketFilter` behaviour. + +**`tests/integration.rs`** + +Add tests verifying that `next bug` returns only bug-type ready tickets and that `next` with no argument still works as before. \ No newline at end of file diff --git a/nbd/.nbd/tickets/3ba7f9.md b/nbd/.nbd/tickets/3ba7f9.md new file mode 100644 index 0000000..26a41b5 --- /dev/null +++ b/nbd/.nbd/tickets/3ba7f9.md @@ -0,0 +1,73 @@ ++++ +title = "Add .nbd/config.toml for per-project defaults" +priority = 5 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +All defaults (output format, file type, default status) are hard-coded in the CLI. Users in a project that always uses `--json` or always creates `md`-format tickets must pass these flags repeatedly. A per-project config file would let them set these once. + +## Config file + +Location: `.nbd/config.toml` (inside the `.nbd/` root, alongside `tickets/` and `cache.db`). + +Format (all keys optional — missing keys fall back to compiled defaults): + +```toml +[nbd] +json = false # default: false — use tabular output +ftype = "json" # default: "json" — ticket storage format +status = "todo" # default: "todo" — initial status for new tickets +``` + +## Precedence (highest to lowest) + +1. Explicit CLI flag (e.g., `--json`, `--ftype md`) +2. Environment variable (not in scope for this ticket) +3. `.nbd/config.toml` +4. Compiled-in default + +## Implementation + +**`src/store.rs`** (or a new `src/config.rs`) + +Add a `NbdConfig` struct and a `load_config(root: &Path) -> NbdConfig` function: + +```rust +pub struct NbdConfig { + pub json: bool, + pub ftype: FileFormat, + pub status: Status, +} + +impl Default for NbdConfig { /* compiled-in defaults */ } + +pub fn load_config(root: &Path) -> NbdConfig { /* read .nbd/config.toml, fall back to Default */ } +``` + +Parse with the `toml` crate (already in `Cargo.toml`). Errors in the config file should produce a helpful message to stderr and fall back to defaults rather than aborting. + +**`src/main.rs`** — `dispatch()` + +After `find_nbd_root()`, call `load_config`. Merge config values with CLI flags: + +- `cli.json = cli.json || config.json` (CLI flag wins) +- For `--ftype`: if the user did not pass `--ftype`, use `config.ftype` +- For `--status` in `create`: if the user did not pass `--status`, use `config.status` + +**`src/main.rs`** — `cmd_init` + +After creating the tickets directory, write `.nbd/config.toml` with default values if it does not already exist. + +**`tests/integration.rs`** + +- Test that a config with `json = true` causes tabular commands to emit JSON. +- Test that a config with `ftype = "md"` causes `create` to write `.md` files. +- Test that explicit CLI flags override config values. +- Test idempotency of `nbd init` (does not overwrite existing config). + +## Dependencies + +None. Can be implemented independently of other tickets. \ No newline at end of file diff --git a/nbd/.nbd/tickets/4aceeb.md b/nbd/.nbd/tickets/4aceeb.md new file mode 100644 index 0000000..b10ff36 --- /dev/null +++ b/nbd/.nbd/tickets/4aceeb.md @@ -0,0 +1,33 @@ ++++ +title = "nbd init: add cache.db to .nbd/.gitignore" +priority = 5 +status = "todo" +ticket_type = "task" +dependencies = [] ++++ +## Problem + +`.nbd/cache.db` is a Turso/libsql SQLite cache file created automatically by `list_tickets_cached`. It should never be committed to git. Currently `nbd init` does not create a `.gitignore` to exclude it. + +## Implementation + +**`src/main.rs`** — `cmd_init` + +After `ensure_tickets_dir` succeeds, write (or append to) `.nbd/.gitignore`: + +``` +cache.db +``` + +Use idempotent logic: read the file if it exists, check whether `cache.db` is already listed, and only append/create if it is absent. This keeps `nbd init` safe to run multiple times. + +Suggested helper (can be inline in `cmd_init`): +1. Read `.nbd/.gitignore` if it exists. +2. If `cache.db` is not a line in the file, append `cache.db\n`. +3. If the file does not exist, create it with `cache.db\n`. + +The JSON output for `--json` should include a `gitignore` key indicating the path that was created/updated (or unchanged). + +**`tests/integration.rs`** + +Add a test that runs `nbd init` and asserts `.nbd/.gitignore` contains `cache.db`. Run `nbd init` a second time and assert the file is unchanged (idempotent). \ No newline at end of file diff --git a/nbd/.nbd/tickets/53fdbe.md b/nbd/.nbd/tickets/53fdbe.md new file mode 100644 index 0000000..e81ff07 --- /dev/null +++ b/nbd/.nbd/tickets/53fdbe.md @@ -0,0 +1,49 @@ ++++ +title = "Add list status sub-commands (list todo, list backlog, etc.)" +priority = 4 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +Filtering by status requires the verbose `--filter status=X`. Common patterns like listing only backlog or only completed tickets should have ergonomic shortcuts. + +## Sub-commands to add (positional arg to `list`) + +Accept an optional positional `` argument to `nbd list`: + +```sh +nbd list backlog # equivalent to: nbd list --filter status=backlog +nbd list closed # equivalent to: nbd list --filter status=closed +nbd list completed # equivalent to: nbd list --filter status=done +nbd list todo # equivalent to: nbd list --filter status=todo +nbd list in_progress # equivalent to: nbd list --filter status=in_progress +``` + +Note: `completed` is an alias for `done` (avoids the awkward `nbd list done`). + +## Implementation + +**`src/main.rs`** — `Commands::List` + +Add an optional positional argument `status` to the `List` variant: + +```rust +List { + status: Option, // new: positional shorthand + filter: Vec, + all: bool, +} +``` + +In `cmd_list`, when `status` is `Some(s)`: +- Map `"completed"" → "done"`, others pass through +- Treat it as if the caller had passed `--filter status=` +- The explicit `--filter` and `--all` flags should still override as today + +If both `status` and `--filter status=...` are given, merge them (OR behaviour within the status group, consistent with `TicketFilter`). + +**`tests/integration.rs`** + +Add tests for each status shorthand. \ No newline at end of file diff --git a/nbd/.nbd/tickets/67209c.md b/nbd/.nbd/tickets/67209c.md new file mode 100644 index 0000000..f2d1009 --- /dev/null +++ b/nbd/.nbd/tickets/67209c.md @@ -0,0 +1,31 @@ ++++ +title = "Investigate: filtering tickets by project / stream of work" +priority = 3 +status = "backlog" +ticket_type = "task" +dependencies = [] ++++ +## Problem + +When multiple streams of work coexist (e.g., refactoring vs. new feature), there is no way to select tickets for one stream only. `nbd next` and `nbd list` operate across all tickets. + +## Questions to answer + +1. Does the existing `project`-type ticket + `deps` mechanism serve this need? Could a project ticket act as a grouping node, and filtering by `--filter type=project` or by the project ticket's subtree (`nbd graph --json`) provide what is needed? + +2. Are there cases where a ticket belongs to multiple projects? If so, a single-parent `deps` tree cannot represent the relationship. + +3. Is a dedicated `project` or `label` field (multi-valued) preferable? What are the trade-offs? + +4. How does this interact with `nbd ready` and `nbd next`? Would a `--project ` flag on these commands be sufficient? + +## Approach + +Investigate by: +1. Manually modelling two parallel workstreams using `project`-type tickets and `deps`. +2. Evaluating whether `nbd graph --json` provides enough to extract a project-scoped ticket list. +3. Writing up findings and creating actionable implementation tickets. + +## Expected output + +Create one or more follow-up tickets with a concrete implementation plan, or close this ticket with a rationale if the existing tools are sufficient. \ No newline at end of file diff --git a/nbd/.nbd/tickets/d9713b.md b/nbd/.nbd/tickets/d9713b.md new file mode 100644 index 0000000..4ee4d6f --- /dev/null +++ b/nbd/.nbd/tickets/d9713b.md @@ -0,0 +1,48 @@ ++++ +title = "Add status convenience sub-commands (open, start, complete, close)" +priority = 5 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +`nbd update --status ` is verbose for the most common lifecycle transitions. `nbd archive` already exists as a top-level convenience — the same pattern should apply to all transitions. + +## Sub-commands to add + +| Command | Status set | +|---|---| +| `nbd open ` | `todo` | +| `nbd start ` | `in_progress` | +| `nbd complete ` | `done` | +| `nbd close ` | `closed` | + +(`nbd archive` already exists — do not duplicate it.) + +## Implementation + +**`src/main.rs`** + +Add four variants to the `Commands` enum, following the existing `Archive` pattern (line 183): + +```rust +Open { id: String }, +Start { id: String }, +Complete { id: String }, +Close { id: String }, +``` + +Add handler functions `cmd_open`, `cmd_start`, `cmd_complete`, `cmd_close`. Each handler should: +1. Call `resolve_id` (supports partial IDs) +2. `find_ticket_path` + `detect_format` to preserve existing file format +3. `read_ticket` +4. Set `ticket.status` to the target status +5. `write_ticket` in the same format +6. Print the ticket (tabular or JSON via `--json`) + +Wire up in `dispatch()` following the same pattern as `Commands::Archive`. + +**`tests/integration.rs`** + +Add integration tests for each command analogous to the existing archive test. \ No newline at end of file diff --git a/nbd/.nbd/tickets/e222cd.md b/nbd/.nbd/tickets/e222cd.md new file mode 100644 index 0000000..dc04418 --- /dev/null +++ b/nbd/.nbd/tickets/e222cd.md @@ -0,0 +1,82 @@ ++++ +title = "Add triage status: new default for tickets lacking implementation detail" +priority = 6 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Problem + +New tickets are created with `status=todo`, implying they are ready to work on. But many tickets need further research or implementation details before work can begin. The TODO describes a `triage` status for exactly this case — tickets that need an LLM or human to fill in details before they become `todo`. + +## Status semantics + +- `triage` — the ticket exists but lacks sufficient detail to begin implementation. An LLM or human should flesh out the body and move it to `todo` when ready. +- `triage` is excluded from `nbd ready` and `nbd next` (not actionable yet). +- `triage` is excluded from `nbd list` by default (like `backlog`). +- `triage` is the new **default status** for `nbd create` (replaces `todo`). + +## Changes + +### `src/ticket.rs` + +Add `Triage` variant to `Status`: + +```rust +/// The ticket needs more detail before it can be worked on. +/// +/// A triage ticket should have its body updated with implementation +/// details and then moved to `todo`. +Triage, +``` + +Change the `#[default]` attribute from `Todo` to `Triage`. + +### `src/main.rs` + +- `parse_status`: add `"triage" => Ok(Status::Triage)` and update the error message. +- `Commands::Create`: change the default value for `--status` from `"todo"` to `"triage"`. + - **Note:** If `.nbd/config.toml` support (separate ticket) is implemented first, the default should be settable via config; fall back to `"triage"` if config is absent. +- `cmd_ready` and `cmd_next`: add `Status::Triage` to the exclusion list. +- `cmd_list`: add `Status::Triage` to the default exclusion list. + +### `src/filter.rs` + +- `status_str`: add `Status::Triage => "triage"`. + +### `src/display.rs` + +- `status_str`: add `Status::Triage => "triage"`. +- Column widths: `STATUS` column is currently 13 chars (`"in_progress" + 2`). `"triage"" is 6 chars — no width change needed. + +### `src/graph.rs` + +- `status_str`: add `Status::Triage => "triage"`. + +### `src/claude_md_snippet.md` + +Update the embedded snippet to document the triage workflow: +- `triage` tickets need implementation details added to their body before they can be worked on. +- When creating a ticket that is ready to work on immediately, pass `--status todo` explicitly. +- Default: `nbd create --title "..."` creates a `triage` ticket. + +### `CLAUDE.md` (project-level) + +Update the workflow section to reflect the new default and document when to use `--status todo` vs. leaving the default. + +### `src/tests.rs` + +Add unit tests: +- `Status::Triage` serialises to `"triage"` +- Round-trip deserialisation +- Default `Ticket::new` has `status == Status::Triage` + +### `tests/integration.rs` + +- `nbd create` with no `--status` flag creates a `triage` ticket. +- `nbd create --status todo` creates a `todo` ticket. +- `nbd ready` does not include `triage` tickets. +- `nbd next` does not include `triage` tickets. +- `nbd list` does not include `triage` tickets by default. +- `nbd list --filter status=triage` shows only triage tickets. +- `nbd list --all` includes `triage` tickets. \ No newline at end of file