lawspec 0.8.0 → 0.10.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 (61) hide show
  1. package/API-MIGRATION.md +180 -3
  2. package/GO.md +144 -0
  3. package/HASKELL.md +171 -0
  4. package/JAVA.md +113 -0
  5. package/KOTLIN.md +214 -0
  6. package/LANGUAGE.md +240 -12
  7. package/NATIVE-BINDINGS.md +1046 -0
  8. package/PRIMITIVES.md +1 -1
  9. package/PYTHON.md +152 -0
  10. package/README.md +86 -23
  11. package/REFINEMENTS.md +289 -3
  12. package/RELEASE-0.10.md +60 -0
  13. package/RELEASE-0.9.md +61 -0
  14. package/RUST.md +115 -1
  15. package/WEB.md +144 -0
  16. package/api.mjs +28 -2
  17. package/bin/lawspec.mjs +23 -7
  18. package/build.json +199 -37
  19. package/core.wasm +0 -0
  20. package/examples/native-payments/go/example/payments/domain.go +38 -0
  21. package/examples/native-payments/go/example/payments/native_generators_test.go +13 -0
  22. package/examples/native-payments/go/lawspec.json +136 -0
  23. package/examples/native-payments/haskell/lawspec.json +149 -0
  24. package/examples/native-payments/haskell/src/PaymentsDomain.hs +21 -0
  25. package/examples/native-payments/haskell/test/PaymentGenerators.hs +12 -0
  26. package/examples/native-payments/java/lawspec.json +165 -0
  27. package/examples/native-payments/java/src/main/java/domain/PaymentsDomain.java +32 -0
  28. package/examples/native-payments/java/src/test/java/domain/PaymentGenerators.java +16 -0
  29. package/examples/native-payments/javascript/lawspec.json +149 -0
  30. package/examples/native-payments/javascript/src/payments_domain.mjs +44 -0
  31. package/examples/native-payments/javascript/test/lawspec_generators.mjs +6 -0
  32. package/examples/native-payments/kotlin/lawspec.json +165 -0
  33. package/examples/native-payments/kotlin/src/main/kotlin/domain/PaymentsDomain.kt +17 -0
  34. package/examples/native-payments/kotlin/src/test/kotlin/domain/PaymentGenerators.kt +12 -0
  35. package/examples/native-payments/python/lawspec.json +149 -0
  36. package/examples/native-payments/python/src/payments_domain.py +60 -0
  37. package/examples/native-payments/python/tests/lawspec_generators.py +15 -0
  38. package/examples/native-payments/rust/lawspec.json +167 -0
  39. package/examples/native-payments/rust/src/domain.rs +37 -0
  40. package/examples/native-payments/rust/src/lib.rs +2 -0
  41. package/examples/native-payments/rust/tests/support/lawspec_generators.rs +11 -0
  42. package/examples/native-payments/typescript/lawspec.json +149 -0
  43. package/examples/native-payments/typescript/src/payments_domain.ts +42 -0
  44. package/examples/native-payments/typescript/test/lawspec_generators.ts +8 -0
  45. package/examples/specs/collections.lawspec +67 -0
  46. package/examples/specs/data_types.lawspec +66 -0
  47. package/examples/specs/finite_data.lawspec +37 -0
  48. package/examples/specs/list_contracts.lawspec +136 -0
  49. package/examples/specs/list_refinements.lawspec +51 -0
  50. package/examples/specs/matching.lawspec +49 -0
  51. package/examples/specs/payments.lawspec +76 -0
  52. package/examples/specs/recursive_refinements.lawspec +41 -0
  53. package/examples/specs/refined_definitions.lawspec +44 -0
  54. package/examples/specs/sum_refinements.lawspec +49 -0
  55. package/examples/specs/total_functions.lawspec +101 -0
  56. package/examples-command.mjs +11 -1
  57. package/files.mjs +16 -7
  58. package/index.d.ts +297 -27
  59. package/native-examples.mjs +81 -0
  60. package/package.json +2 -2
  61. package/templates.mjs +107 -17
