Now I have a complete picture. Here's the review:

---

## Code Review: Pipeline State + Resume Support

### expert_build/pipeline.py — State management helpers
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: COVERED
INTEGRATION: WIRED
REASONING: `_init_state`, `_load_state`, `_save_state`, `_mark_stage`, `_stage_completed` are clean and well-structured. `_load_state` correctly handles corrupt files with a try/except on `json.JSONDecodeError`/`ValueError`. `_save_state` correctly creates parent dirs. The `STAGE_NAMES` dict and state key format (`{n}_{name}`) are consistent. `_now()` uses UTC with seconds precision — fine.

---

### expert_build/pipeline.py:cmd_pipeline — Resume logic
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: PARTIAL
INTEGRATION: WIRED
REASONING: Two issues:

**1. Convergence loop resume replays completed sub-stages within a cycle.** If the pipeline crashes mid-cycle (e.g., during REVIEW in cycle 2), `current_cycle` is saved as 2, so resume restarts at cycle 2. But stages 4-7 have no per-sub-stage skip logic within a cycle — DERIVE will re-run even if it already completed for cycle 2. This is probably acceptable (idempotent-enough stages), but it's worth noting that "resume" means "restart the current cycle," not "resume at the exact sub-stage." The state file tracks per-stage status with cycle metadata, but since stages 4-7 get overwritten each cycle, there's no way to distinguish "completed in cycle 1" from "completed in cycle 2." This is a design choice, not a bug, but could surprise users.

**2. Stage 8 (EXPORT) always re-runs on resume**, even if already completed. Lines 426-430 unconditionally run export without checking `_stage_completed(state, 8)`, unlike stages 1-3. If the pipeline crashes after export but before writing `status: completed` (line 432), resuming will re-export. Minor since export is likely idempotent, but inconsistent with the pattern used for stages 1-3.

---

### expert_build/pipeline.py — `_save_state` atomicity
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: COVERED
INTEGRATION: WIRED
REASONING: `STATE_FILE.write_text(json.dumps(state, indent=2) + "\n")` is not atomic. If the process is killed mid-write (e.g., OOM, `kill -9`), the state file will be truncated/corrupt. `_load_state` does handle corrupt files gracefully (returns `None`), but this means a crash during write loses all state rather than preserving the last good state. The standard pattern is write-to-temp + `os.rename`. Not a blocker since the corrupt-file path is handled, but it's worth noting for a feature whose purpose is crash resilience.

---

### expert_build/cli.py
VERDICT: PASS
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: COVERED
INTEGRATION: WIRED
REASONING: `--resume` flag added correctly as `store_true` with a clear help string. Placed logically after `--domain`.

---

### tests/test_pipeline.py:TestPipelineState
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: PARTIAL
INTEGRATION: WIRED
REASONING:

**1. `import json` is unused.** Added in the diff but never referenced — should be removed.

**2. Missing test for mid-cycle resume.** The resume test (`test_resume_skips_completed_stages`) crashes at stage 3 (extract) and verifies stages 1-2 are skipped on resume. There's no test for the convergence loop resume path — e.g., crashing during REVIEW in cycle 2 and verifying cycle 2 restarts correctly.

**3. Missing test for `--resume` with `--url`/`--pdf`.** All resume tests use `url=None, pdf=None`. There's no test verifying that a resumed pipeline with sources correctly skips the already-completed ingest stage vs. trying to re-ingest.

**4. `test_no_auto_accept_sets_paused` verifies `status == "paused"` but doesn't test that resume from paused state works.** This is a natural flow (pause → manual review → resume) that goes untested.

**5. The existing tests are solid** for the happy path: state creation, failure recording, completed-pipeline guard, and corrupt state file handling are all well-covered.

---

### SELF_REVIEW
LIMITATIONS: Could not verify whether `_stage_derive`, `_stage_review`, etc. are truly idempotent, which affects whether replaying a partial cycle on resume is safe. Could not see full test file for existing tests to check if the `json` import was already there before the diff. Did not verify `_stage_export` idempotency for the always-re-run concern.

---

### FEATURE_REQUESTS
- Include the full test file (not just the diff) so reviewers can verify imports, fixture completeness, and whether existing tests still pass after production changes.
- Show `git log --oneline` for the branch to understand commit progression and whether issues were addressed iteratively.
