68 lines
4 KiB
Markdown
68 lines
4 KiB
Markdown
# Fold assertion policy checkpoint
|
|
|
|
Date: 2026-06-04
|
|
PBI: PBI-012
|
|
Task: assertion-invariant-hardening
|
|
Owner: codex
|
|
|
|
## Research question
|
|
|
|
How should Fold add C++ assertions to catch invariant drift in tests and benchmarks without turning recoverable scan/input failures into process aborts?
|
|
|
|
## Evidence table
|
|
|
|
| Source | Confidence | Relevance |
|
|
|---|---:|---|
|
|
| C++ standard / cppreference `assert` behavior | High | Plain `assert` is disabled when `NDEBUG` is defined, so hard invariants should not rely on it in release-like builds. |
|
|
| Fold `meson.build` | High | Fold defaults to `buildtype=debugoptimized` and `b_ndebug=if-release`; assertion behavior must be explicit rather than accidental. |
|
|
| C++ Core Guidelines / GSL `Expects` and `Ensures` | Medium-high | Supports explicit precondition/postcondition vocabulary for programmer contracts. |
|
|
| Herb Sutter GotW contracts series | Medium-high | Reinforces separating assertions/preconditions/postconditions from recoverable error handling. |
|
|
| Repo assertion audit (`rg assert/CHECK/UNREACHABLE`) | High | Fold had no project-local assertion layer; existing assertion text was mostly SMT strings, Catch2 checks, and target-code `DEBUGASSERT` modeling. |
|
|
|
|
## Recommended assertion tiers
|
|
|
|
- `FOLD_ASSERT(cond, msg)`: always-on hard invariant. Use for corruption, accounting underflow, impossible internal states, and UB-risk programmer bugs.
|
|
- `FOLD_PRECONDITION(cond, msg)`: internal API contract. Use when callers inside Fold must pass a valid state/value.
|
|
- `FOLD_POSTCONDITION(cond, msg)`: mutation/result contract. Use after state transitions and path/fact transformations.
|
|
- `FOLD_DCHECK(cond, msg)`: debug/canary-only consistency check. Use for extra diagnostics that should not change release behavior.
|
|
- `FOLD_UNREACHABLE(msg)`: impossible path only after real invariant reasoning.
|
|
|
|
Do not use these for malformed user input, missing files, toolchain absence, unsupported syntax, solver timeout, or ordinary scan/report failures.
|
|
|
|
## Changes applied
|
|
|
|
- Added header-only assertion surface: `include/fold/core/assert.hpp`.
|
|
- Enabled `FOLD_ENABLE_DCHECK=1` whenever Meson is configured with `build-tests=true`, so Fold's test build path exercises debug checks in core code and test binaries.
|
|
- Added explicit `-Denable-dcheck=true` Meson option for non-test canary/benchmark builds that should exercise `FOLD_DCHECK`.
|
|
- Added signal-isolation death tests for `FOLD_ASSERT`, `FOLD_PRECONDITION`, and `FOLD_DCHECK` in `tests/unit/test_signal_isolation.cpp`.
|
|
- Added boolean-path pre/postcondition checks to `src/symbolic/source_path_state.cpp`.
|
|
- Added guarded-fact invariant checks to `src/symbolic/guarded_facts.cpp`.
|
|
- Added P-Code encoder invariants for initialized boolean path state, bit-vector value coercion, boolean predicate outputs, and observer path readiness in `src/symbolic/pcode_encoder.cpp`.
|
|
- Migrated a first detector/router invariant batch:
|
|
- `src/cli/detector_plan.cpp`: enabled detector flags must match generated profile lists; security detector count cannot exceed total profiles.
|
|
- `src/cli/scan_router.cpp`: router result must run at least one backend lane, record a non-empty mode, and keep merged finding counters consistent.
|
|
|
|
## Validation
|
|
|
|
Passed focused gates:
|
|
|
|
```sh
|
|
meson test -C build/release \
|
|
fold:source_path_state fold:guarded_facts fold:pcode_encoder \
|
|
fold:div_by_zero fold:null_deref fold:oob_access fold:scan_e2e \
|
|
--print-errorlogs
|
|
|
|
meson test -C build/release \
|
|
fold:signal_isolation fold:source_path_state fold:guarded_facts \
|
|
fold:pcode_encoder fold:scan_e2e fold:subprocess_fanout \
|
|
--print-errorlogs
|
|
|
|
git diff --check
|
|
```
|
|
|
|
Results: first gate `7/7 OK`, second gate `6/6 OK`, 0 failures.
|
|
|
|
## Remaining rollout items
|
|
|
|
- Consider a small helper wrapper around `runWithSignalIsolation` for future assertion death tests to reduce boilerplate.
|
|
- Continue migrating older unreachable/default cases after a broader detector/router audit, prioritizing lifecycle/counter/state-machine invariants over recoverable input failures.
|