agent-formal-verify 0.0.0-stage → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +157 -0
- package/LICENSE +232 -0
- package/README.md +92 -2
- package/opencode.mjs +45 -0
- package/package.json +42 -4
- package/skills/formal-verify/SKILL.md +125 -0
- package/skills/formal-verify/examples/kani-template/Cargo.lock +7 -0
- package/skills/formal-verify/examples/kani-template/Cargo.toml +7 -0
- package/skills/formal-verify/examples/kani-template/src/lib.mutations +11 -0
- package/skills/formal-verify/examples/kani-template/src/lib.rs +63 -0
- package/skills/formal-verify/examples/lean-template/Model/BoundedQueue.lean +98 -0
- package/skills/formal-verify/examples/lean-template/Model/BoundedQueue.mutations +11 -0
- package/skills/formal-verify/examples/lean-template/Model/BoundedQueue.statements +231 -0
- package/skills/formal-verify/examples/lean-template/lake-manifest.json +6 -0
- package/skills/formal-verify/examples/lean-template/lakefile.toml +6 -0
- package/skills/formal-verify/examples/lean-template/lean-toolchain +1 -0
- package/skills/formal-verify/examples/tla-template/BoundedQueue.matrix +20 -0
- package/skills/formal-verify/examples/tla-template/BoundedQueue.mutations +26 -0
- package/skills/formal-verify/examples/tla-template/BoundedQueue.tla +42 -0
- package/skills/formal-verify/references/lean-brief.md +57 -0
- package/skills/formal-verify/references/rust-brief.md +81 -0
- package/skills/formal-verify/references/setup.md +85 -0
- package/skills/formal-verify/references/tla-brief.md +62 -0
- package/skills/formal-verify/scripts/kani-check.sh +8 -0
- package/skills/formal-verify/scripts/kani.py +250 -0
- package/skills/formal-verify/scripts/lean-check.sh +171 -0
- package/skills/formal-verify/scripts/lean-statements.py +135 -0
- package/skills/formal-verify/scripts/lean-tools.sh +15 -0
- package/skills/formal-verify/scripts/mutate.py +274 -0
- package/skills/formal-verify/scripts/setup.sh +35 -0
- package/skills/formal-verify/scripts/tlc-matrix.sh +172 -0
- package/skills/formal-verify/scripts/tlc-tools.sh +47 -0
- package/skills/formal-verify/scripts/tlc-trace.py +64 -0
- package/skills/setup/SKILL.md +24 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copyright (c) 2026 Michael Denyer
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Model-checks TLA+ specs with TLC over a matrix of constants read from a
|
|
5
|
+
# matrix file, and prints one PASS or FAIL line per run with its distinct
|
|
6
|
+
# state count, then a SUMMARY line with the totals. A failing run prints TLC's
|
|
7
|
+
# error and its error trace reduced to the variables each step changed
|
|
8
|
+
# (tlc-trace.py).
|
|
9
|
+
#
|
|
10
|
+
# Matrix file, one directive per line (# starts a comment):
|
|
11
|
+
# spec <path.tla> the spec for the runs below, relative to the file;
|
|
12
|
+
# TLC checks the formula it names Spec
|
|
13
|
+
# check <cfg line> an INVARIANTS or PROPERTIES line, kept until the next spec
|
|
14
|
+
# reach <Operator> | <label-substring> a witness in the first matching run
|
|
15
|
+
# run <label> | <Name=Value ...> one TLC run with those CONSTANTS
|
|
16
|
+
#
|
|
17
|
+
# Usage: tlc-matrix.sh <matrix-file> [label-substring]
|
|
18
|
+
# TLC_JAR=<path> use this existing tla2tools.jar
|
|
19
|
+
# TLC_CACHE=<dir> where the pinned jar lives (default ~/.cache/tla)
|
|
20
|
+
# JAVA=<path> the java binary (default: java on PATH)
|
|
21
|
+
# TLC_WORKERS=<n> TLC worker threads (default auto)
|
|
22
|
+
set -uo pipefail
|
|
23
|
+
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
24
|
+
# shellcheck source=skills/formal-verify/scripts/tlc-tools.sh
|
|
25
|
+
source "$HERE/tlc-tools.sh"
|
|
26
|
+
TLC_WORKERS=${TLC_WORKERS:-auto}
|
|
27
|
+
|
|
28
|
+
[ "$#" -ge 1 ] && [ "$#" -le 2 ] || { echo "usage: tlc-matrix.sh <matrix-file> [label-substring]"; exit 2; }
|
|
29
|
+
matrix=$1
|
|
30
|
+
[ -f "$matrix" ] || { echo "FAIL $matrix does not exist"; exit 1; }
|
|
31
|
+
only=${2:-}
|
|
32
|
+
MATRIX_DIR=$(cd "$(dirname "$matrix")" && pwd)
|
|
33
|
+
|
|
34
|
+
require_tlc_runtime || exit $?
|
|
35
|
+
tlc_jar_ready || exit $?
|
|
36
|
+
|
|
37
|
+
OUT=$(mktemp -d)
|
|
38
|
+
trap 'rm -rf "$OUT"' EXIT
|
|
39
|
+
fail=0
|
|
40
|
+
run=0
|
|
41
|
+
passed=0
|
|
42
|
+
total=0
|
|
43
|
+
spec=""
|
|
44
|
+
checks=""
|
|
45
|
+
reaches=()
|
|
46
|
+
runs=()
|
|
47
|
+
reach_count=0
|
|
48
|
+
reach_passed=0
|
|
49
|
+
|
|
50
|
+
check_run() {
|
|
51
|
+
local label=$1 consts=$2 operator=${3:-} name dir pairs c states wrapper invariant status trace
|
|
52
|
+
local workers=$TLC_WORKERS extra=()
|
|
53
|
+
[ -n "$spec" ] || { echo "FAIL run '$label' before any spec line"; fail=1; return; }
|
|
54
|
+
case $label in *"$only"*) ;; *) return ;; esac
|
|
55
|
+
if [ -n "$operator" ]; then reach_count=$((reach_count + 1)); else run=$((run + 1)); fi
|
|
56
|
+
name=$(basename "$spec" .tla)
|
|
57
|
+
[ -f "$MATRIX_DIR/$spec" ] || { echo "FAIL $name $label: spec $spec not found"; fail=1; return; }
|
|
58
|
+
# A directory per run, so a run sees only the modules beside its own spec.
|
|
59
|
+
dir=$(mktemp -d "$OUT/run.XXXXXX")
|
|
60
|
+
cp "$MATRIX_DIR/$(dirname "$spec")"/*.tla "$dir/"
|
|
61
|
+
wrapper=$name
|
|
62
|
+
if [ -n "$operator" ]; then
|
|
63
|
+
# Neither generated identifier may shadow an imported declaration or module.
|
|
64
|
+
while :; do
|
|
65
|
+
wrapper=Reach$(python3 -c 'import uuid; print(uuid.uuid4().hex)')
|
|
66
|
+
invariant=${wrapper}Negated
|
|
67
|
+
grep -Ewq "$wrapper|$invariant" "$dir/"*.tla || break
|
|
68
|
+
done
|
|
69
|
+
printf '%s\n' "---- MODULE $wrapper ----" "EXTENDS $name" "$invariant == ~$operator" "====" > "$dir/$wrapper.tla"
|
|
70
|
+
workers=1
|
|
71
|
+
extra=(-deadlock)
|
|
72
|
+
fi
|
|
73
|
+
read -ra pairs <<< "$consts"
|
|
74
|
+
{
|
|
75
|
+
echo "CONSTANTS"
|
|
76
|
+
# bash 3.2 treats an empty array as unset under set -u.
|
|
77
|
+
for c in ${pairs[@]+"${pairs[@]}"}; do echo " ${c%%=*} = ${c#*=}"; done
|
|
78
|
+
echo "SPECIFICATION Spec"
|
|
79
|
+
if [ -n "$operator" ]; then echo "INVARIANT $invariant"; else printf '%s\n' "$checks"; fi
|
|
80
|
+
} > "$dir/MC.cfg"
|
|
81
|
+
status=0
|
|
82
|
+
"$JAVA" -XX:+UseParallelGC -cp "$TLC_JAR" tlc2.TLC -workers "$workers" ${extra[@]+"${extra[@]}"} -cleanup -metadir "$dir/states" \
|
|
83
|
+
-config "$dir/MC.cfg" "$dir/$wrapper.tla" > "$dir/tlc.log" 2>&1 || status=$?
|
|
84
|
+
if [ -n "$operator" ]; then
|
|
85
|
+
if [ "$status" -eq 12 ] && grep -Fxq "Error: Invariant $invariant is violated by the initial state:" "$dir/tlc.log"; then
|
|
86
|
+
trace=1
|
|
87
|
+
elif [ "$status" -eq 12 ] && grep -Fxq "Error: Invariant $invariant is violated." "$dir/tlc.log"; then
|
|
88
|
+
trace=$(grep -c '^State [0-9][0-9]*:' "$dir/tlc.log")
|
|
89
|
+
else
|
|
90
|
+
trace=0
|
|
91
|
+
fi
|
|
92
|
+
if [ "$trace" -gt 0 ]; then
|
|
93
|
+
echo "PASS $name reach $operator $label: shortest trace $trace states ($((trace - 1)) transitions)"
|
|
94
|
+
reach_passed=$((reach_passed + 1))
|
|
95
|
+
elif [ "$status" -eq 0 ] && grep -q "No error has been found" "$dir/tlc.log"; then
|
|
96
|
+
echo "FAIL $name reach $operator $label: the model never reaches this state"
|
|
97
|
+
fail=1
|
|
98
|
+
else
|
|
99
|
+
echo "FAIL $name reach $operator $label: TLC did not complete the reach check"
|
|
100
|
+
cat "$dir/tlc.log"
|
|
101
|
+
fail=1
|
|
102
|
+
fi
|
|
103
|
+
elif [ "$status" -eq 0 ] && grep -q "No error has been found" "$dir/tlc.log"; then
|
|
104
|
+
# Progress lines carry interim counts; the last match is the final one.
|
|
105
|
+
states=$(grep -o '[0-9,]* distinct states found' "$dir/tlc.log" | tail -1)
|
|
106
|
+
echo "PASS $name $label $states"
|
|
107
|
+
passed=$((passed + 1))
|
|
108
|
+
states=${states%% *}
|
|
109
|
+
states=${states//,/}
|
|
110
|
+
total=$((total + ${states:-0}))
|
|
111
|
+
else
|
|
112
|
+
echo "FAIL $name $label: $(grep -m1 "^Error:" "$dir/tlc.log")"
|
|
113
|
+
grep "^Error:" "$dir/tlc.log" | sed -n '2,5p'
|
|
114
|
+
python3 "$HERE/tlc-trace.py" "$dir/tlc.log" || grep -v '^[[:space:]]*$' "$dir/tlc.log" | tail -40
|
|
115
|
+
fail=1
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
check_reaches() {
|
|
120
|
+
local reach entry operator match label found
|
|
121
|
+
for reach in ${reaches[@]+"${reaches[@]}"}; do
|
|
122
|
+
operator=${reach%%|*}; match=${reach#*|}; found=0
|
|
123
|
+
for entry in ${runs[@]+"${runs[@]}"}; do
|
|
124
|
+
label=${entry%%|*}
|
|
125
|
+
case $label in
|
|
126
|
+
*"$match"*) check_run "$label" "${entry#*|}" "$operator"; found=1; break ;;
|
|
127
|
+
esac
|
|
128
|
+
done
|
|
129
|
+
if [ "$found" -eq 0 ]; then
|
|
130
|
+
echo "FAIL $(basename "$spec" .tla) reach $operator: no run matching '$match'"
|
|
131
|
+
reach_count=$((reach_count + 1))
|
|
132
|
+
fail=1
|
|
133
|
+
fi
|
|
134
|
+
done
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
138
|
+
line=${line%%#*}
|
|
139
|
+
[[ $line =~ [^[:space:]] ]] || continue
|
|
140
|
+
read -r kw rest <<< "$line"
|
|
141
|
+
case $kw in
|
|
142
|
+
spec) check_reaches; spec=$rest; checks=""; reaches=(); runs=() ;;
|
|
143
|
+
check)
|
|
144
|
+
[ -n "$spec" ] || { echo "FAIL check '$rest' before any spec line"; fail=1; continue; }
|
|
145
|
+
read -r kind names <<< "$rest"
|
|
146
|
+
case $kind in
|
|
147
|
+
INVARIANT|INVARIANTS|PROPERTY|PROPERTIES)
|
|
148
|
+
[ -n "$names" ] || { echo "FAIL malformed check '$rest'"; fail=1; continue; } ;;
|
|
149
|
+
*) echo "FAIL malformed check '$rest'"; fail=1; continue ;;
|
|
150
|
+
esac
|
|
151
|
+
checks="$checks${checks:+$'\n'}$rest" ;;
|
|
152
|
+
reach)
|
|
153
|
+
[ -n "$spec" ] || { echo "FAIL reach '$rest' before any spec line"; fail=1; continue; }
|
|
154
|
+
if [[ $rest =~ ^([a-zA-Z_][a-zA-Z_0-9]*)[[:space:]]*\|[[:space:]]*(.*[^[:space:]])[[:space:]]*$ ]]; then
|
|
155
|
+
reaches+=("${BASH_REMATCH[1]}|${BASH_REMATCH[2]}")
|
|
156
|
+
else
|
|
157
|
+
echo "FAIL malformed reach '$rest': expected reach <Operator> | <label-substring>"
|
|
158
|
+
fail=1
|
|
159
|
+
fi ;;
|
|
160
|
+
run)
|
|
161
|
+
[[ $rest == *"|"* ]] || { echo "FAIL run '$rest' has no | before its constants"; fail=1; continue; }
|
|
162
|
+
label=${rest%%|*}; label=${label% }; runs+=("$label|${rest#*|}"); check_run "$label" "${rest#*|}" ;;
|
|
163
|
+
*) echo "FAIL unknown directive: $line"; fail=1 ;;
|
|
164
|
+
esac
|
|
165
|
+
done < "$matrix"
|
|
166
|
+
check_reaches
|
|
167
|
+
|
|
168
|
+
[ "$run" -gt 0 ] || { echo "FAIL no run ${only:+matching '$only' }in $matrix"; exit 1; }
|
|
169
|
+
summary="SUMMARY $passed of $run runs passed"
|
|
170
|
+
[ "$reach_count" -eq 0 ] || summary="$summary, $reach_passed of $reach_count reach checks passed"
|
|
171
|
+
echo "$summary, $total distinct states in total"
|
|
172
|
+
exit $fail
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copyright (c) 2026 Michael Denyer
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
# Shared tool pin and prerequisite checks; nothing here downloads.
|
|
5
|
+
TLA_VERSION=v1.7.4
|
|
6
|
+
TLA_SHA256=936a262061c914694dfd669a543be24573c45d5aa0ff20a8b96b23d01e050e88
|
|
7
|
+
TLA_RELEASE=https://github.com/tlaplus/tlaplus/releases/tag/$TLA_VERSION
|
|
8
|
+
JAVA=${JAVA:-java}
|
|
9
|
+
TLC_CACHE=${TLC_CACHE:-$HOME/.cache/tla}
|
|
10
|
+
TLC_JAR_SUPPLIED=${TLC_JAR:+yes}
|
|
11
|
+
TLC_JAR=${TLC_JAR:-$TLC_CACHE/tla2tools-$TLA_VERSION.jar}
|
|
12
|
+
|
|
13
|
+
require_tlc_runtime() {
|
|
14
|
+
if ! "$JAVA" -version > /dev/null 2>&1; then
|
|
15
|
+
echo "UNAVAILABLE $JAVA has no runtime; install a JDK and set JAVA to its java binary"
|
|
16
|
+
return 3
|
|
17
|
+
fi
|
|
18
|
+
command -v python3 > /dev/null || { echo "UNAVAILABLE python3 is not on PATH; install Python 3"; return 3; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
check_tlc_jar() {
|
|
22
|
+
local jar=$1 got
|
|
23
|
+
[ -f "$jar" ] || { echo "UNAVAILABLE $jar is missing; save tla2tools.jar from $TLA_RELEASE there, or set TLC_JAR"; return 3; }
|
|
24
|
+
if command -v sha256sum > /dev/null; then
|
|
25
|
+
got=$(sha256sum "$jar") || { echo "UNAVAILABLE cannot hash $jar with sha256sum; check its permissions"; return 3; }
|
|
26
|
+
elif command -v shasum > /dev/null; then
|
|
27
|
+
got=$(shasum -a 256 "$jar") || { echo "UNAVAILABLE cannot hash $jar with shasum; check its permissions"; return 3; }
|
|
28
|
+
else
|
|
29
|
+
echo "UNAVAILABLE install sha256sum or shasum to verify TLC"
|
|
30
|
+
return 3
|
|
31
|
+
fi
|
|
32
|
+
got=${got%% *}
|
|
33
|
+
[ "$got" = "$TLA_SHA256" ] || { echo "UNAVAILABLE $jar has sha256 $got, want $TLA_SHA256"; return 3; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# A JAR the user supplies is trusted; the cached one must match the pin.
|
|
37
|
+
tlc_jar_ready() {
|
|
38
|
+
local probe
|
|
39
|
+
if [ -n "$TLC_JAR_SUPPLIED" ]; then
|
|
40
|
+
[ -f "$TLC_JAR" ] || { echo "UNAVAILABLE supplied TLC_JAR $TLC_JAR does not exist"; return 3; }
|
|
41
|
+
else
|
|
42
|
+
check_tlc_jar "$TLC_JAR" || return $?
|
|
43
|
+
fi
|
|
44
|
+
probe=$("$JAVA" -cp "$TLC_JAR" tlc2.TLC -help 2>&1) \
|
|
45
|
+
|| [[ $probe == *"TLC - provides model checking"* ]] \
|
|
46
|
+
|| { echo "UNAVAILABLE tlc2.TLC cannot run from $TLC_JAR; save a working JAR from $TLA_RELEASE or set TLC_JAR"; echo "$probe"; return 3; }
|
|
47
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2026 Michael Denyer
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
"""Reduce the error trace in a TLC log to what each step changed.
|
|
5
|
+
|
|
6
|
+
Reads a TLC log (a file argument or stdin), finds the behaviour TLC prints
|
|
7
|
+
for an invariant violation, a deadlock or a temporal counter-example, and
|
|
8
|
+
prints one block per state: its number and action, then only the variables
|
|
9
|
+
whose value differs from the previous state. The first state prints every
|
|
10
|
+
variable. A "Back to state" line (a liveness lasso) is printed as is.
|
|
11
|
+
Exits 1 when the log holds no trace.
|
|
12
|
+
|
|
13
|
+
Usage: tlc-trace.py [tlc.log]
|
|
14
|
+
"""
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from contextlib import nullcontext
|
|
18
|
+
|
|
19
|
+
START = ("The behavior up to this point is", "constitutes a counter-example")
|
|
20
|
+
STATE = re.compile(r"^State (\d+): <?(.*?)>?$")
|
|
21
|
+
LOCATION = re.compile(r" line \d+, col \d+ to line \d+, col \d+ of module \w+$")
|
|
22
|
+
# TLC omits the leading conjunction when the spec has one variable.
|
|
23
|
+
VAR = re.compile(r"^(?:/\\ )?(\w+) = (.*)$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def states(lines):
|
|
27
|
+
"""Yield (title, {var: value}) per state of the first error trace."""
|
|
28
|
+
lines = (line.rstrip("\n") for line in lines)
|
|
29
|
+
# any() stops on the marker line, so the loop below resumes just after it.
|
|
30
|
+
if not any(marker in line for line in lines for marker in START):
|
|
31
|
+
return
|
|
32
|
+
title, values, var = None, {}, None
|
|
33
|
+
for line in lines:
|
|
34
|
+
if m := STATE.match(line):
|
|
35
|
+
title, values, var = f"state {m[1]}: {LOCATION.sub('', m[2])}", {}, None
|
|
36
|
+
elif line.startswith("Back to state"):
|
|
37
|
+
yield line, {}
|
|
38
|
+
elif title and (m := VAR.match(line)):
|
|
39
|
+
var = m[1]
|
|
40
|
+
values[var] = m[2]
|
|
41
|
+
elif title and var and line.startswith(" "):
|
|
42
|
+
values[var] += "\n" + line
|
|
43
|
+
elif title:
|
|
44
|
+
yield title, values
|
|
45
|
+
title = None
|
|
46
|
+
if title:
|
|
47
|
+
yield title, values
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main():
|
|
51
|
+
with (open(sys.argv[1], encoding="utf-8") if len(sys.argv) > 1 else nullcontext(sys.stdin)) as src:
|
|
52
|
+
prev, found = {}, False
|
|
53
|
+
for title, values in states(src):
|
|
54
|
+
found = True
|
|
55
|
+
print(f"--- {title}")
|
|
56
|
+
for var, value in values.items():
|
|
57
|
+
if prev.get(var) != value:
|
|
58
|
+
print(f" {var} = {value}")
|
|
59
|
+
prev = values or prev
|
|
60
|
+
sys.exit(0 if found else 1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
description: "Prepare all shared tools for the agent-formal-verify plugin: Java, Python, TLC, elan, Lean, Rust and Kani. Use for initial installation, missing prerequisites, or /agent-formal-verify:setup."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Set up formal verification tools
|
|
7
|
+
|
|
8
|
+
Prepare the complete shared toolset once, independently of any repository. Verification selects TLA+, Lean and Kani from each repository's current targets. A global plugin installation uses the same tools across repositories.
|
|
9
|
+
|
|
10
|
+
1. Read [tool setup](../formal-verify/references/setup.md) for pinned versions and environment settings. Inspect the operating system and existing runtimes. Reuse installed prerequisites and tool caches.
|
|
11
|
+
2. Ensure a working JDK, Python 3, a SHA256 tool, elan, Rust, the pinned Kani and the pinned TLC JAR are available. Install missing prerequisites through the user's existing package manager or the upstream documented installer, following their installation constraints. Obtain `tla2tools.jar` from the TLA+ release that `scripts/tlc-tools.sh` pins and save it at the path the helper names; the helper verifies its SHA256 and downloads nothing itself. If no suitable installation method is available, or the user has not allowed installation, report the missing prerequisites and their installation instructions.
|
|
12
|
+
|
|
13
|
+
Install the Kani version from tool setup with Cargo, then run `cargo kani setup`. The plugin's helpers verify it without installing or downloading it.
|
|
14
|
+
|
|
15
|
+
3. Check that `java -version` succeeds. If `java` is not on PATH or macOS resolves it to a stub, set `JAVA` to the working JDK's `java` binary for the commands below, and report that setting, because verification needs it too.
|
|
16
|
+
4. Run the three commands below with the absolute path of the `formal-verify` skill, which is this skill's sibling directory, in place of `<skill-dir>`. Treat installed plugin files as read-only. The Lean template supplies the default pin without creating a model project or changing elan's global default.
|
|
17
|
+
|
|
18
|
+
```shell
|
|
19
|
+
bash <skill-dir>/scripts/setup.sh rust
|
|
20
|
+
bash <skill-dir>/scripts/setup.sh tla
|
|
21
|
+
bash <skill-dir>/scripts/setup.sh lean <skill-dir>/examples/lean-template
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
5. Require a successful exit and a `READY` line from all three commands. Report the tool versions or pins, any environment settings required for verification, and anything still missing. Readiness confirms the tooling; the matrix runner and Lean checker check model correctness. Repeating setup reuses prepared tools. Verification handles repository-specific Lean pins when needed.
|