DEV Community

Charlie Zhu
Charlie Zhu

Posted on

The Receiving Inspection Workshop: A Gate for AI-Written Code

Three agent-written patches landed on a Friday afternoon. Two were nine-line diffs, one touched a helper nobody remembered owning, and all three were reviewed in the ten minutes between a standup and a release. On Monday the nightly seed job died, and nobody could say which candidate produced the line that killed it.

That team did not have a review problem. It had a receiving problem. A candidate sat in a pull request, looked plausible, and crossed into the trunk without anything recording what had actually been checked.

The last few weeks of DEV discussion keep circling a familiar split: model capability climbs, and the software wrapped around it stays about as fragile as the day it was written. That gap is not a model problem. It is a testing problem, and it is teachable in ninety minutes.

This workshop builds a receiving dock. Students leave with a gate that parses a candidate, runs it against a fixture, writes a verdict to a ledger, and refuses to be the last word on anything.

The invariant

The whole workshop hangs on one sentence, written by the students in the first ten minutes and never edited afterward: nothing reaches the trunk without a receipt.

A receipt is one JSON line. It names the task, the model that produced the candidate, the verdict, a machine-readable reason, and a timestamp. It is boring on purpose. When the nightly job breaks three days later, the receipt is what lets someone say this candidate passed a three-case fixture and nothing else, instead of reconstructing the week from chat scrollback.

The run sheet

Clock Segment Artifact at the end
00–10 The Monday failure story, told once The written invariant
10–25 Fixture before gate tasks/03_slugify/test_solution.py, three cases
25–45 Build the gate gate.py, first accept receipt
45–60 Break it deliberately Two quarantines, one false accept
60–75 Boundary sweep A property test that catches the fake
75–90 Yield accounting and transport swap receipts.jsonl and a yield number

Every segment ends with a file. If a segment runs long, the artifact gets cut down, never skipped, because a workshop that finishes with a feeling instead of a file teaches nothing rerunnable.

Fixture before gate

Students almost always want to write the gate first. That order produces a gate that grades whatever it happens to be shown, which is the exact failure mode of human review under deadline pressure.

The fixture comes first. A task is a directory with a prompt and a test file, and the candidate is expected to expose one function.

{
  "id": "03_slugify",
  "model": "fill-from-current-provider-docs",
  "prompt": "Write slugify(text: str) -> str"
}
Enter fullscreen mode Exit fullscreen mode
# tasks/03_slugify/test_solution.py
from solution import slugify


def test_basic():
    assert slugify("Hello World") == "hello-world"


def test_collapses_runs():
    assert slugify("a   b---c") == "a-b-c"


def test_empty_stays_empty():
    assert slugify("") == ""
Enter fullscreen mode Exit fullscreen mode

Three cases is the right number for the first pass. A fixture large enough to be impressive is a fixture nobody in the room can explain, and an unexplained fixture fails silently the day it is edited.

The gate

The gate is deliberately small: it parses, it runs, it records, it stops.

#!/usr/bin/env python3
"""gate.py - receiving inspection for model-written candidates."""
import ast, json, pathlib, subprocess, sys, time

ROOT = pathlib.Path(__file__).parent
MAX_BYTES = 8192


def parse_ok(path):
    try:
        ast.parse(path.read_text())
    except SyntaxError as exc:
        return False, f"syntax_error_line_{exc.lineno}"
    return True, "parsed"


def behavior(test_path, scratch):
    proc = subprocess.run(
        [sys.executable, "-m", "pytest", "-q", str(test_path)],
        cwd=scratch, capture_output=True, text=True, timeout=60,
    )
    lines = proc.stdout.strip().splitlines()
    return proc.returncode == 0, (lines[-1] if lines else "no_output")


def main(task_dir):
    task = json.loads(pathlib.Path(task_dir, "task.json").read_text())
    scratch = ROOT / "scratch" / task["id"]
    scratch.mkdir(parents=True, exist_ok=True)
    candidate = ROOT / "candidates" / f'{task["id"]}.py'
    verdict, reason = "quarantine", "no_candidate"

    if candidate.exists() and candidate.stat().st_size <= MAX_BYTES:
        ok, why = parse_ok(candidate)
        if ok:
            (scratch / "solution.py").write_text(candidate.read_text())
            (scratch / "test_solution.py").write_text(
                pathlib.Path(task_dir, "test_solution.py").read_text()
            )
            ok, why = behavior(scratch / "test_solution.py", scratch)
        verdict, reason = ("accept" if ok else "quarantine"), why
    elif candidate.exists():
        reason = f"oversize_{candidate.stat().st_size}b"

    receipt = {
        "task": task["id"],
        "model": task.get("model", "unspecified"),
        "verdict": verdict,
        "reason": reason,
        "ts": int(time.time()),
    }
    with open(ROOT / "receipts.jsonl", "a") as fh:
        fh.write(json.dumps(receipt) + "\n")
    print(json.dumps(receipt))


if __name__ == "__main__":
    main(sys.argv[1])
Enter fullscreen mode Exit fullscreen mode

A candidate for the slug task, dropped at candidates/03_slugify.py, looks like this:

