London Municipal Audit
An independent review of City of London procurement disclosure, built from the City's own published Contract Awards report.
Every published procurement figure regenerates from the source scrape with one command: raw portal HTML → parser → a 1,978-row ledger → the findings, in about four seconds. An independent review, not an official audit — but one where the numbers survive inspection.
The clearest finding.
On more than two-thirds of catalogued awards, the City discloses that a contract exists but not what it is worth. Across the 584 records that do carry a value, disclosed awards total $1,022,279,436.11.
From source to finding.
The scrape is committed and SHA-256 pinned. One command regenerates the ledger from it — byte-identical to the committed copy — then recomputes every published figure and fails loudly on any mismatch.
| Published figure | Recompute | Result | Status |
|---|---|---|---|
| No-value disclosure rate | count without a dollar value ÷ 1,978 | 1,394 → 70.5% | Reproduced exactly |
| Known-value award total | Σ amount over the 584 disclosed | $1,022,279,436.11 | Reproduced to the cent |
| Top-10 vendor share | group by vendor · top 10 ÷ total | 71.6% | Reproduced |
| Construction share | category filter ÷ total | 94.3% | Reproduced |
| HHI, all known-value awards | Σ(vendor share %)² | 949.4 | Reproduced |
| HHI, construction only | Σ(share %)² within category | 1,065.4 | Reproduced |
| Distinct known-value vendors | unique vendor count | 267 | Reproduced exactly |
| Corporate-family grouping moves concentration | regroup by parent, recompute | no change | Reproduced — 949.4 → 949.4 |
Corporate-family normalization groups only the 14 of 267 vendors whose own legal name discloses a parent ("a division of X", numbered-company DBAs); two near-duplicate name pairs are flagged and deliberately left unmerged. It doesn't move the concentration finding — so the "unconcentrated overall" reading isn't an artefact of vendor-name fragmentation.
One record, end to end.
RFT-21-97 — 2021/2022 Road Improvements Program: Adelaide Street N at CPR Underpass. The single largest known-value award in the dataset.
McLean Taylor Construction Limited · amount 60191223.44 · status known_dollar_value · category Infrastructure/Construction · confidence highhas_known_dollar_value rows — contributes $60.19M, 5.9% of the entire known-value total on its own.What still depends on manual work.
The procurement analysis reproduces from source. The honest boundary is elsewhere:
| Item | Why it isn't a script |
|---|---|
| Capital-project case files | Named projects joined to Council staff reports by hand. The bid → vendor → amount joins are real and checkable; the document search is a person. |
| Bidder ↔ losing-price pairing | The portal interleaves competing names and amounts; only the winning award line pairs them cleanly. Bid values are recovered for 49% of records (97% of >$1M awards); identities aren't. |
| 3 of 5 procurement-policy amendments | Bylaw number and date retrieved; the full amending-bylaw text was not. |
| Report parts beyond procurement | Capital plan, workforce, housing, grants, internal-audit crosswalk — all sourced to named documents, none wired to a reproduce script. |
Named projects, joined to their tender.
| Project | Tender | Awarded to | Award value |
|---|---|---|---|
| Mud Creek East Branch Phase 2B · stormwater | RFT-2024-060 | Van Bree Infrastructure | $11,864,196 |
| Springbank Dam decommissioning | RFT-2024-070 | Bronte Construction (2220742 Ontario Ltd) | $4,350,862 |
| Greenway Flood Protection · wastewater | RFT-2024-084 | Stone Town Construction Limited | $24,477,992 |
| Highbury Avenue reconstruction · road | RFT-2025-270 | J-AAR Civil Infrastructures Limited | $27,426,933 |
Full case-file dossiers — including which budget-to-award gaps stay open and the records requests drafted to close them — are on the published microsite.
Method.
- 01 · Scrape
Drive the City's Contract Awards portal (no API) and save the rendered report unmodified. Checksum it.
- 02 · Parse
Split the report into bid sections, extract vendor / amount / method, classify each record by a fixed keyword cascade, score parser confidence, and route unstructurable rows to a rejects file with a reason.
- 03 · Regenerate
Rebuild the 1,978-row ledger from the checksummed scrape and re-run every published figure. The pipeline exits non-zero on any mismatch; a fresh scrape produces a new dated population, not a silent pass.
- 04 · Publish
Report what the record supports; for what it doesn't, state the specific unresolved test.
- Published analysis — The Public-Records LedgerOpen ↗ opens in a new tab