---
phase: 04-python-sql-rename
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - python/opendeviationbar/clickhouse/bar_count_api.py
  - python/opendeviationbar/clickhouse/bulk_operations.py
  - python/opendeviationbar/clickhouse/cache.py
  - python/opendeviationbar/clickhouse/maintenance.py
  - python/opendeviationbar/clickhouse/migrations.py
  - python/opendeviationbar/clickhouse/query_operations.py
  - python/opendeviationbar/clickhouse/schema.sql
  - python/opendeviationbar/clickhouse/stathera_query.py
  - python/opendeviationbar/constants.py
  - python/opendeviationbar/exness.py
  - python/opendeviationbar/kintsugi/discovery.py
  - python/opendeviationbar/kintsugi/repair.py
  - python/opendeviationbar/kintsugi/verify.py
  - python/opendeviationbar/orchestration/helpers.py
  - python/opendeviationbar/orchestration/open_deviation_bars.py
  - python/opendeviationbar/processors/core.py
  - python/opendeviationbar/rectify.py
  - python/opendeviationbar/sidecar/__init__.py
  - python/opendeviationbar/sidecar/checkpoint.py
  - python/opendeviationbar/sidecar/http_server.py
  - python/opendeviationbar/sidecar/midnight.py
  - python/opendeviationbar/validation/day_mode/guards.py
  - python/opendeviationbar/validation/day_mode/oracle.py
  - scripts/cache_status.py
  - scripts/detect_gaps.py
  - scripts/heartbeat/checks/clickhouse.py
  - scripts/heartbeat/checks/freshness.py
  - scripts/heartbeat/checks/stathera.py
  - scripts/heartbeat/checks/yesterday.py
  - scripts/microstructure_clickhouse.py
  - scripts/migrate_column_rename_us.py
  - scripts/migrate_partition_yyyymm_to_yyyymmdd.sql
  - scripts/repair_direct_parquet.py
  - scripts/upload_eurusd_to_clickhouse.py
  - scripts/validate_microstructure_roundtrip.py
  - scripts/validate_plugin_e2e.py
  - tests/test_exchange_sessions.py
  - tests/test_rectification.py
  - tests/test_sync_flush_atomic_replace.py
autonomous: true
requirements:
  - RENAME-02
  - RENAME-04
  - RENAME-05

must_haves:
  truths:
    - "Zero occurrences of close_time_ms in Python source files"
    - "Zero occurrences of open_time_ms in Python source files"
    - "Zero occurrences of close_time_ms or open_time_ms in SQL strings"
    - "mise run check-full passes with zero failures"
  artifacts:
    - path: "python/opendeviationbar/constants.py"
      provides: "Column name constants using _us suffix"
      contains: "close_time_us"
    - path: "python/opendeviationbar/clickhouse/bulk_operations.py"
      provides: "INSERT column lists using _us naming"
      contains: "close_time_us"
    - path: "python/opendeviationbar/clickhouse/query_operations.py"
      provides: "SELECT queries using _us naming"
      contains: "close_time_us"
  key_links:
    - from: "python/opendeviationbar/constants.py"
      to: "python/opendeviationbar/clickhouse/bulk_operations.py"
      via: "column name constants imported"
      pattern: "close_time_us"
    - from: "python/opendeviationbar/clickhouse/query_operations.py"
      to: "ClickHouse open_deviation_bars table"
      via: "SQL queries referencing _us column names"
      pattern: "close_time_us"
---

<objective>
Rename all `close_time_ms` -> `close_time_us`, `open_time_ms` -> `open_time_us`, and `last_close_time_ms` -> `last_close_time_us` across 38 Python/SQL files (244 occurrences). This is a mechanical find-and-replace completing the v4.0 column rename chain (Rust Phase 2, CH Phase 3, now Python/SQL Phase 4).

