1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
;;; Copyright (c) 2026 Nicholas Vermeulen
;;; SPDX-License-Identifier: AGPL-3.0-or-later
;; fsm.lisp — verified finite-state machines. Pure Lisp, zero
;; interpreter changes, same library pattern as robot.lisp / prover.lisp.
;;
;; An FSM is DATA: a list of states, a list of events (the alphabet), a
;; list of `(from event to)` transition triples, and a start state. The
;; environment — not the machine — chooses which event fires, so unlike
;; robot.lisp (one controller action per state) an FSM BRANCHES: every
;; event is a possible successor. That branching is exactly what the two
;; verification methods here handle.
;;
;; SAFETY, two independent exact methods over the DECLARED domain:
;;
;; (fsm-verify-invariant fsm inv?) — the inductive step, discharged by
;; check-exhaustive over states×events: inv?(s) ⇒ inv?(step(s,e)) for
;; every state s and every event e. With "the start state is safe",
;; induction gives: the machine never leaves the inv? set. Proven on the
;; declared state×event product, per the bounded-verification rule.
;;
;; (fsm-verify-unreachable fsm bad?) — a COMPLETE breadth-first search
;; of the transition graph from the start state. Finite state set ⇒ BFS
;; visits every reachable state exactly once, so "no reachable state is
;; bad?" is exact, not sampled. Returns the bad reachable states as
;; witnesses if the claim fails.
;;
;; The two can disagree instructively: an invariant that isn't inductive
;; still fails (1), yet its bad states may be unreachable and pass (2), or
;; vice-versa. Neither is "safe" in general — both are proven only over the
;; transitions, states and events you DECLARE. A transition missing from
;; the table, or an event outside the alphabet, is outside the claim.
;; ── Construction + accessors (an FSM is plain, inspectable data) ─────────
;; ── Stepping. Partial FSMs are allowed: an undefined (state,event) yields
;; the sentinel 'fsm-stuck (no transition taken), never an error. ─────────
;; Every next-state reachable from `state` in one step, over ANY event
;; (the environment's choice) — deduped. Drives the BFS below.
;; ── Structural properties ───────────────────────────────────────────────
;; Deterministic: no (from,event) pair maps to two distinct `to`.
;; Complete: every (state,event) in the declared domain has a transition.
;; ── Reachability: complete BFS from the start over the finite graph ──────
;; ── Verification ─────────────────────────────────────────────────────────
;; (1) The inductive step, exhaustively checked over states×events.
;; A stuck (state,event) takes no transition, so it can't violate anything
;; — vacuously #t. inv? must be a boolean predicate (check-exhaustive is
;; boolean-strict since 0.62.1: a non-boolean return is a named
;; counterexample, never a silent pass).
;; (2) Bad-state unreachability, exact via the complete BFS. 'verified if
;; no reachable state is bad?, else the bad reachable states as witnesses.
;; A safety verdict as DATA (mirrors shouzhong's certify-report): both
;; methods' verdicts, the reachable-state count, and the domain size that
;; makes the bounded claim countable.