Skip to content

fix(holdings): reject broken fresh holdings with the shared holdings guard - #83

Merged
daggerok merged 1 commit into
mainfrom
fix/holdings-guard
Oct 25, 2026
Merged

daggerok merged 1 commit into
mainfrom
fix/holdings-guard

Conversation

@daggerok

Copy link
Copy Markdown
Owner

What

  • Appends the shared holdingsGuard block (.claude/tools/etfs/etf-std/holdings-guard.block.ts, applied with apply-holdings-guard.ts, not hand-edited) to scripts/update-data.ts
  • Adds an empty HOLDINGS_GUARD_RELAX table next to HOLDINGS_PAGE_SIZE_FALLBACK (every published SPDR fund passes today)
  • Wires the guard into processFund right after a freshly downloaded holdings workbook is parsed, before any other fetch or write of the fund. The previous count is the previous meta.json holdings.totalRows, relax is HOLDINGS_GUARD_RELAX[ticker] ?? HOLDINGS_GUARD_RELAX['*'] ?? []. A rejection throws holdings guard: <reason>, so the fund goes through the existing failed path: nothing of it is written, it keeps its published files and index row and is reported failed
  • The 404 path of commodity trusts (GLD, GLDM) is unchanged and not guarded (nothing was fetched)

Why

Same rollout as daggerok/Schwab#71 (ETF-H1): a renamed Weight header or a header-only workbook used to be written over the published sheet (for SPY: 505 rows replaced by an empty or weightless sheet)

Checks run

  • bun test: 38 pass, 0 fail, exit 0; same with the config values exported as env: 38 pass, exit 0
  • bun build --target=bun scripts/update-data.ts --outfile=/dev/null: ok
  • git diff --check: clean
  • tc/check.sh: esnext 0 errors, ide 51 errors, both the same as on main
  • New tests: the Schwab parsing guard test verbatim; a pipeline test where SPY gets a renamed Weight header and then a header-only workbook: SPY's files and index row stay byte-identical and its result line says failed ... reason=holdings guard: .... With the throw disabled the pipeline test fails for both cases (renamed sheet written with a Weighting column; header-only sheet written as totalRows: 0, status: empty)
  • Real data, read-only (published pages in api/spdr): SPY (505 rows), SPAB (8788), SPTL, BIL pass; SPY and SPAB with a renamed weight header or no rows are rejected; GLD and GLDM (0 rows, no headers) pass; all 187 published funds over all 13 header layouts (equity, fixed income, money market MMK, futures) pass

…guard

Broken holdings (renamed Weight header, header-only sheet, non-numeric weights, bad weight sum, half the rows gone) no longer overwrite the published sheet: the fund fails and keeps its previous state (ETF-H1)
@daggerok
daggerok merged commit 2535ae3 into main Oct 25, 2026
1 check passed
@daggerok
daggerok deleted the fix/holdings-guard branch October 25, 2026 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant