---
phase: 01-mutation-safety
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
  - python/opendeviationbar/rectify.py
  - python/opendeviationbar/kintsugi/discovery.py
  - scripts/cache_clear.py
  - scripts/validate_dedup_hardening.py
  - scripts/validate_microstructure_roundtrip.py
  - scripts/cleanup_oversized_bars.py
  - scripts/purge_crypto_low_threshold.py
  - scripts/cleanup_extra_symbols.py
  - scripts/validate_kintsugi_fixes.py
autonomous: true
requirements:
  - MUT-01

must_haves:
  truths:
    - "Every ALTER TABLE DELETE on open_deviation_bars uses DELETE IN PARTITION syntax"
    - "kintsugi_log DELETEs remain as ALTER TABLE DELETE (no partition key available)"
    - "All DELETEs include SETTINGS mutations_sync = 1"
  artifacts:
    - path: "python/opendeviationbar/rectify.py"
      provides: "Ghost bar DELETE using partition scope"
      contains: "DELETE IN PARTITION"
    - path: "scripts/cache_clear.py"
      provides: "Symbol clear using partition-scoped DELETEs"
      contains: "DELETE IN PARTITION"
  key_links:
    - from: "all modified files"
      to: "ClickHouse open_deviation_bars table"
      via: "DELETE IN PARTITION (symbol, threshold) syntax"
      pattern: "DELETE IN PARTITION"
---

<objective>
Migrate all remaining ALTER TABLE DELETE call sites to DELETE IN PARTITION for the open_deviation_bars table. Document kintsugi_log exception.

Purpose: Complete the MUT-01 audit (per D-01) -- belt-and-suspenders, no unscoped DELETEs anywhere in the codebase for open_deviation_bars.
Output: 8 files migrated to partition-scoped DELETEs, kintsugi_log sites documented as intentional exception.
</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/01-mutation-safety/01-CONTEXT.md
@.planning/phases/01-mutation-safety/01-RESEARCH.md

@python/opendeviationbar/rectify.py
@python/opendeviationbar/kintsugi/discovery.py
@scripts/cache_clear.py
@scripts/validate_dedup_hardening.py
@scripts/validate_microstructure_roundtrip.py
@scripts/cleanup_oversized_bars.py
@scripts/purge_crypto_low_threshold.py
@scripts/cleanup_extra_symbols.py

<interfaces>
<!-- Canonical DELETE IN PARTITION pattern from maintenance.py:325 -->
```python
self.client.command(
    "ALTER TABLE opendeviationbar_cache.open_deviation_bars "
    f"DELETE IN PARTITION ('{symbol}', {threshold_decimal_bps}) "
    f"WHERE first_agg_trade_id <= {new_last} "
    f"AND last_agg_trade_id >= {new_first} "
    "AND first_agg_trade_id > 0 "
    "SETTINGS mutations_sync = 1"
)
```

