---
phase: 01-test-hygiene
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - python/opendeviationbar/validation/schemas.py
  - tests/test_data_contracts.py
  - tests/test_heal_scoping.py
autonomous: true
requirements: [TEST-01, TEST-02]

must_haves:
  truths:
    - "BarOutputSchema expects open_time_us and close_time_us columns (not _ms)"
    - "test_data_contracts.py passes with the updated schema"
    - "test_heal_scoping.py no longer exists in the test suite"
    - "pytest runs with zero unexpected failures from these two test files"
  artifacts:
    - path: "python/opendeviationbar/validation/schemas.py"
      provides: "BarOutputSchema with _us column names"
      contains: "open_time_us"
    - path: "tests/test_data_contracts.py"
      provides: "Schema validation tests against _us columns"
      contains: "open_time_us"
  key_links:
    - from: "tests/test_data_contracts.py"
      to: "python/opendeviationbar/validation/schemas.py"
      via: "BarOutputSchema import"
      pattern: "from opendeviationbar.validation.schemas import BarOutputSchema"
---

<objective>
Fix test suite hygiene: rename schema columns from `_ms` to `_us` so `test_data_contracts.py` passes, and delete the dead `test_heal_scoping.py` file.

Purpose: Establish a clean `check-full` baseline before the bulk `_ms` to `_us` rename in Phases 2-4.
Output: Zero test failures from schema mismatch or dead code references.
</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

@python/opendeviationbar/validation/schemas.py
@tests/test_data_contracts.py
@tests/test_heal_scoping.py

<interfaces>
<!-- Key types the executor needs -->

From python/opendeviationbar/validation/schemas.py:

```python
class BarOutputSchema(pa.DataFrameModel):
    # Current (WRONG — says _ms but values are microseconds):
    open_time_ms: int = pa.Field(ge=_TS_US_MIN, le=_TS_US_MAX)
    close_time_ms: int = pa.Field(ge=_TS_US_MIN, le=_TS_US_MAX)
    # Must become:
    # open_time_us: int = pa.Field(ge=_TS_US_MIN, le=_TS_US_MAX)
    # close_time_us: int = pa.Field(ge=_TS_US_MIN, le=_TS_US_MAX)
```

From tests/test_data_contracts.py (already uses \_us naming):

```python
def _valid_bar_df(**overrides):
    defaults = {
        "open_time_us": [...],
        "close_time_us": [...],
        ...
    }
```

</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Rename BarOutputSchema columns from _ms to _us and update docstring (TEST-01)</name>
  <files>python/opendeviationbar/validation/schemas.py</files>
  <read_first>python/opendeviationbar/validation/schemas.py</read_first>
  <action>
In `python/opendeviationbar/validation/schemas.py`, rename the two timestamp fields in `BarOutputSchema`:

1. `open_time_ms` -> `open_time_us` (line 35)
2. `close_time_ms` -> `close_time_us` (line 36)

Keep the `pa.Field(ge=_TS_US_MIN, le=_TS_US_MAX)` bounds unchanged — they already validate microsecond range.

Update the class docstring (line 27) to say `_us` suffix instead of `_ms` suffix:

- Change "missing \_us suffix detected" reference to be consistent
- Change "ClickHouse expects open_time_ms/close_time_ms" to "ClickHouse expects open_time_us/close_time_us"

Update the module-level docstring in `schemas.py` line 7 to replace "(ms not us)" with "(microsecond scale)".

Do NOT touch `_TS_US_MIN` or `_TS_US_MAX` constants — they are correct.
</action>
<verify>
<automated>cd /Users/terryli/eon/opendeviationbar-py && uv run --python 3.13 pytest tests/test_data_contracts.py -x -v 2>&1 | tail -30</automated>
</verify>
<acceptance_criteria>All tests in test_data_contracts.py pass. BarOutputSchema fields are named open_time_us and close_time_us.</acceptance_criteria>
<done>BarOutputSchema expects \_us column names, all test_data_contracts.py tests pass green.</done>
</task>

<task type="auto">
  <name>Task 2: Delete test_heal_scoping.py (TEST-02)</name>
  <files>tests/test_heal_scoping.py</files>
  <read_first>tests/test_heal_scoping.py</read_first>
  <action>
Delete the file `tests/test_heal_scoping.py` entirely using `git rm tests/test_heal_scoping.py`.

This file contains two test classes:

- `TestHealDayBoundaryGapsScoping` — tests `_heal_day_boundary_gaps()` which still exists, but references stale `open_time_ms` column names in mock assertions. These tests will break during the Phase 4 rename anyway.
- `TestOverlapHealRemoved` — tests that `_check_and_heal_overlaps` is absent from sidecar, which is dead-code verification that no longer needs a permanent test.

Both classes are test debt. Remove the entire file.
</action>
<verify>
<automated>cd /Users/terryli/eon/opendeviationbar-py && test ! -f tests/test_heal_scoping.py && echo "DELETED" || echo "STILL EXISTS"</automated>
</verify>
<acceptance_criteria>tests/test_heal_scoping.py does not exist on disk. pytest collection does not reference it.</acceptance_criteria>
<done>test_heal_scoping.py is deleted from the repository.</done>
</task>

</tasks>

<verification>
Run the full test suite to confirm no regressions:

```bash
cd /Users/terryli/eon/opendeviationbar-py && uv run --python 3.13 pytest tests/test_data_contracts.py tests/ -x --ignore=tests/test_heal_scoping.py -q 2>&1 | tail -20
```

Verify no stale references:

```bash
grep -r "test_heal_scoping" tests/ 2>/dev/null | grep -v __pycache__ || echo "No references"
grep -rn "open_time_ms\|close_time_ms" python/opendeviationbar/validation/schemas.py || echo "No _ms references in schema"
```

</verification>

<success_criteria>

1. `pytest tests/test_data_contracts.py -x` passes (all tests green)
2. `tests/test_heal_scoping.py` does not exist
3. `BarOutputSchema` in schemas.py uses `open_time_us` and `close_time_us` field names
4. No `_ms` references remain in schemas.py column definitions
   </success_criteria>

<output>
After completion, create `.planning/phases/01-test-hygiene/01-01-SUMMARY.md`
</output>
