Cyclops Storm / Lab notes

Building it with AI agents

For people using coding agents on real software. The short version: agents produced plausible code and plausible tests faster than I could check either, and for most of a year I measured progress with the one number they were best at inflating. What follows is what's in the record: commits, test logs, review reports, and programs I ran. It covers one person, one project, and a moving set of models.

Who did what

Phase Built by (from commit trailers) Reviewed by My role
Aug 2025–Feb 2026 Claude via Claude Code: "Claude" (Oct), then Opus 4.5 and 4.6 GPT-5 and 5.2, cited as reviewer in 14 commit messages Direction, prompts, stories, commits
Feb–Aug 2026 Opus 4.6, 4.7, 4.8, then Opus 5 (about 160 trailers; 41 commits have none) One Codex review; the June audits by Fable 5 and Opus 4.8 Same, plus deciding what the audits meant
Sep 2026 rewrite Opus 5 on the 5 commits that carry a trailer; the first kernel commit has none Agents only; no human code review is recorded Wrote three prompts (Sep 13–15, ~540 lines) setting goals, non-goals and the verification regime; gated every commit

I read far less of the code than I directed. Early on I called this "fly-by-wire coding": stay a step or two ahead, and steer by instrumenting and asking.

The retained record does not establish whether the first overnight run was unattended or which model wrote the first kernel commit. Spending by phase was not recorded.

The scale

Earlier engine (Aug 2025–Aug 2026) Rewrite (Sep 2026)
Commits touching engine code ~320 in the sandbox era, ~160 in its own repository 5 (plus docs and harness)
Days with engine commits ~125 4 (6 days with any commit)
Python source (non-test) peaked at ~61,000 lines ~7,400 lines
Test code ~96,000 lines at the end ~13,000 lines
Passing tests peaked at 6,691 1,144 (+253 harness self-tests)

None of this measures productivity. I didn't record hours, and commit timestamps aren't hours worked.

This table describes the baseline used for the published comparison, before the repairs that followed it.

Failure modes, and what caught them

Failure mode A real example from this project What caught it A practice you can steal
Silence: plausible, incomplete answers, no error Depth limits that cut off valid answers. Tabling whose completeness loop never ran. Recursion truncated at depth 50 with only a print() warning A second model reading status reports (Oct 2025); AI audits (Jun 2026) Make "didn't finish" a first-class result, not an empty list
Tests that can't fail Two tests loosened "at most one trust level" from <= 1 to >= 1 and printed a warning whenever the bug fired. Event Calculus tests passed with the Event Calculus switched off The old engine's own August test hardening; later, the rewrite's audit reading every test for what it could detect Mutation testing; reverting each fix to check its test goes red
Tests blind in one direction Cut tests that asked only yes-or-no questions. Tabling tests that all recursed rightward Probing with textbook programs against an outside reference An oracle (here, SWI-Prolog) plus deliberately adversarial cases
Sediment: the old path left beside the new Five negation paths, five parsers, two evaluators, six cache layers. About 5,200 lines deleted in one day in June 2026 The June audits Delete as part of every refactor; audit for code with no callers
Local fixes for global problems A speed fix keyed on the murder-mystery benchmark's predicate name. A loop "fixed" by rerouting one predicate Code review by other agents, eventually Grep for benchmark and test names in production code
Confident wrong diagnoses Investigation notes backed one theory with "Confidence: 90%," and a commit summary "determined it requires senior architectural expertise." The fix was clearing stale caches between queries, written up as "Confidence: 100% (all tests passing)." It cost 36% in speed, and the workaround keyed cache clearing on predicate names Me, rereading and redirecting Ask for the cheapest experiment that would disprove the theory. Treat "all tests passing" as a claim, not a proof
Self-reports that inflate "SLG-style tabling." Integration with NumPy and pandas that didn't exist. One debugging write-up claimed 270 new tests; the test log shows about a hundred added across the whole day Checking claims against code and logs, for this write-up Treat agent-written docs and summaries as claims to verify

I don't think these patterns are unique to AI. They're what happens to any fast-growing codebase with weak feedback. Here they piled up faster than I could spot them.

How verification changed

Period What "verified" meant
Aug–Dec 2025 The test count went up and the suite was green
From Sep 2025 A second model reviewed the first model's work
Jun 2026 AI audits: static reads, then 324 agents reproducing and adversarially checking findings
Jul–Aug 2026 The old engine hardened its own tests: tests that couldn't fail were rewritten, and assertion-free tests were given something to check
Sep 2026 A written brief first; every test must be able to fail; an outside referee

The rewrite's version, in specifics. Most of these were required by my prompts; the agents chose how to carry them out.

The June audit, for scale: 324 agents, about 15.6 million tokens, and about two hours of wall-clock time. Of its 295 raw findings, 18 were refuted outright and about 150 downgraded on verification. That leaves 1 critical, 5 high, 40 medium and about 229 low. Roughly half the raw findings were overrated until a skeptic reread the code.

What the rewrite's checking missed

Repairs after the comparison

Each repair's failure, behavior change and verification are in the repair history; this section records what the reviews caught.

