---
phase: 18-monitor-validation-gates
plan: 01
subsystem: monitoring
tags: [ouroboros, aion, pre-write-gate, stathera, heartbeat, clickhouse]

requires:
  - phase: 13-registry-mode-enum
    provides: OuroborosMode enum and per-symbol ouroboros_mode on SymbolEntry

provides:
  - Per-symbol mode-aware pre-write gate in bulk_operations.py (pandas + Polars)
  - Aion-filtered cross-midnight oracle in stathera.py heartbeat check
  - Unit tests for pre-write gate across both paths

affects: [18-02, heartbeat, sidecar, kintsugi]

tech-stack:
  added: []
  patterns:
    [
      per-symbol mode resolution via _get_day_symbols(),
      extracted testable filter functions,
    ]

key-files:
  created:
    - tests/test_mon02_prewrite_gate.py
  modified:
    - python/opendeviationbar/clickhouse/bulk_operations.py
    - scripts/heartbeat/checks/stathera.py

key-decisions:
  - "Extracted _filter_cross_midnight_pandas/polars as standalone functions for testability"
  - "Global mode fallback preserved for backward compat when symbol column absent"

patterns-established:
  - "_get_day_symbols() pattern: build set of day-mode symbols from registry for filtering"

requirements-completed: [MON-02, MON-03]

duration: 8min
completed: 2026-03-27
---

# Phase 18 Plan 01: Pre-Write Gate + Cross-Midnight Oracle Summary

**Per-symbol mode-aware pre-write gate filtering only day-mode cross-midnight bars, with Aion exclusion in Stathera cross-midnight oracle**

## Performance

- **Duration:** 8 min
- **Started:** 2026-03-27T15:52:47Z
- **Completed:** 2026-03-27T16:00:45Z
- **Tasks:** 1
- **Files modified:** 3

## Accomplishments

- Extracted `_filter_cross_midnight_pandas()` and `_filter_cross_midnight_polars()` from inline code for testability
- Both functions use per-symbol mode lookup via `_get_day_symbols()` from registry
- Aion symbol bars pass through cross-midnight gate; only day-mode bars are filtered
- Stathera cross-midnight oracle query now includes `AND ouroboros_mode = 'day'`
- 6 unit tests covering pandas, Polars, mixed-mode, and global fallback paths

## Task Commits

Each task was committed atomically:

1. **Task 1: Per-symbol pre-write gate + cross-midnight oracle Aion exclusion** - `410525c` (feat)

## Files Created/Modified

- `python/opendeviationbar/clickhouse/bulk_operations.py` - Added `_get_day_symbols()`, `_filter_cross_midnight_pandas()`, `_filter_cross_midnight_polars()`; replaced inline gate code
- `scripts/heartbeat/checks/stathera.py` - Added `AND ouroboros_mode = 'day'` to cross-midnight oracle query
- `tests/test_mon02_prewrite_gate.py` - 6 tests: aion passthrough, day filtering, mixed mode, polars paths, global fallback

## Decisions Made

- Extracted filtering into standalone functions rather than modifying inline -- enables unit testing without mocking full store_bars_batch/store_bars_bulk
- Global mode fallback when symbol column is absent preserves backward compatibility for callers that don't include symbol in their DataFrames

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Merged main to get prerequisite ouroboros_mode field**

- **Found during:** Task 1 (reading SymbolEntry)
- **Issue:** Worktree was behind main; SymbolEntry lacked ouroboros_mode field added in Phase 13
- **Fix:** Fast-forward merge from main
- **Files modified:** Multiple (Phase 13-17 changes)
- **Verification:** SymbolEntry.ouroboros_mode accessible, all tests pass

---

**Total deviations:** 1 auto-fixed (1 blocking)
**Impact on plan:** Prerequisite code was simply not merged yet. No scope change.

## Issues Encountered

None beyond the merge above.

## Known Stubs

None -- all data paths are fully wired.

## User Setup Required

None - no external service configuration required.

## Next Phase Readiness

- Pre-write gate and Stathera oracle are Aion-aware
- Ready for 18-02 (heartbeat Aion checks)

---

_Phase: 18-monitor-validation-gates_
_Completed: 2026-03-27_
