skills/verification/invariant-inference/SKILL.md
Infers likely loop invariants and function contracts by observing execution traces, synthesizing candidates, and checking them inductively. Use when a verifier rejects a loop because the invariant is missing or too weak, when a Daikon-style tool is needed, or before translating code to a verification language.
npx skillsauth add santosomar/general-secure-coding-agent-skills invariant-inferenceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A loop invariant is what stays true every time you hit the loop head. Verifiers need it stated; programmers rarely state it. This skill guesses it from evidence.
| Route | How it works | Good at | Bad at |
| ------------------ | ----------------------------------------------------- | ------------------------------- | ------------------------------- |
| Dynamic (Daikon-style) | Run the code, record state at loop head, generalize patterns | Concrete relationships in traces | Generalization — might be coincidence |
| Static (template-based) | Guess invariants of form ax + by ≤ c, solve for a,b,c | Sound by construction | Limited to the template shapes |
Do both: dynamic finds candidates fast; static checks them.
Template library (what to look for in the dumps):
| Template | Example |
| --------------------------- | ------------------------------------ |
| Constant | n == 10 in every observation |
| Range | 0 <= i <= n |
| Linear relation | i + j == n, 2*i == k |
| Ordering | i < j, a[i] <= a[j] |
| Membership | x in {0, 1, 2} |
| Collection property | sorted(a[:i]), sum(a[:i]) == s |
| Implication | flag => (x > 0) |
(iter, locals) at loop head.x == 5 in every trace but your test inputs all had n == 5 — it's not invariant, it's your test bias. Vary n.I: does I ∧ guard ∧ body ⟹ I'? Use an SMT solver or just plug into the verifier.I ∧ ¬guard ⟹ postcondition? If not, I is true but useless — you need a stronger one.Loop:
def find_max(a):
m = a[0]
i = 1
while i < len(a):
if a[i] > m:
m = a[i]
i += 1
return m # post: m == max(a)
Traces (input a = [3, 1, 4, 1, 5]):
| iter | i | m | a[:i] | | ---- | - | - | ----------- | | 0 | 1 | 3 | [3] | | 1 | 2 | 3 | [3, 1] | | 2 | 3 | 4 | [3, 1, 4] | | 3 | 4 | 4 | [3, 1, 4, 1]| | 4 | 5 | 5 | [3,1,4,1,5] |
Candidate patterns (mined):
1 <= i <= len(a) ✓ all rowsm in a ✓ all rowsm == max(a[:i]) ✓ all rows — this is the onem >= a[0] ✓ all rows — true but weaker than the aboveInductive check on m == max(a[:i]):
i=1, m=a[0] → max(a[:1]) = a[0] = m. ✓m == max(a[:i]). Body sets m' = max(m, a[i]) and i' = i+1. Then m' = max(max(a[:i]), a[i]) = max(a[:i+1]) = max(a[:i']). ✓i == len(a). Then m == max(a[:len(a)]) == max(a). ✓Invariant: 1 <= i <= len(a) ∧ m == max(a[:i]).
a[i] * a[j] < n — nonlinear. → abstract-invariant-generator with a polynomial domain, or hand-write.true is inductive; it proves nothing.i in {1, 2, 3, 4, 5} holds on your one trace — but the real invariant is 1 <= i <= len(a). Prefer the most general candidate that survives.## Candidates (from traces)
<template> : <instantiation> — held on <N>/<N> observations
## Inductive check
<candidate> — <✓ inductive | ✗ fails: <counterexample>>
## Sufficient for postcondition
<candidate> — <✓ | ✗ too weak, missing: <what>>
## Recommended invariant
<the winner — conjunction of surviving, sufficient, minimal candidates>
development
Extracts human-readable pseudocode from a verified formal artifact (Dafny, Lean, TLA+) while preserving the verified properties as annotations, so the proof-carrying logic can be reimplemented in a production language. Use when porting verified code to an unverified target, when documenting what a formal spec actually does, or when handing a verified algorithm to an implementer.
development
Translates natural-language or pseudocode descriptions of concurrent and distributed systems into TLA+ specifications ready for the TLC model checker. Identifies state variables, actions, type invariants, safety properties, and liveness properties from the description. Use when formalizing a protocol, when the user describes a distributed algorithm to verify, when designing a consensus or locking scheme, or when starting formal verification of a concurrent system.
testing
Reduces a TLA+ model so TLC can actually check it — shrinks constants, adds state constraints, abstracts data, or applies symmetry — when the state space is too large to enumerate. Use when TLC runs out of memory, when checking takes hours, or when a spec works at N=2 and you need confidence at larger scale.
development
TLA+-specific instance of model-guided repair — reads a TLC error trace, identifies the enabling condition that should have been false, strengthens the corresponding action, and maps the fix to source code. Use when TLC reports an invariant violation or deadlock and you have the code-to-TLA+ mapping from extraction.