GitHub - second-time-is-a-bug-book/book-companion: Companion code for the book "The Second Time Is A Bug"

GitHub

5 min read Original article ↗

Companion repository for the book The Second Time Is a Bug: A field workbook for engineering with AI coding agents. Everything the book shows runs here:

  • Invoicer (app/), a small invoicing app for freelancers, the running example.
  • billing-export (legacy/), a deliberately tangled 2014 exporter for the legacy chapter.
  • Guards (guards/), one small script per guard the book builds, and the gate that runs them.
  • Drills (drills/), one per agent mistake. Each makes the mistake in a scratch copy and shows the guard catching it. No API key, no model, no network.
  • A toy agent (toy-agent/), an agent loop of two dozen lines driven by a scripted fake model.
  • Templates (templates/) to copy into your own repository, and an experiment kit (experiment/) for measuring whether the harness pays, with the author's own run records in experiment/results/.

TypeScript on Node, with zero runtime dependencies and no build step.

Book website: https://secondtimeisabug.com

The code the book quotes, and the messages the guards and drills print, are kept within 64 columns, the width of the book's printed page. Guard output wraps with a hanging indent (guards/lib/wrap.ts).

Requirements

  • Node.js 22.18 or later. Node runs the .ts files directly (type stripping is on by default from 22.18). Check with node --version.
  • git (the drills make throwaway git repositories in your temp folder).
  • For the two browser drills (D13, D14) and npm run e2e: Chromium for Playwright, installed once with npx playwright install chromium.

Quickstart

npm install            # dev tools only: typescript, @types/node, @playwright/test, axe-core
npm test               # unit tests and the legacy golden master
npm run typecheck      # tsc --noEmit
npm run gate           # every guard, fast ones first; refuses on any failure
npm run drill D06      # one drill: the float-money guard catches a parseFloat
npx playwright install chromium   # once, for drills D13 and D14
npm run drills         # all 26 drills and a summary table (a few minutes)
npm run e2e            # browser and accessibility tests
npm start -- --seed-demo   # Invoicer on http://127.0.0.1:3000 with a demo client

Without Playwright's Chromium, npm run drills does not run D13 and D14. It says so and ends with exit code 1, because the run is not complete:

24 of 26 drills behaved as expected; 2 need Chromium (npx playwright install chromium).

Install Chromium and run it again. The runner's own tests, which are not part of npm test: node --test drills/test/*.test.ts.

npm run gate compares your working tree with HEAD. Outside a git repository the diff-based guards report "skipped" rather than guessing. In CI, set GUARD_BASE=origin/main to check a whole branch, as .github/workflows/gate.yml does.

Folder map

Path What it is
app/src/domain/ Pure rules: money in integer cents, totals, due dates, reminders, CSV
app/src/db/ node:sqlite storage and SQL migrations with up and down steps
app/src/http/ JSON API on node:http, including /health and /version
app/src/ui/ Server-rendered list page and the static invoice form
app/src/jobs/ Overdue-reminder job and a tiny scheduler
app/test/, app/e2e/ Unit tests (node:test) and browser tests (Playwright, axe-core)
legacy/ billing-export.js and its characterisation (golden master) tests
guards/ The guards, gate.ts, seeded-defects.ts, guard-audit.ts, shared lib/
drills/ run.ts and one folder per drill (README.md, drill.json, patch or script)
toy-agent/ loop.ts, scripted-model.ts, tools.ts, unattended.ts and scripts
templates/ AGENTS.md starter, incident log, plan, harness review checklist, hooks
experiment/ Chapter 12 protocol, seven tasks with hidden acceptance tests, score.ts
plans/ Where plan and handoff files go (templates/plan.md)
.github/workflows/gate.yml CI: type check, tests and the gate against the branch's base, then browser tests and mutation runs
.harness/ Guard state: test baseline, approved dependencies; at run time, the guard log and test evidence
AGENTS.md This repository's own instruction file, built from the template
DRILLS.md Every drill's commands and real output, generated by npm run drills:docs

Drills

Run one with npm run drill DNN. Each folder's README.md says what the mistake is, what you will see, and how to try the same mistake with a real agent.

ID Title Chapter Card
D01 Toy loop 1 P01
D02 Incident log 2 P02
D03 Coupling 3 P04
D04 Instructions lint 3 P03
D05 Plan required 4 P07
D06 Float money 5 P11
D07 Gate 5 P12
D08 Layer boundary 5 P13
D09 Deleted test 6 P15
D10 Skipped test 6 P14
D11 Hollow assertion 6 P16
D12 False done 6 P17
D13 Hidden button 7 P18
D14 Accessibility 7 P18
D15 Smoke 7 P19
D16 Seeded defects 7 P20
D17 Destructive command 8 P21
D18 Secret in diff 8 P22
D19 Irreversible migration 8 P23
D20 Phantom package 9 P24
D21 Dependency drift 9 P25
D22 Legacy refactor 10 P27
D23 Diff budget 10 P28
D24 Bounded loop 11 P30
D25 Merge gate 11 P29
D26 Guard audit 13 P34

How the guards report

Every guard prints name: ok (...) or name: FAILED followed by one entry per problem: where it is, what is wrong, and what to do next. They are written to be read by an agent as much as by a person. Exit code 0 means pass, 1 means fail; destructive-command.ts uses 2 to block, the usual convention for agent hooks. Each time a guard fires it appends a line to .harness/guard-log.jsonl, which guards/guard-audit.ts reads to find guards ready to retire.

Network

Nothing here needs the network after npm install, except the optional node guards/package-check.ts --online, which asks registry.npmjs.org.

Licence

Code: MIT, see LICENSE. The book's text is not part of this repository.