name: get-backlog-specs description: "sr:get-backlog-specs — View product-driven backlog from GitHub Issues and propose top 3 for implementation." license: MIT compatibility: "Requires GitHub CLI (gh)." metadata: author: specrails version: "1.0"
Display the product-driven backlog by reading issues/tickets from the configured backlog provider (read from .specrails/backlog-config.json). These are feature ideas generated through VPC-based product discovery — evaluated against user personas. Use /specrails:auto-propose-backlog-specs to generate new ideas.
Input: $ARGUMENTS (optional: comma-separated areas to filter. If empty, show all.)
Phase 0: Environment Pre-flight
Verify the backlog provider is accessible:
Read .specrails/backlog-config.json to determine BACKLOG_PROVIDER (default: github).
- If
BACKLOG_PROVIDER=github: rungh auth status 2>&1. If it fails, stop: "GitHub CLI is not authenticated. Rungh auth loginfirst." - If
BACKLOG_PROVIDER=local: check for.specrails/local-tickets.json. If missing, stop: "No local tickets file found. Run/specrails:auto-propose-backlog-specsto generate ideas." - If
BACKLOG_PROVIDER=none: stop: "No backlog provider configured. Run/specrails:setupto configure."
Set GH_AVAILABLE based on whether gh auth succeeded.
Execution
Launch a single sr-product-analyst agent (subagent_type: sr:product-analyst) to read and prioritize the backlog.
The product-analyst receives this prompt:
You are reading the product-driven backlog from the configured backlog provider (read from .specrails/backlog-config.json) and producing a prioritized view.
Fetch all open product-driven backlog items:
If
BACKLOG_PROVIDER=github:gh issue list --label "product-driven-backlog" --state open --json number,title,body,labels --limit 100If
BACKLOG_PROVIDER=local: read.specrails/local-tickets.jsonand extract theticketsarray.Parse each issue/ticket to extract metadata from the body:
- Area: from
area:*label - Persona Fit: from the body's Overview table — extract per-persona scores and total
- Effort: from the body's Overview table (High/Medium/Low)
- Description: from the body's "Feature Description" section
- User Story: from the body's "User Story" section
- Area: from
Parse prerequisites for each issue:
- Locate the row whose first cell matches
**Prerequisites**in the issue body's Overview table. - If the cell value is
None,-, or empty: setprereqs = []for this issue. - Otherwise: extract all tokens matching
#\d+from the cell and setprereqs = [<numbers>]. - If a prerequisite number does not appear in the fetched issue list, treat it as already satisfied (externally closed). Do not include it in the DAG.
- Locate the row whose first cell matches
Build dependency graph and detect cycles:
- Construct a directed graph where edge
(A → B)means "issue A must complete before issue B". - For each issue with a non-empty
prereqslist, add an edge from each prerequisite to the issue. - Run depth-first cycle detection:
- Maintain
visitedandrec_stacksets. - For each unvisited node, run DFS. If a node in
rec_stackis encountered, a cycle exists.
- Maintain
- Collect all cycle members into
CYCLE_MEMBERS. - If
CYCLE_MEMBERSis non-empty, prepare a warning block to render before the backlog table:> **Warning: Circular dependency detected in backlog.** > The following issues form a cycle and cannot be safely ordered: > #A -> #B -> #A > Review these issues and correct the Prerequisites fields. - Compute
in_degree[issue]for all issues (count of prerequisite edges pointing to each issue from other open backlog issues).
- Construct a directed graph where edge
Compute safe implementation order (Kahn's topological sort):
- Exclude
CYCLE_MEMBERSfrom this computation. - Initialize
ready= all non-cycle issues wherein_degree == 0. - Sort
readyby Total Persona Score descending. - Build
WAVES = []:while ready is non-empty: WAVES.append(copy of ready) next_ready = [] for each issue in ready: for each dependent D of issue (edges issue → D): in_degree[D] -= 1 if in_degree[D] == 0: next_ready.append(D) sort next_ready by Total Persona Score descending ready = next_ready - Store
WAVE_1 = WAVES[0](the set of immediately startable features).
- Exclude
Group by area.
Sort within each area by Total Persona Score (descending), then by Effort (Low > Medium > High) as tiebreaker.
Display as a formatted table per area, then propose the top 3 items from
WAVE_1(features with all prerequisites satisfied) for implementation. If fewer than 3 are inWAVE_1, show as many as available and add: "Note: Only {N} feature(s) are available to start immediately — remaining features have unmet prerequisites."[If
CYCLE_MEMBERSis non-empty, render the cycle warning block immediately before the first area table.]Render each area table with the following format:
- Append
[blocked]to the issue title cell ifin_degree[issue] > 0and the issue is not inCYCLE_MEMBERS. - Append
[cycle]to the issue title cell if the issue is inCYCLE_MEMBERS. Prereqscell: list prerequisite issue numbers as#N, #M, or—if none.
## Product-Driven Backlog {N} open issues | Source: VPC-based product discovery Personas: <read persona names and roles from .specrails/personas/*.md or .claude/agents/personas/*.md> ### {Area Name} | # | Issue | <one column per persona> | Total | Effort | Prereqs | |---|-------|<separators>|-------|--------|---------| | 1 | #42 Feature name [blocked] | ... | X/<N*5> | Low | #12, #17 | | 2 | #43 Other feature | ... | X/<N*5> | High | — | (N = number of personas, max score = N * 5) --- ## Recommended Next Sprint (Top 3) Ranked by VPC persona score / effort ratio: | Priority | Issue | Area | <one column per persona> | Total | Effort | Rationale | |----------|-------|------|<separators>|-------|--------|-----------| ### Selection criteria - Cross-persona features (both 4+/5) prioritized over single-persona - Low effort preferred over high effort at same score - Critical pain relief weighted higher than gain creation Run `/specrails:implement` to start implementing these items.- Append
Render Safe Implementation Order section after the Recommended Next Sprint table:
--- ## Safe Implementation Order Features grouped by wave. All features in a wave can start in parallel. Features in wave N must complete before wave N+1 begins. | Wave | Issue | Title | Prereqs | Score | Effort | |------|-------|-------|---------|-------|--------| | 1 | #N | ... | — | X/<N*5> | Low | | 2 | #M | ... | #N | X/<N*5> | Medium | To implement in this order: /specrails:batch-implement <issue-refs in wave order> --deps "<A> -> <B>, <C> -> <D>, ..." [If no edges exist in the DAG, omit the --deps clause:] /specrails:batch-implement <issue-refs> [If CYCLE_MEMBERS is non-empty, append:] Cycle members excluded from ordering: #A, #B Fix the Prerequisites fields in these issues to include them.Issue refs in the
/specrails:batch-implementcommand are listed in wave order (wave 1 first, then wave 2, etc.), sorted by persona score within each wave. The--depsstring is constructed from all edges in the DAG:"A -> B"for each edge, comma-separated. If the backlog has no dependencies at all (DAG has no edges), the section still renders showing all features in wave 1 and the--depsclause is omitted.If no issues exist:
No product-driven backlog issues found. Run `/specrails:auto-propose-backlog-specs` to generate feature ideas.[Orchestrator] After the product-analyst completes, write issue snapshots to
.claude/backlog-cache.json.Guard: If
GH_AVAILABLE=false(from Phase 0 pre-flight), print[backlog-cache] Skipped — GH unavailable.and return. Do not attempt the write.Fetch all open backlog issues in one call:
gh issue list --label "product-driven-backlog" --state open --json number,title,state,assignees,labels,body,updatedAtFor each issue in the result, build a snapshot object:
number: integer issue numbertitle: issue title stringstate:"open"or"closed"assignees: array of assignee login names, sorted alphabeticallylabels: array of label names, sorted alphabeticallybody_sha: SHA-256 of the raw body string — compute with:
Ifecho -n "{body}" | sha256sum | cut -d' ' -f1sha256sumis not available, fall back toopenssl dgst -sha256 -rorshasum -a 256.updated_at: theupdatedAtvalue from the GitHub API responsecaptured_at: current local time in ISO 8601 format
Merge strategy: If
.claude/backlog-cache.jsonalready exists and is valid JSON, read it and merge: new snapshot entries overwrite existing entries by issue number key; entries for issue numbers not in the current fetch are preserved (they may be needed by an in-progress/specrails:implementrun). If the file does not exist or is malformed, create it fresh.Write the merged result back to
.claude/backlog-cache.jsonwith:schema_version:"1"provider:"github"last_updated: current ISO 8601 timestampwritten_by:"get-backlog-specs"issues: the merged map keyed by string issue number
If the write fails (e.g.,
.claude/directory does not exist): print[backlog-cache] Warning: could not write cache. Continuing.Do not abort.