Uses a model checker's counterexample trace to localize the fault in the model, propose a fix, and propagate that fix back to the source code. Use when a model checker (TLC, NuSMV, Spin) finds a violation and you need to turn the trace into a code change, not just understand it.
Install with Tessl CLI
npx tessl i github:santosomar/general-secure-coding-agent-skills --skill model-guided-code-repair100
Quality
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
A counterexample trace says "here's a sequence of steps that breaks your property." The repair question is: which step shouldn't have been allowed? Strengthen that step's guard, and the trace is blocked.
For TLA+-specific repair, → tlaplus-guided-code-repair. This skill is the general workflow across checkers.
┌─────────────────┐ violation ┌──────────────┐
│ Model checker │────────────────────▶│ Trace │
│ (TLC/NuSMV/Spin)│ │ analysis │
└─────────────────┘ └──────┬───────┘
▲ │
│ ┌─────▼────────┐
│ │ Pick culprit │
│ │ step │
│ └─────┬────────┘
│ check new model │
└────────────────────────────────┌─────▼────────┐
│ Strengthen │
│ guard │
└─────┬────────┘
│
┌─────▼────────┐
│ Propagate to │
│ source code │
└──────────────┘The trace is a sequence of states s₀ → s₁ → ... → sₙ where sₙ violates the property. Walk backwards from sₙ:
sₙ, what's wrong? (Which clause of the invariant is false?)sₙ₋₁ → sₙ, what action fired? Did that action make the bad thing true?sₙ₋₁ → sₙ is a candidate culprit.sₙ₋₁), keep walking back.The culprit is the latest step that could have prevented the violation — the one where adding a guard would have blocked the trace.
Three kinds of fix, from least to most invasive:
| Fix kind | What it does | When |
|---|---|---|
| Strengthen guard | Action can't fire in the bad state | The action was legitimate, just fired too early |
| Add missing action | New action to restore the invariant | Something should have happened but didn't |
| Change the property | The property was wrong, not the system | The "violation" is actually intended behavior |
For a strengthened guard: The new conjunct should be the negation of whatever was true at the culprit step that shouldn't have been. Look at sₙ₋₁ — what predicate would have excluded it?
Re-run the checker. Three outcomes:
Also check: did the fix introduce deadlock? Run with deadlock checking on.
The model action maps to some code region (you built that mapping during → program-to-tlaplus-spec-generator or equivalent). The new guard conjunct translates:
| Model guard | Code change |
|---|---|
/\ lock_holder = self | Add assert(lock_held_by_me) or wrap in lock |
/\ Len(queue) < Max | Add if (queue.size() >= Max) return/wait |
/\ state[p] = "ready" | Add state check: if (p.state != READY) return |
/\ msg.epoch = current_epoch | Add epoch comparison; drop stale messages |
The code change should be at least as strong as the model guard. If the model says x > 0 and you add x > 5 in code — fine, stronger. If you add x >= 0 — weaker, the bug may still exist.
Model (NuSMV): Reader-writer lock. Property: AG !(reading & writing) — never read and write simultaneously.
Trace:
State 1: readers=0, writers=0, reading=FALSE, writing=FALSE
State 2: readers=1, writers=0, reading=TRUE, writing=FALSE [StartRead]
State 3: readers=1, writers=1, reading=TRUE, writing=TRUE [StartWrite] ← VIOLATIONWalk back: State 3 violates. Action StartWrite at 2→3 set writing=TRUE while reading=TRUE. That's the culprit.
Current StartWrite guard: writers = 0. It only checks no other writers. It doesn't check readers.
Fix: StartWrite guard becomes writers = 0 & readers = 0.
Re-check: Property holds. Deadlock check: fine (readers eventually finish → guard becomes true).
Code: Find the acquire_write_lock function. It currently checks if (writer_count == 0). Add && reader_count == 0.
FALSE makes any safety property hold and every liveness property fail. Check the action still fires on good paths.## Counterexample (key steps)
<state i> → [Action] → <state i+1> <-- culprit
## Violation
At state <n>: <which clause of the property is false>
## Culprit
Action: <name>
Fired in state: <key variables>
Why it shouldn't have: <predicate that should have blocked it>
## Model fix
<before/after of the action guard>
## Re-check result
<property status, deadlock status, new counterexamples if any>
## Code fix
File: <path>
Change: <before/after>
Mapping: model action <X> ↔ code <function/region>47d56bb
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.