<!-- Partition key: (symbol, threshold_decimal_bps) -->
<!-- String value quoted with single quotes, int not quoted -->
<!-- DELETE IN PARTITION does NOT support parameterized partition tuples -->
<!-- WHERE clause can use parameters where applicable -->
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Migrate rectify.py and kintsugi/discovery.py</name>
  <files>python/opendeviationbar/rectify.py, python/opendeviationbar/kintsugi/discovery.py</files>
  <read_first>
    - python/opendeviationbar/rectify.py (lines 220-255 -- the _delete_ghost_bar function with ALTER TABLE DELETE)
    - python/opendeviationbar/kintsugi/discovery.py (lines 440-453 -- kintsugi_log DELETE in aletheia purge)
    - .planning/phases/01-mutation-safety/01-RESEARCH.md (rectify.py migration example + kintsugi_log exception)
  </read_first>
  <action>
    **rectify.py** (site #4 from research):

    In `_delete_ghost_bar()` (around line 240), change the DELETE from:
    ```python
    cache.client.command(
        "ALTER TABLE opendeviationbar_cache.open_deviation_bars "
        "DELETE WHERE symbol = {symbol:String} "
        "  AND threshold_decimal_bps = {threshold:UInt32} "
        "  AND ouroboros_mode = {mode:String} "
        "  AND close_time_ms = {close_time_ms:Int64}",
        parameters={...},
    )
    ```

    To partition-scoped:
    ```python
    cache.client.command(
        "ALTER TABLE opendeviationbar_cache.open_deviation_bars "
        f"DELETE IN PARTITION ('{symbol}', {threshold_decimal_bps}) "
        "WHERE ouroboros_mode = {mode:String} "
        "  AND close_time_ms = {close_time_ms:Int64} "
        "SETTINGS mutations_sync = 1",
        parameters={
            "mode": ouroboros_mode,
            "close_time_ms": close_time_ms,
        },
    )
    ```

    Note: symbol and threshold_decimal_bps are function parameters (not from user input), safe for f-string interpolation. The remaining WHERE params use clickhouse-connect parameterized queries.

    **kintsugi/discovery.py** (site #12 from research):

    The `kintsugi_log` DELETE at line 443 targets a table with NO `PARTITION BY` clause. Per the research: "kintsugi_log uses MergeTree() with ORDER BY ... and no PARTITION BY clause. ClickHouse places all data in a single implicit partition. The DELETE is already fast because the table is tiny."

    Add a comment above the DELETE explaining why it remains as `ALTER TABLE DELETE`:
    ```python
    # kintsugi_log has no PARTITION BY clause (single implicit partition).
    # ALTER TABLE DELETE is correct here -- table is tiny (90-day TTL).
    # DELETE IN PARTITION cannot be used without an explicit partition key.
    ```

    Also add `SETTINGS mutations_sync = 1` if not already present (verify -- the research notes it may be missing).

  </action>
  <verify>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && grep -n "DELETE IN PARTITION" python/opendeviationbar/rectify.py && echo "---" && grep -n "mutations_sync" python/opendeviationbar/rectify.py && echo "---" && grep -n "no PARTITION BY" python/opendeviationbar/kintsugi/discovery.py</automated>
  </verify>
  <acceptance_criteria>
    - rectify.py contains `DELETE IN PARTITION ('{symbol}', {threshold_decimal_bps})`
    - rectify.py contains `SETTINGS mutations_sync = 1`
    - rectify.py does NOT contain `DELETE WHERE symbol = {symbol:String}` (old unscoped pattern)
    - kintsugi/discovery.py contains comment explaining kintsugi_log has no PARTITION BY clause
    - kintsugi/discovery.py kintsugi_log DELETE has `mutations_sync = 1` (added if missing)
  </acceptance_criteria>
  <done>rectify.py uses partition-scoped DELETE. kintsugi_log exception documented with comment explaining why ALTER TABLE DELETE is correct for that table.</done>
</task>

<task type="auto">
  <name>Task 2: Migrate 6 scripts to DELETE IN PARTITION</name>
  <files>scripts/cache_clear.py, scripts/validate_dedup_hardening.py, scripts/validate_microstructure_roundtrip.py, scripts/cleanup_oversized_bars.py, scripts/purge_crypto_low_threshold.py, scripts/cleanup_extra_symbols.py, scripts/validate_kintsugi_fixes.py</files>
  <read_first>
    - scripts/cache_clear.py (line 48 -- symbol-only DELETE, needs partition query)
    - scripts/validate_dedup_hardening.py (line 110 -- test symbol DELETE)
    - scripts/validate_microstructure_roundtrip.py (line 295 -- test symbol DELETE)
    - scripts/cleanup_oversized_bars.py (line 96 -- has symbol + threshold in context)
    - scripts/purge_crypto_low_threshold.py (line 109 -- symbol DELETE)
    - scripts/cleanup_extra_symbols.py (line 114 -- symbol DELETE)
    - scripts/validate_kintsugi_fixes.py (lines 83, 231 -- kintsugi_log DELETEs)
    - .planning/phases/01-mutation-safety/01-RESEARCH.md (cache_clear.py migration example, test script pitfall #4)
  </read_first>
  <action>
    Migrate each script following the canonical DELETE IN PARTITION pattern. All modifications add `SETTINGS mutations_sync = 1`.

    **scripts/cache_clear.py** (site #5):
    Symbol-only DELETE (all thresholds). Query distinct partitions first, then delete each:
    ```python
    # Query active partitions for this symbol
    partitions = cache.client.query(
        "SELECT DISTINCT threshold_decimal_bps "
        "FROM opendeviationbar_cache.open_deviation_bars FINAL "
        f"WHERE symbol = '{symbol}'"
    ).result_rows
    for (thr,) in partitions:
        cache.client.command(
            f"ALTER TABLE {table} "
            f"DELETE IN PARTITION ('{symbol}', {thr}) "
            f"WHERE symbol = '{symbol}' "
            "SETTINGS mutations_sync = 1"
        )
    ```

    **scripts/validate_dedup_hardening.py** (site #6):
    Test symbol `__TEST__`. Read the file to find what threshold is used for test data insertion, then use that same threshold in DELETE IN PARTITION. If threshold is known (e.g., 250), use:
    ```python
    cache.client.command(
        "ALTER TABLE opendeviationbar_cache.open_deviation_bars "
        f"DELETE IN PARTITION ('__TEST__', {test_threshold}) "
        "WHERE symbol = '__TEST__' "
        "SETTINGS mutations_sync = 1"
    )
    ```

    **scripts/validate_microstructure_roundtrip.py** (site #7):
    Same pattern as validate_dedup_hardening -- uses a test symbol. Find the threshold used in the INSERT, use it in DELETE IN PARTITION.

    **scripts/cleanup_oversized_bars.py** (site #8):
    Already has symbol and threshold in context. Change:
    ```python
    # FROM: ALTER TABLE ... DELETE WHERE {where_sql}
    # TO:   ALTER TABLE ... DELETE IN PARTITION ('{symbol}', {threshold}) WHERE {remaining_where} SETTINGS mutations_sync = 1
    ```

    **scripts/purge_crypto_low_threshold.py** (site #10):
    Symbol-level DELETE. Same approach as cache_clear.py -- query thresholds, loop DELETE IN PARTITION. Or if the script already knows which thresholds to purge, use those directly.

    **scripts/cleanup_extra_symbols.py** (site #11):
    Same as purge_crypto_low_threshold.py -- query thresholds for symbol, loop DELETE IN PARTITION.

    **For validate_kintsugi_fixes.py** (site #9, lines 83 and 231):
    These DELETE `kintsugi_log` table (not `open_deviation_bars`). kintsugi_log has no PARTITION BY. Add a comment explaining why ALTER TABLE DELETE is correct (same as discovery.py), and add `SETTINGS mutations_sync = 1` if missing. Do NOT try to use DELETE IN PARTITION.

  </action>
  <verify>
    <automated>cd /Users/terryli/eon/opendeviationbar-py && echo "=== open_deviation_bars unscoped DELETEs remaining ===" && grep -rn "ALTER TABLE.*open_deviation_bars.*DELETE" scripts/ python/ | grep -v "IN PARTITION" | grep -v "if False" | grep -v "kintsugi_log" | grep -v "test_mutation" | grep -v "__pycache__" || echo "NONE_FOUND"</automated>
  </verify>
  <acceptance_criteria>
    - scripts/cache_clear.py uses DELETE IN PARTITION with threshold loop
    - scripts/validate_dedup_hardening.py uses DELETE IN PARTITION with test threshold
    - scripts/validate_microstructure_roundtrip.py uses DELETE IN PARTITION with test threshold
    - scripts/cleanup_oversized_bars.py uses DELETE IN PARTITION
    - scripts/purge_crypto_low_threshold.py uses DELETE IN PARTITION
    - scripts/cleanup_extra_symbols.py uses DELETE IN PARTITION
    - All modified scripts include SETTINGS mutations_sync = 1
    - Zero unscoped ALTER TABLE DELETE on open_deviation_bars in entire codebase (grep returns empty)
    - validate_kintsugi_fixes.py kintsugi_log DELETEs have comment explaining no-PARTITION-BY exception
  </acceptance_criteria>
  <done>All open_deviation_bars DELETE call sites use DELETE IN PARTITION. Only kintsugi_log DELETEs remain as ALTER TABLE DELETE (documented exception). MUT-01 fully satisfied across entire codebase.</done>
</task>

</tasks>

<verification>
- `grep -rn "ALTER TABLE.*open_deviation_bars.*DELETE" python/ scripts/ | grep -v "IN PARTITION" | grep -v "if False" | grep -v "__pycache__"` returns empty (zero unscoped DELETEs on open_deviation_bars)
- `grep -rn "DELETE IN PARTITION" python/ scripts/ | grep -v __pycache__` shows all migrated sites
- `grep -rn "mutations_sync" python/opendeviationbar/rectify.py scripts/cache_clear.py scripts/validate_dedup_hardening.py scripts/validate_microstructure_roundtrip.py scripts/cleanup_oversized_bars.py scripts/purge_crypto_low_threshold.py scripts/cleanup_extra_symbols.py` confirms all sites have mutations_sync = 1
- kintsugi_log DELETEs in discovery.py and validate_kintsugi_fixes.py have explanatory comments
</verification>

<success_criteria>

- MUT-01 complete: zero unscoped ALTER TABLE DELETE on open_deviation_bars anywhere in the codebase
- All kintsugi_log exception sites documented
- All DELETEs have mutations_sync = 1
  </success_criteria>

<output>
After completion, create `.planning/phases/01-mutation-safety/01-02-SUMMARY.md`
</output>
