---
phase: 01-yesterday-integrity-check-signal-file
plan: 01
subsystem: sidecar-startup
tags: [startup, integrity-check, signal-file, stathera]
dependency_graph:
  requires: []
  provides: [yesterday-signal-file, check-yesterday-integrity]
  affects: [sidecar-startup, kintsugi-priority-queue]
tech_stack:
  added: []
  patterns: [atomic-file-write, stathera-audit-query]
key_files:
  created:
    - python/opendeviationbar/sidecar/yesterday_signal.py
    - tests/test_yesterday_integrity.py
  modified:
    - python/opendeviationbar/sidecar/midnight.py
    - python/opendeviationbar/sidecar/__init__.py
decisions:
  - "Renamed signal.py to yesterday_signal.py to avoid stdlib signal module collision in sidecar package"
  - "Used local import for write_yesterday_signal in _create_engine() to avoid module-level import shadowing"
  - "Added noqa ARG001 for symbols/thresholds params in _check_yesterday_integrity: kept for API consistency though only preflight_verified is iterated"
metrics:
  duration: 14m45s
  completed: "2026-03-23T08:24:00Z"
  tasks_completed: 2
  tasks_total: 2
  files_created: 2
  files_modified: 2
  test_count: 12
---

# Phase 01 Plan 01: Yesterday Integrity Check + Signal File Summary

Fast integrity check replacing 4h blocking yesterday repair with <5s check + signal file for kintsugi handoff.

## One-Liner

Replace \_repair_yesterday_if_needed() with \_check_yesterday_integrity() (orphan TID + Stathera audit per symbol) writing atomic signal file to /var/tmp for kintsugi pickup.

## What Was Done

### Task 1: Create yesterday_signal.py +\_check_yesterday_integrity() (TDD)

Created `python/opendeviationbar/sidecar/yesterday_signal.py` with:

- `SIGNAL_FILE_PATH = "/var/tmp/opendeviationbar-yesterday-repair.json"`
- `write_yesterday_signal(flagged, sidecar_pid)`: Atomic write via tempfile+rename in /var/tmp
- `read_yesterday_signal()`: Returns parsed JSON dict or None if missing

Created `_check_yesterday_integrity()` in `midnight.py`:

- For each symbol in preflight_verified: orphan check + Stathera audit
- Orphan check: compares `cache.get_yesterday_orphan_tid(symbol)` against `crossover_tid - 1`
- Stathera audit: single CH query per symbol across ALL thresholds for yesterday (PARTITION BY threshold_decimal_bps, LIMIT 1)
- Uses shared `stathera_query.py` builder (STATHERA_CORE_COLUMNS, FULL_TABLE, VALID_TID_FILTER)
- Returns `{symbol: reason}` dict with 5 reason types
- CH unavailable gracefully flags all symbols as "ch_unavailable"

12 tests covering all paths in `tests/test_yesterday_integrity.py`.

**Commit:** bdcb289

### Task 2: Wire into sidecar startup + remove repair path

Updated `sidecar/__init__.py`:

- Replaced `_repair_yesterday_if_needed` import with `_check_yesterday_integrity`
- Replaced call site at Step 2 in `_create_engine()` with check+signal pattern
- Signal file written only when flagged_symbols is non-empty
- Local import of `write_yesterday_signal` to avoid stdlib `signal` module collision

**Commit:** 89cda4b

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Renamed signal.py to yesterday_signal.py**

- **Found during:** Task 2
- **Issue:** Creating `sidecar/signal.py` shadowed Python's stdlib `signal` module. `run_sidecar()` uses `signal.signal(signal.SIGHUP, ...)` which broke with `AttributeError: module 'opendeviationbar.sidecar.signal' has no attribute 'signal'`
- **Fix:** Renamed module to `yesterday_signal.py` and used local import in `_create_engine()` to avoid any module-level shadowing
- **Files modified:** `yesterday_signal.py` (renamed from signal.py), `__init__.py`, `tests/test_yesterday_integrity.py`
- **Commit:** 89cda4b

## Decisions Made

1. **Module naming**: `yesterday_signal.py` instead of `signal.py` to avoid stdlib collision. The plan specified `signal.py` but Python's package system makes any submodule named `signal` shadow the stdlib module.

2. **Import strategy**: Local import of `write_yesterday_signal` inside `_create_engine()` rather than module-level to prevent any residual shadowing risk.

3. **ARG001 noqa for signature params**: `symbols` and `thresholds` parameters kept in `_check_yesterday_integrity()` signature for API consistency with the replaced `_repair_yesterday_if_needed()`, even though only `preflight_verified` is iterated.

## Verification Results

- 12/12 new tests pass (signal file + integrity check)
- `_repair_yesterday_if_needed` removed from `sidecar/__init__.py` (zero grep matches)
- `_check_yesterday_integrity` present at import (line 138) and call site (line 265)
- `write_yesterday_signal` present at import (line 269) and call site (line 271)
- `from opendeviationbar.sidecar.yesterday_signal import write_yesterday_signal, read_yesterday_signal, SIGNAL_FILE_PATH` imports OK
- Existing sidecar watchdog test `test_watchdog_triggers_after_timeout` passes

## Known Stubs

None. All functions are fully implemented with real logic.

## Self-Check: PASSED

- FOUND: python/opendeviationbar/sidecar/yesterday_signal.py
- FOUND: tests/test_yesterday_integrity.py
- FOUND: commit bdcb289
- FOUND: commit 89cda4b
