v0.19.0 — ### Added — Phase 2 begins
- **T-201** Field backfill job — `jobs/backfill_field.py::backfill_once()`. For each tracked encounter, queries `worldData.encounter.fightRankings` for the top public reports, the…
- T-201 Field backfill job —
jobs/backfill_field.py::backfill_once(). For each tracked encounter, queriesworldData.encounter.fightRankingsfor the top public reports, then runs T-004ingest_reporton each. Ledger short-circuit (PLAN Invariant 1) skips anythingcomplete. For the top-N rankings (default 5), also pulls one-fight-scoped events — the slice T-104/T-202/T-204 need without the full 200×-cost of pulling every fight per Ultimate report. - Default encounter set: FRU (1079) + current Savage tier (101–105). Configurable per-run via
--encounters/--reports-per-encounter/--events-top-n. GET /api/field-statsandFieldStats.jsxReact panel under Reports → per-encounterreports_ingested+kills_with_events(green when ≥3, enough for T-104 consensus).python -m jobs.backfill_field [--dry-run]deployment shape mirrors T-101: one-pass script, externally scheduled.- 5 new tests (
tests/test_backfill_field.py— mocked client; covers dry-run, ledger-skip, error-capture, empty rankings, field_stats aggregation). 171 tests total. - Live AC: 103s pass — discovered 60 rankings across 6 encounters, ingested 43 new report-meta records, pulled events for 9 new kill fights (~98k events). All FRU rankings already in dev DB (ledger-skipped). M5S now has 19 pulls / 18 canonical abilities, M6S 18 pulls / 10 canonical — T-104 consensus produces real timelines for current Savage encounters now, not just FRU.