Both bugs landed during `mydeepagent serve` + real OpenRouter run via
/api/runs. Neither was caught by the test suite — each test uses a fresh
sqlite tmp_path or per-test Postgres DB, so a "second run against existing
data" code path was never exercised.
1. `_compose_final_report` did not persist `RunRow.final_report_path`
- CLI users received the path from the RunResult return value and never
noticed.
- API/GUI users read from the DB → got `null` → no link to the report
showed up in the run detail page.
- Fix: at the end of `_compose_final_report`, open a DB session, load
the RunRow, set `final_report_path = str(json_path)`, commit. Both
code paths now see the path.
2. `_run_approval_gate` built `idempotency_key = f"{phase_key}:{artifact_name}"`
- The 2nd run of the same workflow on a populated DB hit
`approval_requests_idempotency_key_key` UNIQUE violation on the first
approval gate (`spec:spec.json` already existed from the previous run).
- The background task died; the run stayed `executing` forever; the GUI
loop never updated.
- Fix: prefix with `run_id`: `f"{run_id}:{phase_key}:{artifact_name}"`.
Same-run replay (resume / repair retry) still collides idempotently as
intended. ApprovalDecisionRow inherits the new key shape automatically.
Verification
- 4th /api/runs POST against the populated Postgres DB completed in ~2 min,
spec + review + verify all `completed`, 3 artifacts schema-valid, and
`RunRow.final_report_path` now resolves to the report .json path.
- Gates: ruff / mypy --strict / 19 engine+resume+wiring tests PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>