You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.6 KiB

+++ title = "Scope nbd next and nbd ready by dependency subtree" priority = 5 status = "todo" ticket_type = "feature" dependencies = [] +++

Summary

Add an optional positional <id> argument to nbd next and nbd ready that scopes the results to the dependency subtree of the given ticket.

Motivation

When a project has many tickets, you often want to focus on tickets that directly unblock a specific goal ticket. Currently nbd next and nbd ready operate over the entire ticket store, with no way to narrow by project/feature scope except through --filter expressions that don't understand the dependency graph.

Desired behaviour

# Highest-priority ready ticket that is a dependency of abc123 (directly or transitively)
nbd next abc123
nbd next abc123 --json

# All ready tickets that are dependencies of abc123 (directly or transitively)
nbd ready abc123
nbd ready abc123 --json

# Both commands still accept --filter alongside the scoping ID
nbd next abc123 --filter type=bug
nbd ready abc123 --filter priority=8

The root ticket itself (abc123) is not included in the results — only its dependencies (the tickets it is waiting on).

Implementation plan

1. CLI changes (src/main.rs)

In Commands::Next and Commands::Ready, add an optional positional argument:

id: Option<String>,   // Optional ticket ID or prefix to scope results

Update dispatch to pass the new argument to cmd_next and cmd_ready.

Update the CLI doc-comments for both subcommands to mention the scoping behaviour.

2. Logic changes (src/main.rs)

In cmd_next and cmd_ready, after loading all tickets, if scope_id is Some:

  1. Resolve the prefix to a full ID using resolve_id.
  2. Build a TicketGraph from all tickets (the full list, not filtered).
  3. Call graph.subtree(&resolved_id) to get the set of all dependency IDs reachable from the root (this already excludes the root itself when it is not in its own dependency list).
  4. Convert the subtree IDs into a HashSet.
  5. Restrict the ready/next candidate pool to tickets whose ID is in that set and whose ID is not the scoping ticket itself (the root should never be returned as a "what to do next" result when it is the scope target).

TicketGraph::subtree is already implemented in src/graph.rs and handles cycles correctly via a visited set.

3. Test coverage (tests/integration.rs)

Add integration tests (using tempdir):

  • test_next_scoped_by_id: create a project ticket P with deps [A, B], where A has dep [C]. Mark C as done. Verify nbd next P returns A (the highest-priority ready dep of P), not P itself or any unrelated ticket.
  • test_ready_scoped_by_id: same setup; verify nbd ready P returns [A] (B is blocked because... actually B has no deps so it's also ready). Adjust setup so exactly the expected set is returned.
  • test_next_scoped_no_ready: all deps of P are done; verify nbd next P returns no ticket ({"next": null} in JSON mode).
  • test_ready_scoped_with_filter: verify --filter still narrows within the scoped set.

4. Relevant files

File Change
src/main.rs:135-156 Commands::Ready and Commands::Next — add id: Option<String>
src/main.rs:308-310 dispatch arms for Next and Ready — pass new arg
src/main.rs:473-511 cmd_ready — add subtree scoping logic
src/main.rs:522-566 cmd_next — add subtree scoping logic
src/graph.rs:165-172 TicketGraph::subtree — already correct, no change needed
tests/integration.rs Add 4 new scoped-by-id tests

5. Validation

cargo fmt && cargo check && cargo clippy && cargo test