Purpose: After this plan, the codebase speaks one language -- every reference matches the actual microsecond unit stored in ClickHouse and emitted by Rust.
Output: 38 files updated, `mise run check-full` passing.
</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/04-python-sql-rename/04-CONTEXT.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Bulk rename all _ms column references to _us across 38 files</name>
  <files>
    python/opendeviationbar/clickhouse/bar_count_api.py,
    python/opendeviationbar/clickhouse/bulk_operations.py,
    python/opendeviationbar/clickhouse/cache.py,
    python/opendeviationbar/clickhouse/maintenance.py,
    python/opendeviationbar/clickhouse/migrations.py,
    python/opendeviationbar/clickhouse/query_operations.py,
    python/opendeviationbar/clickhouse/schema.sql,
    python/opendeviationbar/clickhouse/stathera_query.py,
    python/opendeviationbar/constants.py,
    python/opendeviationbar/exness.py,
    python/opendeviationbar/kintsugi/discovery.py,
    python/opendeviationbar/kintsugi/repair.py,
    python/opendeviationbar/kintsugi/verify.py,
    python/opendeviationbar/orchestration/helpers.py,
    python/opendeviationbar/orchestration/open_deviation_bars.py,
    python/opendeviationbar/processors/core.py,
    python/opendeviationbar/rectify.py,
    python/opendeviationbar/sidecar/__init__.py,
    python/opendeviationbar/sidecar/checkpoint.py,
    python/opendeviationbar/sidecar/http_server.py,
    python/opendeviationbar/sidecar/midnight.py,
    python/opendeviationbar/validation/day_mode/guards.py,
    python/opendeviationbar/validation/day_mode/oracle.py,
    scripts/cache_status.py,
    scripts/detect_gaps.py,
    scripts/heartbeat/checks/clickhouse.py,
    scripts/heartbeat/checks/freshness.py,
    scripts/heartbeat/checks/stathera.py,
    scripts/heartbeat/checks/yesterday.py,
    scripts/microstructure_clickhouse.py,
    scripts/migrate_column_rename_us.py,
    scripts/migrate_partition_yyyymm_to_yyyymmdd.sql,
    scripts/repair_direct_parquet.py,
    scripts/upload_eurusd_to_clickhouse.py,
    scripts/validate_microstructure_roundtrip.py,
    scripts/validate_plugin_e2e.py,
    tests/test_exchange_sessions.py,
    tests/test_rectification.py,
    tests/test_sync_flush_atomic_replace.py
  </files>
  <read_first>
    All 38 files listed above. Read each file, then apply the three replacements using the Edit tool's replace_all feature.
  </read_first>
  <action>
    For each of the 38 files, perform three mechanical replacements in this exact order:

    1. `last_close_time_ms` -> `last_close_time_us` (do this FIRST to avoid partial matches)
    2. `close_time_ms` -> `close_time_us`
    3. `open_time_ms` -> `open_time_us`

    Order matters: replace `last_close_time_ms` before `close_time_ms` to prevent `last_close_time_ms` from becoming `last_close_time_us` via a partial `close_time_ms` -> `close_time_us` match that leaves `last_` prefix orphaned. (Actually both produce the correct result, but doing longest-first is defensive.)

    Process files by subsystem for manageable diffs:

    **Batch 1 -- clickhouse/ (7 files, ~84 occurrences):**
    bar_count_api.py, bulk_operations.py, cache.py, maintenance.py, migrations.py, query_operations.py, stathera_query.py

    **Batch 2 -- schema.sql (1 file, 4 occurrences in comments):**
    schema.sql -- these are in `--` SQL comments (migration notes). Rename for consistency.

    **Batch 3 -- core python/ (11 files, ~75 occurrences):**
    constants.py, exness.py, kintsugi/discovery.py, kintsugi/repair.py, kintsugi/verify.py, orchestration/helpers.py, orchestration/open_deviation_bars.py, processors/core.py, rectify.py, validation/day_mode/guards.py, validation/day_mode/oracle.py

    **Batch 4 -- sidecar/ (4 files, ~21 occurrences):**
    sidecar/__init__.py, sidecar/checkpoint.py, sidecar/http_server.py, sidecar/midnight.py

    **Batch 5 -- scripts/ (9 files, ~48 occurrences):**
    cache_status.py, detect_gaps.py, heartbeat/checks/clickhouse.py, heartbeat/checks/freshness.py, heartbeat/checks/stathera.py, heartbeat/checks/yesterday.py, microstructure_clickhouse.py, migrate_column_rename_us.py, migrate_partition_yyyymm_to_yyyymmdd.sql, repair_direct_parquet.py, upload_eurusd_to_clickhouse.py, validate_microstructure_roundtrip.py, validate_plugin_e2e.py

    **Batch 6 -- tests/ (3 files, ~13 occurrences):**
    test_exchange_sessions.py, test_rectification.py, test_sync_flush_atomic_replace.py

    Per RENAME-02: all Python references updated.
    Per RENAME-04: all SQL query strings updated.

    After all replacements, verify zero remaining occurrences:
    ```bash
    grep -rn "close_time_ms\|open_time_ms\|last_close_time_ms" --include="*.py" --include="*.sql" | grep -v '.claude/'
    ```
    This MUST return zero lines. If any remain, fix them.

  </action>
  <verify>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && grep -rn "close_time_ms\|open_time_ms\|last_close_time_ms" --include="*.py" --include="*.sql" | grep -v '.claude/' | wc -l | xargs test 0 -eq</automated>
  </verify>
  <acceptance_criteria>Zero occurrences of close_time_ms, open_time_ms, or last_close_time_ms in any .py or .sql file (excluding .claude/ directories).</acceptance_criteria>
  <done>All 244 occurrences across 38 files renamed. grep confirms zero _ms column name references remain.</done>
