Builtpublic

tc-team

Feed it a spec, get test cases. The LLM only writes sentences; deterministic code owns every structure and gate.

Point it at a spec document and it fills a live spreadsheet tab with a test-case set. Start to finish, one invocation, unattended.

The interesting part isn’t “an LLM writes test cases.” Anyone can do that badly. The interesting part is the split.

Two lanes — the LLM owns sentences and judgment; deterministic code owns structure and fact.

CODE LLM S0Prepare S1Design S2Isolate S3Skeleton Prose S4Review S5Apply S6Write sheet S7Finalise input design merge four
Black cells are deterministic code; orange cells are the model. Only S3 crosses both lanes — code builds the skeleton, the model fills in prose, code merges it back. Triangles are gates: fail one and the run stops there.

Why split at all

Early on the model drove the whole pipeline. It drifted.

Row counts changed between stages, coverage quietly dropped, and two runs never produced the same shape. Models are excellent at approximately right. They cannot be used for same-input-same-output.

The fix wasn’t better prompting. It was confiscation.

Owner What it holds
Deterministic code stage order, row skeleton, numbering, formatting, merging, coverage ledger, sheet writes
LLM design judgment, sentences, review findings, verdicts

Eight stages

Stage Job Owner
S0 fetch source, take the run lock, self-check the input code
S1 analyse the spec → design skeleton LLM
S2 design isolation gate + source slicing code
S3 build skeleton → fan out for prose → merge code + LLM
S4 adversarial review, verdicts, coverage ledger LLM
S5 apply fixes + four gates code
S6 write to the live sheet code
S7 finalise, dashboard, notify code

S1 through S7 are written up individually. Each stage cost something to learn, and a table row doesn’t hold it.

S3 shows the architecture best. Code builds the skeleton first, cuts it into 25-row chunks, lets the model fill in only the prose, and code merges the results back. On merge it checks index, echo, hash and count — so if the model shifted a row or touched somebody else’s, only that chunk re-runs. Every other chunk’s work survives.

Gates — it stops rather than continues

This is what defines the tool’s character. It does not “carry on doing its best.”

Gate Stage On failure
input self-check S0 one refetch → still bad, refuse to start
design gate S2 design defect → loop back to S1
merge verification S3 re-run only the mismatched chunk
apply before-mismatch S5 rejection is correct behaviour — regenerate the fix plan
content gate S5 block vague phrasing and unsupported deferrals
duplicate gate S5 force a merge when identical cases remain
traceability S5 uncovered rule → stitch in a row, reapply

The before-mismatch gate matters most. Before applying a fix it asks “is the place I’m about to edit still what I read?” and refuses if not. A mismatch means a human touched the sheet or an earlier stage shifted — and pushing through would overwrite the wrong row.

There is exactly one place the run halts: an integrity violation. Everything else completes unattended.

Review is adversarial, not collaborative

Generated cases are read by several lenses in parallel — structure, quality, and comparison against the source. They cannot see each other’s findings. If they could, they would converge, and convergence means missing the same things together.

A separate judge cross-examines the findings, drops the false positives, and produces a fix plan in a form a machine can apply. Surviving findings are applied by code. Nobody asks the model to please go fix it.

The same stage builds the coverage ledger: each rule extracted from the spec is mapped to the cases that cover it, and anything uncovered gets a row stitched in at S5.

The sheet is touched exactly once

S6 contacts the live spreadsheet one time, and decides by ownership marker.

  • Our tab → wipe and rewrite in full (idempotent)
  • Somebody else’s tab → don’t touch it; create a _v2 alongside
  • Every other tab → off limits, unconditionally

Run it five times and the result is identical, and it never overwrites another person’s work. For automation that runs next to humans, those two properties come before everything else.

Two lessons that cost something

Inserting a row renumbers everything below it. The coverage ledger points at cases by number, so stitching in a row makes the whole ledger stale. My first instinct was to remap arithmetically. Wrong. It now joins by content, not position — category, verification stage, and the reproduction step. Positions move; content doesn’t.

Hand-driving costs half the clock. I once ran the stages manually one at a time. Wall clock was 2h23m; the machine actually worked for 1h12m. The rest was the pipeline waiting for me to type the next command. That number is the reason unattended completion exists.

Things that were rejected

The version history is mostly a list of things that looked like progress and weren’t.

Vocabulary-based progress detection — rejected after two measurements. Unifying the thresholds — rejected. The confidence heatmap ended up as pure deterministic output with zero model calls, after the model-scored version failed to reproduce.

Every rejection got a written resume condition. Without one you re-propose the same idea six months later.

An honest limit

A thin spec produces thin output. This tool will not invent design that isn’t there — with no basis for a case, it flags “needs spec clarification” and moves on. I think that’s correct, but in an organisation where specs are thin it yields less than people expect.