Parity broken on purpose
The port to F´ had to match the demo's counts exactly. In 24 of 60 cases it could not, and the requirement was rewritten before any code.
The voting core is Rust. A lot of real flight software is built on F´, the framework from JPL, so the next step was four F´ components around that core: a redundancy manager, a health registry, a replica host and a fault injector. Every decision still goes through the Rust core over a C interface, so no voting logic was written twice.
The requirement was parity. On the bench schedules, the F´ version had to give the same correct, caught and wrong counts as the demo for the same seed. It could not, and the reason turned up in design. The demo kills a replica from the voter, so the voter knows at once. In F´ a replica that dies simply stops answering. A missing reply counts as absent, and three missed cycles in a row mark the replica lost. When that death leaves one replica, the cycles spent waiting end as caught disagreements.
So the requirement was rewritten and approved first: each kill or hang fault may cost up to three correct results. The system test runs 60 cases, and 36 match the demo exactly. In the other 24, a kill that leaves one replica costs 2 correct results and adds 3 caught, as predicted. Wrong results match in all 60 cases. The expected counts are generated from the demo, and CI fails if they go stale.
Matching at all took one change. A first draft decided each cycle at the start of the next. Deciding as soon as every answer is in is what lined the counts up.
The parity test also found a real bug. F´ returns 0 for a parameter that was never loaded, and a known-answer period of 0 means a test every cycle. The manager now falls back to the model’s defaults: a test every 64 cycles, and a saved state every 1,000.