</task>

<task type="auto">
  <name>Task 2: Run check-full to verify zero regressions (RENAME-05)</name>
  <files>None (verification only -- may need to fix files from Task 1 if issues found)</files>
  <read_first>None -- this is a verification task.</read_first>
  <action>
    Run the full quality gate:
    ```bash
    mise run check-full
    ```

    This runs fmt + lint + test (Rust cargo nextest + Python pytest). All must pass.

    Per RENAME-05: `mise run check-full` passes after all renames.

    If any test fails:
    - Read the failing test file
    - Determine if the failure is a missed rename (fix with another replace_all)
    - Or if it reveals a deeper issue (e.g., a fixture producing old column names from Rust -- unlikely since Phase 2 already renamed Rust output)

    Common failure modes to watch for:
    - String literals in tests that hardcode `_ms` column names in expected outputs
    - Dict key access like `row["close_time_ms"]` in test assertions
    - SQL query strings in test fixtures

    If check-full passes on first run, task is complete.

  </action>
  <verify>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && mise run check-full</automated>
  </verify>
  <acceptance_criteria>mise run check-full exits 0 -- all formatting, linting, and tests pass with zero failures.</acceptance_criteria>
  <done>Full quality gate passes. The codebase has zero _ms column references and zero test regressions.</done>
</task>

</tasks>

<verification>
1. `grep -rn "close_time_ms\|open_time_ms\|last_close_time_ms" --include="*.py" --include="*.sql" | grep -v '.claude/'` returns zero lines
2. `mise run check-full` exits 0
3. Spot-check: `grep -n "close_time_us" python/opendeviationbar/constants.py` shows renamed constants
4. Spot-check: `grep -n "close_time_us" python/opendeviationbar/clickhouse/bulk_operations.py` shows renamed INSERT columns
</verification>

<success_criteria>

- RENAME-02: All Python references (~232) updated from \_ms to_us -- verified by grep returning zero \_ms hits
- RENAME-04: All SQL queries (~60) updated from \_ms to_us -- verified by same grep
- RENAME-05: `mise run check-full` passes -- zero regressions from the bulk rename
  </success_criteria>

<output>
After completion, create `.planning/phases/04-python-sql-rename/04-01-SUMMARY.md`
</output>
