@mettascript/fuzz 3.1.2 → 3.2.0

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.
Files changed (2) hide show
  1. package/README.md +20 -127
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # @mettascript/fuzz
2
2
 
3
- Property testing for [MeTTaScript](https://github.com/MesTTo/MeTTaScript), written in MeTTa. You declare a generator and a property, and the library generates cases, shrinks a failure to its smallest form, and hands back a result you can replay. It also enumerates small domains exhaustively, checks a real system against a model over command sequences, and searches a transition relation for a reachable state.
3
+ Property testing for [MeTTaScript](https://github.com/MesTTo/MeTTaScript), written in MeTTa. You declare
4
+ a generator and a property, and the library generates cases, shrinks a failure to its smallest form, and
5
+ hands back a result you can replay. It also enumerates small domains exhaustively, checks a real system
6
+ against a model over command sequences, and searches a transition relation for a reachable state.
4
7
 
5
- The policy lives in MeTTa: generation, shrinking, the run loop, the state machines, and the search are all rewrite rules you can read in `src/metta`. TypeScript supplies only what a representation needs, in one place: a splitmix/xoroshiro random source, a structural key over atoms, and a versioned atom codec.
8
+ The policy lives in MeTTa: generation, shrinking, the run loop, the state machines and the search are all
9
+ rewrite rules you can read in `src/metta`. TypeScript supplies only what a representation needs, in one
10
+ place: a splitmix/xoroshiro random source, a structural key over atoms, and a versioned atom codec.
6
11
 
7
12
  ## Install
8
13
 
@@ -12,7 +17,7 @@ npm install @mettascript/fuzz
12
17
 
13
18
  Importing the package registers the `fuzz` module, so MeTTa code reaches it with `import!`.
14
19
 
15
- ## Usage
20
+ ## In one example
16
21
 
17
22
  ```metta
18
23
  !(import! &self fuzz)
@@ -38,138 +43,26 @@ A passing run reports what it did:
38
43
  `Runs` counts the random cases. Edge cases are drawn on top of them, which is why 200 runs report 213
39
44
  passes here: the generator's boundary values are tried first, then the random ones.
40
45
 
41
- Write the property with the expectation combinators rather than a bare `Bool`, so a failure carries a tag and details you can read:
42
-
43
- - `(fuzz-pass)` and `(fuzz-fail <tag> <details>)` are the two results everything else builds on.
44
- - `(expect-true <bool> <details>)`, `(expect-false ...)`.
45
- - `(expect-atom-equal <a> <b>)` compares structurally; `(expect-alpha-equal ...)` ignores variable names.
46
- - `(expect-results-exact <a> <b>)` compares result bags in order; `-alpha`, `-multiset` and `-set` relax that to variable renaming, order, and multiplicity.
47
-
48
- ## Generators
49
-
50
- Generators are data, not functions: `(gen-int 0 9)` is an atom the runner interprets, which is what lets one declaration be replayed, shrunk, and enumerated.
51
-
52
- - Scalars: `gen-bool`, `gen-int`, `gen-int-origin`, `gen-sized-int`, `gen-float`, `gen-float-range`, `gen-float-bits`, `gen-char`, `gen-char-ascii`, `gen-char-unicode`, `gen-symbol`, `gen-syntax-token`, `gen-const`.
53
- - Text: `gen-string`, `gen-ascii-string`, `gen-unicode-string`.
54
- - Structure: `gen-tuple`, `gen-list`, `gen-option`, `gen-element`, `gen-one-of`, `gen-frequency`.
55
- - Combinators: `gen-map`, `gen-bind`, `gen-filter`, `gen-sized`, `gen-resize`, `gen-recursive`, `gen-custom`.
56
- - Grammars and types: `gen-grammar`, `gen-grammar-root`, `gen-well-typed` generate from a declared grammar or from type constructors, so you can generate well-formed terms of a language rather than raw trees.
57
-
58
- ## Shrinking
59
-
60
- A failure is shrunk before it is reported, under a named order (`mettascript-shrink-v1`) so the smallest form is stable across runs rather than a function of the seed. The result says whether it reached a local minimum or stopped early, and why. A custom generator can supply its own shrinker; anything else shrinks through its decision tree.
61
-
62
- ## Exhaustive checking
63
-
64
- For a small domain, enumerate it instead of sampling:
65
-
66
- ```metta
67
- (: always (-> Atom FuzzProperty))
68
- (= (always $value) (fuzz-pass))
69
-
70
- !(fuzz-check-exhaustive small (gen-bool) always (fuzz-config (MaxEnumerated 10)))
71
- ```
72
-
73
- `(FuzzExhaustivelyVerified (Property small) (DomainCount 2) ...)` means the whole domain was covered, which is a proof over that domain rather than evidence. A domain that does not fit the bound is reported as an incomplete run, never as verified.
74
-
75
- ## State machines
76
-
77
- Check a real system against a model over generated command sequences. Generation walks the model only, so a command sequence is chosen without touching the real system; execution then runs both and compares.
78
-
79
- ```metta
80
- (= (counter-initialize) (new-state 0))
81
- (: counter-command-generator (-> Atom %Undefined%))
82
- (= (counter-command-generator $model) (gen-element (increment reset)))
83
- (= (counter-precondition $model $command) True)
84
- (= (counter-execute $real increment)
85
- (let $seen (get-state $real)
86
- (let $changed (change-state! $real (+ $seen 1))
87
- (+ $seen 1))))
88
- (= (counter-execute $real reset) (let $changed (change-state! $real 0) 0))
89
- (= (counter-next-model (Count $n) increment) (Count (+ $n 1)))
90
- (= (counter-next-model (Count $n) reset) (Count 0))
91
- (= (counter-postcondition (Count $n) increment $result) (== $result (+ $n 1)))
92
- (= (counter-postcondition (Count $n) reset $result) (== $result 0))
93
- (= (counter-invariant (Count $n)) (>= $n 0))
94
- (= (counter-cleanup $real) Done)
95
-
96
- (FuzzMachine Counter
97
- (InitialModel (Count 0))
98
- (InitializeReal counter-initialize)
99
- (CommandGenerator counter-command-generator)
100
- (Precondition counter-precondition)
101
- (Execute counter-execute)
102
- (NextModel counter-next-model)
103
- (Postcondition counter-postcondition)
104
- (Invariant counter-invariant)
105
- (Cleanup counter-cleanup))
106
-
107
- !(fuzz-check-machine Counter (fuzz-config (Runs 20) (MaxSize 6)))
108
- ```
109
-
110
- A divergence shrinks to a shorter command sequence that still diverges, so you get the shortest sequence that separates the real system from the model rather than the one that happened to be generated.
111
-
112
- ## Bounded reachability
113
-
114
- Search a transition relation, breadth first, for a state that satisfies a target:
115
-
116
- ```metta
117
- (= (counter-enumerate (Count $n))
118
- (if (< $n 4) (FiniteCommands up split) (FiniteCommands)))
119
- (= (counter-transition (Count $n) up) (Count (+ $n 1)))
120
- (= (counter-transition (Count $n) split) (superpose ((Count (+ $n 1)) (Count (+ $n 2)))))
121
- (= (counter-target (Count $n)) (== $n 3))
122
-
123
- !(fuzz-reachable Counter (Count 0)
124
- counter-enumerate counter-transition counter-target
125
- (reach-config (MaxDepth 20)))
126
- ```
127
-
128
- A transition may return several next states, and the whole ordered result bag becomes outgoing edges, so a witness records which branch it took and can be replayed through a nondeterministic model.
129
-
130
- The outcomes are deliberately distinct. `FuzzReachable` carries a witness that was replayed from the initial state before being reported. `FuzzReachabilityExhausted` means unreachable in the declared finite model. `FuzzUnreachableWithinDepth` means only that nothing was found at or below `MaxDepth`. Every limit, incomplete enumeration, or replay mismatch is a `FuzzReachabilityCutoff` and never becomes exhaustion.
131
-
132
- ## Running a suite from the command line
133
-
134
- Declare tests as data and run the file with [`@mettascript/node`](https://github.com/MesTTo/MeTTaScript/tree/main/packages/node):
135
-
136
- ```metta
137
- (FuzzTest involution (gen-list (gen-int -20 20) 0 6) reverse-involution
138
- (fuzz-config (Runs 200)))
139
- ```
46
+ Run a whole file of declared properties from the command line with `metta fuzz`:
140
47
 
141
48
  ```bash
142
- metta fuzz suite.metta # run every declaration
143
- metta fuzz --seed 7 --runs 50 suite.metta # override each declaration's config
144
- metta fuzz --exhaustive suite.metta # enumerate each domain instead
145
- metta fuzz --corpus regressions suite.metta # replay stored counterexamples, record new ones
146
- metta reach suite.metta # run every (FuzzReachTest ...)
49
+ metta fuzz properties.metta
147
50
  ```
148
51
 
149
- The CLI runs the declarations a file carries, not the file's own `!` queries. Exit codes are 0 for a pass or a definitive answer, 1 for a property failure, 2 for invalid input or corrupt stored data, and 3 for an incomplete run.
52
+ ## Where the documentation lives
150
53
 
151
- `--corpus <dir>` keeps found counterexamples as text so a later run replays them first, one file per case, meant to be committed. Values go through the versioned codec rather than plain formatting, so a counterexample of `NaN` survives exactly.
152
-
153
- ## Reading a result from TypeScript
154
-
155
- Results are atoms. `decodeFuzzOutcome` turns one into a typed union, `renderOutcomeLine` gives the one-line form, and `exitCodeForOutcomes` gives the worst-first exit code for a whole run:
156
-
157
- ```ts
158
- import { decodeFuzzOutcome, exitCodeForOutcomes, renderOutcomeLine } from "@mettascript/fuzz";
159
-
160
- const outcome = decodeFuzzOutcome(resultAtom);
161
- if (outcome.kind === "failed") console.log(renderOutcomeLine(outcome));
162
- process.exit(exitCodeForOutcomes([outcome]));
163
- ```
164
-
165
- The decoder is strict: an atom it does not recognize becomes an `undecodable` outcome rather than a pass.
166
-
167
- ## Determinism
168
-
169
- A run is a function of its seed. The random source, the shrink order, the replay keys, and the exhaustive enumeration order are all named and versioned, so a reported failure reproduces, and `metta fuzz --seed <n>` twice gives the same cases.
54
+ - [Property testing](https://mestto.github.io/MeTTaScript/fuzz/overview) is the guide: writing a
55
+ property, the generators, shrinking, exhaustive checking, state machines, and bounded reachability.
56
+ - [API reference](https://mestto.github.io/MeTTaScript/reference/fuzz) lists the full surface, including
57
+ reading a result back from TypeScript.
58
+ - [The CLI](https://mestto.github.io/MeTTaScript/tools/cli) covers `metta fuzz` and `metta reach`.
170
59
 
171
60
  ## For language models
172
61
 
173
62
  [`LLMS.md`](./LLMS.md) is a one-page, high-density reference for this package: API surface, working
174
63
  examples, and the mistakes that produce wrong code. The repository root carries an
175
64
  [`llms.txt`](../../llms.txt) index of all of them.
65
+
66
+ ## License
67
+
68
+ [MIT](https://github.com/MesTTo/MeTTaScript/blob/main/LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mettascript/fuzz",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "pure-rand": "8.4.2",
22
- "@mettascript/core": "3.1.2"
22
+ "@mettascript/core": "3.2.0"
23
23
  },
24
24
  "author": "MesTTo",
25
25
  "engines": {