---
phase: 31-aion-first-defaults
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  # Function parameter defaults
  - python/opendeviationbar/ouroboros.py
  - python/opendeviationbar/ouroboros.pyi
  - python/opendeviationbar/population.py
  - python/opendeviationbar/sidecar/clickhouse_write.py
  - python/opendeviationbar/clickhouse/cache.py
  - python/opendeviationbar/clickhouse/query_operations.py
  - python/opendeviationbar/clickhouse/checkpoint_operations.py
  - python/opendeviationbar/clickhouse/bulk_operations.py
  - python/opendeviationbar/checkpoint/__init__.py
  - python/opendeviationbar/checkpoint/__init__.pyi
  - python/opendeviationbar/checkpoint/models.py
  - python/opendeviationbar/checkpoint/storage.py
  - python/opendeviationbar/checkpoint/engine.py
  - python/opendeviationbar/orchestration/open_deviation_bars.pyi
  - python/opendeviationbar/orchestration/count_bounded.py
  - python/opendeviationbar/validation/day_mode/oracle.py
  - python/opendeviationbar/rectify.py
  - python/opendeviationbar/backfill.py
  # Config model defaults
  - python/opendeviationbar/config/population.py
  - python/opendeviationbar/config/cli_models.py
  - python/opendeviationbar/cli.py
  # Fallback lookups
  - python/opendeviationbar/sidecar/__init__.py
  - python/opendeviationbar/sidecar/arrow_flush.py
  - python/opendeviationbar/sidecar/midnight.py
  - python/opendeviationbar/symbol_registry.py
  - python/opendeviationbar/kintsugi/_helpers.py
  # Scripts
  - scripts/detect_gaps.py
  - scripts/validate_microstructure_roundtrip.py
  - scripts/microstructure_patterns.py
  - scripts/heartbeat/checks/yesterday.py
  # Tests (assertions referencing defaults)
  - tests/test_config_telemetry.py
  - tests/test_unified_config.py
  - tests/test_ouroboros_monthly_e2e.py
  - tests/test_kintsugi_repair.py
  - tests/test_heartbeat_yesterday_junction.py
  # Docs
  - python/opendeviationbar/CLAUDE.md
autonomous: true
requirements: [DEFAULT-01, DEFAULT-02, DEFAULT-03, DEFAULT-04, DEFAULT-05]

must_haves:
  truths:
    - 'grep -rn ''ouroboros.*=.*"day"'' python/ scripts/ shows zero hardcoded day defaults in function signatures, config models, .get() fallbacks, or CLI defaults'
    - "Forex symbols resolve to day mode via explicit registry lookup in symbols.toml, not from any default value"
    - "All existing tests pass (with updated assertions reflecting the new aion default)"
    - "get_operational_ouroboros_mode() returns aion when no env override is set"
  artifacts:
    - path: "python/opendeviationbar/config/population.py"
      provides: "Config model with aion default"
      contains: 'ouroboros_mode.*=.*"aion"'
    - path: "python/opendeviationbar/ouroboros.py"
      provides: "Core ouroboros functions with aion defaults"
      contains: 'mode.*=.*"aion"'
    - path: "python/opendeviationbar/symbol_registry.py"
      provides: "Registry fallback changed to aion"
      contains: '.get.*"aion"'
  key_links:
    - from: "python/opendeviationbar/config/population.py"
      to: "python/opendeviationbar/ouroboros.py"
      via: "get_operational_ouroboros_mode() reads config default"
      pattern: 'ouroboros_mode.*"aion"'
    - from: "python/opendeviationbar/symbol_registry.py"
      to: "symbols.toml"
      via: "Forex symbols get day from registry, not default"
      pattern: 'ouroboros_mode.*"day".*symbols.toml'
---

<objective>
Change every hardcoded "day" default for ouroboros_mode to "aion" across the entire codebase: function parameter defaults, pydantic config model defaults, `.get()` fallback values, CLI argparse/click defaults, and script defaults. Forex symbols continue to use "day" mode via explicit registry lookup from symbols.toml, not from any default value.

