eyeprolog 1.5.72 → 1.5.73

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/README.md CHANGED
@@ -84,7 +84,7 @@ The checked [Symbiotic Knowledge Graphs example](examples/symbiotic-knowledge-gr
84
84
  The same RDF → Prolog → RDF boundary is exercised by five additional checked scenarios: [cross-organization data sharing](https://eyereasoner.github.io/eyeprolog/examples/deck/cross-organization-data-sharing), [explainable EV-depot configuration](https://eyereasoner.github.io/eyeprolog/examples/deck/explainable-ev-depot-configuration), [operational incident response](https://eyereasoner.github.io/eyeprolog/examples/deck/operational-incident-response), [software supply-chain vulnerability response](https://eyereasoner.github.io/eyeprolog/examples/deck/sbom-vulnerability-response), and a [scientific evidence graph](https://eyereasoner.github.io/eyeprolog/examples/deck/scientific-evidence-graph). Together they cover policy decisions, reversible configuration reasoning, dependency-graph diagnosis, transitive SBOM exposure, and evidence aggregation with explicit disagreement.
85
85
 
86
86
  ## Benchmarks
87
- EyeProlog has 19 checksum-protected wall-clock benchmarks spanning recursion/indexing, constraints, tabling/WFS, DCGs, Eyelet, search, term I/O, attributes, rewriting, and the classic Prolog naive-reverse workload. Short workloads are adaptively batched before timing so millisecond-scale noise is not mistaken for a regression. Run `npm run benchmark`; create a machine-local comparison point with `npm run benchmark:baseline`; use `npm run test:benchmark` for harness checks. The checked [`examples/bench.pl`](examples/bench.pl) preserves the classic Quintus 1984 `nrev/2` workload on a 30-element list. For a comparable LIPS number, run `npm run benchmark:lips`: it executes the classic failure-driven `dobench/1` and `dodummy/1` loops in Prolog, subtracts dummy-loop CPU time, and applies the historical 496 procedure calls per reversal. The generic benchmark table still shows a quick wall-clock LIPS estimate for `classic-nrev`, but `benchmark:lips` is the canonical engine-speed measurement. LIPS is a historical basic-engine-speed indicator, not a whole-system performance score. Details are in [*The Art of EyeProlog*](the-art-of-eyeprolog.md).
87
+ EyeProlog has 19 checksum-protected wall-clock benchmarks spanning recursion/indexing, constraints, tabling/WFS, DCGs, Eyelet, search, term I/O, attributes, rewriting, and the classic Prolog naive-reverse workload. Short workloads are adaptively batched before timing so millisecond-scale noise is not mistaken for a regression. Run `npm run benchmark`; create a machine-local comparison point with `npm run benchmark -- --save .benchmarks/baseline.json`; use `node test/run-benchmark-tests.mjs` for harness checks. The checked [`examples/bench.pl`](examples/bench.pl) preserves the classic Quintus 1984 `nrev/2` workload on a 30-element list. For a comparable LIPS number, run `node test/lips-benchmark.mjs`: it executes the classic failure-driven `dobench/1` and `dodummy/1` loops in Prolog, subtracts dummy-loop CPU time, and applies the historical 496 procedure calls per reversal. The generic benchmark table still shows a quick wall-clock LIPS estimate for `classic-nrev`, but `node test/lips-benchmark.mjs` is the canonical engine-speed measurement. LIPS is a historical basic-engine-speed indicator, not a whole-system performance score. Details are in [*The Art of EyeProlog*](the-art-of-eyeprolog.md).
88
88
  For the project policy on post-ISO-standard and WG17 compatibility features such as digit separators, see [ISO/WG17 compatibility extensions](test/conformance/ISO-WG17-EXTENSIONS.md).
89
89
  ## Development
90
90
  ```sh
@@ -93,5 +93,13 @@ cd eyeprolog
93
93
  npm install
94
94
  npm test
95
95
  ```
96
- `npm test` is the release gate and fetches the latest seven Neumerkel conformity sources before the local gates; use `npm run test:offline` for a network-free pass, `npm run test:conformance` for all conformance layers, `npm run test:neumerkel` for live upstream only, and `npm run test:neumerkel:cached` only to reproduce the last fetch. Upstream counts are discovered dynamically. A stale tracked [latest Neumerkel report](test/conformance/NEUMERKEL-LATEST.md) produces a warning during normal tests. `npm run conformance:update:neumerkel` refreshes it from live upstream; `npm run conformance:sync:neumerkel` refreshes it from the exact successful snapshot already fetched by `npm test`; and `npm run conformance:check:neumerkel` verifies that snapshot without a second live fetch. npm's version lifecycle uses the sync path and stages the generated reports into the release commit. Exact bytes/hashes stay under Git-ignored `.cache/neumerkel/`. Benchmarks remain `npm run benchmark` and `npm run benchmark:lips`.
96
+ The npm command list is deliberately small:
97
+
98
+ - `npm test` (or `npm run test`): run the release gate, including live upstream conformity checks.
99
+ - `npm run conformance:update:wg17`: update the vendored WG17 syntax cases and their inventory documentation.
100
+ - `npm run generate`: rebuild generated library and book files.
101
+ - `npm run benchmark`: run the wall-clock benchmarks.
102
+
103
+ Use `npm test -- --offline` for a network-free local pass. Focused checks remain available directly, for example `node test/run-regression.mjs docs` or `node test/run-wg17.mjs`; see [test runners](test/README.md). The automatic version hooks still run the release gate, refresh and stage conformance reports, and push the release. Detailed upstream report maintenance is documented in the [conformance guide](test/conformance/README.md).
104
+
97
105
  EyeProlog is released under the [MIT License](LICENSE.md).
@@ -291,7 +291,7 @@ rdf-to-prolog \
291
291
  Run the checked EyeProlog example:
292
292
 
293
293
  ```sh
294
- npm run test:examples
294
+ node test/run-examples.mjs
295
295
  ```
296
296
 
297
297
  Materialize only the RDF-shaped results:
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.72",
6
+ "version": "1.5.73",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -46,36 +46,10 @@
46
46
  },
47
47
  "scripts": {
48
48
  "test": "node test/run-all.mjs",
49
- "test:offline": "node test/run-all.mjs --offline",
50
- "test:conformance": "node test/run-conformance-all.mjs",
51
- "test:conformance:offline": "node test/run-conformance-all.mjs --offline",
52
- "test:neumerkel": "node test/run-neumerkel.mjs",
53
- "test:neumerkel:cached": "node test/run-neumerkel.mjs --cached",
54
- "test:neumerkel:harness": "node test/run-neumerkel-tests.mjs",
55
- "test:iso": "node test/run-iso-strict.mjs",
56
- "test:wg17": "node test/run-wg17.mjs",
57
- "test:regression": "node test/run-regression.mjs",
58
- "test:examples": "node test/run-examples.mjs",
59
- "test:interop": "node test/run-interop.mjs",
60
- "test:playground": "node test/run-playground.mjs",
61
- "test:architecture": "node test/run-architecture.mjs",
62
- "test:openrulebench": "node test/run-openrulebench.mjs",
63
- "test:benchmark": "node test/run-benchmark-tests.mjs",
49
+ "conformance:update:wg17": "node tools/upgrade-wg17.mjs",
64
50
  "benchmark": "node test/benchmark.mjs",
65
- "benchmark:lips": "node test/lips-benchmark.mjs",
66
- "benchmark:baseline": "node test/benchmark.mjs --save .benchmarks/baseline.json",
67
- "conformance:update": "npm run conformance:update:wg17 && npm run conformance:update:neumerkel",
68
- "conformance:report": "node test/run-conformance-report.mjs conformance-report.md",
69
51
  "generate": "node tools/generate-library-autoload-index.mjs && node tools/generate-predicate-reference.mjs && node tools/extract-book-examples.mjs",
70
- "generate:autoload": "node tools/generate-library-autoload-index.mjs",
71
- "generate:reference": "node tools/generate-predicate-reference.mjs",
72
- "preversion": "npm test && npm run conformance:sync:neumerkel && npm run conformance:report && git add test/conformance/NEUMERKEL-LATEST.md conformance-report.md",
73
- "postversion": "git push origin HEAD --follow-tags",
74
- "conformance:update:wg17": "node tools/upgrade-wg17.mjs",
75
- "conformance:update:neumerkel": "node test/run-neumerkel.mjs --update-report",
76
- "conformance:check:neumerkel": "node test/run-neumerkel.mjs --cached --verify-report",
77
- "conformance:sync:neumerkel": "node test/run-neumerkel.mjs --cached --update-report",
78
- "test:http-json": "node test/run-http-json.mjs",
79
- "test:iso-part2-amendment": "node test/run-iso-part2-amendment.mjs"
52
+ "preversion": "npm test && node test/run-neumerkel.mjs --cached --update-report && node test/run-conformance-report.mjs conformance-report.md && git add test/conformance/NEUMERKEL-LATEST.md conformance-report.md",
53
+ "postversion": "git push origin HEAD --follow-tags"
80
54
  }
81
55
  }
