SKW-2 — the skew capture sends dash-class symbols Alpaca rejects; BRK-B and BF-B have never captured
status: open · raised 2026-09-01 (owner session; observed daily in skew logs since at least 08-28, first flagged 2026-08-29) · class: capture-stream completeness — deliberately a queue row, not a stealth fix, because the remedy changes what a pre-registered stream collects · judgement: owner call · effort ~0.25 pd · horizon: every capture day adds two more permanent per-name notches
Plain-language summary. Every skew snapshot logs
skew: BRK-B chain fetch failed: invalid underlying symbol: BRK-B (and the
same for BF-B). Same root cause as EXQ-1 (2026-08-26): the internal canon
is the dash form, Alpaca wants BRK.B. The trading path got a symbology map
at the broker boundary; the capture path (options_skew.py) still sends the
dash form, so the two class-share names in the 903-name universe fail their
chain fetch daily and have NEVER contributed rows to options_skew.jsonl,
options_chains/, or options_iv_term/.
Why this is not just "apply the fix"
The fix is one line (to_alpaca_symbol at the fetch, reverse-map on write —
the helpers already exist in execution/alpaca_broker.py). But applying it
CHANGES A PRE-REGISTERED CAPTURE STREAM mid-accrual: two names would begin
appearing on the fix date, with a permanent pre-fix hole behind them. The
moat doctrine treats stream semantics as registered, and the B4 skew
activation pin computes cross-name medians and row floors over the stream —
so the change must be:
- dated in
CAPTURES.md(per-name coverage note: BRK-B/BF-B absent<stream start>→<fix date>, present after — a vendor-reject hole, not a collection change); - checked against the B4 activation criteria for sensitivity (adding 2 of ~900 names to medians is almost certainly immaterial — say so with the arithmetic, don't assume it);
- applied to the capture path only (the trading boundary is already fixed).
Test design + kill criterion
Unit: a fake chain client asserting the wire symbol is BRK.B while the
persisted row's underlying is BRK-B; revert the map → red. Live check:
first post-fix capture day shows both names in the day's jsonl and chains
parquet, and the daily chain fetch failed warnings disappear.
Explicitly out of scope
The per-name backfill of the pre-fix hole (no free historical source — the same reason the whole stream is forward-only).
Triage note — 2026-09-07 (annotation only; the body above is untouched)
Premise re-verified from the data, and the row GAINED scope this week.
At HEAD e25f80b, src/thales/data/options_skew.py:280 still sends the
internal dash canon straight to the wire
(OptionChainRequest(underlying_symbol=symbol)), with no to_alpaca_symbol
— the helper that exists and is applied at every trading boundary
(alpaca_broker.py:31, used at :153, :199, :240, :410). Independently
confirmed: zero rows for BRK-B/BF-B (or their dot forms) anywhere in
data/options_skew.jsonl across the whole stream, while BRK-B is in the
universe (data/universe.csv:684). Neither prerequisite has landed —
CAPTURES.md still carries no per-name coverage note for either name.
New scope, not triage's doing: SKW-4's built record explicitly re-routes
its own leg 3 — the per-name coverage census — into this row
(research/queue/built/skw-4-non-session-rows-served-and-per-name-coverage.md:126-129),
naming GHC and NVR at 0 of 62 capture days. So this row now owns four
never-captured names, not two, and the CAPTURES.md dated note it asks for
covers all of them. Effort is still ~0.25 pd; the census leg adds a little.