Skip to content

Changed-line coverage and binary windows

Use a clean build of the commit you want to review. Bind and ingest coverage for each binary that ran. Keep every cover directory until its labels and evidence have been checked.

Terminal window
reflow coverage ingest --cover-dir /tmp/server-cov --processes launched \
--window abc123-W1-a1 --member server --batch <batch-key>
reflow coverage ingest --cover-dir /tmp/gateway-cov --processes 2 \
--window abc123-W1-a1 --member gateway --batch <batch-key>
reflow coverage documents --window abc123-W1-a1 --json
reflow coverage status --window abc123-W1-a1 --expect-members server,gateway --json
reflow coverage diff --window abc123-W1-a1 --base <base-commit> --json

Windows select documents; they do not sum their metrics. Each member is one binary at the same repository, commit, tree and dirty state. A duplicate member label or a binary repeated under another label is refused. Window and member labels are part of the canonical document hash, so correcting one means ingesting again from the retained cover directory. A suggested label is <INTEGRATION-short>-W<n>-a<attempt>. Window labels allow letters, digits, ., _, :, - (up to 128 characters; . and .. are refused); member labels allow letters, digits, ., _, - (up to 32). Pass both labels on ingest, including LLVM ingest.

status --window returns the binding, members, instrumented packages, overlapping packages and batch keys. --expect-members requires an exact member set and refuses window_incomplete if it is missing or has extras. An older server that ignores the list filter is refused. Windows can hold Go and LLVM members. A changed-line review may select both: each changed file is judged by the documents of the format that measures it (lists the file or holds its package), and a file no format measures is a gap over every document. formats maps each format to its labels, and the batch check counts each format’s documents separately.

A window can contain at most 64 members. Collection retention still keeps only the latest 50 clean and, separately, 20 dirty documents; windows do not pin their members against retention. Ingesting more documents can therefore retire a member even while the window is below its 64-member cap. Use --expect-members on every status check to detect missing members before citing the window.

The exit-only Go document stores the checked process expectation as producer.processes: {source: "count" | "launched", n}. The command checks exactly n distinct counter-producing processes before ingest. This preserves the launch-count claim; it is not proof that a process could not have started and crashed without producing counters. Older and verified Go documents may not store a process count: cite the ingest receipt when the report warns that this evidence is absent. LLVM documents expose the number of ingested profiles.

The existing document comparison stays available:

Terminal window
reflow coverage diff --document <after-document-id> --base <before-document-id> --json

A UUID base selects that form. It compares documents and reports like-for-like gain or a descriptive comparison. See reading coverage.

Any other base must resolve locally to a commit. This form returns kind: "changed-lines"; without --json it prints a text summary (warnings first, totals, one line per changed file with its changed, hit and missed lines), and --json gives the full per-hunk report:

