RecycLA audit · How we made this

A methods explainer · 3 minutes

How we made this —
and what should change

18.5% – 24.1%

confirmed diverted — a range, not a number

We wanted to answer one question: where does LA’s commercial waste go? The records exist; the City receives them every month. This is the story of why the honest answer is a range — and the five sentences of reporting law that would change that.

What we found →

or just scroll

Step one · parse

The records arrive

A Public Records Act request produced one spreadsheet: three years of RecycLA hauler tonnage reports — 4,828,942 tons, once parsed. Step one was discovering that 25,486 lines of text are 20,518 rows of data — some fields contain line breaks, so rows wrap mid-record.

Every count in this piece had to be earned like that. A ledger accounts for all 20,518 parsed rows — 100%, no silent drops.

Real rows from the raw file (safe columns only; ⏎ marks an embedded line break as received):
PeriodRSPZoneBinCommodityFacilityNext destinationTons
1/1/22AthensF-HBBlackMixed Solid Waste (MSW)Athens Services (Industry)Mid-Valley Landfill30.08
1/1/22AthensF-WLABlackMixed Solid Waste (MSW)Athens Sun Valley MRF & TSChiquita Canyon Landfill348.65
1/1/22CalMetF-EDTBlackMixed Solid Waste (MSW)Central Los Angeles Recycling Center & Transfer Station620.71
1/1/22CalMetF-EDTBlueCommingled RecyclablesUniversal Resource Recovery230.09
1/1/22NASAF-DTBlackMixed Solid Waste (MSW)Puente Hills Materials Recovery FacilityFrank G Bowerman 38.3
1/1/22NASAF-DTBlackMixed Solid Waste (MSW)Central Los Angeles Recycling Center & Transfer StationEl Sobrante and Chiquita Canyon 967.14
1/1/22UWSF-NEGreenFood WasteSun Valley Recycling Park28.69
1/1/22WMF-SEVBlueCommingled RecyclablesSimi Valley Landfill and Recycling CenterSun Valley Recycle Park0.59
1/1/24AthensF-HBGreenMixed OrganicsDowney Area Recycling and Transfer Facility (DART)Crown Recycling Services 193.12
Raw file: 25,486 text lines (wc -l) → 20,518 parsed rows (embedded newlines rejoined).
docs/methodology.md §2 · csv_outputs/row_ledger.csv (SHA-256-pinned raw file)

The first surprise

Half the spreadsheet is a mirror

Exactly half the rows — 10,259 of 20,518 — aren’t loads at all. They’re a zone × bin summary pivot table appended below the data, with no marker saying so.

We reconciled all 45 of its cell totals against the real rows: they match to the cent, cell by cell; the panel’s grand total is 4,828,072.27. The export is internally honest — but a machine-readable format would not make you guess which half is data.

  • F-DT · Black: summary-panel cell 244607.89 = sum of its 271 kept rows, 244,607.89 t.
  • F-DT · Blue: summary-panel cell 44344.33 = sum of its 154 kept rows, 44,344.33 t.
  • F-EDT · Black: summary-panel cell 78320.78 = sum of its 209 kept rows, 78,320.78 t.
  • Studio-F-WLA · Green: summary-panel cell 841.45 = sum of its 62 kept rows, 841.45 t.
Kept rows 10,259 · summary-pivot rows 10,259 · all 45 pivot cells reconcile to the cent.
docs/audit/row_reconciliation.md · docs/methodology.md §2

Step two · names

The name cloud

Facilities are reported as free text. The same physical building arrives under five spellings; some strings name no findable facility at all.

The funnel: 197 distinct raw spellings120 after case and whitespace → 89 normalized destinations → 56 canonical facilities. A residue never resolves: 46,859 tons (0.97%) stays unknown.