package/PRIMITIVES.md CHANGED
@@ -1,4 +1,4 @@
1
- # LawSpec scalar reference (0.8.0)
1
+ # LawSpec scalar reference (0.10.0)
2
2
 
3
3
  A scalar has a declared domain, checked literals, equality, property inputs, and
4
4
  boundary fixtures. General collections, objects, pointers, and type-only constructs
package/PYTHON.md ADDED
@@ -0,0 +1,152 @@
1
+ # Python backend
2
+
3
+ Python output follows [PEP 8](https://peps.python.org/pep-0008/): four-space
4
+ indentation, a 79-column code target, 72-column prose comments/docstrings, and
5
+ two blank lines between top-level definitions. Explicit `--minify` permits
6
+ compact layout while preserving Python indentation and semantics.
7
+ `tools/python-formatting-integration.mjs` checks generated runtime, adapter,
8
+ definition, and test files with pycodestyle 2.14.0 at both machine widths. It
9
+ also compares readable and compact syntax trees, including literal contents.
10
+
11
+ Use Python 3.13 or later. Generated tests use Hypothesis; generated data classes,
12
+ scalar operations, and schema validation do not depend on a test framework.
13
+
14
+ ## Native structural values
15
+
16
+ | LawSpec type | Adapter representation |
17
+ | --- | --- |
18
+ | `List a` | `list[A]` |
19
+ | `Maybe a` | `lawspec_schema.Maybe[A]`, with `Nothing` and `Just` variants |
20
+ | `Either a b` | `lawspec_schema.Either[A, B]`, with `Left` and `Right` variants |
21
+ | User-defined products and sums | Named generic dataclasses in `lawspec_data` |
22
+ | `Nullable a`, `Optional a` | Tagged `lawspec_runtime.Presence` values |
23
+
24
+ `Nothing()` differs from `Just(Nothing())`. `Left(value)` differs from
25
+ `Right(value)`. These algebraic variants are separate from the interoperability
26
+ states represented by `Nullable`, `Optional`, `Null`, and `Undefined`.
27
+
28
+ For example, this declaration:
29
+
30
+ ```lawspec
31
+ type Tree (a :: Type) is
32
+ Leaf value :: a
33
+ Branch children :: List (Tree a)
34
+ end
35
+ ```
36
+
37
+ produces a generic `Tree` base and `TreeLeaf` and `TreeBranch` dataclasses. A
38
+ product has one variant: `Pair` with constructor `Pair` produces `PairPair`.
39
+ When names conflict across units or between types and variants, the compiler
40
+ plans distinct names using their Core identities. Use the emitted declarations
41
+ and adapter annotations as the authoritative names.
42
+
43
+ Native pattern matching works on the generated variants:
44
+
45
+ ```python
46
+ import lawspec_data as data
47
+
48
+
49
+ def count_leaves(tree: data.Tree[int]) -> int:
50
+ match tree:
51
+ case data.TreeLeaf():
52
+ return 1
53
+ case data.TreeBranch(children=children):
54
+ return sum(count_leaves(child) for child in children)
55
+ raise TypeError("unknown Tree variant")
56
+ ```
57
+
58
+ Classes are frozen and use slots. Container payloads are copied when crossing
59
+ adapter boundaries, so modifying a native list does not change another use of
60
+ the logical test input. An abstract base cannot be directly constructed; empty
61
+ types acquire no artificial variant.
62
+
63
+ LawSpec equality uses schema-directed comparisons, including IEEE NaN and
64
+ signed-zero rules and Symbol identity. Generated dataclasses do not derive
65
+ Python field equality, whose container shortcuts can change those rules.
66
+
67
+ ## Checked boundaries
68
+
69
+ Generated calls validate input values, convert them to native classes, call the
70
+ adapter, and validate its result. A field extracted from a native product can
71
+ be returned directly from an adapter with the corresponding LawSpec result
72
+ type. The same representation is used for nested and standalone containers.
73
+
74
+ Diagnostics identify constructor fields and list indices. Checks distinguish
75
+ Bool from integers, enforce primitive ranges under the selected `machineBits`,
76
+ reject invalid scalar text, and preserve raw code units, code points, and bytes.
77
+ Preconditions and postconditions operate on validated logical values.
78
+
79
+ ## Total definitions
80
+
81
+ Checked total definitions emit reusable Python functions separately from adapters:
82
+
83
+ ```lawspec
84
+ unit example.total
85
+
86
+ definition increment (x :: Int8) :: BigInt is x + 1 end
87
+ ```
88
+
89
+ ```python
90
+ from lawspec_definitions.example import total
91
+
92
+ symbols = {}
93
+ assert total.increment(symbols, 127) == 128
94
+ ```
95
+
96
+ The first argument is the Symbol fixture context shared within an example.
97
+ Public signatures preserve native container and variant annotations; checked
98
+ conversion enforces element types, ranges, machine profiles, and nested presence
99
+ states at runtime. Errors include the resolved definition name. Source functions
100
+ and implementation bodies have no Hypothesis or pytest dependency.
101
+
102
+ `lawspec_definition_bodies.py` holds checked logical implementations. Public
103
+ modules under `lawspec_definitions/` provide native entry points grouped by unit.
104
+ Both belong in source directories and are generated-owned. Properties invoke
105
+ those checked bodies directly; definitions do not create adapter stubs. Unit
106
+ modules cannot shadow support modules or another unit's package. Native function
107
+ names such as `str` do not shadow built-ins used by generated validation.
108
+
109
+ Definition bodies and Python properties share typed expression rendering. Match
110
+ inputs are evaluated once, branches remain lazy, guards short-circuit, and exact
111
+ Decimal arithmetic is independent of Python's ambient rounding context.
112
+ Definitions must pass structural termination and definedness checks. Generic
113
+ definitions specialize to concrete uses. Refined signatures become checked
114
+ contracts, and refinement predicates may call checked definitions.
115
+
116
+ `tools/python-definitions-integration.mjs` checks standalone source calls,
117
+ properties, incorrect adapters, both machine profiles, custom layouts, compact
118
+ execution, and regeneration protection. The bundled Python corpus passes PEP 8 checks at 79 code columns and 72 prose
119
+ columns. The style audit includes the standalone total-definition fixture.
120
+
121
+ ## Generation and layouts
122
+
123
+ Hypothesis composes tuples, alternatives, lists, and dependent strategies.
124
+ Recursive generation reserves each field's minimum node budget before sharing
125
+ the remainder. List length and element budgets vary together. Shrinking remains
126
+ native to Hypothesis and preserves the schema and structural size bound.
127
+ Small finite domains are enumerated; empty domains do not pass vacuously.
128
+
129
+ `lawspec_data.py` and `lawspec_schema.py` belong in the configured source
130
+ directory. `lawspec_data_strategies.py` belongs in the test directory. Configure
131
+ Python's import paths for those directories when using a custom layout.
132
+ Adapters remain user-owned. Unit names cannot shadow generated support modules.
133
+
134
+
135
+ Internal checked Core definition contracts now run at native and logical entry
136
+ points. Emission proves the obligations first; argument validation precedes
137
+ ordered preconditions, and result validation precedes postconditions. Contract
138
+ binders map explicitly to body inputs and the checked result. Refined source
139
+ signatures now produce these contracts through template proof and specialization.
140
+
141
+ `tools/portable-definition-contract-integration.mjs` exercises Python, JavaScript
142
+ and strict TypeScript with both machine profiles and layouts, without property
143
+ frameworks. It also verifies that deliberately corrupted results are rejected.
144
+ The fixture in `test/DefinitionContractFixture.hs` is shared with the JVM checks.
145
+
146
+ ## Checking Python style
147
+
148
+ Set `LAWSPEC_CORE` to the native compiler, `LAWSPEC_PYTHON` to Python 3.13 or
149
+ later, and `LAWSPEC_PYCODESTYLE` to the `pycodestyle.py` source from version
150
+ 2.14.0. Then run `node tools/python-formatting-integration.mjs`. The checker is
151
+ a development dependency; generating and executing runtime code does not
152
+ require it. Optional positional arguments select individual specification files.
package/README.md CHANGED
@@ -2,10 +2,23 @@
2
2
 
3
3
  **State the law once. Check it everywhere.**
4
4
 
5
- LawSpec 0.8 compiles reusable laws into native property tests, executable examples,
5
+ LawSpec 0.10 compiles reusable laws into native property tests, executable examples,
6
6
  and implementation adapters. The compiler is Haskell, distributed as prebuilt
7
7
  WebAssembly with a Node CLI and an asynchronous, typed JavaScript API.
8
8
 
9
+ ## Structural data and total definitions in 0.9.0
10
+
11
+ LawSpec 0.9 adds `List`, algebraic `Maybe` and `Either`, named
12
+ parameterized products and sums, and checked total definitions on all eight
13
+ backends. See the [language reference](LANGUAGE.md),
14
+ [collections](examples/specs/collections.lawspec),
15
+ [data types](examples/specs/data_types.lawspec), and
16
+ [total functions](examples/specs/total_functions.lawspec).
17
+
18
+ Generated code is readable by default; `--minify` explicitly selects compact
19
+ output. Python follows PEP 8. See the [0.9 release notes](RELEASE-0.9.md)
20
+ and [API migration guide](API-MIGRATION.md).
21
+
9
22
  ## Rust and the typed front end in 0.8.0
10
23
 
11
24
  Rust joins Java, Python, JavaScript, TypeScript, Go, Haskell, and Kotlin. All
@@ -23,12 +36,27 @@ Compiler API consumers should read the [schema v3 migration guide](API-MIGRATION
23
36
  Scalar values use tagged, lossless encodings; generated runtime placement is
24
37
  separate from artifact ownership. Java 25+ and Python 3.13+ baselines are unchanged.
25
38
 
39
+ ## Native domain bindings in 0.10
40
+
41
+ LawSpec 0.10 adds configuration for existing application products and sums,
42
+ function bindings, checked conversion hooks, and native property-generator
43
+ factories on all eight targets. Generators retain their framework's shrinkers;
44
+ explicit examples, boundaries, and finite-domain checks remain independent.
45
+ See the [native-binding reference and acceptance status](NATIVE-BINDINGS.md),
46
+ the [release notes](RELEASE-0.10.md), and
47
+ [schema 4 migration notes](API-MIGRATION.md#schema-4-native-bindings).
48
+ Existing specifications without bindings retain their behavior.
49
+
26
50
  ## Install and try it
27
51
 
52
+ LawSpec has its own [syntax-highlighting grammar and VS Code extension](editors/vscode/README.md)
53
+ for keywords, types, refinements, literals, and law names. GitHub needs upstream Linguist
54
+ support to use it; GitHub currently uses the Haskell fallback.
55
+
28
56
  Install [LawSpec from npm](https://www.npmjs.com/package/lawspec):
29
57
 
30
58
  ```sh
31
- npm install --save-dev lawspec@0.8.0
59
+ npm install --save-dev lawspec@0.10.0
32
60
  npx lawspec --version
33
61
  ```
34
62
 
@@ -42,8 +70,8 @@ local dependencies so LawSpec can create its `package.json` and test script:
42
70
  ```sh
43
71
  mkdir lawspec-example
44
72
  cd lawspec-example
45
- npm exec --package=lawspec@0.8.0 -- lawspec init --target javascript
46
- npm install --save-dev lawspec@0.8.0
73
+ npm exec --package=lawspec@0.10.0 -- lawspec init --target javascript
74
+ npm install --save-dev lawspec@0.10.0
47
75
  npx lawspec check
48
76
  npx lawspec explain 'example.atoi_codec::itoa and then atoi yields a'
49
77
  npx lawspec doctor
@@ -179,11 +207,13 @@ Reusable laws can declare typed unary function parameters and `requires Eq a`, n
179
207
  [parameterized refinements and executable contracts](REFINEMENTS.md).
180
208
  Definitions support law application, function application/composition, universal
181
209
  quantification, `implies`, Boolean predicates, scalar literals, and equality.
182
- Function signatures and quantified inputs support the [scalar catalog](PRIMITIVES.md),
183
- including mixed and multiple inputs. Generic variables are supported in reusable
184
- laws. Scalar types can also be intermediate or compared results. Functions are synchronous and support curried signatures with any positive
185
- number of scalar arguments. Text literals are double-quoted, with escapes such as `\"`, `\\`,
186
- `\n`, and `\t`; examples must bind each input to a literal of its declared type.
210
+ Function signatures and quantified inputs support the [scalar catalog](PRIMITIVES.md)
211
+ and [structural types](LANGUAGE.md), including mixed and multiple inputs.
212
+ Generic variables are supported in reusable laws. Scalar and structural values
213
+ can also be intermediate or compared results. Functions are synchronous and
214
+ support curried signatures with any positive number of arguments. Text literals
215
+ are double-quoted, with escapes such as `\"`, `\\`, `\n`, and `\t`; examples must
216
+ bind each input to a concrete value of its declared type.
187
217
  Text values contain Unicode scalar values; surrogate code points are rejected.
188
218
 
189
219
  Examples refer to the expanded input names, including names inherited from the
@@ -193,9 +223,10 @@ produce literal braces. Law blocks use this order:
193
223
  definition, optional description, optional rationale, examples, optional references.
194
224
  `--` starts a line comment. Names that cannot be emitted portably are diagnosed.
195
225
 
196
- General collections, external law packages, cross-unit imports beyond the
197
- prelude, async functions, direct existing-symbol binding and browser hosting are
198
- outside this release.
226
+ Collections beyond `List`, external law packages, cross-unit imports beyond the
227
+ prelude, async functions and browser hosting remain outside the language.
228
+ Direct existing-symbol bindings are part of LawSpec 0.10 described
229
+ above; the published 0.9 release uses implementation adapters.
199
230
 
200
231
  ## Algebra and currying (0.6)
201
232
 
@@ -250,7 +281,7 @@ reusable laws accept these curried functions, their partial applications, and
250
281
  scalar parameters. Quantified test inputs remain scalar.
251
282
 
252
283
  The prelude defines the following laws. Every row has an executable example in
253
- [algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/algebra.lawspec), including both sides of every
284
+ [algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/algebra.lawspec), including both sides of every
254
285
  combined law. `f` and `g` are binary operations, `inverse` is unary, and `e` and
255
286
  `zero` are scalar parameters. All these laws require equality of the element type.
256
287
 
@@ -318,7 +349,7 @@ their entire consequence. Every conjunct is type-checked and emitted. As with
318
349
  existing assertions, the first failure stops that individual test. `and` is now
319
350
  a reserved word.
320
351
 
321
- [Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/currying.lawspec) demonstrate a four-argument
352
+ [Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/currying.lawspec) demonstrate a four-argument
322
353
  function partially applied twice, a formatter with four heterogeneous arguments,
323
354
  and composition after partial application. Each example states its exact outputs.
324
355
  Run `node npm/bin/lawspec.mjs examples` after rebuilding to inspect all nine units
@@ -370,7 +401,7 @@ law `valid ports round trip` is
370
401
  end
371
402
  ```
372
403
 
373
- The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/parse_port.lawspec)
404
+ The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/parse_port.lawspec)
374
405
  defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
375
406
  negative values, and 65536. All explicit `expect` assertions run regardless of
376
407
  the law's condition. A false condition skips only the consequence: invalid ports
@@ -388,7 +419,7 @@ The prelude includes `satisfies predicate` (the predicate holds for every input)
388
419
  and `left inverse when predicate parse render` (the guarded round trip above).
389
420
  These reusable laws preserve the condition and its lexical bindings when expanded.
390
421
  `equivalent` can also compare two predicates, since `Bool` supports equality.
391
- The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/boolean_flags.lawspec)
422
+ The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/boolean_flags.lawspec)
392
423
  checks that flipping twice restores both `false` and `true`; all targets generate
393
424
  Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
394
425
  possible input combinations (up to 100), avoiding generator exhaustion.
@@ -467,7 +498,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
467
498
  The example inherits the input name `x` from the prelude. Both functions are
468
499
  user-owned adapter functions; either may delegate to your existing code.
469
500
 
470
- [The complete example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/equivalent.lawspec) compares decimal
501
+ [The complete example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/equivalent.lawspec) compares decimal
471
502
  renderers and two implementations that clamp negative integers to zero. For
472
503
  JavaScript, their adapters can be:
473
504
 
@@ -504,7 +535,7 @@ law `normalizers agree` is
504
535
  end
505
536
  ```
506
537
 
507
- The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/slug.lawspec)
538
+ The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/slug.lawspec)
508
539
  compares two implementations of ASCII-space replacement. It includes empty,
509
540
  Unicode and escaped text. Each target uses its native string generator:
510
541
  JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
@@ -531,7 +562,7 @@ law `canonicalization reaches a fixed point` is
531
562
  end
532
563
  ```
533
564
 
534
- The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/canonical_url.lawspec)
565
+ The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/canonical_url.lawspec)
535
566
  uses removal of **all trailing slashes** as a small fixed-point demonstration,
536
567
  not a complete URL canonicalization algorithm. For JavaScript:
537
568
 
@@ -540,13 +571,31 @@ export const canonicalize = value => value.replace(/\/+$/, "");
540
571
  ```
541
572
 
542
573
  Removing just one trailing slash fails the supplied repeated-slash example.
543
- The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.8.0/examples/specs/mixed_inputs.lawspec)
574
+ The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/mixed_inputs.lawspec)
544
575
  shows `Text` and `Int32` in the same quantified property and executable example.
