fold/AGENTS.md
trvon c9811b7d41
fix(scan): report unanalyzed translation units and exit 2
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.
2026-09-24 20:37:51 -06:00

131 lines
5.4 KiB
Markdown

---
description: Fold repo supplement — SAST boundaries, router contract, and validation rules
argument-hint: "[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|binary` behavior until explicitly removed.
- Existing `fold scan --input-type binary --binary <path>` commands must keep
working.
- Exit codes stay stable: `0` no actionable findings, `1` actionable findings,
`2` input/config/report failure, including a scan that left any requested
translation unit unanalyzed (see `coverage` in docs/USAGE.md).
- Reports must preserve per-finding backend provenance.
Router implementation rules:
- Put orchestration in `include/fold/cli/scan_router.hpp` and
`src/cli/scan_router.cpp` when 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.hpp` is intentionally narrow.
- `ClangBackend` owns source analysis and delegates to the mature detector
pipeline.
- `R2Backend` owns 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:
```sh
ninja -C build/release
meson test -C build/release --print-errorlogs
git diff --check
```
Useful focused tests:
```sh
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.rs` near `FindingData`, `tools/fold-swift/
fold_swift.cpp` near `emitFinding`).
- 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_kind` values must also be added to `FindingKind` +
`findingKindName()` (`src/report/finding.cpp`) and to the backend parse
tables; unknown wire kinds map to `FindingKind::UnknownForeign` with a
warning, never silently relabeled.
- `fromJson` may 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.