A later review found another loss of information: the solver could report an unfinished search while the game API treated its partial guard list as enough to approve an action. Temporal explanations could also turn incomplete reasoning into a definite answer. The repair and an executable example are recorded separately from the earlier comparison.

The repair used adversarial player and software-engineering reviewers plus an independent test agent. They challenged the plan before implementation and checked the finished behavior, including whether a player would receive a false refusal message and whether unaffected actions still worked. Both reviewers caught an initial compatibility bug: directly constructing a disabled menu entry could still label it allow. The final implementation derives the kind from the entry's state, and regression cases cover that constructor.

The engine repository now retains a summary of this round in docs/INCOMPLETE_RESULT_REVIEW.md, including the reproductions, plan corrections, checks and API changes. It reports 39 new conformance cases, one new differential case, and nine targeted mutations all detected by tests. This is a retained summary, not recovered raw reports from the earlier reviews, and those targeted checks do not establish correctness elsewhere in the engine.

The next repair to calculated effect times addressed a deeper failure: a timer's closure was derivable but ignored by temporal reasoning, so it still returned a wrong answer marked complete. The reviewers challenged discovery against provisional state and caught a false assumption that mentioning a time variable in another predicate makes it ground. Implementation review then caught discovery running during global timeline loading, outside the caller's budget; the test agent caught arithmetic-input rules being mistaken for time generators; a final ordering check caught a change to existing cycle presentation. Each was corrected and given a regression test. The record for this round is docs/COMPUTED_EFFECT_TIME_REVIEW.md in the engine repository.

The subsequent circular-temporal repair, engine commit 6d6a9a9, exposed another distinction: recording uncertainty after a provisional false answer is too late if that answer has already produced a proof. Reviewers required evaluation to stop, and the engineering reviewer found a tabled helper could bypass the obvious recursion guard. The final repair isolates that table work too. Mutation testing then found a missing wildcard-candidate test; the stronger case now detects removal of that isolation independently. The tests also record the cost of the conservative policy: some shipped alarm/secret and broad enumeration results become explicitly uncertain. The source models were preserved, and the benchmark now measures completed trust-slot work. The review and retained mutation evidence are in docs/CIRCULAR_TEMPORAL_REVIEW.md in the engine repository.

The time-scoped listing repair, engine commit 2e0c386, addressed an overly broad dependency: an early room description could fail because candidate discovery ran a later circular alarm rule. Reviewers rejected a tempting optimization that would join events to occurrences first, because it changes Prolog input modes, cuts and authored exclusive winners. The bounded implementation supplies earlier ticks to the existing evaluator while preserving rule-body order. The independent agent's 58 cases produce 33 failures against the archived baseline, and all 14 targeted mutations are detected, including missing budget charges and unfinished table reads. The retained record is docs/SCOPED_TEMPORAL_REVIEW.md. It states both the restored early-listing behavior and the cases that can still reach future reasoning.

The strict game API repair, engine commit 33cd9a9, addressed a configuration boundary. Existing strict checking catches undefined predicates in a misspelled guard, but also rejected intentionally absent optional guards and labels. Reviewers required the exception to stay at the host's metadata lookup: a wrong arity or an undefined helper inside a real rule must remain an error. They also rejected a direct empty-list shortcut because it would bypass query analysis and resource handling. The 45 new cases produce ten failures against the archived baseline and detect all ten curated mutations. The retained record is docs/STRICT_GAME_API_REVIEW.md. The engine's default policy remains unchanged, and the documentation distinguishes reached predicate calls from misspelled optional heads or fluent arguments that strict mode cannot validate.

The circular timeline construction repair, engine commit c736a1b, addressed a warning that did not constrain answers. The engine could warn that time order depended on temporal state, then still approve an action using that incomplete order. Reviewers required both retained static dependencies and a runtime stop: either check alone misses cases. They also required the stop before initial state and effect discovery, and isolation between the three timeline source queries. The engineering reviewer supplied a stronger shared-table regression, which detects provisional answers leaking from an interrupted source into the next one. The independent agent's 80 cases produce 74 failures against baseline, and all 16 curated mutations are detected without test errors. docs/CIRCULAR_TIMELINE_BOOTSTRAP_REVIEW.md retains the review and verification record, including the cost of rejecting statically visible dependencies even in unvisited branches.

The timeline preparation limits repair, engine commit 828c0fe, addressed work omitted from a request's accounting. Reviewers challenged an exception-only fix: tabled searches can catch exhaustion and return buffered answers, while a frame-limit failure must not become permanent cached damage to the world's timeline. The implementation checks source completion and publishes derived state only after preparation finishes. Player probes required cancellation to suppress authored refusals as well as permissions, and required retries to recover unchanged menu order and labels. The 57 new cases produce 50 failures against baseline, and all 16 targeted mutations are detected. Older tests now prepare the seed before measuring temporal work and delay cancellation until the stage they intend to exercise. The retained record is docs/TIMELINE_PREPARATION_LIMITS_REVIEW.md; it states that static analysis is not fully metered and menu budgets remain per internal query.

What I can't claim

On transfer: the oracle trick needs a reference. Here that was SWI-Prolog; elsewhere it might be a spec, an older system, or a slow but obviously correct implementation. The broken-engine and revert-the-fix checks need nothing but the test suite.

Further reading

What I wrote along the way: