flow/ answers two questions about this repository: which artifacts does nothing read, and
which readers have a producer that never runs? As of 2026-09-25 the tree held 1,246 tracked
.mjs, 495 of them under bin/.
lib/oversight.mjs covers human attestation over determinations; /layingpipe flow is an LLM
reading. Neither is a graph. This is the graph.
House invariants in CLAUDE.md apply without restatement. The one that governs this document is a guard needs a second witness that cannot share its failure mode — this module is that invariant, applied to itself.
witness and existence are two axes and must not be collapsed into one. Every other
three-state truth in this system separates what was found from how sure we are — and a provenance
value wearing a verdict's clothes is how unknown loses its home. The mapping:
| situation | witness | existence |
|---|---|---|
| both passes agree | both |
confirmed |
| static only, in a module the runtime DID enter | static |
unknown + unconfirmed: true |
| static only, in a module the runtime never entered | static |
unknown |
| runtime only | — | no edge: the observation is reported as a false negative, a dynamic path, or a match by name |
a touches edge, which no runtime observation can refute |
static |
unknown + unfalsifiable: true |
contradicted is currently a value nothing can soundly assign, and the implementation is right to
refuse it. Assigning it to static-only edges inside a visited module is unsound at module
coverage granularity: the bucket is the union of a wrong static claim and a correct static claim
about a branch the run never took. Distinguishing them needs branch coverage from C2, which does not exist. Until it does,
the value is reserved and unused — a verdict no evidence can reach is worse than a missing verdict,
because it will be assigned anyway by whoever needs the bucket filled.
Every record carries "v": 1 as its first field: an old-shape record is not a parse failure, it is
a silently different meaning, and fail-closed does not catch it.
C1. Static pass — flow/static.mjs. Read/write/spawn edges from path literals, CW_* env keys,
and spawn/spawnSync targets.
Do not hand-roll a parser. A hand-rolled comment stripper treats the /* inside a glob string
as a comment opener, swallows the rest of the file, and reports a clean tree it never read.
bin/lib/tracked-imports.mjs is the model.
But note what that model does and does not give you: V8 hands over dependencySpecifiers and
nothing else. Path, env and spawn literals are a different extraction and V8 will not hand them to
you. They therefore get four witnesses with separated directions:
- a mask lexer, assumed unsound;
- V8 re-parsing a mask-consistency rewrite — masked interiors replaced with neutral filler, delimiters preserved, so a desynced mask garbles real code and V8 refuses it. This is the false-positive floor;
dependencySpecifiers⊆ the lexer's literals — a V8-sourced false-negative test, bounded to the import subset, and blind to every dynamicimport(), with a literal specifier or a computed one. State that blindness as a limitation rather than letting it sit inside a coverage claim;- a line-local quote scan with no state machine, whose failure mode (odd quote count on one line,
don't) differs from the mask's (regex/comment desync across lines).
Two tempting shortcuts are rejected: whole-file block-comment stripping, and a second hand-rolled tokenizer as the "second witness" — two hand-rolled lexers is one witness wearing two hats.
vm.SourceTextModule requires --experimental-vm-modules and parses without executing.
C2. Runtime pass — flow/runtime.mjs. Record actual opens and spawns during a run. It shares no
extraction code with C1; if it imports from static.mjs it is not a second witness.
Interception uses a CommonJS -r preload. An --import hook misses named and
namespace ESM imports (import { readFileSync } from 'node:fs'), because importing the builtin
freezes the named bindings against the original functions (flow/README.md). flow/runtime.mjs
therefore preloads with -r and refuses to report unless the hook witnesses its own read. A hook
that misses the reads observes nothing, an empty trace reads as a clean run, and this module would
manufacture the exact false-clean it exists to catch.
A second witness that silently observes nothing is worse than none, because it manufactures
agreement.
C3. Reconcile — flow/reconcile.mjs. Compare the two, asserting false negatives separately
from false positives — only one of those directions lies to you, and a merged confidence number
destroys the distinction.
Most bin/ scripts never execute during a test run, so a naive comparison reports almost every edge
as a divergence. That is over-reporting: unsupported finding, which this repo treats as exactly as
costly as green. C2 emits a coverage set — which modules the runtime actually entered — and the
headline divergence count is restricted to edges inside visited modules. unvisited is reported
separately and is never a finding. A zero divergence must be a measured zero, never a defaulted one.
C4. Orphans, both directions — flow/orphans.mjs. Artifacts written and never read; readers
whose producer never runs. The second is the dangerous one: a default makes a dead dependency look
alive, because the fallback never fails. Where a reader has a fallback, reads X, falls back to Y is
a distinct finding from reads X.
C5. Store liveness — flow/liveness.mjs. Last write per store against the freshness each
consumer assumes. A consumer that assumes nothing is itself the finding.
The code is how you get the measurement; the measurement is the point. Over commitwork:
- modules analysed / modules unanalysable (unanalysable is
unknown, neither clean nor a finding) - orphaned artifacts
- readers with absent producers
- static-vs-runtime divergence, restricted to visited modules, with
unvisitedreported apart
Owns flow/** only. Reads anything; writes nothing else.
package.json's test script globs flow/**/*.test.mjs, and README's ## Documentation index
lists this document.
- Whether C2 can observe anything at all — see the preload probe above. Everything downstream of C2 is conditional on it.
- What counts as "a run" for coverage purposes. The test suite is one answer and a biased one: it visits what is tested, which is not what is shipped.
- Dynamic path construction. A path assembled from variables is invisible to every static witness here. It is a known false negative, bounded and disclosed, not solved.