---
phase: 16-sidecar-aion-startup
verified: 2026-03-27T10:15:00Z
status: passed
score: 7/7 must-haves verified
re_verification: null
gaps: []
human_verification:
  - test: "Deploy sidecar with one Aion symbol in symbols.toml and one Day symbol. Restart sidecar."
    expected: "Aion symbol resumes from checkpoint (no DELETE today in logs), Day symbol runs clean-slate DELETE (DELETE today in logs). Both symbols accumulate bars after startup."
    why_human: "Requires live ClickHouse + Binance WS, can't verify partition isolation or per-symbol log output programmatically in CI."
  - test: "Trigger a Puck gap fill on an Aion symbol by artificially introducing a trade-ID gap."
    expected: "Gap fill completes without producing orphan bars (no OuroborosMode::Day midnight reset inside puck_fill)."
    why_human: "Requires live streaming + forced gap injection; can't simulate gap-fill execution without running engine."
---

# Phase 16: Sidecar Aion Startup Verification Report

**Phase Goal:** Sidecar starts Aion symbols via checkpoint-resume without wiping today's bars, while Puck inline gap fill remains fully operational
**Verified:** 2026-03-27T10:15:00Z
**Status:** passed
**Re-verification:** No — initial verification

## Goal Achievement

### Observable Truths

| #   | Truth                                                                                                                              | Status     | Evidence                                                                                                                                                                                                                                              |
| --- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | OuroborosMode is threaded per-symbol from Python through PyO3 to every Rust call site that previously hardcoded OuroborosMode::Day | ✓ VERIFIED | `symbol_modes` field on `LiveEngineConfig` (types.rs:191); `fill_from_rest` (mod.rs:343,373,566), `start()` (mod.rs:828,843), `symbol_task` (symbol_task.rs:45 — param used at lines 168,221,263,281,331,414,439), `puck_fill` (puck.rs:35,85)        |
| 2   | Puck gap fill uses per-symbol mode so Aion symbols never produce orphan bars during gap recovery                                   | ✓ VERIFIED | `puck_fill` signature has `ouroboros_mode: OuroborosMode` at puck.rs:35; line 85 passes it to `maybe_reset_at_midnight`; zero `OuroborosMode::Day` hardcodes in puck.rs                                                                               |
| 3   | fill_from_rest uses per-symbol mode so REST backfill processes trades with correct midnight behavior                               | ✓ VERIFIED | mod.rs:343 clones `symbol_modes`; mod.rs:373 performs `symbol_modes.get(&symbol).copied().unwrap_or_default()`; mod.rs:566 passes `ouroboros_mode` to call site                                                                                       |
| 4   | Aion symbol startup resumes from checkpoint TID without running midnight preflight or clean-slate DELETE                           | ✓ VERIFIED | `__init__.py:258-274` — aion path calls `_inject_checkpoints` + `_seed_trade_ids_from_clickhouse`; midnight preflight (`_run_midnight_preflight`) is in `if day_symbols:` branch (line 277) only; DELETE loop at line 363 iterates `day_symbols` only |
| 5   | Day symbol startup is unchanged — midnight preflight and clean-slate DELETE still run                                              | ✓ VERIFIED | `__init__.py:277-414` — day path unchanged: `_run_midnight_preflight`, `_check_yesterday_integrity`, `ALTER TABLE DELETE IN PARTITION` loop (day_symbols only), `_process_parquet_ticks` all execute for day symbols                                  |
| 6   | Cross-midnight bars from Aion symbols are not discarded in main loop or sync_flush                                                 | ✓ VERIFIED | Main loop line 1210: `symbol_modes.get(symbol, "day") == "day"` guard. sync_flush line 516: `sym_mode = (symbol_modes or {}).get(symbol, "day")` guard — both short-circuit for Aion symbols                                                          |
| 7   | Mixed Day+Aion symbol fleet works correctly in a single sidecar instance                                                           | ✓ VERIFIED | `_create_engine` returns 4-tuple including `symbol_modes`; recovery.py propagates it through restart (recovery.py:54,65); `symbol_modes` passed to `StreamManager` constructor (line 241) for Rust-side routing                                       |

