Symptom
frontend/public/data/stocks/STZ.json (Constellation Brands, Consumer Staples, rank 308) has market_cap: null on the 2026-05-14 cron.
{
"ticker": "STZ",
"current_price": 151.18,
"market_cap": null,
"has_history": true,
"raw_metrics": {
"shares_outstanding": null,
...
},
"data_quality": {
"missing_metrics": ["eps_basic", "eps_diluted", "shares_outstanding", "research_and_development"],
"latest_period_end": "2026-02-28",
"latest_filed_date": "2026-04-22",
"filing_lag_days": 28
},
"risk_flags": []
}
Surfaced by the stock-detail-auditor subagent's Step 2 deterministic prefilter (SCHEMA category — market_cap ≤ 0 or None) on its dry-run during PR #175.
Root cause hypothesis
XBRL fact extraction (compute/ingest/fundamentals.py) failed to pull 4 per-share / count metrics from STZ's 10-Q filed 2026-04-22 (covering period ending 2026-02-28):
eps_basic
eps_diluted
shares_outstanding
research_and_development
With shares_outstanding: None, market_cap = current_price × shares_outstanding cannot be computed → null flows to output JSON → frontend /stock/STZ page renders a partial detail.
Related to #10 (shares_outstanding wrong for ~12 tickers), but STZ is a different failure mode — not "wrong value" but "no value at all". Possibly the XBRL fact name STZ uses for shares-outstanding differs from the _FUNDAMENTALS_REQUIRED_ATTRS manifest.
Why data_quality_input_corruption did NOT fire
risk_flags: [] — the Step 7.5 sanity guard in scoring did not flag this, even though data_quality.missing_metrics lists 4 missing fields. The current data_quality_input_corruption threshold appears to require both wrong-value AND mcap drift; missing-value paths are not caught.
This is issue #18 territory (composite scoring doesn't yet respect the data-quality flag) plus a sub-issue: the flag itself isn't being emitted on the "all None" failure mode.
Proposed investigation order
- Pull STZ's 10-Q XBRL directly via
edgartools and check the fact names for shares-outstanding (dei:EntityCommonStockSharesOutstanding vs us-gaap:CommonStockSharesOutstanding vs share-class–scoped variants — Constellation Brands has Class A / Class B share classes).
- If fact name differs, extend the manifest in
compute/ingest/fundamentals.py (and bump _FUNDAMENTALS_REQUIRED_ATTRS drift-detector test).
- Add fallback: if XBRL doesn't yield
shares_outstanding, scrape from 10-Q cover-page text (the SEC requires a count there).
- Tighten
data_quality_input_corruption to fire on "all None across critical metrics" paths so this is visible without an auditor pass.
Reproduction
python3 -c "
import json
d = json.load(open('frontend/public/data/stocks/STZ.json'))
print('market_cap:', d.get('market_cap'))
print('shares_outstanding:', (d.get('raw_metrics') or {}).get('shares_outstanding'))
print('missing_metrics:', d.get('data_quality',{}).get('missing_metrics'))
"
Tickers to cross-check (audit candidates)
Run the stock-detail-auditor Step 2 prefilter across the full universe — STZ is the only SCHEMA market_cap: null on this cron, but the dual-share-class hypothesis (Class A + Class B) might affect: BRK.B (already special-cased?), GOOG / GOOGL, FOX / FOXA, NWS / NWSA, DISH-class tickers if any. Worth confirming none silently degraded.
Out of scope for this issue
- Fair-price ensemble extreme estimates on growth-heavy stocks (separate issue — see follow-up filed in same audit)
extended_top5 Rule 16 invariant (passed on this cron)
Owner
Unassigned. Pickup ladder: edgar-debugger for fact-name debug → patch in compute/ingest/fundamentals.py + tests/test_ingest/.
Symptom
frontend/public/data/stocks/STZ.json(Constellation Brands, Consumer Staples, rank 308) hasmarket_cap: nullon the 2026-05-14 cron.{ "ticker": "STZ", "current_price": 151.18, "market_cap": null, "has_history": true, "raw_metrics": { "shares_outstanding": null, ... }, "data_quality": { "missing_metrics": ["eps_basic", "eps_diluted", "shares_outstanding", "research_and_development"], "latest_period_end": "2026-02-28", "latest_filed_date": "2026-04-22", "filing_lag_days": 28 }, "risk_flags": [] }Surfaced by the
stock-detail-auditorsubagent's Step 2 deterministic prefilter (SCHEMA category —market_cap ≤ 0 or None) on its dry-run during PR #175.Root cause hypothesis
XBRL fact extraction (
compute/ingest/fundamentals.py) failed to pull 4 per-share / count metrics from STZ's 10-Q filed 2026-04-22 (covering period ending 2026-02-28):eps_basiceps_dilutedshares_outstandingresearch_and_developmentWith
shares_outstanding: None,market_cap = current_price × shares_outstandingcannot be computed →nullflows to output JSON → frontend/stock/STZpage renders a partial detail.Related to #10 (
shares_outstandingwrong for ~12 tickers), but STZ is a different failure mode — not "wrong value" but "no value at all". Possibly the XBRL fact name STZ uses for shares-outstanding differs from the_FUNDAMENTALS_REQUIRED_ATTRSmanifest.Why
data_quality_input_corruptiondid NOT firerisk_flags: []— the Step 7.5 sanity guard in scoring did not flag this, even thoughdata_quality.missing_metricslists 4 missing fields. The currentdata_quality_input_corruptionthreshold appears to require both wrong-value AND mcap drift; missing-value paths are not caught.This is issue #18 territory (composite scoring doesn't yet respect the data-quality flag) plus a sub-issue: the flag itself isn't being emitted on the "all None" failure mode.
Proposed investigation order
edgartoolsand check the fact names for shares-outstanding (dei:EntityCommonStockSharesOutstandingvsus-gaap:CommonStockSharesOutstandingvs share-class–scoped variants — Constellation Brands has Class A / Class B share classes).compute/ingest/fundamentals.py(and bump_FUNDAMENTALS_REQUIRED_ATTRSdrift-detector test).shares_outstanding, scrape from 10-Q cover-page text (the SEC requires a count there).data_quality_input_corruptionto fire on "all None across critical metrics" paths so this is visible without an auditor pass.Reproduction
Tickers to cross-check (audit candidates)
Run the
stock-detail-auditorStep 2 prefilter across the full universe — STZ is the only SCHEMAmarket_cap: nullon this cron, but the dual-share-class hypothesis (Class A + Class B) might affect: BRK.B (already special-cased?), GOOG / GOOGL, FOX / FOXA, NWS / NWSA, DISH-class tickers if any. Worth confirming none silently degraded.Out of scope for this issue
extended_top5Rule 16 invariant (passed on this cron)Owner
Unassigned. Pickup ladder:
edgar-debuggerfor fact-name debug → patch incompute/ingest/fundamentals.py+tests/test_ingest/.