> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sequency.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Lineage warnings triage

# Runbook — `lineage_warnings` Triage (#527)

## What this is

`debate_recommendation_outcomes.lineage_warnings` is an `Array(LowCardinality(String))` column populated at write time from a frozen vocabulary (4 active contamination + 2 informational Pass 3 + 2 reserved-deferred tags). Empty array means no detected contamination.

A live-emission v5 row (`confluence_version` ∈ `{'v5', 'v5_partial'}`) with **contamination** warnings (`net_sign_mismatch`, `zero_triple`, `magnitude_outlier_v5`, `conviction_inconsistent`) is the silent-leak detection cohort — what should never happen if the v5 pipeline is healthy. Smoke D gates exclusively on this cohort.

Pass 3 reconstructed rows (`v5_reconstructed`, `v5_partial_reconstructed`) carry only informational tags (`reconstructed`, optionally `v5_source_unknown`) — Pass 3 does NOT call `derive_lineage_warnings`, so these rows are invisible to Smoke D by construction. Surface them in cohort filtering for calibration jobs (filter out reconstructed cohort to avoid forward-looking-bias contamination of v5 calibration). Activating contamination surveillance on Pass 3 rebuilds is a Phase 1.5 follow-up.

## Vocabulary

### Active contamination warnings (Phase 1)

| Tag                       | Trigger                                                         | First-look location                                                                     |
| ------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `net_sign_mismatch`       | `sign(net_v5)` ≠ `sign(bull_v5 - bear_v5)`                      | Check the upstream parser at `stock_parser.py:684-712` for column-mapping drift         |
| `zero_triple`             | v5 row has bull = bear = net = 0.0                              | Parser dropped values silently — check `confluence_v5.bull/bear/net` at the source      |
| `magnitude_outlier_v5`    | a v5 component is outside \[0,100] (or net outside \[-100,100]) | Scale bleed — likely a v3 0–100 unidirectional value stuffed into a v5 directional slot |
| `conviction_inconsistent` | `conviction_score > 0` but all v5 components 0 or NULL          | Meta-composite computed against a non-v5 input — see `worker_conviction.go:107-167`     |

### Informational tags (Pass 3 reconstruction; NOT contamination)

| Tag                 | Trigger                                                 | On-call action                                                                                      |
| ------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `reconstructed`     | Pass 3 filler rebuild                                   | None — Smoke D excludes these from the alarm cohort. Filter rows out of forward-return calibration. |
| `v5_source_unknown` | Pass 3 rebuild from pre-#497 history with no source tag | None — pair appears with `reconstructed`. Same handling.                                            |

### Reserved (deferred to Phase 1.5)

These tags are in `LINEAGE_WARNING_VOCAB` but never emitted from Phase 1 derivation. Activation requires data outside the orchestrator hot path (prev-day v43 lookup, cross-table tier observation). Tracked in the Phase 1.5 follow-up issue.

| Tag                       | Planned trigger                                                               | Activation notes                                                                                        |
| ------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `stale_v5_with_fresh_v43` | (bull, bear, net) bit-identical to yesterday's v43 triple for the same symbol | Phase 1.5 adds a prev-day-lookup helper that the orchestrator calls outside the synchronous debate path |
| `tier_threshold_mismatch` | v5 row's `confluence_tier` carries a v3-thresholded label                     | Phase 1.5 activates after #556 lands the versioned `confluence_tier_v5` column                          |

## Response when smoke-d fires

1. Query the offending rows (matches the Smoke D gate exactly — live-emission v5 family + hasAny on contamination tags):
   ```sql theme={null}
   SELECT symbol, debate_timestamp, confluence_version, lineage_warnings,
          confluence_bull_v5, confluence_bear_v5, confluence_net_v5
   FROM sequency.debate_recommendation_outcomes
   WHERE debate_timestamp > now() - INTERVAL 1 DAY
     AND confluence_version IN ('v5', 'v5_partial')
     AND hasAny(lineage_warnings, ['net_sign_mismatch', 'zero_triple', 'magnitude_outlier_v5', 'conviction_inconsistent'])
   ORDER BY debate_timestamp DESC
   LIMIT 100;
   ```
2. Bucket by warning tag (most common first).
3. If a single tag dominates (>80% of rows), follow that tag's "First-look" location above.
4. If multiple tags co-occur on the same rows, the producer is upstream of all of them — likely the parser. Check `stock_parser.py` recently-shipped changes (`git log -- api/app/services/stock/`).
5. File a tracking P0 referencing the smoke-d issue. Roll back the most recent debate\_outcome\_writer or stock\_parser deploy if no fix in \<2h.

## Adding new vocab

1. Add tag to `LINEAGE_WARNING_VOCAB` in `api/app/services/debate/lineage_warnings.py`.
2. Add detection logic in `derive_lineage_warnings`.
3. Add a row to the table above + a "First-look" location.
4. Cardinality ceiling 32 — if you're approaching it, audit existing tags first.
