Colex / Build log

A test bench I can rely on.

Agents write the code. A coordinator hands out the work. I decide what gets built. Testing stayed with me, picked by hand out of memory before every run.

· 7 min read

What the factory left to me

I picked the checks out of memory

Before each run I chose the checks by hand. Nothing broke. It was tedious rather than dangerous, and it got slower as the project grew.

Picking from memory has a second cost. I reached for the checks I reach for, and those cluster. The coverage looked broad and was narrow.

To hand testing to a machine I had to write down two decisions I had never stated. Which checks fit this change. What evidence makes a run trustworthy.

The run

The checklist described work I meant to do

I ran it for the first time in July. About 65 items on a live stack, across a dozen agents in waves. Some drove the tool surface, some drove the browser.

One agent only read the commit history. It looked for shipped work the checklist did not list, and it found real problems.

That agent answers the first question. My checklist described the work I meant to do. The commit history holds the work that shipped. The assumptions that wrote the code also wrote the list, so guardrails and one-commit fixes never reached it. A diff sweep is now standing.

One wave did nothing but re-test results the other agents reported. Fresh agents, fresh fixtures.

A complete-looking run can still hide a defect

Three of nine failures did not survive

Nine failures went into that wave. Three came back overturned. Eleven passes went in and eleven held.

The three overturned failures had three causes:

  1. A fixture that lacked a required stamp.
  2. A synthetic double-click that fired on the same tick, which no person can produce.
  3. Tool calls spaced closer together than the debounce window.

All three were defects in the test setup, not the product.

The agents followed their instructions. The instructions never said what a valid fixture was, or that a failure had to prove itself.

Inside this loop the two errors do not cost the same. A failure starts a fix, so a wrong failure spends real work on a bug that does not exist. A wrong pass costs a round, because the run that follows tests the same surface again. That holds here and not everywhere. A pass that ships to users costs far more.

So every failure re-runs before it drives a fix. A pass re-runs when the evidence is thin or when the check beside it can fail the same way.

Running it added one exception. A failure skips the re-run when the behavior is absent from the source and a unit test fences the absence. Two kinds of evidence already agree.

Every test needs an oracle

Two checks count as two only when they fail apart

One of those false failures came from a stale build, and that case is worth following.

A backend fix does not reach the running system until the bundle rebuilds and the container restarts. Before that, the tests measure the old code and report that the fix works. Timestamps do not settle it. A rebuild can touch a file without changing its contents. So every backend check now proves which build the stack serves. It asks the served artifact for a marker tied to the commit under test.

The stale build also corrupted the rest of the evidence. A page assertion and a screenshot both passed against it. Their oracles differ. Their dependency on the running build does not, so one stale bundle took out both. Two checks, one failure mode.

That is the question worth asking of any pair of checks. Not which is stronger, but whether they can fail for the same reason.

Every test needs an oracle, the thing that decides whether what it saw is acceptable. Some sit outside the code. A conformance check tests an invariant with a query the code did not write. Some sit very close to it. A unit test asks an assertion, and that assertion may repeat the assumption the code already makes. A mock can model column types, but it never exercises the real engine.

So the useful count is not how many checks ran. It is how many different ways they can fail. Two weak methods that fail apart can beat two strong methods that fail together.

The table carries the full set: what each method answers, its oracle, its blind spot, and its pair.