Terminal window
mkdir -p oracle
cat > oracle/pathspecs.txt <<'EOF'
server/**
tunnel/**
EOF
reflow coverage diff --document A=<server-document-id> --document B=<gateway-document-id> \
--base <base-commit-or-ref> --head <head-commit> \
--paths oracle/pathspecs.txt --exclude '**/*_test.go' \
--report-only 'client/**' --json

--head defaults to the documents’ bound commit. All selected documents must have clean, matching repository, commit, tree and object format, and the checkout must contain those Git objects. At most 16 documents contribute to one report. Each CLI --paths <file> reads a regular UTF-8 file of Git pathspecs, one per line, and restricts measured paths. Repeat it to combine files. The files may total at most 1 MiB and contain 1-256 pathspecs altogether, each at most 4096 bytes. LF and CRLF are accepted, with an optional final newline; blank lines, control characters and symlinks are refused. Spaces and Git magic such as :!*_test.go are preserved literally: omit shell quotes and comments inside the file. Missing or malformed files refuse usage; raw inline CLI pathspecs are not accepted. MCP keeps its inline paths array and never reads these files. With no matching measured changes, the command refuses no_changes, even when report-only paths changed. A base whose tree equals the head tree (the head commit itself, or only empty commits between them) instead returns an empty changed-lines report, exit 0, zero changed lines, and the warning base and head trees are identical; there are no changed lines. --report-only gets a separate diff, so browser paths can appear even when outside --paths. Symlinks and submodules are listed as excluded. Hunk selection uses the Myers diff algorithm, the indent heuristic and zero inter-hunk context regardless of local Git configuration.

Each added or changed head line has one verdict. A line is hit if any covering block in any selected document has a positive count. It is missed if it has covering blocks and all have zero counts. A measured file’s comments, declarations and other lines without blocks are notInstrumentable. A file absent from every document is an explicit gap, never a non-instrumentable line: the report separates fileNotInDocument from notInBinary (no document contains its package). The latter can also mean the binary linked the package but the build omitted it from -coverpkg. A file Go coverage never measures (a _test.go file, or a non-Go file such as CHANGELOG.md, a .sql file or package.json) is notInBinary for a Go document even beside an instrumented package. Stored documents do not record GOOS/GOARCH. A file omitted by an OS build constraint is therefore reported conservatively as fileNotInDocument with reason not_compiled_or_no_statements; the command does not guess from the local host platform.

The lines totals count changed source lines. The separate statements totals sum canonical block numStmt values for blocks intersecting changed lines; they are not renamed line counts. They are null when no Go document is selected or a changed file was judged by LLVM documents; a Go file’s own statements stay set when Go and LLVM documents are selected together. Per-hunk arrays and document labels show the hit/miss evidence. Absent files have unknown statement weights, so statements.notInBinary and statements.fileNotInDocument remain zero; their line gap counts retain the known evidence. Deleted lines are top-level only, because head blame cannot attribute removed lines. Batch keys and process evidence accompany each document. This is process-wide evidence over the selected window; it does not isolate individual flow steps.

A missed line is only “not hit in the selected documents”. When any changed line is missed, the report adds batchCoverage: for each batch key the selected documents name, the server’s count of executed runs, the batch’s runs (id, flow, status), the selected labels that name it and any unselected documents at the same commit, format and producer that name it. state is incomplete when a batch executed more runs than selected documents name it, or when unselected documents name it; unknown when the runs cannot be read or a document names no batch; otherwise complete. The check assumes one document per run (a cover directory bound per flow), so one document holding several runs’ processes reports a short batch. A warning then leads the report and names the runs that did not succeed: a failed flow whose processes quit normally still wrote coverage, and it is often the only one that reached the change. Ingest its cover directory with the same --batch and select it before treating a missed line as unreached. The check is advisory; read failures become warnings and never change per-line verdicts.

When any changed instrumentable line is hit in no selected document, the report leads with untestedChange (the first key after kind) and a first warning that starts untested change:, naming the count, the files and their line ranges. state is untested when none of the changed instrumentable lines ran and partly_untested when some did; missed and instrumentable give the numbers, and files lists every missed run. It is computed over all selected documents, never a PR row’s document subset. scope is batch only when batchCoverage.state is complete: then no run of the batch executed those lines, executedRuns counts the runs, and no flow in the review reaches the change, so a regression there would pass every flow. Otherwise scope is selected_documents and the claim covers only the selected documents until the batch check is resolved. It is null when every changed instrumentable line ran. Lines in files absent from every document are reported as gaps, not counted here.

A changed line in a file no selected document measures (notInBinary) is neither hit nor missed, so it never produces untestedChange. When any exist, the report adds unmeasuredChange (lines, the selected document formats and files with each file’s [start, end] runs) and a warning that starts unmeasured change:, naming the files and saying which languages the selected documents measure. Coverage cannot say whether any flow ran those lines: a TypeScript dashboard change under Go documents, for example, is unmeasured, not neutral. Check it with a flow that asserts its visible result, or reflow evidence diff. fileNotInDocument lines keep their own gapReason and are not counted here; use --exclude for changed files, such as documentation, that no flow should run.

A hit line only proves that a flow executed it, not that any checkpoint saw what it produced. When changed lines that ran sit in code that writes state (a call such as Insert..., Emit, Publish, Audit, log or Exec, an sqlc Insert...Params literal or SQL INSERT/UPDATE/DELETE, in the enclosing Go, Swift or Objective-C function or one same-file function those lines call), the report adds unobservedChange and a warning that starts unobserved change (hint, not a verdict):. functions lists each path, function, changed hit line runs and the write-shaped signals found. Each function’s source: {side: "head", commit} identifies the exact committed head source; signalLocations lists each signal and its lines as [start, end] runs in that source. Signal locations are lexical context: they can be elsewhere in the enclosing function or a same-file callee, and need not be changed or hit. The text names changed hit lines separately from write-signal locations, all on the head side; evidence lists what the batch’s runs retained: runs listed, checkpoint steps by kind (query, mcp, command, screenshot, accessibility, other) and the runs (named by flow) that retained no checkpoint at all. A wrong value written there (a row, an event, an audit entry) passes every flow unless a checkpoint reads it, so name the checkpoint that does and compare it between base and head with reflow evidence diff, or add one. When runs of the batches failed, timed out or were cancelled, evidence.failedRuns lists them and the warning names them first, with the step each one ended at and that step’s error (or the run’s summary when every recorded step passed): a failed assert there may be the checkpoint that read the written value. Coverage is process-wide, so the report never says which run executed the changed lines. Detection is lexical and checkpoint contents are not read, so the lead never claims the output is unobserved; it is null when no changed line that ran writes.

--attribution <manifest.json> maps PRs to commits and optionally to document labels. For example:

{
"integration": "<bound-head-commit>",
"base": "<recorded-base-commit>",
"prs": [{"pr": 7363, "tier": "T3", "commits": ["<pr-commit>"], "documents": ["A"]}]
}

A PR can instead supply base and head to select its commit range. Commits can belong to only one PR. Changed lines whose head blame belongs to no listed PR appear in the integration row. --pr <number> selects emitted PR rows and can be repeated. To support a claim about a flow’s own window, use one invocation per window, or set each PR’s documents in the manifest. Only those members can produce that PR’s hit verdicts. --dispositions <file.json> attaches missed-hunk explanations: a planned flow, unit-tests-only:<test name>, or unreachable-locally:<reason>. Attribution and disposition inputs must be regular JSON files at most 1 MiB each; symlinks are refused. A disposition file is an array such as [{"pr":7363,"path":"server/a.go","start":17,"end":19,"disposition":"unit-tests-only:TestGuard"}]. An omitted disposition remains null in the report and must be resolved before making a tier claim. Report-only browser hunks need their own assertion evidence.

The local MCP query_code_coverage diff operation accepts the same changed-line inputs as inline arrays and objects. It does not read manifest or disposition filenames. A hosted connection without a checkout refuses no_checkout. The changed-line form requires the additive server batch-lines API: an older server returns server_unsupported; the client does not substitute per-file reads.