Matching names to real facilities is where the next two problems were hiding.

  • Sun Valley Recycling Park (546 rows, 295,414.46 t), Sun Valley Paper Stock MRF and TS (128 rows, 39,044.93 t), Sun Valley Recycle Park (72 rows, 2,705.03 t), WM - Sun Valley (1 rows, 346.70 t), WM Sun Valley Recycling Park (1 rows, 10.60 t) → sun valley mrf; family total 337,521.72 t.
  • Frank R. Bowerman Landfill (310 rows, 189,290.97 t), Frank R Bowerman (120 rows, 135,694.94 t), Frank G Bowerman (20 rows, 26,606.37 t), Frank Bowerman (26 rows, 2,888.51 t), Frank Bowerman Landfill (2 rows, 73.60 t) → frank r bowerman landfill; family total 354,554.39 t.
  • EL SOBRANTE LANDFILL (106 rows, 39,529.22 t), El Sobrante Landfill/ end use markets (1 rows, 270.40 t), El Sobrante and Chiquita Canyon (59 rows, 42,164.57 t) → el sobrante landfill; family total 81,964.19 t.
  • Southeast Resource Recovery Facility (SERRF) (205 rows, 126,972.35 t), SERRF (2 rows, 2,896.28 t), Southeast Resource Recovery Facility (1 rows, 1,553.00 t) → southeast resource recovery facility; family total 131,421.63 t.
  • Puente Hills Material Recovery Facility (215 rows, 48,491.16 t), Puente Hills Materials Recovery Facility (335 rows, 25,914.44 t), Puente Hills MRF (37 rows, 3,953.39 t), PHMRF (3 rows, 286.51 t) → puente hills mrf; family total 78,645.50 t.
  • The Orex (66 rows, 37,276.57 t), OREX (1 rows, 48.10 t) no findable facility — stays unknown; family total 37,324.67 t.
Name funnel: 197 raw → 1208956 facilities; unresolved 46,859 t.
recomputed from the raw PRA file, normalized_waste_data and standardized_waste_data

Case study 1 · the mis-merge

The wrong facility got the credit

For most of this project’s life, 319,202.70 tons (216 report lines) recorded at a Republic transfer station in Gardena were filed under the name of an Athens compost facility in Victorville — a free-text synonym merge, made in this project’s own pipeline, that state facility records (SWIS) contradicted. Another 3,703.76 t of American Reclamation’s rode the same canonical name.

We caught it by cross-checking every name merge against SWIS — verdicts on the 16 unverified merges: 4 confirmed, 8 likely, 1 not found, 3 contradicted — and reversed the merge on July 4. Pre-fix, 365,071.03 t (7.56%) sat under SWIS-contradicted names.

The diversion math was unaffected — facility types were locked before names were merged — but the audit would have named the wrong facility. A required facility ID on each report line would have made this error impossible to write and trivial to catch. It was a naming artifact, not misconduct — by anyone.

Before: 319,202.70 t filed under the wrong facility name. After the SWIS cross-check: merge reversed 2026-07-04.
docs/audit/contradicted_merges_impact.md §3a · docs/audit/external_crosscheck.md Part 2

Case study 2 · the ‘and’

Two landfills, one string

59 report lines name two different landfills in one cell — el sobrante and chiquita canyon — seventy miles apart, different operators. The reports never say how much went to each.

This project’s legacy pipeline silently gave all 42,164.57 tons to one site. The audit now splits it 5.00% / 95.00% in proportion to each site’s unambiguous tonnage (39,799.62 t vs 755,724.97 t), keeping the original string as provenance; the file grows 10,25910,318 rows. Our split is a documented policy, not knowledge.

One row per facility would have made this a non-event.

El Sobrante 2,109.47 t (5.00%) · Chiquita Canyon 40,055.10 t (95.00%).
docs/methodology.md, owner decisions of 2026-07-05 · split recomputed from standardized_waste_data.csv

Case study 3 · the coin-flip

One name, two facilities — and a judgment worth 7 points

The reverse problem: one canonical name, sun valley mrf, was quietly holding two separately permitted facilities748 rows / 337,521.72 t, splittable 298,476.79 t (WM Sun Valley Recycling Park) vs 39,044.93 t (Sun Valley Paper Stock MRF & TS). No load ever arrived under the literal name. With the mis-merge included, 702,592.75 t (14.55%) sat under conflated or contradicted names pre-fix.