MethodAreaOracleAnswersBlind toRe-runsPair with
Owner smokeAllMy judgmentDoes the product really work, and does it look right to meRules that span many rows. Anything not visible on screenEvery run, requiredSQL checks, for what my eye cannot scan
DB / state readsData · engineThe stored row itselfWhat the system really stored. Rows, payloads, status changesWhether the screen showed it. How it got thereOnceNetwork capture, to compare sent against stored
Network captureFE ↔ BE seamThe wire, recordedWhat the client really sent and received, including repeat postsWhether the write survived. What the server did insideOnceDatabase reads, to confirm the write landed
CI suiteAllAssertions I wrote earlierNothing regressed in the behaviour it already coversAnything without a test. Integration and screen behaviourOnceNarrow runs for the new behaviour
Targeted unit runLogic · engineAn assertion, which may repeat the code's own assumptionsOne behaviour, precisely and cheaplyEverything outside the suite it ran. Real engine behaviour, when it mocksOnceThe full CI run, to catch what it missed
SQL conformanceData · engineAn invariant, checked by a query the code did not writeA rule holds for every row, not just the one you sampledWhether any of it renders, or whether a user can reach itOnceMy own check, for what a query cannot judge
Playwright E2EUI · FEA script, replayable by anyoneBrowser behaviour in a script that anyone can run againAnything off the scripted path. Visual judgementOnceMy own check, for look and feel
DOM / JS assertionsUIThe document, as servedWhat the page really contains. Stronger than a screenshotWhether it looks right. Whether the data survivedOnce, if scriptedNetwork capture, which fails in a different way
Chrome smokeUI · FEThe rendered page, read by an agentDoes the real flow work when driven like a userWhat the system stored. It also hides stale builds2 to 3 matching runsDatabase reads, to prove the effect was real
MCP testEngine · APIThe tool's own responseThe tool surface behaves as specifiedThe screen above it. Any write it triggers2 to 3 matching runsDatabase reads for effects, telemetry for internals
Runtime telemetryEngine · LLMEvents the system emittedStructured events. Model calls, prompt hashes, trace idsWhether the result was right. Anything never loggedOnceDatabase reads, for the resulting state
Container logsAllWhatever the system chose to reportWhat the system reported while it ran. Visible errorsSilent failures. Anything it never loggedOnceTelemetry, which is narrower but far more reliable
Code readAllA reader, judging source against expected behaviourWhat the code is written to doWhat actually happened at runtime. This is never proof by itselfnoneAny method that runs. It never stands alone.

A verdict is not a diagnosis

Two runs disagreed and both were right

Different oracles answer different questions. Network capture reports what the client sent. A database read reports what the system stored. Neither reports what the user saw.

That gap produced one finding. Two rigorous runs returned opposite verdicts on the same rule. One said constraints were not enforced. The other said they were.

The first instinct is to call one run flaky and drop it. That throws away the evidence. Both runs differed only in their fixtures. The passing fixture carried a stamp. The failing one did not.

The stamp was the answer. Enforcement trusted a value the client supplied. Neither verdict said that. The difference between the fixtures did.

So a disagreement is a lead, not noise. A third run isolates the delta between the fixtures. Then the bench can report a cause rather than a verdict.

What shipped

The session orchestrates and never tests by hand

That first run was three documents and a person. A checklist, a brief every agent read, and a ledger. It worked at the speed of one person dispatching agents by hand.

The protocol is now a skill the session loads. It answers both questions I started with.

For selection, it reads the change and routes to the methods that fit. It then sweeps the diff for what the checklist missed. For trust, it proves which build the stack serves. It re-runs every failure before that failure drives a fix. It finds the cause when two runs disagree.

The protocol also holds its shape at any size. A branch of 246 commits and a single commit run the same invariants, and only the machinery shrinks. The evidence rules, the build check and the re-run of every failure do not move. The rate of false failures does not fall when the scope is small.

Each finding names the check that should have caught it

The only step that makes the next run cheaper

A run that ends in a good report has produced nothing permanent. The next branch repeats it by hand.

So every confirmed finding answers one question before the run closes. Which permanent check should have caught this?

  1. An engine rule proven by hand with SQL becomes a conformance fixture, judged by a separate SQL oracle.
  2. A bug that mocked tests were blind to becomes a live-database probe, never another mock.
  3. A UI contract proven by an agent in a browser becomes a scripted end-to-end test.
  4. A tool-surface contract becomes a schema test in the bundle suite.

Each promoted check, or the decision not to promote it, goes in the ledger. This step alone makes the next run cheaper. It also feeds selection. A promoted check leaves my list of choices and joins the set that always runs.

Where this goes

The plan executor followed the same path before

It started as a skill I invoked by hand for one unit of work. Writing its rules down made it something the factory could dispatch. It now runs inside a run.

The test bench is part way along. The rules exist and the orchestration works. I still start it by hand, and I still approve every fix.

The skill has run on one branch. The three-of-nine and eleven-of-eleven figures come from that single run. They set the current policy, and they need more runs before they mean much.