revenue_concept='general' silently returns own-source only for all 35,040 local governments: IG revenue is aggregate-only and dropped by revenue_long #38
Closed
opened 2026-08-05 12:36:40 -04:00 by jared
·
1 comment
No Branch/Tag Specified
main
ci/mirror-canonical-tags
chore/release-47-badges-mirror-pr
docs/readme-perf-remeasure-56
feat/pagination-search-balances-57
feat/duckdb-threads-60
feat/cohort-predicates-58
fix/windows-backslash-paths
ci/mirror-to-github
ci/github-actions-matrix
feat/public-release-0.3.0
chore/fixture-sb203
ci/apt-https
fix/pushdown-pagination
feat/all-categories-37
fix/partial-coverage-signposting-9
fix/schema-v7
fix/cog-categories-balance-subtype
feat/cog-balances-25
feat/revenue-concepts-12
feat/expenditure-concepts-11
feat/coverage-disclosure-13
feat/complete-argument-18
fix/kodor-batch-14-15-16
fix/all-scoped-series-breaks-19
fix/regen-fixture-corpus-18
test/walkthrough-findings
feat/expenditure-concept
fix/3-url-trailing-slash
feat/phase-r3-signposting
fix/fixture-option-b-aggregates
feat/phase-r2-harmonization
feat/phase-r1-forward
feat/cog-gov-search-basket-mode
v0.4.0
Labels
Clear labels
kodor
kodor/feature-proposal
kodor/fix
kodor/needs-review
kodor/triaged
madison-walkthrough
severity/high
severity/low
severity/medium
south-guide
verdict/defect
verdict/definitional
kodor
kodor/feature-proposal
kodor/fix
kodor/needs-review
kodor/triaged
Kodor should process this issue
Kodor has written a feature proposal
Kodor should implement a fix (assigned to Kodor)
Kodor's work or failure needs Jared's review
Kodor has already triaged this issue (skip)
Surfaced while building the client-facing Southern API guide
needs
human
Cannot move without a person -- a decision, a check an agent cannot make, something outside the repo
origin
client
Came from a client ask
origin
obligation
Created by a change elsewhere
origin
review
Came from human review
origin
roborev
Promoted from a roborev finding
type
chore
Maintenance with no behaviour change
type
debt
Owed work -- docs, tests, cleanup a change obligated
type
decision
Needs a decision before work can proceed
type
defect
Something is wrong
type
feature
New capability
ws
api
Query verbs and results
ws
corpus
Corpus, mirror, provenance
ws
docs
Vignettes and guides
Assign a task to kodor
Kodor thinks this needs a feature.
Kodor should fix this
Kodor thinks the user is ready to review this.
Kodor is done with this issue.
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Civilytics/uscogdata#38
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found while building the client-facing Southern API guide (
cog_explorer/docs/superpowers/specs/2026-08-05-south-api-guide-design.md). Measured against the full published corpus 2026-08-05. Verdict: defect.Symptom
cog_revenue(..., revenue_concept = "general")returns own-source revenue only for every local government. Thefederal,stateandlocal_aidsubtypes come back empty — silently, with no suggestion and no provenance note — even thoughgeneralis defined as their sum.Nine large Southern cities, FY2022 and FY2024, via the deployed API:
Identical at FY2024. A city receiving zero federal and state aid is not a credible figure.
Asking for the IG categories directly returns nothing:
basis=rawreturns the same thing, so this is not the harmonization layer.Root cause
The dollars are in the corpus. Atlanta FY2022:
All three are in
summary_categories—B89under IG Federal,C89under IG State,D89under IG Local. Butinst/sql/21-revenue_long.sqlfilters:so every one of them is dropped before
revenue_annotatedever sees it.Magnitude
Corpus-wide, FY2022, item-code prefixes B/C/D:
is_aggregateThe 50 survivors are the states — they report leaf-level IG codes. Every county, city and township reports intergovernmental revenue only at the aggregate
*89level, so all of it is dropped.US cities alone, FY2022: B $59.38B + C $80.13B + D $17.33B = $156.84B excluded, across 9,270–17,196 cities per family. Not one non-aggregate B/C/D row exists for any city that year.
Why this is not covered by the existing aggregate rule
cog_pipeline/docs/reader-specification.md§3.1 and §4 discuss this pattern at length and state theNOT is_aggregaterule is "exactly right for Direct". But that discussion is about legacy-era spending (M/Lfamilies, the Total concept), and it says explicitly that those families' "leaves first appear in the modern era."That premise does not hold here. This is modern-era revenue (FY2022, FY2024), and for local governments the B/C/D leaves do not appear at all — the aggregate is the only row that exists. So the rule that correctly prevents double-counting when leaves exist instead deletes the entire quantity when they do not.
The spec's own remedy for the spending case — assemble year-scoped via recipes — has no revenue counterpart.
Why it is a contract violation, not just a coverage caveat
revenue_concept = "general"is documented asown_source + federal + state + local_aid, mirroring Census's published concept. Returning own-source alone is not a narrower answer to the question asked; it is a different quantity presented under the same name. For Atlanta FY2022 the reported general revenue of $1,627,842,000 omits $392,819,000 that the corpus holds and the crosswalk classifies.Nothing signals it:
suggestionsis[], and nonotesfield on any returned row mentions the exclusion.Suggested
(government, year)exist only on aggregate rows, excluding them yields zero rather than preventing a double count — the filter should be conditional on whether leaves exist, not unconditional.suggestionsentry or anotesvalue on the affected query, so a caller sees that a defined component of the requested concept was dropped. Silence is the part that makes this dangerous.revenue_concept = "general"should refuse to answer for governments where the federal/state/local_aid legs are entirely unavailable, rather than returning a number that reads as complete.Impact on current work
This blocks a chart in the Southern API guide ("revenue mix — own-source vs IG Federal vs IG State share"), and it understates the guide's headline revenue-per-capita measure for every county and city. That is the small version of the problem; the general version is that any consumer computing local-government revenue from this corpus is currently missing roughly $425B a year with no indication.
Related:
cog-api#37 (all-categories totals) — an all-categories revenue total inherits this exclusion silently too.Withdrawing this. It is not a defect — the behaviour is a documented, deliberate ruling with a working remedy, and I filed it without reading the ruling first.
What I got wrong
cog_pipeline/docs/phase_r_harmonization_review.md§0.2 namesB89, C89, D89explicitly among the codes that areis_aggregate = TRUE, and rules:So
revenue_long'sNOT is_aggregateis the ruling working as intended, not an oversight. §2 then ships the remedy — fiveig_{federal,state,local}_?89_wide/ige_{state,local}_?89_widerecipes — and §0.3 specifies that.build_suggestions()should surface them.All three parts verified working
The recipe returns the money:
And the signposting fires, naming the right recipe and the exact amount:
Those are precisely the three figures I reported as "silently dropped". They are not silent; I did not look where the signpost is emitted.
The actual mistake
I queried
revenue_concept="general"without acategory..build_suggestions()returns early whencategoryisNULL(R/suggestions.R:68) — a deliberate pre-existing design decision — so an unscoped query yields no suggestions. I read that documented silence as an undisclosed exclusion and generalised it to "$424.88B dropped for 35,040 governments with no indication".The dollar figures in the original report are arithmetically correct. The characterisation built on them is not: the money is reachable, classified, and signposted whenever the caller scopes the query the way the signposting was designed for.
One thing genuinely improved by adjacent work
uscogdata PR #37 (
feat/all-categories-37) extends the same signposting to the all-categories path, which previously could never fire because"All Categories"is not a crosswalk row. Verified against the full corpus:So a caller asking for a revenue total now gets the disclosure automatically.
Residual, if anything
The only thing left that is arguably worth tracking is the unscoped (
category = NULL) case staying silent — which is a deliberate decision, not a defect, and is now the one remaining shape where a caller can total local-government revenue without meeting the signpost. Not reopening for it; noting it in case that decision is ever revisited.Closing. My apologies for the noise — this is exactly the case
phase_r_harmonization_review.mdexists to prevent, and I should have read it before filing.