Purpose: Crypto is the only enabled use case (16 symbols, all Aion). The default should match reality. Forex symbols are disabled and get their mode from explicit registry lookup.
Output: All ~40 call sites changed, all tests passing with updated assertions.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/31-aion-first-defaults/31-CONTEXT.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Batch refactor all ouroboros_mode defaults from "day" to "aion"</name>
  <files>
    python/opendeviationbar/ouroboros.py
    python/opendeviationbar/ouroboros.pyi
    python/opendeviationbar/population.py
    python/opendeviationbar/sidecar/clickhouse_write.py
    python/opendeviationbar/clickhouse/cache.py
    python/opendeviationbar/clickhouse/query_operations.py
    python/opendeviationbar/clickhouse/checkpoint_operations.py
    python/opendeviationbar/clickhouse/bulk_operations.py
    python/opendeviationbar/checkpoint/__init__.py
    python/opendeviationbar/checkpoint/__init__.pyi
    python/opendeviationbar/checkpoint/models.py
    python/opendeviationbar/checkpoint/storage.py
    python/opendeviationbar/checkpoint/engine.py
    python/opendeviationbar/orchestration/open_deviation_bars.pyi
    python/opendeviationbar/orchestration/count_bounded.py
    python/opendeviationbar/validation/day_mode/oracle.py
    python/opendeviationbar/rectify.py
    python/opendeviationbar/backfill.py
    python/opendeviationbar/config/population.py
    python/opendeviationbar/config/cli_models.py
    python/opendeviationbar/cli.py
    python/opendeviationbar/sidecar/__init__.py
    python/opendeviationbar/sidecar/arrow_flush.py
    python/opendeviationbar/sidecar/midnight.py
    python/opendeviationbar/symbol_registry.py
    python/opendeviationbar/kintsugi/_helpers.py
    scripts/detect_gaps.py
    scripts/validate_microstructure_roundtrip.py
    scripts/microstructure_patterns.py
    scripts/heartbeat/checks/yesterday.py
    tests/test_config_telemetry.py
    tests/test_unified_config.py
    tests/test_ouroboros_monthly_e2e.py
    tests/test_kintsugi_repair.py
    tests/test_heartbeat_yesterday_junction.py
    python/opendeviationbar/CLAUDE.md
  </files>
  <read_first>
    python/opendeviationbar/ouroboros.py
    python/opendeviationbar/config/population.py
    python/opendeviationbar/config/cli_models.py
    python/opendeviationbar/symbol_registry.py
    python/opendeviationbar/backfill.py
    python/opendeviationbar/kintsugi/_helpers.py
    python/opendeviationbar/checkpoint/engine.py
    python/opendeviationbar/cli.py
    tests/test_config_telemetry.py
    tests/test_unified_config.py
  </read_first>
  <action>
    Batch grep-and-replace all ouroboros_mode defaults from "day" to "aion". Work through these categories systematically:

    **Category 1: Function parameter defaults (DEFAULT-01)**
    Change `ouroboros_mode: str = "day"` to `ouroboros_mode: str = "aion"` in ALL of these:
    - `python/opendeviationbar/ouroboros.py` lines 91, 151 (get_ouroboros_boundaries, iter_ouroboros_segments)
    - `python/opendeviationbar/ouroboros.pyi` line 31
    - `python/opendeviationbar/population.py` line 98
    - `python/opendeviationbar/sidecar/clickhouse_write.py` lines 25, 80
    - `python/opendeviationbar/clickhouse/cache.py` line 89
    - `python/opendeviationbar/clickhouse/query_operations.py` lines 49, 536
    - `python/opendeviationbar/clickhouse/checkpoint_operations.py` lines 76, 162, 249
    - `python/opendeviationbar/checkpoint/__init__.py` line 1258
    - `python/opendeviationbar/checkpoint/storage.py` lines 23, 65
    - `python/opendeviationbar/validation/day_mode/oracle.py` lines 48, 90
    - `scripts/detect_gaps.py` line 634

    **Category 2: Config model defaults (DEFAULT-02)**
    - `python/opendeviationbar/config/population.py` line 57: change `Literal["day", "aion"] = "day"` to `= "aion"`
    - `python/opendeviationbar/checkpoint/models.py` line 116: change `ouroboros_mode: str = "day"` to `= "aion"`
    - `python/opendeviationbar/clickhouse/cache.py` line 89: change default to "aion"
    - `python/opendeviationbar/config/cli_models.py` lines 41-42: change default from `"day"` to `"aion"`, AND change `Literal["day"]` to `Literal["day", "aion"]` (the CLI model was restricting to day-only -- must now accept both)

    **Category 3: Fallback lookups (DEFAULT-03)**
    Change `.get(sym/symbol/key, "day")` to `.get(sym/symbol/key, "aion")` in ALL of these:
    - `python/opendeviationbar/sidecar/arrow_flush.py` line 154
    - `python/opendeviationbar/sidecar/__init__.py` lines 995, 1334, 1379
    - `python/opendeviationbar/sidecar/midnight.py` lines 570, 659
    - `python/opendeviationbar/symbol_registry.py` line 332
    - `python/opendeviationbar/checkpoint/storage.py` line 117
    - `python/opendeviationbar/orchestration/count_bounded.py` line 271

    Also change safe-default returns in helper functions:
    - `python/opendeviationbar/backfill.py` line 90: change `return "day"` to `return "aion"` and update docstring on line 83
    - `python/opendeviationbar/kintsugi/_helpers.py` line 76: change `return "day"` to `return "aion"` and update docstring on line 69
    - `scripts/detect_gaps.py` line 125: change `return "day"` to `return "aion"` and update comment on line 108
    - `scripts/heartbeat/checks/yesterday.py` line 53: change both `"day"` fallbacks to `"aion"`

    Also change hardcoded mode assignments in sidecar:
    - `python/opendeviationbar/sidecar/__init__.py` line 224: change `symbol_modes[sym] = "day"` to `= "aion"`
    - `python/opendeviationbar/sidecar/midnight.py` lines 124, 321, 414: change hardcoded `= "day"` to `= "aion"`
    - `python/opendeviationbar/sidecar/midnight.py` line 908: change fallback `"day"` to `"aion"`
    - `python/opendeviationbar/rectify.py` line 68: change `mode = "day"` to `mode = "aion"`

    **Category 4: CLI defaults (DEFAULT-04)**
    - `python/opendeviationbar/cli.py` line 107: change `type=click.Choice(["day"])` to `click.Choice(["day", "aion"])` and add `default="aion"`
    - `scripts/validate_microstructure_roundtrip.py` line 66: change `default="day"` to `default="aion"`
    - `scripts/microstructure_patterns.py` line 255: change `ouroboros="day"` to `ouroboros="aion"`

    **Category 5: Checkpoint engine guard (DEFAULT-02)**
    - `python/opendeviationbar/checkpoint/engine.py` line 26: change `if ouroboros_mode != "day":` guard. This guard was written when only day mode was supported. Now aion is the default, so change to accept both: `if ouroboros_mode not in ("day", "aion"):` and update the docstring on line 24.

    **Category 6: Test assertions that reference the old default (all requirements)**
    Update tests that assert the DEFAULT is "day" to assert "aion" instead. Tests that explicitly pass `ouroboros_mode="day"` to test day-mode BEHAVIOR should be kept as-is (they test the mode itself, not the default).

    Changes needed:
    - `tests/test_config_telemetry.py` line 90: change `config.ouroboros_mode == "day"` to `== "aion"`. Lines 329-339: update the test to use "aion" as the default config value and update result keys.
    - `tests/test_unified_config.py` line 47: change `assert cfg.ouroboros_mode == "day"` to `== "aion"`. Line 82: change `assert s.population.ouroboros_mode == "day"` to `== "aion"`.
    - `tests/test_ouroboros_monthly_e2e.py` line 53: change default param `ouroboros_mode: str = "day"` to `= "aion"`, and update lines 66-67 assertion to expect "aion". Lines 31, 80, 87, 98: these explicitly pass `ouroboros_mode="day"` to test day-mode behavior -- KEEP AS-IS.
    - `tests/test_kintsugi_repair.py`: lines 56, 99, 128, 156, 181, 254 mock `get_operational_ouroboros_mode` to return `"day"` -- change to return `"aion"`. Line 87: change assertion `== "day"` to `== "aion"`.
    - `tests/test_heartbeat_yesterday_junction.py` line 51: change default `ouroboros_mode: str = "day"` to `= "aion"`. Line 463: this explicitly passes `ouroboros_mode="day"` for forex testing -- KEEP AS-IS.

    **Category 7: Documentation (all requirements)**
    - `python/opendeviationbar/CLAUDE.md`: Update the `ouroboros` parameter table row from `"day"` to `"aion"` (line ~430) and any docstring comments that say "Only day supported" to "day and aion supported, default aion".

    **DO NOT change these (keep as-is):**
    - Enum definitions: `DAY = "day"` in `ouroboros.py` line 38, `.pyi` line 7
    - `Literal["day", "aion"]` type annotations (these define the valid VALUES, not defaults)
    - Conditional logic: `if mode == "day":` branches in `backfill.py` (469, 646, 708), `kintsugi/repair.py` (204, 239), `kintsugi/verify.py` (76) -- these are MODE-SPECIFIC behavior, not defaults
    - `sidecar/arrow_flush.py` line 88: `day_symbols = [s for s, m in _modes.items() if m == "day"]` -- this FILTERS for day symbols, not a default
    - `sidecar/__init__.py` line 226: `day_symbols = [s for s, m in symbol_modes.items() if m == "day"]` -- FILTER
    - `bulk_operations.py` lines 169, 207, 256: mode-specific guards checking `== "day"` -- behavior logic, not defaults
    - `validation/day_mode/observation.py` line 69: `modes.get("day", 0) > 0` -- counting modes, not a default
    - `repair_direct_parquet.py` line 308: `if _mode == "day"` -- conditional behavior
    - `scripts/populate_full_cache.py`, `scripts/populate_range.py`: `choices=["day", "aion"]` without a "day" default -- these are just choice lists
    - Test files that explicitly pass `ouroboros_mode="day"` to test day-mode behavior (not testing defaults)
    - `tests/test_ouroboros.py`: ALL instances -- these test the ouroboros module behavior with explicit "day" mode values, not defaults
    - `tests/test_overlap_strategy.py`: explicitly passes `ouroboros_mode="day"` -- tests day-mode behavior
    - `tests/test_compat_bootstrap.py` line 105: `"ouroboros_mode": ["day"]` -- test fixture data, not a default

    **Verification approach (DEFAULT-05):**
    After all changes, verify forex still resolves correctly: the forex entries in `symbols.toml` have `ouroboros_mode = "day"` explicitly, so `get_symbol_entry("EURUSD").ouroboros_mode` returns `OuroborosMode.DAY` regardless of defaults. The new "aion" default only affects cases where NO registry entry exists or where the parameter is not explicitly passed.

  </action>
  <verify>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && grep -rn 'ouroboros_mode.*=.*"day"' python/opendeviationbar/ scripts/ --include="*.py" | grep -v "if.*==.*day" | grep -v "\.value.*==.*day" | grep -v "DAY.*=.*day" | grep -v "Literal\[" | grep -v "# KEEP" | grep -v "choices=" | grep -v "_modes.items()" | grep -v "modes.get.*0)" | grep -v "test_" && echo "FAIL: found remaining day defaults" || echo "PASS: no day defaults remain"</automated>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && uv run --python 3.13 pytest tests/test_ouroboros.py tests/test_config_telemetry.py tests/test_unified_config.py tests/test_ouroboros_monthly_e2e.py tests/test_kintsugi_repair.py tests/test_heartbeat_yesterday_junction.py -x -q 2>&1 | tail -5</automated>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && uv run --python 3.13 pytest tests/ -x -q --ignore=tests/test_e2e_optimized.py --ignore=tests/test_get_n_open_deviation_bars.py 2>&1 | tail -10</automated>
  </verify>
  <acceptance_criteria>
    - `grep -rn '"day"' python/ scripts/ --include="*.py"` shows ZERO hardcoded "day" values used as defaults in function signatures, config models, `.get()` fallbacks, or CLI defaults
    - The ONLY remaining "day" strings are: enum definition (`DAY = "day"`), `Literal["day", "aion"]` type annotations, conditional branches (`if mode == "day"`), filter expressions, and explicit test fixtures testing day-mode behavior
    - All tests pass (updated assertions reflect the new "aion" default)
    - `PopulationConfig().ouroboros_mode` returns `"aion"`
    - `get_operational_ouroboros_mode()` returns `"aion"` when no env override is set
  </acceptance_criteria>
  <done>Every default ouroboros_mode value is "aion". Forex remains day via explicit registry lookup. All tests green.</done>
</task>

</tasks>

<verification>
1. `grep -rn 'ouroboros_mode.*=.*"day"' python/ scripts/ --include="*.py"` -- only enum defs, Literals, conditionals, and explicit test values remain
2. `uv run --python 3.13 pytest tests/ -x -q` -- all tests pass
3. `uv run --python 3.13 -c "from opendeviationbar.config.population import PopulationConfig; print(PopulationConfig().ouroboros_mode)"` prints `aion`
</verification>

<success_criteria>

- Zero "day" defaults remain in function signatures, config models, fallbacks, or CLI defaults across python/ and scripts/
- All existing tests pass with updated assertions
- Forex symbols resolve to "day" via registry lookup, not via default value
  </success_criteria>

<output>
After completion, create `.planning/phases/31-aion-first-defaults/31-01-SUMMARY.md`
</output>