package/test/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Test runners
2
+
3
+ Normally, run `npm test`. Use `npm test -- --offline` to skip live upstream fetching.
4
+ `npm run conformance:update:wg17` refreshes the vendored WG17 snapshot.
5
+
6
+ For development, run a focused check directly from the repository root:
7
+
8
+ ```sh
9
+ node test/run-regression.mjs # all regression sections
10
+ node test/run-regression.mjs docs # documentation checks only
11
+ node test/run-conformance-all.mjs # conformance layers; accepts --offline
12
+ node test/run-wg17.mjs # vendored WG17 syntax
13
+ node test/run-iso-strict.mjs # strict ISO core
14
+ node test/run-iso-part2-amendment.mjs
15
+ node test/run-neumerkel.mjs # live upstream; --cached reproduces last fetch
16
+ node test/run-neumerkel-tests.mjs # upstream-fetch harness
17
+ node test/run-examples.mjs
18
+ node test/run-playground.mjs
19
+ node test/run-architecture.mjs
20
+ node test/run-openrulebench.mjs
21
+ node test/run-http-json.mjs
22
+ node test/run-interop.mjs # requires the comparison engines
23
+ node test/run-benchmark-tests.mjs # benchmark harness
24
+ ```
25
+
26
+ These runners retain their existing options; there is no separate npm alias for
27
+ each one. Focused checks do not replace the full release gate.
28
+
29
+ For performance measurements, use `npm run benchmark`. Save a local baseline with
30
+ `npm run benchmark -- --save .benchmarks/baseline.json`, or run
31
+ `node test/lips-benchmark.mjs` for the classic LIPS measurement.
32
+
33
+ See the [conformance guide](conformance/README.md) for report maintenance.
@@ -102,7 +102,7 @@ if (baselinePath != null) {
102
102
  const loaded = JSON.parse(await fs.readFile(baselinePath, 'utf8'));
103
103
  if (loaded.format === 2) baseline = loaded;
104
104
  else {
105
- baselineWarning = `Ignoring legacy timing baseline format ${loaded.format ?? 'unknown'}; regenerate it with npm run benchmark:baseline.`;
105
+ baselineWarning = `Ignoring legacy timing baseline format ${loaded.format ?? 'unknown'}; regenerate it with npm run benchmark -- --save .benchmarks/baseline.json.`;
106
106
  baselinePath = null;
107
107
  }
108
108
  }
