Тема
Harvester v1 — план валидации ПЕРЕД длинным запуском
ИСТОРИЧЕСКИЙ ПЛАН. Replay,
SimOrderClientи capture-пункты ниже отменены решением оператора 2026-08-22. Документ сохранён как история планирования и не является текущей очередью работ или процедурой проверки.
Директива оператора 2026-06-24: НИКАКОГО «запустить и ждать 2 недели» (это 3-месячный цикл «ой опять баг, рестарт»). Порядок: (A) найти ВСЕ баги; (B) покрыть код E2E+интеграционными тестами на РЕАЛЬНЫХ данных (не синтетика), каждый метод валидирован на данных; (C) записать реальный трафик за часы, прогнать код, проверить каждую строку/переменную/логическую цепочку; (D) только потом длинный запуск. Совпадает с CLAUDE.md quality-bar.
Ключевой факт
Полный реальный capture с Gamma уже есть: .tmp/captures/01KTYQT30JD0V7BEP0T289AE60 (3.6-дневный погодный, 8.3G: rest_gamma.jsonl.zst 3.1G c реальными outcomePrices/ endDate/gameStartTime, ws_market.jsonl.zst 5.1G book/trade/resolved, rest_clob, rtds). Значит реальные тесты можно начинать БЕЗ починки native --capture-only (у того нет Gamma — это отдельный форвард-трек A5, нужен позже для post-fix записи).
Граница точности (честно, не маскировать): EngineReplayDriver гоняет РЕАЛЬНЫЕ DirectionalStrategy + SimOrderClient + PositionRegistry + OrderRouter, но wall-clock tokio::select! StrategyDriver-цикл НЕ frame-steppable — драйвер ЗЕРКАЛИТ его детерминированное ядро. Любое block-D «построчно» — в рамках реальных компонентов; зеркалированный цикл помечается явно.
SCOPE: реплей этого баг-эра capture (2026-06-16) сертифицирует КОРРЕКТНОСТЬ/ СОГЛАСОВАННОСТЬ учёта (инварианты, числа самосогласованы), НЕ +EV (+EV требует форвард post-fix данных). Все block-B тесты проверяют учётные тождества, не доходность.
Критический путь
A1 → A2 → A3 → A4 → (B + C-real-traffic) → D → verdict (после сходимости ResolutionSweep)
A5 параллельно (форвард-трек); C-code-trace параллельно с AБлок A — harness (чтобы реальный трафик → проверяемый engine.db) [АВТОНОМНО]
- A1 (keystone): file-backed
SqliteStorageвEngineReplayDriver(сейчасopen_memory()→ in-memory отчёт; нуженopen(path)на bare::new, replay/mod.rs). Без файла engine.dbforensics.sh --engine/harvester-evне читают. - A2 (основной объём): reader мульти-стримов
.jsonl.zst→ReplayFrame. Сейчас такого нет (прежний pm-bot Parquet-мост удалён при cutover 2026-07-22). Должен: слить все стримы по ts_ns/monotonic_seq в один глобальный порядок; восстановитьasset_id→condition_idизMarketEvent::NewMarket/Gamma clobTokenIds (book-кадры имеют пустой condition_id); раскрыть PriceChange multi-level в per-level BookDelta; подавать GammaResponse (иначеbuild_snapshot=None). Parse-половина уже есть и real-data-протестирована (parse_gamma_body_to_metadata/apply_gamma_body). - A3: CLI
pm-engine replay --capture <dir> --out engine.db. - A4 (ОБЯЗАТЕЛЬНЫЙ guard): детерминизм ×2 на bare real-LDI (
::new, НЕnew_ldi_forced). Не bit-identical → документировать какask_persistence_decayhazard, НЕ маскировать. ЭСКАЛАЦИЯ. - A5 (параллельно, build автономно / deploy operator): native
--capture-onlyGamma-tee для БУДУЩИХ форвард-записей (не на крит-пути первого теста).
Блок B — реальные E2E/интеграционные тесты [АВТОНОМНО]
ЖЁСТКО: каждый admitting-тест на ::new (bare real LDI), НИКОГДА new_ldi_forced. Сейчас ВСЕ P&L-тесты на new_ldi_forced → реальный LDI-гейт (решает весь набор позиций, что читает вердикт) имеет НОЛЬ покрытия. Оракул — руками из глубины стакана capture, НЕ из вывода кода. Стадии: 1 ingestion→snapshot (sort/None-инварианты); 2/3 decision→ entry→order→sim-fill (entry_cost==price×size/10^4; min-size; FOK без resting-leak; reserve→fill→release баланс); 4 sim-matcher на РЕАЛЬНОЙ thin-book глубине (зона phantom-fill бага); 5 positions/budget (realized==exit−entry; oversold reject); 6 closure/resolution (winner только по WS winning_token_id/чистый Gamma 1.0/0.0, никогда default-loser; каждая held на resolved → non-NULL realized).
Блок C — адверсариальный bug-hunt
- C1 [АВТОНОМНО, ТОП]: winner-классификация (
resolve_winner/book_resolution_pnl) forward-audit на реальных resolved-строках — точный 1.0/0.0 знак, ноль default-loser, ноль вечно-NULL realized. Доминирует: r=0 ⇒ лузеры досиживают; mis-winner → знак вердикта. - C2 [АВТОНОМНО]: allocate add/sub паритет (
order_router) — счётчик на реальном трафике, нет double-release при закрытии двумя путями (stop SELL + resolution). - C3 [OPERATOR]: 24h VPS shadow на live WS — все
*_err/*_missing_total==0. - C4 [INFERRED→certify]: stop realized полный цикл на реальном recovery-трафике (r/s_eff измерить, не утверждать; FOK без phantom-loss).
- C5 [OPERATOR]: канон sim-fill (partial vs all-or-nothing + ≥5-или-0) — 3 матчера расходятся 5/7; ≥5-или-0 residual НЕ реализован в engine entry_sizing.
- C6 [OPERATOR]: LDI-асимметрия vs прежний pm-bot (tick-7 структурное расхождение, историческое сравнение) — разные входные популяции → разный p_e/winrate.
- C7 [АВТОНОМНО, LOW]: TODO/unimplemented — подтвердить, что не на dry-run hot-path, закрыть.
Блок D — построчная верификация на часах реального трафика [АВТОНОМНО]
На file-backed engine.db из A3. Через forensics.sh --engine/skill trade-forensics, НЕ ad-hoc. D1 per-position ledger (каждый realized → реальный BUY FILLED, нет phantom-booking); D2 variable-инварианты (entry_cost, realized, budget-identity, нет negative-allocated); D3 logic-chain (порядок entry-гейтов на реальных тиках; snapshot Some только после Gamma; один snapshot для decision И sim-rematch); D4 детерминизм bit-identical (иначе hazard live → ЭСКАЛАЦИЯ); D5 convergence-gate (ноль resolved с NULL realized) перед чтением вердикта. Вердикт = CONSISTENCY на баг-эра данных, НЕ +EV.
Что требует operator sign-off (не решать самому)
C3 (24h live shadow), C5 (канон sim-fill/entry), C6 (канон LDI), ЛЮБОЙ +EV-вердикт (только форвард post-fix), любой non-bit-identical A4 (hazard), любой sources-disagree.
Оценка
Автономная инженерия ~12–18 дней по крит-пути A→B/C→D (A5 + C-code-traces параллельно); исключает operator-gated shadow/adjudication. Полная детализация: workflow-вывод tasks/wxy5h8wps.output.