Static-analysis triage, 27 October 2026#
The first local audit recorded 745 distinct
diagnostics against solver source 88ca313. This review assigns a disposition
to every original row in the
machine-readable triage record. A disposition can
be a guarded case, an intentional contract, a cleanup candidate or an open
follow-up; it does not mean that every warning has been removed or that the
solver has a complete lifetime or exception-safety proof.
Confirmed rollback allocation defect#
Tracing the ModelScope exception warning led to
FlatCongruenceClosure::pop.
It resized three representative-proof arrays to the saved node count in every
mode. ensure_node_count allocated those arrays only for the representative
proof-forest mode or explicit proof diagnostics. A first pop could therefore
allocate unused arrays during BFS or the separate reoriented/structural modes,
including a pop invoked by ModelScope destruction.
The repair applies the same mode condition during pop. On a 64-bit build the three arrays previously added 24 bytes of unused element payload per saved node, before allocator overhead. This is a storage calculation, not a measured RSS or timing improvement. The regression exercises nested checkpoints and post-checkpoint nodes across all five explanation modes, with diagnostics both on and off. Disabled representative-proof capacities must stay zero, and equality rollback must remain correct. The new EUF test translation unit aborts when linked to the preserved pre-fix libraries; it passes against the repaired release libraries.
ModelScope still treats a violated checkpoint invariant as fatal through its
destructor and noexcept move assignment. The repair removes this particular
allocation path; a complete exception-guarantee review with allocation-failure
injection remains open. No solver option or search heuristic changes here.
Dispositions of the original inventory#
Diagnostics |
Count |
Review result |
|---|---|---|
Include-cleaner |
409 |
20 paired self-includes retained; 389 direct-provider suggestions remain dependency hygiene work |
Optional access |
109 |
37 production accesses have local or caller/factory engagement invariants; 72 test accesses depend on assertions or fixture setup |
Enum size and record padding |
82 |
Layout candidates; measure containing records and allocation frequency before changing types |
Easily swappable parameters |
40 |
API readability risks; no swapped call demonstrated by these warnings |
Test exception boundaries, enum casts, assertion side effects and multiplication |
36 |
Test failure behavior or bounded/explicitly enabled setup; no production overflow or discarded release setup found |
Identical switch branches |
5 |
Inspected alternatives intentionally have the same result |
Value/copy/move and optional wrapping |
44 |
Ownership boundaries, harmless moves, copy-only receiving interfaces and verified-cleanup candidates are distinguished per row |
Vector reserve |
6 |
Potential allocation cleanup; three sites are test-only |
String operations |
11 |
Test-only construction/search cleanup |
Production cleanup exception warnings |
2 |
Broader exception contract remains a follow-up after the allocation repair |
Dependency leak warning |
1 |
CLI11 |
For example, the LRA model mutation warning at the original line 1671 concerns
saved = current: current is already an owning immutable snapshot, so the
second copy is a cleanup candidate. Keeping the original snapshot matters
because apply changes the assignment vector. Likewise, five const payloads
passed through std::move still copy vector/model data. These merit a focused
verified cleanup rather than a blanket conversion of values to references.
The optional-access dispositions state their preconditions individually. Fixture-only
unchecked operator*/operator-> accesses need explicit presence checks to
guarantee a clean failure when an assumed fixture condition breaks. A missing
value otherwise violates the optional observer precondition and can cause
undefined behavior. The standard observer contract
distinguishes that from value(), which throws on absence. These are test
hardening follow-ups, not production fallback policies. Enum width changes also require
care around existing bitmask/index types and public option layouts; a smaller
enum alone does not establish a smaller containing record.
Make redundant includes fail#
Before the first local audit, the default clang-tidy invocation in
the Makefile at 88ca313
did not enable include-cleaner. Its separate include scan was advisory and
could skip a missing tool successfully. The first audit’s 4dca989 tooling
already enabled the broad profile and made missing tools fatal; findings still
needed triage and an explicit acceptance gate. Tool completion was being
confused with review acceptance.
The local workflow now keeps missing
analyzers fatal and makes both make tidy and make include-cleaner fail on an
unused include, except an implementation’s own .hpp/.h header. That
exception follows source-relative quoted lookup and the configured
-iquote, -I, -isystem and -idirafter order for every compile context.
Unsupported search configurations conservatively receive no exemption. A
source-relative or earlier search-path shadow is rejected, as is an unrelated
matching basename. All 20 current unused-header reports are one-line paired
header anchors, which preserve standalone header compilation. The gate records
them explicitly. Other deliberate includes can use a reviewed LLVM-supported
IWYU pragma: keep; no such suppression was added by this review.
Missing direct includes remain visible as advisory findings. Of 389 rows, the tool supplies 225 insertion proposals. Those proposals are retained in the triage data; owning/public providers and layer boundaries still need review before applying a cleanup batch. A synthetic real-clang probe verifies that an unused extra header fails the gate and using its declared type passes, while the paired header remains retained.
The LLVM include-cleaner documentation describes the unused/missing distinction; LLVM’s include-cleaner design documents intentional include retention.
Evidence and remaining work#
The qualification report and run ledger record 4,200 generated inputs/sessions, 8,086 direct Dolmen model checks, 45/45 release and 45/45 ASan/LSan native tests, and 284 Python tests on the frozen native snapshot. The final changed tooling separately passes 26 static-workflow tests plus the full-driver mocked replay test. The fresh static pass covers 132 translation units and retains 745 visible diagnostics, with zero redundant-include blockers. The source repair is qualified separately from the metadata gate. Release, ASan, differential fuzzing, explicit online-EUF checking and a fresh whole-source static pass are recorded in the run ledger. The initial online-EUF job stopped before generating inputs because its reused tool prefix lacked cvc5; retries use an existing verified cvc5 binary in fresh output directories. That setup failure contributes no solver evidence. Model replay also needed to cover finite QF_UF and preserve its forced strong preprocessing. Finite QF_UFLRA uses the requested profile. The corrected driver shares per-corpus option vectors between generation and replay; its mocked regression compares both call sets, including a requested preprocessing override. The accepted run’s metadata agrees on those options. It records and compares the solver’s SHA-256 before and after execution, captures its version and reference binary hashes, and rejects reuse of an existing task output directory. Intermediate replay attempts with omitted or changed options are retained as provisional evidence.
The CLI11 warning traces a copied callback into App-owned option state. A separate ASan/LSan CLI construction/destruction probe checks the exercised help/version path; it is not proof that every dependency path is leak-free. The remaining priorities are the checkpoint exception contract, direct-header hygiene and avoidable const-payload copies. No performance-profile promotion or full-corpus performance conclusion follows from this review.
UF resource measurement after the repair#
CPU SLURM job 258443 compares frozen native sources 0d59704 and ed8c227.
Their production source differs only in the guarded proof-array resizing in
FlatCongruenceClosure::pop. Both use the same Release compiler, -O3 -DNDEBUG,
explicit C++ header search paths and CaDiCaL 3.0.0-7b99c07; no sanitizers.
The fixed binary is the correctness-qualified candidate described above.
The six-input manifest
selects two previous quantified-UF failures, two successful quantified-UF cases
and two QF_UF controls. Historical selection times are not the measured baseline.
Each input has three repetitions per binary, alternating execution order within
one allocation. The controller pins itself and its children to one CPU on an
Intel Xeon E5-2690 v4. SLURM allocates two logical CPUs despite a one-CPU request;
the explicit affinity restricts execution to one. The scheduler reservation is
10 GiB; each solver has an observed hard and soft 8 GiB RLIMIT_AS, a 180-second
deadline and arguments --quiet --stats. All six array tasks completed their
six measurements, for 36 solver runs total. No run reached its deadline.
The table gives medians over three repetitions. Failure times measure time to
failure, not solve time. Peak RSS is the kernel’s per-process wait4 measurement.
Input suffix (logic) |
Result, both binaries |
Time before → after (s) |
Peak RSS before → after (MiB) |
|---|---|---|---|
|
Cleanup abort |
54.09 → 54.13 |
5942.5 → 5943.4 |
|
Explicit |
83.43 → 83.34 |
6345.8 → 6344.3 |
|
UNSAT |
22.67 → 22.16 |
111.3 → 110.7 |
|
UNSAT |
22.20 → 22.00 |
133.3 → 139.2 |
|
UNSAT |
14.44 → 14.44 |
24.7 → 24.2 |
|
UNSAT |
22.08 → 22.18 |
364.6 → 365.5 |
Each binary solves four of six inputs in every repetition. All 24 successful runs agree with the declared UNSAT status and their paired binary. There are six explicit allocation-failure runs and six cleanup-abort runs, zero timeouts and no other execution errors. No SAT answer needs post-run model validation; this does not independently certify the UNSAT answers. The cleanup abort has the same signature as the earlier allocation trace, but this measurement does not collect a fresh allocation stack trace.
All reported non-timer counters match across binaries and repetitions for each successful input. The two quantified cases report respectively 15,641 and 19,564 instances, 4,311 and 10,976 pool terms, and 1,249 and 259 SAT conflicts. The QF_UF controls report 27,841 and 119,338 incremental-EUF backtracks and 5,890 and 161,966 SAT conflicts. Quantified UNSAT’s zero incremental-EUF counters have a publication limitation; they do not measure resident closure size. Failed runs print no final counters.
Neither failing case is recovered. Their sampled virtual-memory peaks are essentially unchanged: about 7,498.5 and 8,145.2 MiB. Sampling every 50 ms gives lower bounds on those peaks; an allocation can fail below the address-space cap when its next buffer cannot fit. The successful cases have mixed memory changes, and timing differences from −2.24% to +0.46% on this small selected sample do not establish a general speedup. The removed dormant arrays are a real allocation defect, but these measurements do not attribute the prior memory-outs to it. Term-lifetime and storage attribution remains open.
The machine-readable report
contains full source and binary hashes, metric ranges, selected counters and
raw-artifact fingerprints. Local raw results are
results/uf-rollback-memory-20261027/258443/. Reproduce execution with the
CPU batch script:
set PRIMO_WORKDIR, PRIMO_UF_PLAN, PRIMO_UF_ROOT, PRIMO_QF_UF_ROOT,
PRIMO_UF_OUT, PRIMO_UF_BASELINE and PRIMO_UF_FIXED to the prepared workspace,
manifest, corpus roots, fresh output directory and fingerprint-matching binaries,
then submit with sbatch. The controller is single-threaded, as required by its
child-only preexec_fn resource-limit setup. Rebuild the summary with the
summary script without running
the solver:
python3 scripts/summarize_uf_memory.py \
--plan benchmarks/uf-rollback-memory-20261027.json \
--results results/uf-rollback-memory-20261027/258443 \
--output docs/coverage/uf-rollback-memory-20261027.json