skills/verification/tlaplus-guided-code-repair/SKILL.md
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.
npx skillsauth add santosomar/general-secure-coding-agent-skills tlaplus-guided-code-repairInstall 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.
Specialization of → model-guided-code-repair for TLC. The general loop is the same; this skill covers the TLC-specific mechanics: reading error traces, understanding action enablement, and the .cfg levers.
TLC prints the trace as numbered states. Between states, it names which Next disjunct fired:
Error: Invariant Inv is violated.
State 1: <Initial predicate>
pc = [p1 |-> "start", p2 |-> "start"]
lock = "free"
...
State 2: <Read(p1) line 42, col 3 ...>
pc = [p1 |-> "locked", p2 |-> "start"]
...
State 3: <Read(p2) line 42, col 3 ...>
...
State 4: <Write(p1) line 50, col 3 ...> ← invariant fails here
Key TLC-specific info:
Next disjunct.-difftrace).\E p : Foo(p) — TLC tells you which p.In TLA+, every action is guard /\ effect. The guard is the conjunction of all unprimed conjuncts. The culprit's guard was true when it shouldn't have been.
At the culprit step, ask: what unprimed predicate, if added to this action's guard, would have been FALSE in the pre-state? That predicate is your fix candidate.
Extracting the candidate from the pre-state:
Inv is about to be violated?| TLC error | Likely culprit | Typical fix |
| ----------------------------------- | --------------------------------------------- | ------------------------------------------------ |
| Invariant violated | Action that wrote the invariant-breaking value | Add guard preventing that write |
| Deadlock | All actions' guards are false — none can fire | Weaken a guard, or add a recovery action |
| Temporal property violated (liveness) | Missing fairness, or an action that blocks progress | Add WF_vars(Action) or fix the blocker |
| Action property [][A]_v violated | Some Next step changed v in a way A forbids | That disjunct needs to respect A |
Deadlock is the opposite of invariant violation. For invariants, guards are too weak. For deadlock, guards are too strong. Don't tighten guards after a deadlock.
TLA+ spec: Two-phase commit coordinator. Invariant: Inv == committed => \A p \in Participants : vote[p] = "yes".
TLC trace:
State 1: <Init>
vote = [p1 |-> "none", p2 |-> "none"]
committed = FALSE
State 2: <Vote(p1) ...>
vote = [p1 |-> "yes", p2 |-> "none"]
State 3: <Commit ...> ← Inv violated: committed=TRUE but vote[p2]="none"
committed = TRUE
Culprit: Commit at step 2→3. Its pre-state: vote = [p1 |-> "yes", p2 |-> "none"]. Commit fired with p2 un-voted.
Current action:
Commit ==
/\ committed = FALSE
/\ committed' = TRUE
/\ UNCHANGED vote
No check on votes at all. Missing guard: all participants voted yes.
Fix:
Commit ==
/\ committed = FALSE
/\ \A p \in Participants : vote[p] = "yes" \* NEW
/\ committed' = TRUE
/\ UNCHANGED vote
Re-run TLC: Inv holds. Deadlock check: Commit is now disabled when p2 votes "no" — is there an Abort action? If not, add one (this is the "add missing action" repair).
Code mapping: Commit ↔ Coordinator.finalize(). The code:
def finalize(self):
if not self.committed:
self.committed = True
self.broadcast("COMMIT")
Missing the vote check. Fix:
def finalize(self):
if not self.committed and all(v == "yes" for v in self.votes.values()):
self.committed = True
self.broadcast("COMMIT")
Sometimes the model's action was wrong, not the code. Two cases:
Before touching code, verify: does the TLA+ action actually match the code? Go look.
Commit == /\ Inv /\ ... makes Inv vacuously preserved by Commit. That's cheating — and usually causes deadlock.Next for every disjunct that writes the variables in the violated clause.UNCHANGED when adding new variables during repair. Every action must account for every variable.Commit is one TLA+ step but three code statements, the code fix might need a lock.## TLC trace (relevant steps)
State <i>: <vars>
→ <Action(params)> [<file:line>]
State <i+1>: <vars>
## Culprit
Action: <name>
Pre-state: <the vars that mattered>
Missing guard: <predicate>
## TLA+ fix
```tla
<before>
--- becomes ---
<after>
Invariant: <pass/fail> Deadlock: <pass/fail> Other properties: <status>
file:function — the TLA+ action's code counterpart Before/after: <diff> Atomicity note: <does the code need additional synchronization to match the TLA+ atomicity?>
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.
testing
Translates specifications into temporal logic formulas (LTL, CTL, or TLA) by matching the specification's shape to the right logic and operators. Use when formalizing requirements for any model checker, when choosing between LTL and CTL for a property, or when the user has a temporal claim and doesn't know which operators express it.