tlaplus-model
Skill27 starsChanged 49 days agoMITView on GitHub
TLA+/TLC model-checking skill. Use it whenever TLA+, PlusCal, Quint, Apalache, TLC, a .tla/.cfg file, or model-checker output appears. That includes a pasted TLC error, \"Invariant X is violated\", or a State 1/State 2… counterexample trace the user wants explained or triaged (real bug, model artifact, or too-strong property), even if it looks answerable by reading alone. Also use it to write, run, scale, or fix specs; debug slow or exploding TLC runs; check invariants, liveness, and fairness; replay production logs against a spec; or \"model\"/\"exhaustively check\" concurrent code, protocols, queues, caches, schedulers, leases, retries, and state machines for races, lost updates, double-spends, deadlocks, and ordering bugs, even for a design with no code yet. Not for runtime race detectors, Jepsen, Alloy, Lean/Coq proofs, or UI state-machine libraries. Pair with formal-verify for the full bug-hunt loop.
In this repository6 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- loreSkill
- questSkill
- formal-verifySkill
- lean-modelSkill
- proof-simplifySkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 1 is distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
When the task is a full bug hunt, follow formal-verify for the outer loop: scope, properties, correspondence map, reproduction, fix, and …
- Distinctive: line 67
Prove the model can fail: Before trusting a green run:
- Very specific: line 79
Read the counterexample: Use trace.md, then tell the story in domain terms.
- Very specific: line 83
add a SYMMETRY set (safety only),
- Very specific: line 84
add a VIEW to drop auxiliary variables,
- Very specific: line 85
add a CONSTRAINT (and report it),
- Very specific: line 87
use Apalache for bounded symbolic checks (references/apalache-quint.md).
- Very specific: line 94
Every action must mention every variable (primed, or in UNCHANGED).
- Very specific: line 98
Only the agent's *own* modeling choices may change freely.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
tlaplus-model
TLA+ describes a system as state plus actions (atomic steps). TLC explores every interleaving of those actions from every initial state, within the bounds you give it, and returns the shortest trace that breaks a property. That exhaustive search is what finds races no test would hit. The skill's job is to make the model faithful, small, and able to fail.
When the task is a full bug hunt, follow formal-verify for the outer loop: scope, properties,
correspondence map, reproduction, fix, and report. This skill covers the modeling and checking inside it.
Setup
S=${CLAUDE_SKILL_DIR}/scripts
$S/setup_tla.sh --with-jre # tla2tools.jar + CommunityModules (+ portable JRE if no Java)
$S/tlc.sh Spec.tla --sany-only # parse/level-check: the fast inner loop
$S/tlc.sh Spec.tla --config MC.cfg # model check; writes .tlc-out/MC/{tlc.log,cex.json,trace.md}
$S/tlc.sh Spec.tla --config MC.cfg -- -simulate num=200000 -depth 60 # random walks for big modelstlc.sh runs SANY, then TLC with -workers auto, and saves the JSON counterexample. It also
prints a step table (action, code pointer, changed variables) produced by parse_tlc_trace.py.
It exits with TLC's code: 0 ok, 11 deadlock, 12 invariant, 13 temporal/action property,
10 ASSUME/POSTCONDITION false, 150 parse error. Full CLI notes: references/tlc-cli.md.
The modeling loop
Start from a pattern.
assets/patterns/has seven small, runnable, checked specs. Each has a fix toggle and three configs (MC.cfgpasses,MC_bug.cfgfails, andMC_sanity.cfgfails on purpose). Copy the closest one, then readreferences/patterns.md.Code shape Pattern read-modify-write, counters, check-then-act, locks LostUpdateasync/await, cache-aside, fill vs. invalidate CacheAside(+ trace validation harness)retries, webhooks, at-least-once delivery, dedup tables IdempotentRetryleases, job claiming, leader leases, GC pauses, fencing tokens JobLeasestreams, queues, batching, backpressure, end-of-stream BatchPipelineDAG executors, build systems, workflow engines, graph invariants DagSchedulerpersisted status fields, order/payment/ticket lifecycles OrderStateMachinePick the atomicity. One action is one atomic region of the real code:
- the code between two
awaits, - a locked section,
- a single shared read or write,
- a single SQL statement, or
- one message handler.
Merging regions the code runs separately hides exactly the race you want. Put
\* src: path:linesabove each action.formal-verifyuses these tags for its correspondence map, drift checks, and trace tables.- the code between two
- Model only what the properties need. Collections become sets or functions, and
payloads become small ranges. Keep 2 actors and 2–3 values at first. Time becomes an
Expire/Timeoutaction rather than a clock. The environment (network drop, crash, pause) becomes explicit actions, so their interleavings are explored too. Write properties before running.
- Invariants hold in every state.
- Action properties constrain every step, e.g.
[][status' # status => <<status, status'>> \in Allowed]_vars. - Liveness (
<>,~>) needs fairness (see step 6).
Guard "coherence" invariants with a quiescence predicate when mid-flight staleness is legitimate.
Prove the model can fail. Before trusting a green run:
- (a) a sanity invariant that must be violated (for example, "the done state is never reached"), which shows the interesting states are reachable;
- (b) the bug toggle: turn the fix off and confirm the target property fails;
- (c)
-coverage 1shows no action that is never enabled.
A spec that has never failed is not evidence.
- Add fairness honestly. Liveness needs
WF_vars(A)orSF_vars(A)on the actions the real system guarantees will eventually run (a scheduler runs ready work, a consumer keeps polling). Never put fairness on environment faults, and never add it only to make a property pass. Keep liveness and symmetry in separate configs: symmetry reduction is unsound for liveness. - Read the counterexample. Use
trace.md, then tell the story in domain terms. Decide between a real bug, a model artifact (a missing guard, or steps merged or split wrongly), and a property that is too strong. Hand real bugs back toformal-verifyfor reproduction. - Scale carefully. Grow one bound at a time. If the state space explodes:
- add a
SYMMETRYset (safety only), - add a
VIEWto drop auxiliary variables, - add a
CONSTRAINT(and report it), - switch to
-simulate, - use Apalache for bounded symbolic checks (
references/apalache-quint.md).
- add a
Hard-won rules
None == -1needsEXTENDS Integers; Naturals has no unary minus. Mixing strings, model values, and integers in comparisons is legal only for model values; otherwise use one type per variable.IF … THEN … ELSE …swallows every conjunct after it. Parenthesize:x' = (IF c THEN a ELSE b).- Every action must mention every variable (primed, or in
UNCHANGED). A missing one means "any value", which gives states that make no sense.TypeOKcatches it. - A terminating system deadlocks by design. Add an explicit
Terminated == Done /\ UNCHANGED varsdisjunct rather than disabling deadlock checking;-deadlockhides real stuck states. - Only the agent's own modeling choices may change freely. Frozen properties (
Properties.tla),CONSTRAINTs, and fairness changes are reported in the findings, never quietly edited.
Beyond checking a model
- Trace validation (strongest drift defense): log events from the real system as ndjson
and replay them through the spec.
assets/patterns/CacheAside/TraceCacheAside.tlais a working harness (trace-ok.ndjsonis accepted;trace-drift.ndjsonis rejected with exit 10). Seereferences/trace-validation.md. - Model-based testing: generate traces from the model and drive the real code with them
(
quint run --mbt, or TLC-simulatewith-dumpTrace json). - Refinement: show a detailed spec implements a simple one (
INSTANCE … WITH, checked as a PROPERTY).proof-simplifyuses refinement to show a simpler design keeps the guarantees.
Reference files
references/patterns.md— the seven patterns: the bug each one finds, the fix, and how to adapt it.references/tlc-cli.md— TLC/SANY/PlusCal flags,.cfgkeywords, exit codes, performance.references/pitfalls.md— vacuity, over- and under-modeling, liveness traps, agent reward hacking.references/trace-validation.md— logging events from code and replaying them in TLC.references/apalache-quint.md— when and how to use Apalache and Quint instead of TLC.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

