fix: scope suppressed-dollar measurement to the calling verb's own flow family (#9)

Final whole-branch review fix wave for the partial-coverage signposting
feature:

- I1: .suppressed_components() now filters measured recipe components to
  the calling verb's own flow_prefixes. Without this, a candidate recipe
  from the OTHER flow family was always absent from the verb's own view by
  construction and so was always reported as "suppressed" -- fabricating a
  dollar claim across flow families (cog_revenue(category = "Corrections")
  claimed $3.63B excluded that cog_spending() actually reports in full).
- I2: reworded provenance-v1.json's trigger/suppressed_amount descriptions
  to describe what the code actually measures (the verb's underlying long
  view, not "the result"), and to note suppressed_amount can be negative.
  Added ig_recipe_id to the suggestions items' required list, matching the
  key's always-set/nullable runtime behavior.
- I3(a): restated the year/govid literals inside .suppressed_components()'s
  NOT EXISTS subquery so DuckDB can partition-prune that side too (verified
  via EXPLAIN: Scanning Files 1/4 instead of an unfiltered full scan;
  all.equal(old, new) results confirmed unchanged).
- I3(b): added .needs_suppression_query(), a free, exact pre-check reusing
  the verb's own already-computed result$codes_included to skip the anti-
  join round trip on the common fully-covered path, without weakening the
  "suppression can fire with zero gap years" guarantee.
- M4: corrected the overbroad "confines every fire to 2011" scope claim in
  R/suggestions.R and NEWS.md -- the suppressed-dollar measurement is now
  flow-scoped (post-I1), but the empty_year trigger itself is not, and can
  still fire in modern years for a mis-scoped cross-flow-family category.

Added a regression test for I1 plus direct unit-test coverage for the new
flow-family filter and the I3(b) pre-check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-04 23:22:52 -04:00
co-authored by Claude Opus 5
parent f77adb6c83
commit 4b749205a5
4 changed files with 252 additions and 18 deletions
+15 -4
View File
@@ -17,10 +17,21 @@
FY2011 `Miscellaneous Revenue` reported $943,842,000 while dropping
$1,899,995,000 of aggregate-published `U4-` rents and royalties.
* The trigger stays recipe-driven, so it only fires where a harmonization
recipe actually exists to name the fix. Measured on the bundled fixture,
every fire lands in the wide era; `higher_ed_e18_wide` and
`general_gov_e89_wide` stay silent, because their components are ordinary
classified leaves even pre-2012.
recipe actually exists to name the fix. `higher_ed_e18_wide` and
`general_gov_e89_wide` stay silent in every year measured on the bundled
fixture, because their components are ordinary classified leaves even
pre-2012.
* The `suppressed_component` trigger (and any `suppressed_amount`/
`suppressed_codes` an `empty_year` fire also carries) is scoped to the
calling verb's own flow family: `cog_spending()` only ever measures E/F/G
component dollars, `cog_revenue()` only T/A/U/B/C/D. A component from the
OTHER flow family reports `suppressed_amount = 0` rather than a fabricated
claim. The `empty_year` trigger itself is not flow-scoped -- a category
belonging to the other flow (e.g. `cog_spending(category = "IG Local")`)
still returns zero rows and can still fire, in any year including modern
ones, naming the recipe whose own generic join finds real data for this
government. That is a mis-scoped query, not a corpus-format gap, so its
`suppressed_amount` is correctly 0.
## New: `cog_balances()` for cash-and-security holdings