Problem: a TU the front end could not parse left no trace. ClangTool::run's status was discarded, TUs with compile errors were analyzed on a partial AST and counted as done, and the isolated-worker path dropped TUs that never produced an AST. A scan of only broken files printed no findings and exited 0, the same as a clean scan. Separately, the scan logger wrote to stdout, so any warning corrupted a SARIF/JSON report written to stdout. Fix: every TU now yields an outcome (clean, compile-errors, analysis-failed); workers carry it on their file_finished record, and the fan-out parent adds timeout/crash/worker-exit for the TU a worker died on. tuCoverage() turns the requested file list plus outcomes into a ScanCoverage (requested TUs with no outcome are frontend-failed). SARIF gets a run `invocations` entry (executionSuccessful, one toolExecutionNotification per gap); findings JSON gets an additive `coverage` object. A scan with any gap, or with nothing analyzed, exits 2 after writing its report. CLI diagnostics now go to stderr, as docs/internal/cli-philosophy.md principle 12 requires. USAGE.md and AGENTS.md document the coverage fields and the exit code. Test: tests/unit/test_scan_coverage.cpp (classification, path spelling, complete/untracked controls, merge, both writers, and unchanged output without coverage); test_cli_paths "[coverage]" covers a clean TU (exit 0, complete) and a TU with a missing include (exit 2, gap in JSON, SARIF on stdout parseable with executionSuccessful=false). Before: both probe files exited 0 with no coverage information. Full suite: 139/139.
5.4 KiB
| description | argument-hint |
|---|---|
| Fold repo supplement — SAST boundaries, router contract, and validation rules | [TASK=<description>] [MODE=engineering] [PHASE=<start|checkpoint|complete>] |
AGENTS.md (Repo Supplement)
Operating model (retrieval contract, execution loop, metadata, handoff,
ask-first list): ../yams/docs/prompts/PROMPT-eng-codex.md. C++ design/TDD/
assertion/profiling flow: ../yams/docs/prompts/PROMPT-yams-cpp-workflow.md.
Use ../yams/AGENTS.md as the reference style for keeping this file lean:
generic agent behavior belongs in the shared prompt files; this file holds only
what is specific to Fold.
Project Contract
fold is a C++ SAST with two analysis paths:
- Source path: Clang LibTooling plus bounded Z3-backed detectors.
- Binary path: radare2-backed heuristic extraction for strings, symbols, calls, sections, xrefs, and simple pseudocode patterns.
Keep these paths separate unless a task explicitly asks for router work. Do not try to lift binary instructions into the source symbolic IR as a shortcut.
CLI And Router Work
Read docs/research/cli-router.md before changing scan-mode behavior.
The intended CLI contract is:
fold scan --mode source ...runs only the source backend.fold scan --mode binary --binary <path>runs only the binary backend.fold scan --mode hybrid --compile-db <db> --binary <path>runs both through the scan router.fold scan --mode auto ...chooses hybrid when both source and binary inputs are present.
Compatibility requirements:
- Keep existing
--input-type source|binarybehavior until explicitly removed. - Existing
fold scan --input-type binary --binary <path>commands must keep working. - Exit codes stay stable:
0no actionable findings,1actionable findings,2input/config/report failure, including a scan that left any requested translation unit unanalyzed (seecoveragein docs/USAGE.md). - Reports must preserve per-finding backend provenance.
Router implementation rules:
- Put orchestration in
include/fold/cli/scan_router.hppandsrc/cli/scan_router.cppwhen implementation starts. - Prefer small router-specific structs over widening
AnalysisBackend. - Hybrid mode should begin as
merge-only; add hint exchange only after tests prove independent source and binary behavior is unchanged. - Binary hints may focus source work, but must not silently hide source coverage unless the user requested a capped hybrid candidate set.
- Source hints may focus binary decompilation, but raw binary string/symbol scans should still run unless explicitly disabled.
Backend Boundaries
include/fold/core/analysis_backend.hppis intentionally narrow.ClangBackendowns source analysis and delegates to the mature detector pipeline.R2Backendowns radare2 sessions and binary heuristics.- Keep binary findings heuristic unless there is source/Z3 corroboration.
- Keep source solver metrics and binary heuristic metrics separate in profiles.
Validation
Run focused tests for the area you changed, then the full suite for CLI/router or backend changes:
ninja -C build/release
meson test -C build/release --print-errorlogs
git diff --check
Useful focused tests:
meson test -C build/release fold:scan_e2e --print-errorlogs
meson test -C build/release fold:binary_backend --print-errorlogs
meson test -C build/release fold:subprocess_fanout --print-errorlogs
meson test -C build/release fold:cross_tu_security --print-errorlogs
For binary or hybrid changes, run a curl source/binary smoke if the fixture paths are available in the workspace.
Worktree Safety
This repository often has unrelated local edits from benchmark and experiment
runs. Do not revert or rewrite files you did not intentionally modify. Keep
changes minimal and check git diff --check before handing off.
Cross-Language Wire Formats
When a production path spans Rust and C++ (fold-rust NDJSON → RustBackend parsing, fold-swift NDJSON → SwiftBackend parsing), JSON/NDJSON key names must be centralized in a single source of truth. Do not duplicate string literals across Rust and C++.
Canonical key homes:
- IR expression trees:
include/fold/ir/expr_json.hpp(namespace json_keys). - Frontend record envelopes (
info/error/function/finding/summary/symbolic-summary/interface/ir) and their data keys:include/fold/frontend/ndjson_keys.hpp(namespace fold::frontend::ndjson_keys).
Rules:
- C++ serialization (
toJson) and deserialization (fromJson, backend parsing) MUST use these constants, not raw string literals. - Rust/other-language producers MUST match the canonical names. Document the
expected keys in a comment block at the emitter site
(
tools/fold-rust/src/main.rsnearFindingData,tools/fold-swift/ fold_swift.cppnearemitFinding). - When a new expression shape (Call, Member, ArraySubscript, etc.) or NDJSON record shape is added, add its canonical keys to the owning header first, then implement on both sides.
- New
finding_kindvalues must also be added toFindingKind+findingKindName()(src/report/finding.cpp) and to the backend parse tables; unknown wire kinds map toFindingKind::UnknownForeignwith a warning, never silently relabeled. fromJsonmay accept legacy key aliases (read:j.value("canonical_key", j.value("legacy_key", default))) for backward compatibility, but MUST prefer the canonical form on write.