And whether a facility with “MRF and TS” in its state-registry name counts as diversion is a typing judgment worth +5.31 to +6.99 points of the headline rate. Two such facilities are typed oppositely — the sun-valley buckets are typed transfer (not diverted) while athens sun valley mrf ts is typed mrf (diverted) — a judgment that lives in a config file, inherited from the original curated mapping, not in any regulation.

One name → two SWIS facilities (298,476.79 t / 39,044.93 t); typing swing +5.31 to +6.99 pp on D2.
docs/audit/contradicted_merges_impact.md §1 · docs/methodology.md §5.2

The blind spot

The database designed not to see

To check the transfer wedge we used the state’s own facility database, RDRS. Of 1,072,909 transfer tons, 941,811 t (87.78%) matched. It corroborated the black-bin story almost perfectly: 666,440.50 t at est. 98.88% → landfill.

But for the blue and green share — 275,371 tons — RDRS structurally cannot answer:

“…recycling and composting inflows are never reported, so RDRS does not contain any such information”— CalRecycle’s ReadMe accompanying its PRA response; recycling outflows withheld for confidentiality

So every recycling number we can compute is a ceiling read off disposal records, not a measurement — including the matched-transfer est. 98.68% (≈929,386 t) and the sorting-facility figure in the companion piece (489,226 t → est. 458,018 t = 93.62%, a disposal-scoped ceiling, shown as one). The band on this stage never settles — both readings are drawn, and the records permit no better.

Matched transfer 941,811 t: black-bin 666,440.50 t corroborated at est. 98.88% landfill; blue+green 275,371 t genuinely indeterminate — ceiling reading landfill, alternative reading outside RDRS scope. Neither chosen.
transfer_fate.csv · docs/audit/transfer_fate_analysis.md §3, §7.3 · CalRecycle PRA ReadMe

The green bin’s paperwork

What the records can’t say about compost

Even the good news is unaudited. LA’s commercial organics stream is 227,976 tons. Material that reaches a compost facility — 77,156.98 tons across all streams (1.60% of everything) — drops off every ledger: no residual reporting, no outbound check, in this data.

The state’s public organics metric (Report 9) measures where trucks went within ten days — not what was composted: Bradley/WM Sun Valley reports routing 90.06%, Athens Sun Valley 69.13%, Crown mixed-waste organics 64.78% — and by Report 9’s own caveat, a rate is a routing claim, not a performance adjudication.

And two public datasets disagree about one facility’s organics by roughly 2×: LA County shows 53,735.3 t outbound food waste at Puente Hills; Report 9 shows 25,538.8 t of source-separated organics. Different measures, never reconciled — no contradiction asserted.

Routing-within-10-days gauges: 90.06% / 69.13% / 64.78%; compost-typed 77,156.98 t has no outbound accounting.
docs/audit/sb1383_recovery_rates.md · docs/audit/transfer_fate_analysis.md §8 · docs/methodology.md §5.2

The sum

What all that work bought: a range

None of these defects was fraud. Each was a reporting-design choice. Their sum is that the headline diversion number depends on definitions and typing judgments no regulation specifies: the legacy definition says 41.665%; the defensible answer is 18.476% – 24.054% — and even the range leans on a database designed not to see recycling. The most stable bucket: 58.34% (2,816,965 t) went straight to a landfill — a figure that moves by well under a point across every judgment this piece names.

Where independent systems can check each other, they agree: LA County and RDRS match Puente Hills disposal to 15.4 t (0.002%), and the summary pivot reconciled to the cent. Reconciliation works when the data supports it.

Small print: the export even duplicates honestly — 93 rows in 45 exact-duplicate groups (547.8 t excess), 44 of 45 groups already present in RecycLA’s export as received; headline effect 0.005 points. The largest, as filed:

row 9,797 12/1/23 · F-WLA · Green · Mixed Organics
Downey Area Recycling and Transfer Facility (DART) → Crown Recycling Services · 486.64 t

row 9,798 12/1/23 · F-WLA · Green · Mixed Organics
Downey Area Recycling and Transfer Facility (DART) → Crown Recycling Services · 486.64 t

