Header menu logo TestPrune

Soundness disclosure

Status: design only — not scheduled for implementation.

The problem

TestPrune builds its symbol dependency graph statically from the F# AST via FSharp.Compiler.Service. That graph is the authority for "what could this test reach?" — but the AST cannot see every edge that exists at runtime. When a real edge is missing from the graph, TestPrune may skip a test that would have failed if run. That is unsound test selection, and it is the worst failure mode the tool has: silent, looks like a green CI, masks regressions.

Today we treat soundness as an implicit property — users who happen to read the code or hit a bad miss learn the boundaries the hard way. We should make it a first-class, surfaced concept instead.

What "unsound" means here

A test selection is sound if every test that would have observed the change is in the selected set. We are unsound whenever the static graph is missing an edge that exists at runtime. Concretely, the F# constructs that produce hidden edges are:

Proposal

Three pieces, ordered by cost.

1. A documented unsoundness list

Ship a docs/soundness.md page that enumerates the constructs above, explains why each one defeats static analysis, and states what TestPrune does in practice (typically: records an edge to the call site but not to the runtime target). This is mostly writing — no code — and it earns credibility the way Ekstazi's papers do by being explicit about limitations rather than hiding them.

2. Detection during indexing

When the AST walker encounters one of these constructs, record a soundness note alongside the edge. Schema sketch (additive to the existing SQLite store):

soundness_notes(
  symbol_id     INTEGER,   -- the symbol whose body contains the construct
  kind          TEXT,      -- 'reflection' | 'type_provider' | 'srtp' | ...
  detail        TEXT,      -- e.g. the reflected type name if known
  source_range  TEXT       -- file:line:col for the diagnostic
)

Detection is per-construct and varies in difficulty:

3. Surfacing in CLI output

Two surfaces, both opt-in-by-default-on:

A --sound flag widens selection to include any test transitively reaching a symbol with a soundness note in the changed closure. This trades precision for safety and is the right default for release branches.

What this is not

Open questions

Prior art

Type something to start searching.