import re


def slugify(text):
    return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
Enter fullscreen mode Exit fullscreen mode
$ pip install pytest
$ python gate.py tasks/03_slugify
{"task": "03_slugify", "model": "fill-from-current-provider-docs", "verdict": "accept", "reason": "3 passed", "ts": 1792000000}
Enter fullscreen mode Exit fullscreen mode

If pytest reports an ImportError on solution, the instructor has hit the classic path problem; run the gate with PYTHONPATH=. set and the scratch directory as the working directory, and the failure disappears. That detour is worth five minutes, because everyone meets it eventually in their own repository.

Break it deliberately

This is the segment that changes how students think about green checks. The instructor supplies a candidate that hardcodes the fixture's answers:

def slugify(text):
    return {
        "Hello World": "hello-world",
        "a   b---c": "a-b-c",
        "": "",
    }.get(text, "")
Enter fullscreen mode Exit fullscreen mode

The gate accepts it. Three tests, three passes, one receipt that says accept, and a function that will return an empty string for every input the product actually receives. The fixture graded the fixture.

The correction is a property check that ties output back to input instead of to remembered examples:

# tasks/03_slugify/test_boundary.py
import random, string
from solution import slugify


def test_alnum_digest_matches():
    rng = random.Random(7)
    for _ in range(200):
        raw = "".join(
            rng.choice(string.printable) for _ in range(rng.randint(1, 40))
        )
        digest = "".join(ch for ch in raw.lower() if ch.isalnum())
        assert slugify(raw).replace("-", "") == digest
Enter fullscreen mode Exit fullscreen mode

The hardcoded candidate now fails within a handful of iterations, while the real implementation passes all two hundred. Students see the difference between evidence and a memory test, and they see it in code rather than in a slide.

Where the compute comes from

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Two hundred generated candidates per cohort is small by industry standards and painful in a classroom, because it forces every student to hold a paid key before they have learned anything worth paying for. MonkeyCode, the project behind this article's operator, describes a free tier and a free server option; the operator currently reports the free allowance at ten million tokens. Those terms live on the project's own page and can change, so this workshop treats them as a starting point to check rather than constants to quote in a syllabus, and it asserts nothing about which models are included, how large the server is, or how long either offer lasts.

The harness itself assumes no vendor at all. The transport is one function, and the shape below is what students fill in from whatever endpoint they are actually using:

import json, os, urllib.request


def ask(prompt):
    request = urllib.request.Request(
        os.environ["BASE_URL"] + "/chat/completions",
        data=json.dumps({
            "model": os.environ["MODEL"],
            "messages": [{"role": "user", "content": prompt}],
        }).encode(),
        headers={
            "Authorization": f'Bearer {os.environ["API_KEY"]}',
            "Content-Type": "application/json",
        },
    )
    with urllib.request.urlopen(request, timeout=120) as response:
        return json.load(response)["choices"][0]["message"]["content"]
Enter fullscreen mode Exit fullscreen mode

The free server option earns its place in the boundary-sweep segment, where a loop of two hundred candidates would otherwise compete with whatever else is on the student's laptop. It is a convenience for the exercise, not a claim about production throughput.

Yield accounting

The last artifact is a number students compute themselves:

import collections, json

rows = [json.loads(line) for line in open("receipts.jsonl")]
counts = collections.Counter(row["verdict"] for row in rows)
total = sum(counts.values())
print(counts, "yield=", round(counts["accept"] / total, 3))
Enter fullscreen mode Exit fullscreen mode

Yield is acceptance rate per task, and it belongs to the task, not to the model. A slug function with a clear contract collects accepts; a task whose prompt hides its error handling collects quarantines and tells the room nothing about model quality. That distinction is worth more than any leaderboard.

The verdict table

The gate produces four outcomes, and each maps to one action.

Verdict Signal Action
Accept Parses, fixture green, sweep green Merge with a link to the receipt
Repair Parses, one named test fails with a local cause One bounded revision, re-run the gate, two attempts maximum
Quarantine Syntax error, oversize candidate, timeout, or missing file Move the candidate to quarantine/ and stop
Escalate Sweep green but the diff touches files, network, or credentials Human review of record, no exceptions

What this gate cannot see

The gate checks exactly what the fixture encodes and nothing more. A candidate that passes one afternoon is not guaranteed to pass tomorrow, because sampling variance means the same prompt can produce a different artifact; that is why the candidate bytes get stored next to the receipt and re-run on demand. The byte limit is arbitrary and exists to keep reviews short. The sixty-second timeout catches hangs, not deadlocks that resolve in ninety seconds. Concurrency, license headers, and secret handling are invisible to it.

Teams in regulated pipelines should not treat this gate as a control. It is a teaching device and a first filter, and a named human reviewer of record is still required wherever the rules say so. Teams with no fixture-writing habit should fix that habit before building the gate, or they will automate the grading of nothing.

A cohort that wants to see the sweep finish before deciding whether the gate belongs in its pipeline can start on the free tier and the free server option; the current terms page is the only place worth trusting for what is included today.

MonkeyCode provides free models that can run this workflow.

Top comments (0)