fix(sec): A3 sign-off hardening — validate per-concept units structure

- Extend the companyfacts structural check to reject a concept with a
  missing/non-dict `units` mapping (not just the top-level `facts`), so a
  partially-malformed payload fails promotion instead of silently dropping that
  concept's facts. New fixture proves it fails.
- Strengthen the newly-added-issuer test: keep latest_index equal to the prior
  run so ONLY the universe fingerprint changes the revision — proving the
  fingerprint alone prevents a new ticker from being starved/no_op'd.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 20:04:11 +02:00
co-authored by Claude Opus 4.8
parent 8dcdcac2a6
commit 96d3b40560
2 changed files with 46 additions and 4 deletions
+28 -2
View File
@@ -322,10 +322,13 @@ async def test_newly_added_issuer_backfills_without_filing(engine):
789019: {"cik": 789019, "sic": "7372", "sic_description": "Prepackaged Software",
"filings": [_filing("M", "10-K", "2025-06-30", "2025-07-30", "2025-07-30T10:00:00.000Z")]},
},
latest_index=date(2026, 2, 3),
# SAME index date as the prior run and no filing: only the universe
# fingerprint (MSFT added) changes the revision, so this proves the
# fingerprint alone prevents starvation.
latest_index=date(2026, 1, 31),
daily={}, # MSFT did not file
)
run = await run_import(_importer(incr, today=date(2026, 2, 4)), engine=engine)
run = await run_import(_importer(incr, today=date(2026, 2, 1)), engine=engine)
assert run.status == STATUS_PROMOTED
async with factory() as s:
msft = (await s.execute(
@@ -355,6 +358,29 @@ async def test_malformed_companyfacts_fails_validation(engine):
assert await _count(factory, FundamentalSnapshot) == 0 # nothing promoted
async def test_missing_units_structure_fails_validation(engine):
factory = _factory(engine)
await _seed(factory, ["AAPL", "MSFT"])
client = FakeSecClient(
tickers={"AAPL": 320193, "MSFT": 789019},
companyfacts={
320193: _companyfacts([CF_K, CF_Q1], [SH_K, SH_Q1]),
# facts present, but a concept is missing its units mapping
789019: {"cik": 789019, "facts": {"us-gaap": {"Revenues": {"label": "Revenues"}}}},
},
submissions={
320193: _submissions(SUB_FILINGS),
789019: {"cik": 789019, "sic": "7372", "sic_description": "x", "filings": []},
},
latest_index=date(2026, 1, 31),
)
run = await run_import(_importer(client), engine=engine)
assert run.status == STATUS_FAILED
assert "malformed" in (run.error_details or "")
assert "units" in (run.validation_json or "")
assert await _count(factory, FundamentalSnapshot) == 0
async def test_discrepancy_in_shares_is_detected_and_reported(engine):
from app.models.system_event import SystemEvent
utc = timezone.utc