Skip to content

Commit 8084401

Browse files
committed
The specification does not collect failure names
`legalFailures` had no business in properties.lean. It returned a `List String` — a diagnostic, useful when a sweep goes red — and I defended it as a design principle when it is a runtime concern. The specification says what `Legal` is. Reporting is the implementer's problem, and the harness's. properties.lean legalAt is the fold, directly: catalogue.all fun l => match l.property with | .state f => f now a && f now b | .trans f => f now a b properties-check failingNames, three lines, where debugging happens What made this cheap is unchanged and is the actual portable part: each entry carries its `name` as data, so anyone — Go, TypeScript, Verus, a harness in this repo — can collect them in three lines. The specification does not have to provide the facility to make the names useful; it has to carry the names. Everything builds; the sweeps pass; report and transReport still return [] over the battery.
1 parent a129944 commit 8084401

3 files changed

Lines changed: 35 additions & 31 deletions

File tree

spec/02-abstract/properties.lean

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,36 +1266,31 @@ def catalogue : List Named :=
12661266

12671267
/-! ### The walk
12681268
1269-
ONE fold, matching `Legal` in `04-theorems/system.lean`: take the
1270-
catalogue, and apply each entry to whatever its constructor says it
1271-
takes — a `.state` property to a state, a `.trans` property to the pair.
1272-
There is no second notion and no lemma between them; the sweep evaluates
1273-
this and `Legal` quantifies it.
1274-
1275-
A `.state` entry is checked at BOTH endpoints of the pair. `Legal`
1276-
applies it at every index of an infinite trace, where every state is
1277-
some step's pre-state; a finite run has a last state that is nobody's
1278-
pre-state, and checking both ends is what covers it without a special
1279-
case. The redundancy is free — the initial state satisfies everything.
1280-
1281-
Both endpoints are checked at the instant of the STEP, not at some later
1282-
reading. That is stricter, not weaker: the two `_lte_now` entries are
1283-
monotone in `now`, so passing at the earlier instant implies passing at
1284-
the later one.
1285-
1286-
Report by NAME. It is the same string in Lean, Go, TypeScript and Verus,
1287-
so a violation means the same thing everywhere. -/
1288-
1289-
def legalFailures (now : Nat) (a b : ServerState) : List String :=
1290-
catalogue.filterMap fun l =>
1291-
match l.property with
1292-
| .state f => if f now a && f now b then none else some l.name
1293-
| .trans f => if f now a b then none else some l.name
1269+
ONE fold, and it is the definition `Legal` in `04-theorems/system.lean`
1270+
quantifies: take the catalogue, apply each entry to whatever its
1271+
constructor says it takes — a `.state` property to a state, a `.trans`
1272+
property to the pair.
1273+
1274+
A `.state` entry is checked at BOTH endpoints. `Legal` applies it at
1275+
every index of an infinite trace, where every state is some step's
1276+
pre-state; a finite run has a last state that is nobody's pre-state, and
1277+
checking both ends covers it with no special case. Both at the STEP's
1278+
instant, which is stricter rather than weaker — the two `_lte_now`
1279+
entries are monotone in `now`.
1280+
1281+
Each entry carries its `name`, and that is the portable part: an
1282+
implementation reporting a violation reports the same string in Go,
1283+
TypeScript or Verus. Collecting those names is the implementation's
1284+
business — and the harness's, when something breaks. Not the
1285+
specification's. -/
12941286

12951287
def legalAt (now : Nat) (a b : ServerState) : Bool :=
1296-
(legalFailures now a b).isEmpty
1288+
catalogue.all fun l =>
1289+
match l.property with
1290+
| .state f => f now a && f now b
1291+
| .trans f => f now a b
12971292

1298-
/-- The SAME fold, restricted to the `.state` half — `legalAt` with the
1293+
/-- The same fold restricted to the `.state` half — `legalAt` with the
12991294
`.trans` entries answering `true`. Not a second notion of legality:
13001295
it is what an induction HYPOTHESIS needs, a claim about one state
13011296
with no successor in hand. -/

spec/04-theorems/properties-check.lean

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,19 @@ def allPairs (w : List (Step × Nat)) :=
6767
def legalRun (w : List (Step × Nat)) : Bool :=
6868
(allPairs w).all (fun (n, a, b) => legalAt n a b)
6969

70-
/-- Which entries fail, by name — the shape a counterexample report
71-
needs, and the reason the catalogue carries its names as data. -/
70+
/-- Which entries fail, by name. A HARNESS concern: the specification
71+
says what `legalAt` is, and collecting the names of what broke is
72+
for whoever is debugging a red sweep. The catalogue carries the
73+
names as data, which is what makes this three lines here rather
74+
than a facility the specification has to provide. -/
75+
def failingNames (now : Nat) (a b : AbstractModel.ServerState) : List String :=
76+
AbstractModel.Properties.catalogue.filterMap fun l =>
77+
match l.property with
78+
| .state f => if f now a && f now b then none else some l.name
79+
| .trans f => if f now a b then none else some l.name
80+
7281
def report (ws : List (List (Step × Nat))) : List String :=
73-
(ws.flatMap fun w => (allPairs w).flatMap (fun (n, a, b) => legalFailures n a b)).eraseDups
82+
(ws.flatMap fun w => (allPairs w).flatMap (fun (n, a, b) => failingNames n a b)).eraseDups
7483

7584
def witnesses (ws : List (List (Step × Nat))) (p : AbstractModel.ServerState → Bool) : Bool :=
7685
ws.any fun w => (trace w).any (fun (_, s) => p s)

spec/04-theorems/properties-step.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def transHoldsRun (w : List (Step × Nat)) : Bool :=
3434
(steps w).all (fun (n, a, b) => legalAt n a b)
3535

3636
def transReport (ws : List (List (Step × Nat))) : List String :=
37-
(ws.flatMap fun w => (steps w).flatMap (fun (n, a, b) => legalFailures n a b)).eraseDups
37+
(ws.flatMap fun w => (steps w).flatMap (fun (n, a, b) => failingNames n a b)).eraseDups
3838

3939
def stepWitnesses (ws : List (List (Step × Nat)))
4040
(p : ServerState → ServerState → Bool) : Bool :=

0 commit comments

Comments
 (0)