Add curated security mutations and assertion checks
🔎 Visual review of8798df35: explainer, review flags and prompts to ask an AI about the change
A passing test can still miss a security regression if its assertions are weak. This change adds a curated Go-overlay mutation runner (scripts/test-security-mutations.py), seven security mutants, and regression assertions, with a deliberately weak control that is expected to survive.
The mutants cover the on-chain supermajority boundary and impossible signer counts in HasSuperMajority, global and per-chain signing halt activation in CheckSigningHalt, peer attestation signature verification and replay handling in AttestationState.AddAttestation, and SafeSub direction. New targeted tests are TestSecurityMutationQuorumBoundary, TestSecurityMutationSigningHaltBoundary and TestSecurityMutationSafeSubConservation. The attestation mutants use the existing observer tests. The TestMutationWeakUnanimity control runs against the quorum mutant and must survive, which shows that the runner does not count every mutation as killed.
The runner applies each mutation through a Go overlay, so it never changes the checkout. Each mutant needs exactly one source anchor; if the anchor is missing or appears more than once, the mutant is marked invalid. A mutant counts as killed only when a named test fails. The run exits nonzero on any unexpected survivor, invalid mutant, timeout or failed baseline. A single suite deadline bounds the whole run, and a command that times out has its whole process group killed. Builds are capped at four processors and two package builds, and runs are serialized through a shared build lock.
All seven mutants are now in the fast set. The new security-mutations-fast merge request job (long-test stage, 1800 s budget, 40 m job timeout) runs them and always saves mutations-fast.json as an artifact. The job is skipped for fork MRs because the observer baseline needs tss-lib-private.
Validation: scripts/test-security-mutations-results.json records local runs at an earlier commit. There, the fast set killed 5/5 mutants and the deep set killed 7/7, and the control survived both times. That snapshot was taken before the attestation mutants were retargeted to AddAttestation, the chain-halt and signer-count mutants were moved into the fast set, and the deep CI job was dropped. For the current configuration, the CI mutations-fast.json artifact is the record. This topic is also included in draft !5181 (closed). This MR will run its own CI against develop; review and merge coordination should avoid landing the same change twice.