📦 WP-004 — ci(pr): lint, types, tests, coverage, license audit¶
Engineering log · Phase 0 — Foundation
🔗 Depends on WP-002, WP-003 · Needed by WP-005, WP-084, WP-085, WP-086, WP-109
WP-004 — auditing what a wheel bundles, not what it declares¶
The copyleft audit read what a distribution says about itself — License, License-Expression, OSI classifiers — and nothing about what it ships. A wheel may vendor a native library under a licence its own metadata never mentions, which is precisely the case the gate exists to catch.
Reading the License-File documents too, it found something on the first run:
| package | declares | bundles |
|---|---|---|
| numpy | BSD-3-Clause | libgfortran, libgcc — GPL-3.0-or-later with GCC Runtime Library Exception; libquadmath — LGPL-2.1-or-later |
| shapely | BSD-3-Clause | GEOS binaries — LGPL-2.1 |
numpy is a runtime dependency, not a test-only one, and no audit this project had run could ever have seen it.
Three rules keep the result meaningful rather than merely red:
- Only declaration lines are read, never licence prose. A bundled copy of the LGPL names the GPL on dozens of its own lines, and a gate that fires on every wheel shipping a licence file teaches its reader to bypass it.
- A recognized licence exception is permissive by construction, matched by expression rather than by name, so a future gcc-built dependency needs no entry.
- The exception is tested before the allowlist, not after. Written the other way round the allowlist short-circuits first, the exception never runs, and a per-package entry quietly covers whatever that package vendors next. Mutation testing is what caught the ordering: with numpy delisted the audit reports libquadmath alone, and with the exception disabled it reports the GPL-3 line — evidence the two mechanisms act independently rather than one masking the other.
A related gate lesson from the same package: the decisions test moved from a hardcoded D1–D14 to contiguity plus a floor. The hardcoded upper bound failed the moment a decision was added, which is a test asking for maintenance rather than reporting a defect; contiguity alone would not notice the last row being deleted, since what remains stays contiguous. The roadmap's _WP_FLOOR ratchet is the same pattern.