byte-identical in the export as received (group 30, the largest of the 45 pairs)

A conditioned bracket, computed in the audit on the pre-July-5 base: taking the disposal-stream reading of the matched wedge at face value moves D2 only +0.14 to +0.26 pp (→18.6018.72%); the favorable blue/green reading tops out at ~24.324.4%; assuming every unmatched ton diverted, 27.1%.

Legacy D1 41.665% collapses to the honest band 18.47624.054%.
diversion_rates.csv (ALL rows) · docs/audit/transfer_fate_analysis.md §4, §6 · docs/audit/duplicate_adjudication.md

The pivot

Every defect is an ask

Each defect you just crossed maps to an auditable reporting requirement — venue-labeled, State or City, each with a test for what compliance looks like. Not “haulers should be more transparent”; a rule a validator script can check.

Where an ask partially exists already — the SWIS registry is published; SB 1383 reporting exists — the table says so and names the gap.

the full bill of particulars follows — embedded as data (#defect-data) and rendered below

The bill of particulars

Defect → recommendation

Eight observed defects, what each cost the audit, the requirement that kills it, the venue, and the compliance test. Rendered from the embedded #defect-data JSON; reconciled at load and by tests/test_methods_numbers.py against the design spec’s table.

#Defect (observed, cited)Cost to this auditRecommendation (auditable requirement)VenueCompliance test
R1Reports are a formatted export: embedded newlines, a 10,259-row unlabeled summary-pivot panel, 44/45 duplicate groups present in the export as received (row_reconciliation.md, duplicate_adjudication.md)Weeks of parse/reconcile work before analysis could start; a "how many rows is this file" question with four defensible answersRow-level, machine-readable tonnage reporting (defined schema, one load-aggregate per row, no summary rows, unique row IDs) filed to LASAN; the human-readable report becomes a rendering, not the recordCity (franchise contract / LASAN reporting spec)A validator script accepts or rejects each monthly filing; rejected filings are re-filed
R2Destination facilities are free text: 197 raw spellings for 56 real facilities; 46,859 t unresolvable (§5 names funnel)An entire synonym-mapping config whose judgments (69 entries) carried no rationale and had to be forensically reconstructed and SWIS-auditedSWIS ID required on every report line naming a destination facility; free-text names allowed only as a display fieldCity (contract) + State (CalRecycle publishes the authoritative SWIS lookup; already exists)Filing validator rejects rows with no valid SWIS ID; % rows with valid ID = 100
R3Synonym merging mis-filed 319,202.70 t under the wrong facility (american organics ↔ Gardena, SWIS-contradicted, reversed 2026-07-04); 365,071 t (7.56%) carried contradicted names (contradicted_merges_impact.md)The audit's largest single correction; found only by cross-checking every merge against SWISSame as R2 — standardized facility identifiers make the error class unwritable; interim: LASAN publishes its own facility crosswalk with each data releaseCity; State for the registryZero tonnage attributable to a name that contradicts the row's SWIS ID
R4One cell naming two facilities: el sobrante and chiquita canyon, 59 rows / 42,164.57 t, resolved only by a documented proportional-split policy (methodology.md owner decision 1)An ambiguity the source created and only the source could truly resolve; our split is a policy, not knowledgeOne destination facility per report line; multi-facility loads filed as multiple rows with actual per-facility tonnageCity (contract reporting spec)Validator rejects rows whose facility field parses to >1 SWIS ID
R5Diversion treatment hangs on unregulated typing judgments: two "MRF and TS" facilities typed oppositely, a +5.3–7.0 pp headline swing living in a config file (methodology.md §5.2)The single largest open lever on the headline; unresolvable from the data aloneFacility type / diversion treatment derives from the state registry's activity classification, not the reporter's or analyst's judgment; where a facility is dual-activity, the flow, not the facility, carries the treatment (see R7)State (CalRecycle classification) + City (adopt it in rate computation)Recomputing the City's rate from registry types alone reproduces the published rate
R6RDRS is disposal-only by design: "recycling and composting inflows are never reported"; recycling outflows withheld for confidentiality (CalRecycle PRA ReadMe)Every recycling estimate is a ceiling read off disposal records — the 93.6%/98.7% figures can never become point estimates under current law; 275,370.74 t is structurally indeterminateRequire recycling- and composting-stream reporting to RDRS (inflows and outflows), with confidentiality handled by aggregation rather than omissionState (Legislature / CalRecycle regulation)For matched facilities, inflow = Σ outflows across all streams; the residual-with-two-readings class disappears
R7No load-level tracing: the records show a first stop and sometimes an end; 1,072,909 t (22.2%) dead-ends at transfer stations; the audit's transfer-fate work is a facility-level estimate, never row tracing (transfer_fate_analysis.md §7.1)Six ceilings and brackets where one number should be; the D1→D2 ~23-point collapse is mostly this wedgeEnd-fate reporting for franchise tonnage: each load-aggregate reports first destination and final disposition facility (both as SWIS IDs), with transfer/processing facilities obligated to report onward allocation of franchise tonnageCity (contract) for the hauler leg; State for the facility onward legThe "transfer — endpoint unconfirmed" bucket computes to ~0% of tonnage
R8Compost outbound unchecked: compost-typed tonnage (77,156.98 t) drops off every ledger; Report 9 measures 10-day routing, not composting; two public datasets differ ~2× on one facility's organics (sb1383_recovery_rates.md, methodology.md §5.2)"Reached a compost facility" is the best the audit can certify — it cannot say compostedResidual and outbound-product reporting for organics processors (tons in, product out, residual to disposal), published per facility; reconcile Report 9 and county ledgers under one definitionState (CalRecycle / SB 1383 reporting)Per-facility organics mass balance closes within a stated tolerance

Five sentences · 1 of 5

Every tonnage report line carries the destination facility’s state SWIS ID — machine-readable, one facility per line.

One sentence in a reporting spec. It retires the format archaeology (), the name cloud (), the mis-merge (), and the two-landfills string () — the entire naming class of defect becomes unwritable.

kills R1–R4 · venue: City (contract / LASAN spec); SWIS registry already exists

Five sentences · 2 of 5

Facility type and diversion treatment follow the state facility registry, not a spreadsheet’s judgment.

The seven-point coin-flip () stops being a config-file judgment. Recomputing the City’s rate from registry types alone would reproduce the published rate — that’s the test.

kills R5 · venue: State classification, adopted by the City

Five sentences · 3 of 5

Facilities report recycling and composting flows to RDRS the way they already report disposal.

The blind spot () closes: the ceiling readings become measurements, and the 275,371 indeterminate tons get a fate. Confidentiality is handled by aggregation, not omission.

kills R6 · venue: State (Legislature / CalRecycle regulation)

Five sentences · 4 of 5

Franchise haulers report end fate — first stop and final destination — for every load.

The second hop stops going silent (): the 22.22% transfer wedge — the largest single reason the legacy number collapsed — computes to ~0% “endpoint unconfirmed.”

kills R7 · venue: City (contract) for the hauler leg; State for the facility leg

Five sentences · 5 of 5

Organics processors report outbound product and residual tonnage.

“Reached a compost facility” becomes “was composted, with a mass balance” () — and Report 9 and the county ledgers reconcile under one definition.

kills R8 · venue: State (CalRecycle / SB 1383 reporting)

The finale

A query

Five sentences, and the swamp this piece just crossed — parse, ledger, names, types, cross-check — compresses to a single step: read the answer out of the records.

We followed what the records show. These five sentences are what it would take for the records to simply answer.

All eight defects covered by five plain-English requirements; the pipeline becomes a query.
docs/superpowers/specs/2026-07-05-recycla-methods-explainer-design.md §7

Check our work.
Every number regenerates from a fresh clone.

The findings live in the companion explainer; the full audited record — every hauler, facility, and month, every cross-check shown — lives in the dashboard. The methodology document walks every decision made here.

What we found Explore the data

Verify our work: the repository’s docs/methodology.md states every decision, and every number on this page regenerates from a fresh clone.