---ci---
phase: 2
milestone: v0.2
status: verify
requirements:
covered: [REQ-07, REQ-12, REQ-13, REQ-14, REQ-15, REQ-16, REQ-18, REQ-19, REQ-20, REQ-21, REQ-22, REQ-23, REQ-24]
partial: []
lessons:
- "P0s both masked by fake tests: SSRF guard existed but was never wired into the fetch boundary; writer prompts had no evidence slot so the visited-only constraint was vacuous - both fixed with regression pins (evidence-in-prompt recording test, refused-fetch test)"
- "Fake-adapter tests mask integration gaps: the three highest-risk behaviors were exactly the untested ones. P4 adversarial suite must include real-boundary probes"
- "P2 flags for post-hoc: DNS-rebinding TOCTOU residual risk (getaddrinfo vs connect-time resolution), evidence store not persisted across restarts (renumbered IDs on resume), reflecting step is re-planning (depth>1 loop untested), liveness body accumulation vs HEAD"
---/ci---
Structural: mypy strict 36 files, ruff clean. Behavioral: 108
backend tests (full walk, approval halt, pause/resume mid-run,
cancel pre+post approval, restart from AWAITING_APPROVAL and
SEARCHING, probe wiring both paths, trust thresholds, SSRF
refusals, export gate) + 90 frontend green. REQ-13..24 mapped.
Security: SSRF enforced at every fetch path + per-hop redirect
guarding; no value leaks (probe persists typed data only). The
phase is shippable: both P0s and all blocking P1s resolved with
regression coverage.
---ci---
phase: 2
milestone: v0.2
status: verify
persona: backend-engineer
task: T-2.5
requirements:
covered: [REQ-16, REQ-18, REQ-20, REQ-23]
---/ci---
P0-1: SSRF guard now lives in adapters/url_guard.py (IPv4+IPv6
via getaddrinfo, fails closed on unresolvable) and is wired into
Tavily.fetch, DDG.fetch, AND the engine extract step (refused
URLs logged as source_refused events, never fetched). Liveness
redirects followed manually with per-hop re-guarding - a 302 to a
private IP now classifies DEAD, and STALE is reachable (redirect
loop / missing location).
P0-2: writer prompts gain {evidence} slots; render_writer_prompt
requires evidence; regression test proves evidence text reaches
the writer prompt (recording model).
P1s: ledger anchoring now anchors the REPORT's extracted quotes
against all passages (mismatch detection real, REQ-20 testable);
trace.jsonl append-only per run (REQ-16 trace file); _execute is
position-aware - resume works from SEARCHING/EXTRACTING/
REFLECTING/SYNTHESIZING/VERIFYING (mid-run restart test); cancel
honored post-approval via per-iteration checks in the research
loop; REQ-23 export gate (>= 0.99 liveness with failure listing
in the checklist); authority tiers dot-anchored; extracting step
processes ALL sub-questions (breadth fix); AdapterError taxonomy
covers raw httpx errors at the extract boundary. 108 tests.
---ci---
phase: 2
milestone: v0.2
status: execute
persona: backend-engineer
task: T-2.6
requirements:
covered: [REQ-15, REQ-31]
---/ci---
Actor is position-aware: a restored state past PLANNING skips the
planning phase and re-enters at the approval/research boundary with
its persisted plan (state file round-trip proven: state, cursor,
plan all survive). researchx.engine.demo walks the full pipeline on
fakes into a temp runs dir (state.json, report.md, ledger.json,
checklist.md + 10 events). make_run_config now uses the storage
adapter's reduced-alphabet run-id generator (verify P2 flag
resolved: dual generators consolidated). 99 tests green.