Task: Implement Hotfix: the nightly Valgrind run reports uninitialised members and library leaks

Table of Contents

This page documents a task in the Hotfix: the nightly Valgrind run reports uninitialised members and library leaks story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

Take the four suites the nightly dynamic analysis reports defects for to zero: ores.trading.api.tests (4), ores.refdata.core.tests (3329), ores.ore.core.tests (99) and ores.analytics.core.tests (214). Fix the defects that are ours and suppress the allocations that are the libraries'.

2. Status

Field Value
State DONE
Parent story Hotfix: the nightly Valgrind run reports uninitialised members and library leaks
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-29

3. Acceptance

  • Every uninitialised-value defect the four suites reported is gone, and the member that caused it is initialised by its model's :default_value:.
  • The 3329 still-reachable leak records for ores.refdata.core.tests are suppressed by rules anchored on the allocating library, each with the reason it is not a leak.
  • A nullable column whose :cpp_type: is a bare value type can no longer be merged without a declared default: org_loader refuses it.
  • The full test suite passes, the codegen suite passes, and the sweep over every catalogue component reports no file this branch changed as drifted.

4. Plan

  1. Reproduce each reported defect locally with --track-origins=yes so the member and the site that leaves it unset are named, rather than inferred from a truncated CDash page.
  2. Fix each at the model: :default_value: on the column, then regenerate the component.
  3. Census the rest of the class so the size of what is left is on record.
  4. Extend build/valgrind/custom.supp for the library allocations, and add the codegen gate for the nullable shape.
  5. Re-run the four binaries under valgrind until the logs are empty, and run the whole suite and the drift gates before raising the pull request.

5. Measurements

Every number this task claims about a tree, with the command that produced it and the commit it was measured at.

What Value Command Commit
Nightly baseline, 2026-09-29 linux-clang-debug 1 test failed of 87; defects: trading.api 4, refdata.core 3329, ore.core 99, analytics.core 214 gh run view --job 109252162647 --log main at 3632865c
ores.trading.api.tests, before 3 conditional jumps on an uninitialised date valgrind ... ores.trading.api.tests this branch, before the fix
ores.analytics.core.tests, before 190 uninitialised reads (111 conditional, 79 value) in std::to_string(double) from the sqlgen INSERT same this branch, before the fix
ores.ore.core.tests, before 43 defects: 40 in yyjson writing bond_trs::funding_rate, 3 in the date reflector reading bond_future's dates tmp/ores.ore.core.tests.valgrind (CDash page) main at 3632865c
ores.trading.api.tests, after valgrind log empty, 426 assertions in 99 test cases pass valgrind ... ores.trading.api.tests this branch
The 3329 CI leak stacks against the final suppression file 0 unmatched, of 203 rules build/scripts/check_valgrind_suppressions.py this branch
ores.analytics.core.tests, after valgrind log empty, 140 assertions in 39 test cases pass same this branch
ores.ore.core.tests, after valgrind log empty, 436970 assertions in 576 test cases pass same this branch
ores.refdata.core.tests, after valgrind log empty, 4085 assertions in 243 test cases pass same this branch
Full suite on a recreated database 89 of 89 pass; a second run on the same database fails ores.iam.core.tests on residue its first run left ctest --preset linux-clang-debug-make -j 4 this branch
Codegen suite 1001 passed pytest projects/ores.codegen/tests -q this branch
Catalogue sweep no file this branch touched would change check_component_drift.py --sweep --dry-run this branch
Census after the fix 236 non-nullable columns declare no default; 149 are int (synthesised by codegen), 87 are not build/scripts/census_generated_defaults.py this branch

6. Notes

  • The CDash pages saved under tmp/ are truncated at about a megabyte, so the analytics page listed 64 of the 214 defects and the first fix left 146 behind in a second entity. The complete set came from re-running each binary locally, which is also what proves the fix.
  • The uninitialised values are all created by a stack frame in the test or the generated generator, and read in a write path: sqlgen stringifies every entity member to build the INSERT, so an entity built by a generated generator that skips a column is read whole.
  • bond_trs::funding_rate and bond_repo::repo_rate are nullable: true with a bare double, so the generated entity wraps them in std::optional and the generated mapper maps zero to nullopt. The declared default of 0.0 is that convention, not a value the domain intends.
  • The full suite passes on a recreated database, and only on a recreated database: ores.iam.core.tests is not idempotent. Its first run leaves three seed profiles open, and the second run fails three cases on seed_profiles_code_uniq_idx. That is a pre-existing defect of the tests rather than of this change, and it is filed as The iam seed-profile eventing tests only pass once per database. It also cost this task two verification runs: the residue was misread at first as damage the valgrind runs had done.
  • The gate refuses the nullable shape rather than synthesising a default, because the value that means "absent" is the model's to state. For the non-nullable shape the same choice is open and is filed as a capture; this task declares the defaults the reported instances needed and records the census, so the decision is taken with the size known.

7. PRs

PR Title
#2380 Address the valgrind findings: indeterminate members and library leaks

8. Review

# Comment summary File Decision Notes
1 t1 defaults to 0.0 although the model's own text says the upper bound is 1.0 in every shipped example, so the synthetic interval is degenerate. ores.analytics.credit_simulation_matrix_config.org Fixed t1 declares 1.0 now, the component is regenerated, and the analytics valgrind run was repeated against the new value.
2 The gate's type set includes std::int16_t, std::int32_t, std::uint16_t and std::uint32_t, which the generated mapper's nullable-numeric convention does not cover, so for those the rule demands a default for a shape nothing implements. org_loader.py Fixed The set is now exactly the types is_nullable_numeric maps zero to nullopt for, plus the two chrono value types. A nullable bare type outside it is a modelling question this rule cannot answer, and the comment says so.
3 The two libpq suppression rules carried no allocator anchor and wildcards on both sides, so they would hide any still-reachable allocation in a PQexec stack, including a PGresult the caller never clears. custom.supp Fixed Both rules pin the allocator (malloc, calloc) and the libpq entry point, and their comments state the width: they match reachable blocks only, so a block that becomes unreachable is definitely lost and still surfaces. The matcher still covers all 3329 reported stacks.
4 The task says fifteen model columns where the diff adds twenty-four. this file Fixed Corrected to twenty-four, in the task and in the pull request body.
5 The measurements cite scripts under build/hotfix_red/, which the branch does not carry, so the census and the suppression coverage are not reproducible from the pull request. this file, the census capture Fixed Both are now committed: build/scripts/census_generated_defaults.py and build/scripts/check_valgrind_suppressions.py, and every reference points at them.
6 The gate's comment and its test attribute the analytics reads to credit_simulation_config.seed, a column the truncated CDash page does not name. org_loader.py, the test Declined The attribution is from the local run rather than the page: the valgrind log names that test's frame as the origin of the reads, and the census lists seed and paths as its nullable bare ints. The comment names it as an example among others.

9. Result

The four suites the nightly reported are clean under valgrind: each log holds no error and no leak record. Twenty-four model columns declare the default their generated member needs, the nullable shape that caused several of them is refused by org_loader, and the still-reachable library allocations are suppressed with the reason each is not a leak.

The full suite passes 89 of 89 on a recreated database, the codegen suite passes 1001 tests, and the sweep over the catalogue reports no file this branch changed as drifted. What is left of the class is filed as a capture with the census: 87 non-nullable columns still generate an indeterminate member.

Emacs 29.3 (Org mode 9.6.15)