**Score:** 7/7 truths verified

### Required Artifacts

| Artifact                                                           | Expected                                                      | Status     | Details                                                                                                                                                            |
| ------------------------------------------------------------------ | ------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| `crates/opendeviationbar-streaming/src/live_engine/types.rs`       | symbol_modes field on LiveEngineConfig                        | ✓ VERIFIED | `pub symbol_modes: HashMap<String, OuroborosMode>` at line 191; initialized to `HashMap::new()` at line 220                                                        |
| `crates/opendeviationbar-streaming/src/live_engine/symbol_task.rs` | Per-symbol OuroborosMode parameter threaded to all call sites | ✓ VERIFIED | `ouroboros_mode: OuroborosMode` param at line 45; used at 7 call sites (168, 221, 263, 281, 331, 414, 439); zero hardcoded `OuroborosMode::Day` in production code |
| `crates/opendeviationbar-streaming/src/live_engine/puck.rs`        | ouroboros_mode parameter replacing hardcode                   | ✓ VERIFIED | `ouroboros_mode: OuroborosMode` at line 35; passed to `maybe_reset_at_midnight` at line 85                                                                         |
| `src/stream_manager_bindings.rs`                                   | PyO3 constructor accepts symbol_modes dict                    | ✓ VERIFIED | `symbol_modes=None` in `pyo3(signature)` at line 96; `Option<HashMap<String, String>>` param at line 110; conversion block at lines 131-138                        |
| `python/opendeviationbar/sidecar/__init__.py`                      | Branched startup path and conditional cross-midnight filter   | ✓ VERIFIED | `aion_symbols`/`day_symbols` partition at lines 212-213; branched startup 256-414; conditional cross-midnight filter at line 1210; returns 4-tuple at line 494     |
| `python/opendeviationbar/sidecar/midnight.py`                      | Conditional cross-midnight filter in sync_flush               | ✓ VERIFIED | `symbol_modes: dict[str, str]                                                                                                                                      | None = None`param at line 442;`sym_mode` guard at lines 516-522 |
| `python/opendeviationbar/sidecar/recovery.py`                      | 4-tuple return propagates symbol_modes                        | ✓ VERIFIED | `new_symbol_modes` unpacked at line 54; returned at line 65                                                                                                        |

### Key Link Verification

| From                                                       | To                                                                 | Via                                                               | Status  | Details                                                                                                |
| ---------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `src/stream_manager_bindings.rs`                           | `crates/opendeviationbar-streaming/src/live_engine/types.rs`       | `symbol_modes HashMap<String, OuroborosMode>` on LiveEngineConfig | ✓ WIRED | Bindings line 138: `engine_config.symbol_modes.insert(sym, mode)`                                      |
| `crates/opendeviationbar-streaming/src/live_engine/mod.rs` | `crates/opendeviationbar-streaming/src/live_engine/symbol_task.rs` | `ouroboros_mode` parameter passed to symbol_task spawn            | ✓ WIRED | mod.rs:828,843 — `ouroboros_mode` passed to `symbol_task()`                                            |
| `python/opendeviationbar/sidecar/__init__.py`              | `python/opendeviationbar/symbol_registry.py`                       | `get_symbol_entries()` for per-symbol ouroboros_mode lookup       | ✓ WIRED | `__init__.py:202-211`: `entries = get_symbol_entries()` + `entry.ouroboros_mode == OuroborosMode.AION` |
| `python/opendeviationbar/sidecar/__init__.py`              | `src/stream_manager_bindings.rs`                                   | `symbol_modes` dict passed to StreamManager constructor           | ✓ WIRED | `__init__.py:241`: `symbol_modes=symbol_modes` in constructor call                                     |

### Data-Flow Trace (Level 4)

Not applicable for this phase. All artifacts are control-flow/startup orchestration code, not components that render or query dynamic data from a database. The data source is the Binance WebSocket stream and ClickHouse CH seeding, which requires live runtime.

### Behavioral Spot-Checks

