## Code Review: `--parallel` flag for concurrent LLM calls

### ftl_project_expert/llm.py:invoke_concurrent
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Clean async implementation using `asyncio.Semaphore` for concurrency control and `asyncio.gather(return_exceptions=True)` for in-band error reporting. Mirrors the existing `invoke`/`invoke_sync` pattern well. The sync wrapper follows the same `asyncio.run()` approach as `invoke_sync`.
---

### ftl_project_expert/cli.py:_build_topic_prompt
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Clean extraction of prompt-building logic from `_run_topic`. The function is pure except for `click.echo` on warning paths (issue fetch failures). Used by both `_run_topic` (sequential) and `_explore_loop_parallel` (batch). `_run_topic` now delegates to it correctly, and the remaining `_run_topic` body handles LLM invocation and result processing as before.
---

### ftl_project_expert/cli.py:_explore_loop_parallel
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Two concerns. (1) `check_model_available` is never called in this path — `_run_topic` calls it per-topic in the sequential path, but the parallel path skips it entirely. The failure would surface as subprocess errors caught by `isinstance(result, Exception)`, so it's not broken, but the error message is worse (subprocess stderr vs. clean "CLI not available"). Consider adding a single check before the while loop. (2) Minor: `_build_topic_prompt` calls `_get_source(config)` which may do network I/O for issue fetching; building prompts sequentially in the list comprehension (line 614) is correct but could be slow if issue fetching is expensive — that's fine for now since the parallelism targets LLM calls, not I/O.
---

### ftl_project_expert/cli.py:_explore_loop
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Clean dispatch — delegates to `_explore_loop_parallel` when `max_parallel > 1`, otherwise runs the existing sequential loop unchanged. The `> 1` guard also safely handles `--parallel 0` or negative values by falling back to sequential.
---

### ftl_project_expert/cli.py:propose_beliefs
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Prompts are built eagerly for both paths. The `if max_parallel > 1 and len(prompts) > 1` guard avoids unnecessary overhead for single-batch or sequential runs. Error handling via `isinstance(r, Exception)` is consistent with the `return_exceptions=True` contract. The sequential fallback preserves original behavior exactly.
---

### ftl_project_expert/cli.py:review_proposals
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Same parallel/sequential dispatch pattern as `propose_beliefs`. Prompts are now built eagerly in all paths (lines 1173-1180) which is a minor behavioral change from the original (which built them one at a time inside the loop), but `_build_proposals_section` has no side effects so this is safe.
---

### ftl_project_expert/cli.py:update
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: WIRED
REASONING: Good simplification — the inline explore loop (previously ~15 lines) is replaced by a single `_explore_loop` call that handles both sequential and parallel. The `--parallel` flag is correctly threaded through to `propose_beliefs` and `review_proposals` via `ctx.invoke`. `update` already calls `check_model_available` at the top, which partially mitigates the missing check in `_explore_loop_parallel`.
---

### ftl_project_expert/cli.py (import)
VERDICT: CONCERN
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: UNTESTED
INTEGRATION: PARTIAL
REASONING: `invoke_concurrent` (the async version) is imported on line 15 but never used in `cli.py` — only `invoke_concurrent_sync` is called. Unused import.
---

### SELF_REVIEW
LIMITATIONS: No tests exist for any of the modified code (`test_count: 0` for both `cli.py` and `llm.py`). Cannot verify behavior empirically. Could not see `_load_cached_issues`, `_get_source`, or `_report_beliefs` implementations to fully assess side-effect safety in the parallel path. Could not verify whether `pop_next`'s file-based queue operations are safe if two separate processes somehow call the CLI simultaneously (not a concern introduced by this diff, but worth noting).
---

### FEATURE_REQUESTS
- Include test runner output (e.g., `pytest --co -q`) to show whether any tests exist at all for the project
- Show the full import block of modified files to catch unused imports more systematically
---
