## Code Review

### expert_build/propose.py:cmd_propose_beliefs
VERDICT: CONCERN
CORRECTNESS: QUESTIONABLE
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: PARTIAL
INTEGRATION: WIRED
REASONING: The core refactor is sound — moving from collect-then-write to write-after-each-batch gives crash resilience for the output file. However, `_save_processed` is still called only at the end (after the loop). If a crash occurs after batch N is written but before `_save_processed` runs, those entries aren't marked processed. On the next invocation, those same entries will be re-proposed and **appended again** to the output file, producing duplicates. The existing-ID filter (`existing_ids`) only catches beliefs already accepted into `reasons.db`, not pending proposals in the output file. This gap undermines the crash-safety goal — either `_save_processed` should also be called incrementally (per-batch), or the code should deduplicate against proposals already in the output file on restart. Additionally, the print message always says "Wrote" even when appending to an existing file, which is a minor UX regression from the previous "Appended to" distinction.

---

### tests/test_propose.py
VERDICT: CONCERN
CORRECTNESS: VALID
SPEC_COMPLIANCE: N/A
ISSUE_COMPLIANCE: N/A
BELIEF_COMPLIANCE: N/A
TEST_COVERAGE: PARTIAL
INTEGRATION: WIRED
REASONING: The three tests cover the key scenarios well: crash mid-batch, full success, and dedup filtering. The crash test (`test_proposals_written_after_each_batch`) correctly verifies that batch-1 content survives a batch-2 error. However, the tests don't cover: (1) appending to an existing non-empty output file (the `else` branch of the header logic), (2) the crash-restart scenario where `_save_processed` wasn't called — which would reveal the duplication concern above, and (3) the `entry` attribute path (`has_entry_flag=True`). The mock setup is clean and the fixtures are well-structured. The `work_dir` fixture correctly creates `.expert-build` for the processed-entries path.

---

### SELF_REVIEW
LIMITATIONS: Could not inspect `invoke_sync` to confirm its exception behavior. Could not verify whether any existing tests in the suite cover the append-to-existing-file path. The observations provided the full function body and callers, which was sufficient for the core logic review.

---

### FEATURE_REQUESTS
- Show the git commit message alongside the diff — it often states the author's intent and helps evaluate whether the implementation matches the goal
- Include existing test files that were NOT modified but test the same function, to check for broken assumptions

---

**Summary:** The incremental-write refactor works correctly for the happy path and partial-crash path. The main gap is that `_save_processed` still runs only at the end, so a crash-and-restart will re-propose and duplicate entries in the output file. Consider calling `_save_processed` incrementally (after each batch's proposals are flushed to disk) to close this gap.