| Behavior                                                       | Command                                                     | Result                          | Status            |
| -------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------- | ----------------- |
| `cargo check -p opendeviationbar-streaming` compiles clean     | `cargo check -p opendeviationbar-streaming`                 | "Finished dev profile"          | ✓ PASS            |
| No hardcoded `OuroborosMode::Day` in puck.rs or symbol_task.rs | `grep -n "OuroborosMode::Day" symbol_task.rs puck.rs`       | No matches                      | ✓ PASS            |
| No Phase 16 TODOs remaining                                    | `grep -rn "TODO.*Phase 16" live_engine/`                    | No matches                      | ✓ PASS            |
| `_sync_flush_rest_bars` accepts `symbol_modes` at runtime      | Python introspection                                        | `symbol_modes in params: True`  | ✓ PASS            |
| All 5 phase commits verified in git history                    | `git log --oneline 3135308 c76db6a d867990 fac3d5d ab3fcd8` | All 5 found                     | ✓ PASS            |
| `OuroborosMode::Day` in mod.rs test functions only             | Context check lines 1349, 1402                              | Both inside `#[test]` functions | ✓ PASS (expected) |

### Requirements Coverage

| Requirement | Source Plan | Description                                                                                       | Status      | Evidence                                                                                                                               |
| ----------- | ----------- | ------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| SIDE-01     | 16-02       | Aion symbols skip midnight preflight and clean-slate DELETE on sidecar startup                    | ✓ SATISFIED | `__init__.py:282-388` — midnight preflight and DELETE are inside `if day_symbols:` block only; aion path at lines 258-274 has neither  |
| SIDE-02     | 16-02       | Aion symbols use checkpoint-resume startup (TID-based resume, not genesis recompute)              | ✓ SATISFIED | `__init__.py:259-269` — aion path calls `_inject_checkpoints` + `_seed_trade_ids_from_clickhouse` on startup                           |
| SIDE-03     | 16-01       | Puck inline gap fill remains wired and operational for Aion symbols (mode-agnostic gap detection) | ✓ SATISFIED | `puck.rs:35,85` — `ouroboros_mode` param replaces hardcoded Day; gap detection is unchanged, mode only affects midnight reset behavior |

**Note:** REQUIREMENTS.md table still shows SIDE-01 and SIDE-02 as "Pending" (the `[ ]` checkboxes were not updated). This is a documentation tracking issue only — the implementation is verified complete. The SIDE-03 row correctly shows `[x]` Complete.

### Anti-Patterns Found

| File                | Line       | Pattern                       | Severity | Impact                                                                                     |
| ------------------- | ---------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------ |
| `crates/.../mod.rs` | 1349, 1402 | `OuroborosMode::Day` hardcode | ℹ️ Info  | Both in `#[test]` functions testing Day mode behavior — expected and correct per plan spec |

No blockers or warnings found.

### Human Verification Required

#### 1. Aion vs Day Startup Path Isolation

**Test:** Configure one Aion symbol and one Day symbol in `symbols.toml`. Start the sidecar. Inspect startup logs.
**Expected:** Aion symbol logs show "aion: injected N checkpoints" with no "pre-fill: killed mutations + DELETE today" entry for that symbol. Day symbol logs show "pre-fill: clean slate via partition-scoped DELETE loop" with day_symbols only.
**Why human:** Requires live ClickHouse connection and a live symbol registry with at least one symbol having `ouroboros_mode = "aion"` configured.

#### 2. Puck Gap Fill Behavior for Aion Symbols

**Test:** During live streaming of an Aion symbol, force a Puck gap fill event (e.g., by simulating a trade-ID jump). Inspect resulting bars.
**Expected:** Bars produced during gap fill span across midnight correctly (no orphan bar at midnight boundary, continuous trade-ID chain maintained).
**Why human:** Requires live streaming environment with Puck gap-fill trigger capability.

### Gaps Summary

No gaps. All 7 observable truths are verified. All artifacts are substantive and wired. All 3 requirement IDs (SIDE-01, SIDE-02, SIDE-03) have implementation evidence. The only open items are live-runtime human verification tests which require a running sidecar instance.

---

_Verified: 2026-03-27T10:15:00Z_
_Verifier: Claude (gsd-verifier)_
