## Code Review: sprint-plan command

### ftl_project_expert/cli.py:_compute_gating_analysis
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: PARTIAL
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: The BFS in `_transitive_count` (line 2129) is called once per node in `dependents_map`. For the 10,800+ node network mentioned in the issue, this is O(n^2) worst-case. The issue explicitly warns "the sprint-plan command will need to handle large networks gracefully" (referencing issues #7 and #8). Consider computing transitive counts with topological sort + DP, or capping the iteration to the top-N nodes by direct dependent count before running full BFS.

---

### ftl_project_expert/cli.py:_compute_team_signals
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: ADDRESSES
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Logic correctly handles both dict and list issue formats, gracefully handles missing/malformed date strings. The naive-vs-aware datetime comparison at line 2200 (`now - closed_dt.replace(tzinfo=None)`) compares local time against a UTC timestamp stripped of its zone — off by up to ~12 hours, immaterial for a 30-day window.

---

### ftl_project_expert/cli.py:_format_gating_section
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: ADDRESSES
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Straightforward formatting. Caps at 30 items, truncates text at 120 chars, shows overflow count. The `gated` variable (line 2236) computes an `"indirect"` fallback that is never displayed since the block is guarded by `if item["gated_conclusions"]` — dead string, not a bug.

---

### ftl_project_expert/cli.py:_format_backlog_section
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: ADDRESSES
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Cross-referencing logic (lines 2280-2294) compiles a regex per cached issue ID and searches every gating analysis text against every pattern — O(issues x gating_nodes) regex evaluations. With 500+ issues and thousands of gating nodes, this may be slow. Functionally correct for all three source adapters (GitHub, GitLab, Jira all produce `list[str]` for assignees).

---

### ftl_project_expert/cli.py:sprint_plan
VERDICT: CONCERN
CORRECTNESS: VALID
SPEC_COMPLIANCE: PARTIAL
ISSUE_COMPLIANCE: PARTIAL
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Well-structured, follows the existing `summary` command pattern closely. However, several features from the issue spec are missing: (1) `--retrospective` option for feeding back previous sprint outcomes, (2) `--format` option (shown in issue usage examples), (3) sprint outcomes feeding back into the belief network. The `prompt_size_kb` variable is computed twice (lines 2443 and 2452) — harmless but redundant outside dry-run.

---

### ftl_project_expert/cli.py:_cache_issues (closed field addition)
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: ADDRESSES
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: The `closed` field was already on the `Issue` dataclass (models.py:124) and populated by all three source adapters. Adding it to cache serialization at line 462 is a correct fix — without it, `_compute_team_signals` couldn't distinguish genuinely closed issues from updated ones when computing `is_recent_close`.

---

### ftl_project_expert/prompts/sprint_plan.py
VERDICT: CONCERN
CORRECTNESS: VALID
SPEC_COMPLIANCE: PARTIAL
ISSUE_COMPLIANCE: PARTIAL
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Prompt covers all five output sections from the spec. Good grounding constraint ("do not invent information"). However, the prompt does not include today's date — the `sprint_length` is passed but the actual date range (the issue example shows "Sprint Plan: 2026-07-01 to 2026-07-14") cannot be computed by the LLM without knowing the current date.

---

### ftl_project_expert/prompts/__init__.py
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: ADDRESSES
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Clean addition of `build_sprint_plan_prompt` to both the import and `__all__`. Alphabetically placed.

---

## Key Findings Summary

1. **Performance risk on large networks** (`_compute_gating_analysis`): O(n^2) BFS on 10k+ node networks may be unacceptably slow. The issue itself warns about this.

2. **Missing spec features**: `--retrospective`, `--format`, and sprint date calculation are absent. These may be acceptable as future work if the PR is scoped as Phase 1, but the usage examples in the issue show them.

3. **Sprint date range missing from prompt**: The LLM receives `sprint_length` but not today's date, so it can't produce the date-specific headers shown in the spec example.

4. **No test coverage**: All new pure functions (`_compute_gating_analysis`, `_compute_team_signals`, `_format_backlog_section`, `_format_gating_section`) have zero tests despite being ideal unit test candidates with well-defined inputs/outputs.

---

### SELF_REVIEW
LIMITATIONS: No test files exist in this project to check for broken assertions. Could not verify `invoke()` function signature (defined in `.llm` submodule, not included). Could not run against a real network.json to verify `_compute_gating_analysis` performance empirically. Inferred cache format from `_cache_issues` — returns flat dict keyed by issue ID, so the `cached_issues.get("issues", cached_issues)` pattern correctly falls through.

---

### FEATURE_REQUESTS
- Include the `_load_cached_issues` return value structure explicitly — the polymorphic dict-or-list handling in multiple functions is hard to review without knowing the actual cache format
- Auto-detect sibling command implementations for pattern comparison (e.g., `summary` was useful context)
- Flag functions called with potentially large inputs for performance review automatically

---
