What happens
derive_size_bands (src/deltatrack/parsers/pdf_anchors.py) returns None when a
document's sub-body heading sizes form more than one strong cluster. Account
detection is glyph-size based, so returning None means the bill gets no
account-level anchors at all — its deepest breadcrumb is TITLE/SEC.
Since #114 retired the For necessary expenses text-trigger fallback (ADR 0018),
there is no second path behind the size bands. Widening their coverage is now the
only way a bill in this state gets its account structure back.
Why it bails
Reconciliation bills print in three heading sizes rather than the two that
appropriations bills use. The clustering step splits the sub-body heading sizes,
finds a second cluster at >=30% of the dominant one, and bails rather than guess
which cluster is the account band:
dominant = max(clusters, key=len)
# More than one *strong* cluster (>=30% of the dominant) => trimodal => bail.
if any(c is not dominant and len(c) >= 0.3 * len(dominant) for c in clusters):
return None
Bailing was the right call when it was written — a wrong band assignment mislabels
every heading in the document, and there was a fallback to land on. The fallback is
now gone, so the cost of bailing has gone up.
Affected bills
On the current 68-PDF corpus this is 119-hr-1 (engrossed-in-house,
placed-on-calendar-senate, engrossed-amendment-senate — ~1,000 pages each). The
other bills that reach the degraded path are unaffected by this issue: 11
enrolled/public-law files are declined outright as unnumbered layouts (#141, and
#261 tracks a pass for them), and 19 introduced/engrossed shells of 3-14 pages
contain no accounts to find.
Measured 2026-08-04 against develop. Note the PDF corpus is smaller than the XML
corpus, so treat the bill list as a floor rather than a census.
What "done" looks like
derive_size_bands returns usable bands for a trimodal document — picking the
account band on evidence rather than on cluster size alone — and 119-hr-1 gains
account anchors, without changing the anchors any currently-working bill produces.
The anchor goldens (test_pdf_anchor_golden.py) are the regression check for that
second half: they must not move.
Worth confirming before building: that reconciliation bills have an account level
worth recovering. 119-hr-1 is not an appropriations bill, and the one anchor the
retired trigger produced on it was a wrapped section-heading fragment, not an
account. It may be that the right band assignment yields sections, not accounts —
which would still be a coverage improvement, just a different one than the title
suggests.
Context
What happens
derive_size_bands(src/deltatrack/parsers/pdf_anchors.py) returnsNonewhen adocument's sub-body heading sizes form more than one strong cluster. Account
detection is glyph-size based, so returning
Nonemeans the bill gets noaccount-level anchors at all — its deepest breadcrumb is
TITLE/SEC.Since #114 retired the
For necessary expensestext-trigger fallback (ADR 0018),there is no second path behind the size bands. Widening their coverage is now the
only way a bill in this state gets its account structure back.
Why it bails
Reconciliation bills print in three heading sizes rather than the two that
appropriations bills use. The clustering step splits the sub-body heading sizes,
finds a second cluster at >=30% of the dominant one, and bails rather than guess
which cluster is the account band:
Bailing was the right call when it was written — a wrong band assignment mislabels
every heading in the document, and there was a fallback to land on. The fallback is
now gone, so the cost of bailing has gone up.
Affected bills
On the current 68-PDF corpus this is
119-hr-1(engrossed-in-house,placed-on-calendar-senate, engrossed-amendment-senate — ~1,000 pages each). The
other bills that reach the degraded path are unaffected by this issue: 11
enrolled/public-law files are declined outright as unnumbered layouts (#141, and
#261 tracks a pass for them), and 19 introduced/engrossed shells of 3-14 pages
contain no accounts to find.
Measured 2026-08-04 against
develop. Note the PDF corpus is smaller than the XMLcorpus, so treat the bill list as a floor rather than a census.
What "done" looks like
derive_size_bandsreturns usable bands for a trimodal document — picking theaccount band on evidence rather than on cluster size alone — and
119-hr-1gainsaccount anchors, without changing the anchors any currently-working bill produces.
The anchor goldens (
test_pdf_anchor_golden.py) are the regression check for thatsecond half: they must not move.
Worth confirming before building: that reconciliation bills have an account level
worth recovering.
119-hr-1is not an appropriations bill, and the one anchor theretired trigger produced on it was a wrapped section-heading fragment, not an
account. It may be that the right band assignment yields sections, not accounts —
which would still be a coverage improvement, just a different one than the title
suggests.
Context
no fallback behind the size path