Cyclops Storm / Lab notes

How the numbers were measured

For anyone checking my work. Every number on the front page comes from a file in this folder or from a pinned commit in the engine's private repositories. This page says which, how each number was produced, and what it can't tell you.

The measurements answer three different questions:

Evidence Question it answers Where to read it
Engine unit tests Does each version satisfy its own suite? The suites differ in scope. Test counts and their limits
Shared cross-checks How do both versions handle the same 21 selected programs and API probes? Current cross-check results
Performance benchmarks How long do three queries take in the rewrite and SWI-Prolog? Current timings and method
File What it is
test-timeline-2026-09-25.csv Dated passing-test counts, Sep 2025–Sep 2026 (433 observations, each with its timestamp and source)
render-test-timeline.py Draws assets/test-timeline.png from that CSV
cases-2026-09-24.json The 21 comparison programs and their expected results
comparison-2026-09-24.json, .md The comparison's raw results and the harness's own report
development-progress-2026-09-25.json The same 21 checks replayed on 11 saved versions
replay-development-progress.py, render-development-progress.py The replay and its chart
index.html Historical results under the original scoring, case by case (web version)

Current comparison and migration

The SWI-Prolog feature comparison is a separate inventory of 34 small probes against SWI and the rewrite, with raw observations and a reproduction script. It documents compatibility differences without changing the original 21 cross-check scores.

The migration drill-down includes measured package/file/function counts and the test dispositions, backed by raw measurements and a reproducible inventory script. The revised comparison reruns the same 21 cases with corrected timeout and refusal scoring: old 11 pass / 10 fail, rewrite 16 pass / 3 fail / 2 unknown.

The architecture evidence record lists the revisions, source locations and matched probes behind the Architecture page, with raw probe results and the diagram script.

The revised benchmarks collect full bindings in both engines, validate exact answers and separately measure Python conversion. Earlier comparison/replay scores and original benchmark timings below are historical records under their original methods.

What the test counts mean

Where they come from. From the start the project kept a log with one pytest summary line per test run, added by hand at first and later mostly in the agents' own commits. It holds 814 runs through Aug 30, 2026. Most lines carry no date. The 167 lines from Sep 7–25, 2025 carry their own timestamps; the rest I dated three ways:

The first 139 runs carry no date and can't be anchored; the count grew from about 90 to about 500 passing over them. About the first 60 probably predate the earliest surviving repository (Aug 30, 2025), whose first commit already had about 320 test functions. The rewrite's counts come from its TestScores.md. Its first commit was rebuilt and run to get 540.

What they count. Passing pytest cases in each engine's test suite, where a parameterized test counts once per case. The earlier engine's suite covered more than the reasoning core. At its final commit (6,759 cases collected), about 79% tested engine semantics, 8% the text parser, 5% narrative models, 4% the host API, and 3% the command-line game and ontology tools.

Why the final figure varies. The earlier engine's final suite is quoted four ways:

Figure What it counts
6,660 Passing, as logged by the project's runner, which excludes slow and benchmark tests
6,683 Passing under plain pytest
6,759 Collected
6,778 Collected, including benchmarks and one file skipped at collection. This is what the rewrite's audit classified, file by file

What the counts don't tell you:

The 21-check comparison

Historical scoring. This section explains the original report and replay. For the corrected policy and a fresh endpoint run, use the revised comparison.

What it is. 21 small Prolog programs, each with an expected result, run on the earlier engine (e62758c, Aug 30) and the rewrite (9be7804, Sep 24). Each case runs in its own process with a timeout.

Where expected answers come from:

No case takes its expected answer from either engine's own documentation. The harness refuses that by name, after a review caught an early design draft doing it.

Results:

Earlier engine Rewrite
Pass 10 18
of which, passes by test-runner timeout 0 2
Pass, if a timeout counts as a failure 10 16
Pass, if the undecodable why/1 refusal counts as correct 11 18

The earlier engine's 11 failures, by kind:

Kind Count
Couldn't parse the program (div, =..) 2
Wrong or missing answers: sort/msort, operator precedence, 1/'1'/1.0 merged 3
Endless search returned as a short, final-looking list (2 answers; 0 answers) 2
Bad arithmetic answered "no" instead of raising an error 1
Shared cache: a caller's edit changed a later answer 1
evidence_chain/1 called a non-fact "derived" 1
why/1 replied "No derivation path found," which the harness couldn't decode and scored as an answer 1

Read it with these caveats:

The raw JSON records both as "stopped on an unbounded answer set, as required." Only the replay file's timed_out marker shows how they actually stopped. - One-sided coverage. The cases probe places where the earlier engine departs from ISO. They don't probe the rewrite's own departures from SWI, which are: - default deduplication; - the occurs check; - no cut in recursive predicates; - rejecting programs that are safe only because of their data; - unsupported built-ins quietly failing; - the non-classical reading of timed effect rules.

None covers story time, source provenance or the game hooks, the things the engine is actually for. - Blind spots. The corpus missed some of the earlier engine's known bugs. Its cut case puts the cut after a goal in the clause body, which the earlier engine handled. The failing case is a cut that should stop a later fact clause from being tried. Its negation case negates a variable that's already bound. - Explanation probes check that a request gets an answer, not that the answer is right. Each probe has a negative control, a request that should get nothing. Only derivation/1 passes. The rewrite has none of the three predicates.

The replay. The same 21 checks were rerun on 11 saved versions: 5 of the earlier engine (May–August 2026) and 6 of the rewrite.

Both endpoints reproduce the published verdicts exactly. Dates are commit dates, and the intervals between commits aren't hours worked.

Semantic probes

The "same programs, three engines" table in HISTORY.md comes from small programs run against snapshots exported from the private repositories (git archive into scratch directories), with SWI-Prolog 10.0.2 as the reference. Every program is written out in full in that table. Reproducing the engine columns needs the engine code.

Code size

Non-test Python lines, counted per commit with git show and wc -l:

What isn't measured

Earlier engine Rewrite
First (cold) query 6.3 ms 2.4 ms
Repeated (warm) query 0.004 ms 0.44 ms
Warm, deep-copying each answer (the bug fix) 0.008 ms n/a

So the old engine was faster on repeated queries because it cached, and it would have stayed that way with the bug fixed. The rewrite has no cross-query cache, by design, and is faster on a first query. Neither is a general benchmark. Revised full-binding timings for the rewrite against SWI-Prolog are in the benchmark detail page. - Memory, effort and cost. Nothing comparable was recorded for either engine.

Reproducing

Everything here can be rerun from local clones of the engine repositories, with Python 3.12+ and SWI-Prolog 10.0.2. The commands are in development-progress-notes.html. The replay script uses the comparison harness's scoring code from the private rewrite repository, so without access to those repositories, the JSON files here are the record.