545
- The JavaScript API represents input bindings and expected values as `number | string | boolean`.
546
- Each example includes `expectations: { actual: Expr; expected: number | string | boolean }[]`.
576
+ The JavaScript API represents example bindings as typed records whose values use
577
+ `DataValue`: lossless tagged scalar payloads or structural constructors. Expected results are
578
+ typed expressions inside `Assertion` trees, not untyped JavaScript primitives.
579
+ See the [API migration guide](API-MIGRATION.md) for their wire representation.
547
580
 
548
581
  ## Generate all example artifacts
549
582
 
583
+ To use native domain bindings, export the runnable payment
584
+ example (omit `--target` to export all eight languages):
585
+
586
+ ```sh
587
+ lawspec examples --example payments --target rust --output native_payments
588
+ cd native_payments/rust
589
+ # Install the dependencies listed in README.md, then:
590
+ lawspec check
591
+ lawspec generate
592
+ cargo test
593
+ ```
594
+
595
+ These projects include application-owned types, native generators, binding
596
+ configuration, and build files. Re-exporting preserves edits and leaves the
597
+ compiler's generation manifest separate from the example export manifest.
598
+
550
599
  ```sh
551
600
  npx lawspec examples
552
601
  # Or select a target and a relative output directory:
@@ -605,7 +654,7 @@ by the JS shim.
605
654
  ## Build and verify
606
655
 
607
656
  For contributors working from a repository checkout, build a local archive with
608
- `npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.8.0.tgz`.
657
+ `npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.10.0.tgz`.
609
658
  The package payload lives in `npm/`.
610
659
 
611
660
  ```sh
@@ -644,6 +693,20 @@ unchanged. Arguments select individual targets. `LAWSPEC_PYTHON=3.14` selects th
644
693
  additional Python reference environment. CI also exercises Node 22/24/26 and packs
645
694
  and installs the npm archive. Registry publication is a separate release action.
646
695
 
696
+ The native-binding acceptance command uses the installed npm archive, public CLI,
697
+ and the selected target's normal test command:
698
+
699
+ ```sh
700
+ node tools/native-example-integration.mjs rust
701
+ LAWSPEC_MACHINE_BITS=32 LAWSPEC_MINIFY=1 node tools/native-example-integration.mjs rust
702
+ ```
703
+
704
+ Replace `rust` with any supported target. It exports the payment project, verifies
705
+ correct behavior, rejects an incorrect fee, and checks regeneration. Per-command
706
+ logs live in `.artifacts/native-example-integration/`. See the
707
+ [acceptance reference](NATIVE-BINDINGS.md) for dependency overrides and remaining
708
+ verification gates.
709
+
647
710
 
648
711
  Refinement predicates can depend on earlier inputs. Generated tests backtrack from
649
712
  impossible prefixes, preserve the domain during shrinking, and validate function