@@ -296,10 +296,10 @@ if (options.json) {
296
296
 
297
297
  process.stdout.write(`\n${results.length} benchmarks; ${options.runs} measured batch${options.runs === 1 ? '' : 'es'} each after ${options.warmup} warm-up batch${options.warmup === 1 ? '' : 'es'}, calibrated after one priming execution toward ${options.targetMs} ms per batch.\n`);
298
298
  if (baselineWarning) process.stdout.write(`${baselineWarning}\n`);
299
- if (baselinePath == null && !baselineWarning) process.stdout.write('No timing baseline found; run npm run benchmark:baseline to create .benchmarks/baseline.json.\n');
299
+ if (baselinePath == null && !baselineWarning) process.stdout.write('No timing baseline found; run npm run benchmark -- --save .benchmarks/baseline.json to create .benchmarks/baseline.json.\n');
300
300
  if (options.save != null) process.stdout.write(`Saved timing baseline: ${path.relative(root, options.save)}\n`);
301
301
  process.stdout.write('Change compares the current median/op directly with the saved baseline median/op; the measured range is shown separately.\n');
302
- process.stdout.write('The classic-nrev LIPS column is a quick wall-clock estimate. Use npm run benchmark:lips for the Quintus-style dummy-subtracted CPU measurement.\n');
302
+ process.stdout.write('The classic-nrev LIPS column is a quick wall-clock estimate. Use node test/lips-benchmark.mjs for the Quintus-style dummy-subtracted CPU measurement.\n');
303
303
  if (summary.comparable > 0) {
304
304
  process.stdout.write('Suite score is the geometric mean of current/baseline ratios, so every benchmark has equal relative weight; Time-weighted total compares summed medians and is dominated by longer workloads.\n');
305
305
  }
@@ -286,14 +286,14 @@ architecture gates. The upstream case counts are discovered dynamically.
286
286
  Useful focused commands are:
287
287
 
288
288
  ```sh
289
- npm run test:conformance
290
- npm run test:neumerkel
291
- npm run test:iso
292
- npm run test:wg17
293
- npm run test:offline
289
+ node test/run-conformance-all.mjs
290
+ node test/run-neumerkel.mjs
291
+ node test/run-iso-strict.mjs
292
+ node test/run-wg17.mjs
293
+ npm test -- --offline
294
294
  ```
295
295
 
296
- `test:offline` is a development/reproduction aid; it is not sufficient for a
296
+ `npm test -- --offline` is a development/reproduction aid; it is not sufficient for a
297
297
  release claim that says EyeProlog passes the latest Neumerkel suites. Expected
298
298
  conformance outputs are never auto-accepted.
299
299
 
@@ -31,8 +31,8 @@ Counts are output from upstream, not hard-coded test constants.
31
31
  Exact fetched bytes, SHA-256 hashes, fetch timestamps, and HTTP validators remain under
32
32
  Git-ignored `.cache/neumerkel/` for local inspection/reproduction and are intentionally not committed.
33
33
  A normal test run warns when this tracked report is stale. Refresh directly from live
34
- upstream with `npm run conformance:update:neumerkel`, or sync the exact successful
35
- snapshot already fetched by `npm test` with `npm run conformance:sync:neumerkel`.
36
- `npm run conformance:check:neumerkel` verifies the tracked report against that last
34
+ upstream with `node test/run-neumerkel.mjs --update-report`, or sync the exact successful
35
+ snapshot already fetched by `npm test` with `node test/run-neumerkel.mjs --cached --update-report`.
36
+ `node test/run-neumerkel.mjs --cached --verify-report` verifies the tracked report against that last
37
37
  successful live snapshot without fetching upstream a second time.
38
38
 
@@ -4,7 +4,7 @@ EyeProlog treats Ulrich Neumerkel's current ISO/WG17 conformity material as a
4
4
  moving upstream release gate, not as a frozen snapshot with permanent case
5
5
  counts.
6
6
 
7
- `npm run test:neumerkel` fetches these seven TU Wien sources on every live run:
7
+ `node test/run-neumerkel.mjs` fetches these seven TU Wien sources on every live run:
8
8
 
9
9
  1. `conformity_testing` — Part 1 syntax/reader/writer matrix;
10
10
  2. `number_chars_cont_quad.pl` — `number_chars/2` continuation corpus;
@@ -32,13 +32,13 @@ Markdown no longer matches, the test still reflects engine conformance and print
32
32
  warning with the refresh command:
33
33
 
34
34
  ```sh
35
- npm run conformance:update:neumerkel
35
+ node test/run-neumerkel.mjs --update-report
36
36
  ```
37
37
 
38
38
  Commit the resulting `test/conformance/NEUMERKEL-LATEST.md` after reviewing the
39
- change. After a successful `npm test`, `npm run conformance:sync:neumerkel` writes
39
+ change. After a successful `npm test`, `node test/run-neumerkel.mjs --cached --update-report` writes
40
40
  the tracked report from the exact cached source bytes that just passed, avoiding a
41
- second network fetch. `npm run conformance:check:neumerkel` verifies the tracked
41
+ second network fetch. `node test/run-neumerkel.mjs --cached --verify-report` verifies the tracked
42
42
  report against that same last successful snapshot. The npm version lifecycle
43
43
  uses this race-free sync path and stages the generated reports into the release
44
44
  commit. The tracked report intentionally omits fetch timestamps and HTTP validators,
@@ -53,14 +53,14 @@ Git-ignored: it is an inspection/reproduction cache, not published project evide
53
53
  Use:
54
54
 
55
55
  ```sh
56
- npm run test:neumerkel
56
+ node test/run-neumerkel.mjs
57
57
  ```
58
58
 
59
59
  for the canonical live check. For offline reproduction of the exact last live
60
60
  fetch, use:
61
61
 
62
62
  ```sh
63
- npm run test:neumerkel:cached
63
+ node test/run-neumerkel.mjs --cached
64
64
  ```
65
65
 
66
66
  The cached command never claims to check the latest upstream suites by itself.
@@ -83,39 +83,39 @@ npm test
83
83
  For an offline-only development pass:
84
84
 
85
85
  ```sh
86
- npm run test:offline
86
+ npm test -- --offline
87
87
  ```
88
88
 
89
89
  The conformance commands are:
90
90
 
91
91
  ```sh
92
- npm run test:conformance # live Neumerkel + local ISO/conformance layers
93
- npm run test:conformance:offline # same local layers, no network
94
- npm run test:neumerkel # the seven live upstream suites only
95
- npm run test:neumerkel:cached # exact last fetched bytes; reproduction only
96
- npm run conformance:check:neumerkel # verify tracked report against last successful live snapshot
97
- npm run test:iso # Part 1 + Corrigenda strict-core processor gate
98
- npm run test:iso-part2-amendment # 2013 Part 2 amendment module requirements
99
- npm run test:wg17 # vendored reviewed WG17 syntax regression
92
+ node test/run-conformance-all.mjs # live Neumerkel + local ISO/conformance layers
93
+ node test/run-conformance-all.mjs --offline # same local layers, no network
94
+ node test/run-neumerkel.mjs # the seven live upstream suites only
95
+ node test/run-neumerkel.mjs --cached # exact last fetched bytes; reproduction only
96
+ node test/run-neumerkel.mjs --cached --verify-report # verify tracked report against last successful live snapshot
97
+ node test/run-iso-strict.mjs # Part 1 + Corrigenda strict-core processor gate
98
+ node test/run-iso-part2-amendment.mjs # 2013 Part 2 amendment module requirements
99
+ node test/run-wg17.mjs # vendored reviewed WG17 syntax regression
100
100
  ```
101
101
 
102
- `test:neumerkel` always fetches the current TU Wien sources. It does not skip a
103
- fetch because a cache exists. The runner discovers the number of active tests from those sources and fails on any newly introduced case EyeProlog does not pass. If the stable, tracked [NEUMERKEL-LATEST.md](NEUMERKEL-LATEST.md) is stale, normal tests warn rather than turning a passing engine run into a failure. `npm run conformance:update:neumerkel` performs a fresh live run and refreshes the report; after `npm test`, `npm run conformance:sync:neumerkel` refreshes it from the exact successful cached snapshot; and `npm run conformance:check:neumerkel` verifies that snapshot without a second network fetch. Exact bytes, SHA-256 hashes, timestamps, and HTTP validators stay under Git-ignored `.cache/neumerkel/` for inspection/reproduction only. See [NEUMERKEL-LIVE.md](NEUMERKEL-LIVE.md).
102
+ `node test/run-neumerkel.mjs` always fetches the current TU Wien sources. It does not skip a
103
+ fetch because a cache exists. The runner discovers the number of active tests from those sources and fails on any newly introduced case EyeProlog does not pass. If the stable, tracked [NEUMERKEL-LATEST.md](NEUMERKEL-LATEST.md) is stale, normal tests warn rather than turning a passing engine run into a failure. `node test/run-neumerkel.mjs --update-report` performs a fresh live run and refreshes the report; after `npm test`, `node test/run-neumerkel.mjs --cached --update-report` refreshes it from the exact successful cached snapshot; and `node test/run-neumerkel.mjs --cached --verify-report` verifies that snapshot without a second network fetch. Exact bytes, SHA-256 hashes, timestamps, and HTTP validators stay under Git-ignored `.cache/neumerkel/` for inspection/reproduction only. See [NEUMERKEL-LIVE.md](NEUMERKEL-LIVE.md).
104
104
 
105
- The vendored WG17 syntax snapshot is intentionally secondary. Update all upstream conformance evidence with `npm run conformance:update`, or use the focused commands:
105
+ The vendored WG17 syntax snapshot is intentionally secondary. Update it with `npm run conformance:update:wg17`. For focused upstream report maintenance and verification:
106
106
 
107
107
  ```sh
108
108
  npm run conformance:update:wg17
109
- npm run conformance:update:neumerkel
110
- npm run conformance:sync:neumerkel
111
- npm run test:wg17
109
+ node test/run-neumerkel.mjs --update-report
110
+ node test/run-neumerkel.mjs --cached --update-report
111
+ node test/run-wg17.mjs
112
112
  ```
113
113
 
114
114
  Every vendored syntax case is still checked against its upstream Codex
115
115
  expectation; reviewed exact outcomes are an additional regression lock rather
116
116
  than an alternative acceptance rule.
117
117
 
118
- Regenerate the top-level local-corpus report with `npm run conformance:report`. It links to [NEUMERKEL-LATEST.md](NEUMERKEL-LATEST.md) rather than duplicating live evidence. Counts in the tracked Neumerkel report are generated evidence, not permanent constants in project policy.
118
+ Regenerate the top-level local-corpus report with `node test/run-conformance-report.mjs conformance-report.md`. It links to [NEUMERKEL-LATEST.md](NEUMERKEL-LATEST.md) rather than duplicating live evidence. Counts in the tracked Neumerkel report are generated evidence, not permanent constants in project policy.
119
119
 
120
120
  Run a matching local file-based conformance subset directly with:
121
121
 
@@ -46,6 +46,6 @@ None.
46
46
 
47
47
  ## Maintenance
48
48
 
49
- 1. Run `npm run conformance:update` to reconcile the dated fixture with upstream.
49
+ 1. Run `npm run conformance:update:wg17` to reconcile the dated fixture with upstream.
50
50
  2. Review every new or changed ISO expectation before adding its expected outcome.
51
- 3. Run `npm run test:wg17` and keep this generated status page synchronized.
51
+ 3. Run `node test/run-wg17.mjs` and keep this generated status page synchronized.
@@ -59,8 +59,8 @@ async function fetchOne(source) {
59
59
  } catch (error) {
60
60
  throw new Error(
61
61
  `Neumerkel live fetch failed for ${source.key} (${source.url}). ` +
62
- 'The canonical conformance gate requires network access; use npm run test:offline ' +
63
- 'for a network-free local run or npm run test:neumerkel:cached to reproduce the last live fetch.',
62
+ 'The canonical conformance gate requires network access; use npm test -- --offline ' +
63
+ 'for a network-free local run or node test/run-neumerkel.mjs --cached to reproduce the last live fetch.',
64
64
  { cause: error },
65
65
  );
66
66
  }
@@ -88,7 +88,7 @@ async function fetchOne(source) {
88
88
  function readCachedOne(source, cacheDir) {
89
89
  const filename = path.join(cacheDir, source.filename);
90
90
  if (!fs.existsSync(filename)) {
91
- throw new Error(`Neumerkel cache is incomplete: missing ${path.relative(packageRoot, filename)}; run npm run test:neumerkel online first`);
91
+ throw new Error(`Neumerkel cache is incomplete: missing ${path.relative(packageRoot, filename)}; run node test/run-neumerkel.mjs online first`);
92
92
  }
93
93
  const bytes = new Uint8Array(fs.readFileSync(filename));
94
94
  return {
@@ -476,9 +476,9 @@ export function formatNeumerkelMarkdown({ summary }) {
476
476
  'Exact fetched bytes, SHA-256 hashes, fetch timestamps, and HTTP validators remain under',
477
477
  'Git-ignored `.cache/neumerkel/` for local inspection/reproduction and are intentionally not committed.',
478
478
  'A normal test run warns when this tracked report is stale. Refresh directly from live',
479
- 'upstream with `npm run conformance:update:neumerkel`, or sync the exact successful',
480
- 'snapshot already fetched by `npm test` with `npm run conformance:sync:neumerkel`.',
481
- '`npm run conformance:check:neumerkel` verifies the tracked report against that last',
479
+ 'upstream with `node test/run-neumerkel.mjs --update-report`, or sync the exact successful',
480
+ 'snapshot already fetched by `npm test` with `node test/run-neumerkel.mjs --cached --update-report`.',
481
+ '`node test/run-neumerkel.mjs --cached --verify-report` verifies the tracked report against that last',
482
482
  'successful live snapshot without fetching upstream a second time.',
483
483
  '',
484
484
  );
@@ -649,7 +649,10 @@ ${profile}`;
649
649
  const publishIndex = publishWorkflow.indexOf('run: npm publish');
650
650
  assertEqual(testIndex >= 0 && testIndex < publishIndex, true, 'publish workflow test gate');
651
651
  assertEqual(packIndex >= 0 && packIndex < publishIndex, true, 'publish workflow package gate');
652
- assertEqual(pkg.scripts?.['test:openrulebench'], 'node test/run-openrulebench.mjs', 'OpenRuleBench test script');
652
+ assertArrayEqual(Object.keys(pkg.scripts).sort(), ['benchmark', 'conformance:update:wg17', 'generate', 'postversion', 'preversion', 'test'], 'small npm command surface');
653
+ assertEqual(pkg.scripts.test, 'node test/run-all.mjs', 'full release gate');
654
+ const runner = fs.readFileSync(path.join(packageRoot, 'test', 'run-all.mjs'), 'utf8');
655
+ assertIncludes(runner, 'runOpenRuleBenchChecks(reporter)', 'OpenRuleBench remains in release gate');
653
656
  },
654
657
  },
655
658
  {
@@ -123,17 +123,13 @@ export function runNeumerkelHarnessTests(reporter = new TestReporter()) {
123
123
  reporter.test('release workflow reuses the successful live snapshot instead of refetching', () => {
124
124
  const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
125
125
  const scripts = pkg.scripts ?? {};
126
- if (scripts['conformance:sync:neumerkel'] !== 'node test/run-neumerkel.mjs --cached --update-report') {
127
- throw new Error('Neumerkel sync must update from the cached successful live snapshot');
128
- }
129
- if (scripts['conformance:check:neumerkel'] !== 'node test/run-neumerkel.mjs --cached --verify-report') {
130
- throw new Error('Neumerkel report check must not refetch live upstream');
131
- }
132
- if (!String(scripts.preversion ?? '').includes('conformance:sync:neumerkel')) {
126
+ const releaseSteps = String(scripts.preversion ?? '').split(' && ');
127
+ if (releaseSteps[0] !== 'npm test' || releaseSteps[1] !== 'node test/run-neumerkel.mjs --cached --update-report') {
133
128
  throw new Error('preversion must synchronize the tracked report from the successful npm test snapshot');
134
129
  }
135
- if (String(scripts.preversion ?? '').includes('conformance:check:neumerkel')) {
136
- throw new Error('preversion must not perform a second report check/fetch cycle after synchronization');
130
+ if (releaseSteps[2] !== 'node test/run-conformance-report.mjs conformance-report.md' ||
131
+ releaseSteps[3] !== 'git add test/conformance/NEUMERKEL-LATEST.md conformance-report.md' || releaseSteps.length !== 4) {
132
+ throw new Error('preversion must generate and stage both reports without another fetch');
137
133
  }
138
134
  });
139
135
 
@@ -29,7 +29,7 @@ function parseArgs(argv) {
29
29
 
30
30
  function printHelp() {
31
31
  process.stdout.write(
32
- 'Usage: npm run test:neumerkel -- [--cached] [--source-dir DIR] [--verify-report] [--update-report]\n\n' +
32
+ 'Usage: node test/run-neumerkel.mjs [--cached] [--source-dir DIR] [--verify-report] [--update-report]\n\n' +
33
33
  'Default: fetch all seven current Neumerkel conformity sources live and run\n' +
34
34
  'every discovered case. A stale tracked report is reported as a warning, not\n' +
35
35
  'an engine-test failure. --verify-report makes report freshness mandatory;\n' +
@@ -54,7 +54,7 @@ export async function runNeumerkel(reporter, options = {}) {
54
54
  if (committed !== result.reportText) {
55
55
  const message =
56
56
  `tracked Neumerkel report is stale: ${relativeReportPath}\n` +
57
- 'Run npm run conformance:update:neumerkel and commit the updated report.';
57
+ 'Run node test/run-neumerkel.mjs --update-report and commit the updated report.';
58
58
  if (effective.verifyReport) throw new Error(message);
59
59
  reporter.stdout.write(`WARN ${message}\n`);
60
60
  } else {
@@ -7034,7 +7034,7 @@ library dependency should be explicit. `--iso-strict` always disables EyeProlog
7034
7034
  library autoloading, so strict ISO execution never gains procedures from this
7035
7035
  implementation convenience.
7036
7036
 
7037
- `-w` / `--warnings` reports explicit dependencies on non-profile libraries and calls to non-profile predicates from otherwise common modules. `--portable` turns those diagnostics into a failing run, making the conservative profile suitable for continuous integration. Cross-engine portability can be exercised with `npm run test:interop` when EyeProlog, Trealla, and Scryer are installed.
7037
+ `-w` / `--warnings` reports explicit dependencies on non-profile libraries and calls to non-profile predicates from otherwise common modules. `--portable` turns those diagnostics into a failing run, making the conservative profile suitable for continuous integration. Cross-engine portability can be exercised with `node test/run-interop.mjs` when EyeProlog, Trealla, and Scryer are installed.
7038
7038
 
7039
7039
  ### Specialized library implementation notes
7040
7040
 
@@ -10339,7 +10339,7 @@ hand.
10339
10339
  Run all 210 normal answer goldens and the 61 selected proof goldens with:
10340
10340
 
10341
10341
  ```sh
10342
- npm run test:examples
10342
+ node test/run-examples.mjs
10343
10343
  ```
10344
10344
 
10345
10345
  Run the complete conformance, regression, example, and proof corpus
@@ -10354,8 +10354,8 @@ and fast:
10354
10354
 
10355
10355
  ```sh
10356
10356
  npm run benchmark
10357
- npm run benchmark:baseline
10358
- npm run benchmark:lips
10357
+ npm run benchmark -- --save .benchmarks/baseline.json
10358
+ node test/lips-benchmark.mjs
10359
10359
  ```
10360
10360
 
10361
10361
  The benchmark suite contains 19 representative workloads and stores their
@@ -10368,7 +10368,7 @@ warm-up batch, five measured batches are reported as milliseconds per workload
10368
10368
  execution. Naturally long workloads keep a batch size of one.
10369
10369
 
10370
10370
  The classic `examples/bench.pl` workload also has a dedicated LIPS harness.
10371
- `npm run benchmark:lips` follows the 1984 Quintus method more closely than the
10371
+ `node test/lips-benchmark.mjs` follows the 1984 Quintus method more closely than the
10372
10372
  generic runner: `dobench/1` and `dodummy/1` execute failure-driven loops inside
10373
10373
  Prolog, the dummy CPU time is subtracted, and the remaining time is converted
10374
10374
  using 496 procedure calls for one reversal of the 30-element list. Node's
@@ -10409,9 +10409,9 @@ lists, terms, atoms, variables, negation, queries, rules, and
10409
10409
  syntax. Separate corpora cover expected errors, warnings, and proofs:
10410
10410
 
10411
10411
  ```sh
10412
- npm run test:conformance
10413
- npm run test:iso
10414
- npm run test:wg17
10412
+ node test/run-conformance-all.mjs
10413
+ node test/run-iso-strict.mjs
10414
+ node test/run-wg17.mjs
10415
10415
  node test/run-conformance-report.mjs
10416
10416
  ```
10417
10417
 
@@ -10452,7 +10452,7 @@ The repository exposes several forms of executable evidence:
10452
10452
  Run the browser contract independently with:
10453
10453
 
10454
10454
  ```sh
10455
- npm run test:playground
10455
+ node test/run-playground.mjs
10456
10456
  ```
10457
10457
 
10458
10458
  ### Supported ISO Prolog implementation
@@ -97,7 +97,7 @@ const text = `// Generated by tools/generate-library-autoload-index.mjs.\n` +
97
97
  if (process.argv.includes('--check')) {
98
98
  const current = fs.existsSync(outputFile) ? fs.readFileSync(outputFile, 'utf8') : '';
99
99
  if (current !== text) {
100
- console.error(`${path.relative(root, outputFile)} is stale; run npm run generate:autoload`);
100
+ console.error(`${path.relative(root, outputFile)} is stale; run npm run generate`);
101
101
  process.exit(1);
102
102
  }
103
103
  console.log(`${path.relative(root, outputFile)} is up to date`);
@@ -209,7 +209,7 @@ const wanted = expectedBook(book, generated);
209
209
 
210
210
  if (process.argv.includes('--check')) {
211
211
  if (wanted !== book) {
212
- console.error(`${path.relative(root, bookFile)} predicate reference is stale; run npm run generate:reference`);
212
+ console.error(`${path.relative(root, bookFile)} predicate reference is stale; run npm run generate`);
213
213
  process.exit(1);
214
214
  }
215
215
  console.log(`${path.relative(root, bookFile)} predicate reference is up to date (${surface.indicators.length} predicates)`);
@@ -134,9 +134,9 @@ ${untracedIds.length === 0 ? 'None.' : `${formatRanges(untracedIds)}.`}
134
134
 
135
135
  ## Maintenance
136
136
 
137
- 1. Run \`npm run conformance:update\` to reconcile the dated fixture with upstream.
137
+ 1. Run \`npm run conformance:update:wg17\` to reconcile the dated fixture with upstream.
138
138
  2. Review every new or changed ISO expectation before adding its expected outcome.
139
- 3. Run \`npm run test:wg17\` and keep this generated status page synchronized.
139
+ 3. Run \`node test/run-wg17.mjs\` and keep this generated status page synchronized.
140
140
  `;
141
141
  }
142
142
 
@@ -158,7 +158,7 @@ if (process.argv[1] != null && path.resolve(process.argv[1]) === fileURLToPath(i
158
158
  if (process.argv.includes('--check')) {
159
159
  const current = fs.readFileSync(statusPath, 'utf8');
160
160
  if (current !== rendered) {
161
- process.stderr.write('WG17 syntax status is stale; run npm run conformance:update and update the file.\n');
161
+ process.stderr.write('WG17 syntax status is stale; run npm run conformance:update:wg17 and update the file.\n');
162
162
  process.exitCode = 1;
163
163
  }
164
164
  } else {
@@ -457,7 +457,7 @@ function parseArgs(argv) {
457
457
  }
458
458
 
459
459
  function printHelp() {
460
- process.stdout.write(`Usage: npm run conformance:update -- [--check] [--source URL_OR_FILE]\n\n` +
460
+ process.stdout.write(`Usage: npm run conformance:update:wg17 -- [--check] [--source URL_OR_FILE]\n\n` +
461
461
  `Refreshes the vendored WG17 conformity tests from the TU Wien table.\n` +
462
462
  `New or changed rows are executable immediately against the upstream\n` +
463
463
  `Codex expectation; existing reviewed exact outcomes remain pinned only as additional regression checks.\n`);
@@ -477,7 +477,7 @@ export async function upgradeWg17({ check = false, source = syntaxSource } = {})
477
477
 
478
478
  if (check) {
479
479
  if (semanticChanges > 0) {
480
- process.stderr.write('WG17 snapshot is stale; run npm run conformance:update.\n');
480
+ process.stderr.write('WG17 snapshot is stale; run npm run conformance:update:wg17.\n');
481
481
  process.exitCode = 1;
482
482
  return { changed: true, ...reconciliation };
483
483
  }
@@ -524,7 +524,7 @@ export async function upgradeWg17({ check = false, source = syntaxSource } = {})
524
524
  process.stdout.write(`Updated WG17 snapshot (${fixture.cases.length} cases).\n`);
525
525
  if (upstreamAssertions.length > 0) {
526
526
  process.stdout.write(
527
- `Direct upstream assertions used by test:wg17: ${formatIdList(upstreamAssertions)}\n`,
527
+ `Direct upstream assertions used by the WG17 runner: ${formatIdList(upstreamAssertions)}\n`,
528
528
  );
529
529
  }
530
530
  return { changed: semanticChanges > 0, upstreamAssertions, ...reconciliation };