fix(cpg): prove the frontend can read the source before building, and check the graph always

Two permanent guards, and a correction that motivates both.

THE CORRECTION. There is no php2cpg defect. Every PHP build on this machine
ran through a local `php` shim proxying stdio through `docker run`, and
php2cpg depends on the INTERLEAVING of its parser's stderr banners with its
stdout JSON to attribute each document to a file. The proxy scrambles it:

    streaming shim   drops 72-88   767KB / 969KB / 1.2MB   different every run
    buffered shim    drops 0       5,703 bytes             empty, and silent
    NATIVE php       drops 0       2,872,996 bytes         identical x3

The 637-file tree with native php: 0 drops, a 4,329,471-byte CPG, 20.0s,
1.9GB peak -- which is exactly what task 5.10 said before I started
"correcting" it. No intermittent failures, no malformed graph, no dataflow
assertion, no scale limit.

GUARD 1: the interpreter must prove it can read the tree, before anything is
built. Every instrument failure this project has had takes one shape -- the
input was unreadable and the tool reported a plausible ZERO -- and it has cost
four wrong diagnoses in a day:

    a php that could not see ~/.cache   -> "cannot handle 637 files"
    a php whose stdio was proxied       -> "drops half its batches"
    a container missing a bind mount    -> "0 dropped", 5,703-byte graph

None announce themselves: file_exists() is false, the parser writes nothing,
the frontend exits 0, and "no errors" reads exactly like success. Checking the
output cannot separate that from an empty repository. Checking that the input
arrived can, and costs one exec.

GUARD 2: the graph census is taken on EVERY build, not only one the frontend
warned about. A graph holding nothing arrives with no warnings at all --
silence is the symptom that has no symptom -- and the shortfall is now
reported as "the frontend reported no failures but the graph holds N of M
source files".

`cpg_build_failed` carries that detail through to the report instead of being
a bare reason.

Also adds tests/test_cpg_queries_compile.py: the queries are 900 lines of
Scala that nothing compiled. A rowsFor signature change shipped with 850 tests
passing and broke every query at runtime, found by a measurement failing hours
later. Each query is now compiled AND run against a real graph wherever joern
exists, and must return a parseable payload.

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-10 11:35:35 +02:00
co-authored by Claude Opus 5
parent bffadd5741
commit 07df314e12
7 changed files with 264 additions and 13 deletions
+23 -1
View File
@@ -675,7 +675,29 @@
- The next measurement is therefore the per-request cost itself, and specifically what the two-stage joins
cost at scale: they were 1.6x on a slice (62s -> 97s) and are plausibly the dominant term here. Until
that is known, raising the timeout would only buy a slower way to find out.
- Still open: that measurement; and nothing here recovers a cross-shard flow.
- **ALL OF THE ABOVE ABOUT php2cpg IS WRONG. There is no frontend defect. 2026-09-10.** Every PHP build on
this machine ran through a local `php` shim that proxies stdio through `docker run`, and php2cpg depends
on the INTERLEAVING of its parser's stderr banners with its stdout JSON to attribute each document to a
file. The proxy scrambles that ordering. Measured on the same 177-file tree:
streaming shim drops 72-88 767KB / 969KB / 1.2MB different every run
buffered shim drops 0 5,703 bytes empty, and silent about it
NATIVE php drops 0 2,872,996 bytes byte-identical across three runs
So: no intermittent batch failure, no ~50% drop rate, no malformed CPG, no dataflow assertion, and no
scale limit. The `AssertionError: astParent ... has two parents` was a graph the shim had corrupted.
- **The real 637-file envelope, native php:** `drops=0`, a **4,329,471-byte CPG**, **20.0s wall**, **1.9GB
peak RSS**. Which is what the ORIGINAL note in this task said -- "php2cpg builds a 4.3MB CPG for a
637-file plugin with no errors" -- and it was right; everything written against it since was measured
through a broken instrument.
- Three of today's mechanisms were therefore built against an artifact: `_build_with_retries`, the sharded
build with its island CPG, and the census-before-split guard. They are inert on a correct instrument (no
drops means no retry and no split) and would still help against a genuine frontend failure, so they
stay -- but the evidence offered for them in their commit messages was not real, and this is the record
of that.
- Still open, and now measurable for the first time: whether the queries survive a well-formed 637-file
graph, what they cost, and what they find. Nothing here recovers a cross-shard flow, but on a correct
instrument there are no shards.
- Still open: wiring that exclusion loop, the 637-file build with it in place, and whether the REST handler
two calls away can reach the sink -- the slice found it through a same-function flow, not across the
object.