Bring your enterprise CI/CD practices to your vibe coding
Field notes · one laptop, several agents
The moment you run more than one AI agent on the same code, you have a team. Teams without a pipeline ship whatever happened to be on the workbench. Here is the pipeline, sized for one machine, with the receipts from a single day of building a real product.
The release pipeline, local edition
The incident that makes the case
Two AI sessions shared one laptop. One changes the build tool; the other runs a long build with it. At lunchtime the first session announced a new release of the tool as tested and ready to install. The binary had been built correctly, in a separate frozen checkout of one commit. The tests had not. They ran in the working folder, and while they ran, that session committed three more times into the same folder.
One test noticed, in so many words:
thread 'the_engine_carries_its_own_revision' panicked at tests/drift.rs:241:
the binary is stamped "35f0007-dirty" but the tree says "bce7d09" — this build is staleA second failure in the same run was real. The release made every passing test run keep its entire output, in memory and in the build's records, breaking a rule that a green run keeps nothing. Had it been installed on that "green", every unit of the product build would have carried the weight. The release was withdrawn, fixed, rebuilt in a frozen copy, and tested there. That time the full suite took five minutes and passed 1,805 of 1,805.
Nobody on a real engineering team would let a release be certified by tests running on someone's desk while they keep typing. Vibe coding with several agents does exactly that by default, because there is only one desk.
Practice 1: The workbench is not the factory
A team's CI server makes a fresh checkout of the tagged commit on a throwaway machine, builds it there, tests it there, and throws the machine away. You don't need a server to get the property that matters: the thing you release is exactly one commit, built and tested where nothing else can reach it.
Git gives you this for free. A worktree is a second checkout of the same repository, with the same history, frozen at the commit you name:
# the local equivalent of a CI runner's clean checkout
git worktree add --detach ~/dev/.releases/$SHA/tree $SHA
cd ~/dev/.releases/$SHA/tree
test "$(git rev-parse --short HEAD)" = "$SHA" || exit 1Your agents keep editing the workbench at full speed. The release copy doesn't move.
Practice 2: Build, test and prove in the same copy
The failure above came from splitting the steps: build in the frozen copy, test on the workbench. Keep every step in one place, and make the log say which commit it certifies before anything else:
echo "release sha: $SHA" > fullsuite.log # the first line names what is tested
cargo build --release # in the frozen tree
cargo test --no-fail-fast >> fullsuite.log # the same tree
bash scripts/mutate.sh # the same tree"Prove" is the step most setups skip. A mutation check breaks the code on purpose and confirms a test fails. It is how you learn that the tests your agents wrote actually test something. On the same day, the checking agent found three places where tests stayed green with the feature deliberately broken: a character's hair parting drawn on the wrong side, a line-wobble effect frozen still, and a repair request built with the wrong prompt versions.
RECEIPT. A mutation check on the new release hung forever instead of failing. Killing it made the script report "survived", which read as a missing test. The real cause was a bug: if a test run panicked, the tool would wait on a thread forever. A hang is its own outcome, and the checks now have a time limit.
Practice 3: The only road to production is the pipeline
On a laptop, "production" is whatever binary your long-running agent jobs execute. cargo install --path . (or npm link, or pip install -e .) copies the workbench, uncommitted edits and all, straight into that slot. It is the local version of scp'ing a binary from your laptop to the prod server.
Install only from the release store, and only what you have verified:
shasum -a 256 ~/dev/.install/tool-$SHA # matches what the release announced
cp "$(which tool)" ~/dev/.install/tool-previous-$(tool --version) # rollback target
cp ~/dev/.install/tool-$SHA "$(which tool)"
tool --version # prints $SHA, not $SHA-dirtyRollback has a limit worth knowing: an older binary can only go back if it can still read what the newer one wrote. Write that limit down with each release.
Practice 4: Tag what is installed
A folder name is not a record. Put the release in the repository itself so "what's running?" is one command:
git tag -a release-3ecc718 -m "full suite 1805/0; anchors 27/27"
git tag -f installed 3ecc718
git tag installed-20260929-1214-3ecc718
git tag release-729ab6d-withdrawn -m "a green gate kept its whole output"The withdrawn tag carries its reason, so nobody reinstalls it six weeks later.
Practice 5: Throw the machine away
CI gets hygiene for free because runners are disposable. A laptop accumulates. By the afternoon of this build, the home directory held about 130 hidden tool folders from earlier experiments, 29 old binaries (182 MB), and a temporary folder with 145,839 entries.
That last one was not cosmetic. One component listed the temporary folder on every call, and its tests went from 38 seconds to 526, fourteen times slower, purely from other tests' leftovers. The fixes were all pipeline rules:
- A superseded release's worktree is removed when the next release starts.
- The install folder keeps exactly three binaries: the release, the installed one, the previous one.
- Every test removes what it creates in the temporary folder.
- Everything the tooling writes lives under one root, not loose in your home directory.
Practice 6: Parallel agents need the rules of parallel people
Several agents in one checkout is several developers sharing one keyboard. The rules that work are the ones teams already use:
- One editor per working tree. A second agent that edits gets its own worktree and branch, and merges back through review.
- Hand-offs are messages with states. The two sessions here spoke in keywords:
staged <sha>with a checksum,green <sha>,installed <sha>,bug:,pause. Each is a state change the other side can check, not a mood. - The receiver verifies. "Green" was checked against the log's first line, its exit code and its failure count before anything was installed. That check is what caught the incident.
Practice 7: Share the machine like a runner fleet
A CI fleet schedules jobs so one team's build doesn't starve another's. On one laptop, agents compete for the same cores, and the costs are measurable. The product's full test run took 12.8 minutes with the machine calm. When the tool session's test suite was allowed six cores beside a three-wide product build, the same run took 31.9 minutes and then failed on a test with a wall-clock limit.
Cap the background jobs, measure the foreground ones, and change the cap one step at a time. Give each job a pause protocol (kill -STOP and -CONT work fine) for the moments that must not be disturbed, such as a baseline measurement.
Practice 8: Tests are infrastructure
A test whose verdict depends on how fast the machine is will fail the day your agents are busy. Two of the day's red builds were exactly that: a 60-second limit on a fake provider call, and a check that a timeout "returns within 3.0 seconds", which failed only inside the full suite. Each red build cost a full re-run and blocked every unit waiting to land.
The rule adopted: a test asserts what happened, never how fast. If a bound must exist, it is at least ten minutes, or it is injected so the product keeps its real default. Then a search of the whole test suite for elapsed-time assertions found exactly three, and all three were fixed at once instead of one per failure.
The map
The map: team practice to one laptop, several agents
What vibe coding keeps, and what you add
Keep the speed of writing. Agents drafting code in parallel is the real gain, and none of this slows it down. What you add is the part that decides what reaches production: one frozen copy per release, the whole check inside it, and an install step that only accepts what that check produced.
It costs a script and a folder. On the day these notes come from, it cost one withdrawn release instead of a broken build running for hours on a binary nobody had actually tested.