feat(model): let the project say what it ships, and rank that first

contributor-scan 2.10. Every one of libpng's twenty-five entry-point
regions was a `main()` -- twenty-three under contrib/, plus example.c and
pngtest.c -- and those regions carried the fifty interprocedural taint
requests that dominated a 300s query phase. A library has no entry points,
so entry-point-driven analysis had nothing to anchor to except its own
example programs.

The task said to derive this rather than hardcode `contrib/`, and a project
already declares it. Makefile.am names installed targets in
`lib_LTLIBRARIES` and `bin_PROGRAMS`, each target's `<target>_SOURCES`
names its files, and `check_PROGRAMS`/`noinst_PROGRAMS` are automake's own
word for "not installed" -- exactly the distinction wanted. Taking every
`_SOURCES` would have called libpng's whole test suite shipped code, since
contrib/libtests declares its sources the same way the library does.

  interprocedural regions   25 -> 1
  taint query               300.1s -> 125.0s
  total query               311.9s -> 138.5s
  regions                   121 -> 121, unchanged by design

The surviving interprocedural region is contrib/tools/pngfix.c:main, which
libpng genuinely installs.

Two rules keep it honest. **Silence is not exclusion**: a project with no
build file this can read gets None back and nothing changes, so vampi's
numbers are untouched. **Not shipped is not unscanned**: those regions are
still arbitrated function-locally, because a contributor editing a test
must still get an answer -- what they lose is the expensive treatment and
first claim on the budget.

Entailed findings went 0 -> 3, and all three are false positives at
contrib/gregbook/wpng.c:374, `strcpy(outname+len, ".png")`. It is safe:
outname is char[256] and line 325 guards with
`if ((len = strlen(inname)) > 250)`. They were not introduced so much as
uncovered -- a different callDepth meant a different sink set, and the 26
fgets false positives were masking them.

The structural cause is recorded as 2.12: c/injection and c/memory carry
ZERO sanitizers, so no C flow can ever be discharged and every
`argv -> strcpy` entails however carefully it is bounded. The discharge
here is not a call but a guard that dominates the sink, which is precisely
what dominance.sc already does for access control and is wired to that one
family.

Also adds Group 7, five tasks for stopping the same false positive twice.
Two of today's three false-positive classes were one bug in three
matchers -- `resolve` in `resolveUrl`, `user` in `users`, `gets` in
`fgets` -- each fixed locally and each next one surviving because nobody
carried the fix across. A model may group counter-examples by cause and
draft an edit; only a split-respecting measurement may adopt one, because
the predecessor's improve lever admitted shapes taught by the holdout pairs
they then recovered and turned a -0.059 Youden into +0.059.

Gates byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-09 12:45:21 +02:00
co-authored by Claude Opus 5
parent 67497c4d68
commit 6dc25d77e4
9 changed files with 377 additions and 10 deletions
@@ -1,13 +1,13 @@
{
"counts": {
"load_bearing": 89,
"load_bearing": 90,
"standalone_capability": 7
},
"generated": "2026-09-09 (regenerated after contributor-scan 2.1 added the pinned-checkout recipes)",
"generated": "2026-09-09 (regenerated after contributor-scan 2.10 added model/shipped.py)",
"kept_with_a_lever_stripped": {
"improve/evolve.py + improve/validator.py": "the design's Removed list named these whole, but each is a mix: the mechanism lever (removed) and the deterministic rule-status improve loop that `ousast improve` still runs from benchmark evidence, which has no LLM in it and is not what this feature supersedes. Deleting the files would have taken a retained capability with them."
},
"modules": 96,
"modules": 97,
"note": "Classification of every source module after the group-1 subtraction. load_bearing = reachable from a gate, the CLI, or named by a surviving requirement; standalone_capability = its own entry point or protocol, deliberately off the scan path; orphaned = no importer, no entry point, no surviving requirement -- removed, never carried.",
"orphaned": [],
"removed_in_this_pass": {
@@ -0,0 +1,25 @@
{
"caveat": "One repository, and the only one measured with a build declaration this module can read. The automake and CMake readers are first cuts; a project using bazel, meson or setuptools declares nothing to them yet and is correctly left alone rather than guessed at.",
"delta": {
"entailed_findings": "0 -> 3, and all three are false positives -- see below",
"interprocedural_regions": "25 -> 1 (the survivor is contrib/tools/pngfix.c:main, which bin_PROGRAMS installs)",
"regions": "121 -> 121, unchanged by design",
"taint_query_seconds": "300.1 -> 125.0",
"total_query_seconds": "311.9 -> 138.5"
},
"generated": "2026-09-09",
"how": {
"not_shipped_is_not_unscanned": "Non-shipped regions are still arbitrated, function-locally. A contributor editing a test must still get an answer about it; what they lose is the expensive interprocedural treatment and first claim on the budget.",
"silence_is_not_exclusion": "A repository with no build file this can read gets None back and nothing changes. vampi declares nothing, so its numbers are untouched.",
"the_project_declares_it": "Makefile.am names installed targets in lib_LTLIBRARIES and bin_PROGRAMS, and each target's <target>_SOURCES names its files. check_PROGRAMS and noinst_PROGRAMS are automake's own word for 'not installed', which is exactly the distinction wanted. Taking every _SOURCES would have called libpng's whole test suite shipped code -- contrib/libtests declares its sources the same way the library does."
},
"reading": "A project's own build files say what it ships, and libpng's say the library plus two installed tools. Every one of its twenty-five entry-point regions was a main() in an example program or a test tool, and those regions carried the interprocedural analysis that dominated the query phase. Ranking build-declared code first and withholding the interprocedural treatment from the rest cuts the taint query by 58% and changes no region count: not shipped is not unscanned.",
"repo": "libpng at cf53e54ccf631d8de68a30ebb3b8eeed1d6f1a08",
"spec": "contributor-scan 2.10 (Req 4.2, 8.1)",
"the_three_new_findings_are_false_positives": {
"site": "contrib/gregbook/wpng.c:374 -- strcpy(outname+len, \".png\")",
"structural_cause": "Both C families carry ZERO sanitizers: c/injection and c/memory each have 0. No C flow can ever be discharged, and there is no notion of a bounds guard at all -- so every argv -> strcpy in any C codebase entails, however carefully it is bounded. Guard dominance is exactly the abstraction that decides this, and it is wired to access_control only. Recorded as task 2.12.",
"why_it_is_safe": "outname is char[256], and line 325 guards with `if ((len = strlen(inname)) > 250)`. By line 374 len <= 250, so the copy writes at most 255 bytes. The bound dominates the sink.",
"why_they_appeared_now": "They were not introduced by this change so much as uncovered by it: with callDepth 3 the sink set was different, and the 26 fgets false positives were masking them. Three identical rows for one site is task 2.9 again."
}
}