TAI BUI
Back to curriculum
BuildPython

Initialization Scripts for Agents

Every session that starts cold pays a setup tax: the agent reads the same files, retries the same probes, and rediscovers the same paths. An init script pays it once and writes the answers into state.

TIME: ~45 minutesPREREQUISITES: Phase 14 · 32 (Minimal Workbench), Phase 14 · 34 (Repo Memory)

Learning Objectives

  • Identify the work an agent should never have to redo per session.
  • Build a deterministic init script that probes runtime, dependencies, and repo health.
  • Persist the probe result so the agent reads it instead of re-running checks.
  • Fail loud, fast, and with one place to look when initialization fails.

The Problem

Open a session on a repo the agent has never seen. It guesses the Python version, then guesses the test command. It lists the repo root five times looking for the entry point, tries to import a package that isn't installed, and asks the user where the config file lives. By the time it makes a real edit, ten thousand tokens have gone to setup work that one script could have done.

That script is the fix. It runs before the agent does anything else and writes an init_report.json the agent reads at startup. The agent stops guessing and starts from facts.

Warm-up0/3
1. An agent crashed mid-session last night and left its state file behind. How should a new session treat that file?Warm-up
2. A script ends with exit status 0 in one run and exit status 1 in another. What do callers such as CI runners and shell wrappers conventionally conclude?Warm-up
3. In a CI workflow, the agent job lists a setup job as a dependency, and the setup job fails. What happens to the agent job?Warm-up
Check: The Problem0/2
1. In the context of agent sessions, what is the setup tax?Define
2. Which piece of work should an agent never have to redo at the start of each session?Explain

The Concept

An initialization script is a deterministic program that runs before the agent touches anything, checks that the workbench is in a usable state, and records what it found in init_report.json. Each check is a probe: a small function that returns (name, status, detail). The agent reads the report instead of rediscovering the same facts every session, which is how the script pays the setup tax once.

Mermaid diagram

What the init script probes

ProbeWhy it matters
Runtime versionsA wrong Python or Node version produces silent wrong-version bugs
Dependency availabilityA missing package found later costs ten times what catching it now does
Test commandThe agent has to know how to verify its work. If the command is missing, the workbench is broken
Repo pathsHard-coded paths drift. Resolve them once and pin them
Environment variablesA missing OPENAI_API_KEY is a known failure surface, not a runtime mystery
State + board freshnessStale state from a crashed session is a footgun
Last-known-good commitThe anchor for the handoff diff at the end of the session

Fail loud, fail fast, fail in one place

When a probe fails, the script halts and surfaces the failure to the human. There is no "the agent will figure it out." Init exists to refuse to start when the workbench is broken, and the failure shows up in one place.

Idempotent

Run it twice in a row and the second run should change nothing except a fresh timestamp. That property is what lets you wire the script into CI, hooks, or a pre-task slash command.

Init versus startup rules

Rules (Phase 14 · 33) describe what must be true before the agent acts. Init is the script that makes those rules checkable. Rules without init collapse into "be careful." Init without rules is a polished failure.

Check: The Concept0/11
1. What is a probe in an initialization script?Define
2. What is init_report.json?Define
3. What does it mean for an init script to be idempotent?Define
4. What does fail loud mean for an init script?Define
5. Which of these is NOT one of the probes the lesson lists?Define
6. Why probe dependencies at init instead of letting the agent hit an import error later?Explain
7. Why does the init script write its results to init_report.json instead of only printing them?Explain
8. Why does the lesson say init must be idempotent?Explain
9. A required test runner turns out to be missing when a session starts. What should happen?Explain
10. How does the lesson relate init scripts to startup rules (Phase 14 · 33)?Explain
11. A team makes every probe return warn on problems so the agent always starts. Two weeks later agents keep editing with a required env var missing. Which principle was broken?Analyze

Build It

code/main.py implements init_agent.py:

  • Six probes: Python version, listed dependencies (via importlib.util.find_spec), test command resolvability, required env vars, state file freshness, and a last-known-good diff against a pinned commit. The Concept table lists seven because it also names repo paths, which the demo does not implement, and its "state + board freshness" row is covered here by the single state-file probe.
  • Each probe returns (name, status, detail).
  • The script writes init_report.json with the full probe set and exits non-zero if any block-severity probe fails.

Run it:

python3 code/main.py

It prints a table of probes and writes init_report.json. On the happy path it exits zero. Otherwise it exits non-zero and lists the failed probes.

Check: Build It0/6
1. probe_runtime runs on Python 3.9 while REQUIRED_PYTHON is (3, 10). What does it return?Apply
2. probe_lkg_diff finds a valid baseline commit, and git reports 60 changed files against a budget of 50. What happens?Apply
3. On a first run there is no last_known_good.json. What does probe_lkg_diff report?Apply
4. agent_state.json was last modified 30 hours ago and STATE_FRESHNESS_SECONDS is 24 hours. What does probe_state_freshness return, and does init still succeed?Apply
5. A probe returns pass but takes 4000 ms, and PROBE_BUDGET_SECONDS is 3.0. What does the _timed wrapper do?Apply
6. Init reports all-pass, but forty minutes in, the agent keeps hitting ImportError for a package that is not installed. Which probe is the likely culprit?Analyze

Production patterns in the wild

Three patterns separate a useful init script from a ceremony.

Last-known-good commit anchoring. Compare the current commit against an LKG file written on the last successful merge. If the diff exceeds a budget (default 50 files), the script refuses to start until a human ratifies the new baseline. Cloudflare's AI Code Review uses this to scope reviewer agents: every review session anchors against the same last-known-good, so drift never compounds across sessions.

Lock files with TTL. After the first successful probe pass, write a prereqs.lock. Later runs trust the lock for N hours (24h default) and skip the expensive probes. The script reads the lock first, and if it is fresh and the dependency manifest hash matches, it short-circuits. Docker's layer cache works the same way: an idempotent probe plus a content hash means skip.

No network, no LLM, no surprises in the hot path. Init probes are deterministic plumbing. A probe that asks an LLM to classify a failure, or hits an external service to check a license, is a workflow, not a probe. If a probe takes longer than three seconds in a dry run, treat it as a workbench smell and either move it out of init or cache its result.

Check: Production patterns in the wild0/6
1. What is last-known-good commit anchoring?Define
2. What does the lock file with TTL pattern do?Explain
3. Init passed this morning and wrote a prereqs.lock with a 24-hour TTL. This afternoon, three hours later, a teammate adds a required dependency. What happens on the next run?Apply
4. Init takes 40 seconds because one probe calls a web API to check a license. What is the right diagnosis?Analyze
5. You want init to also flag when the README looks out of date, which needs a language model to judge. Where does this check belong?Design
6. Several of your probes are expensive but deterministic, and init runs before every task. How do you cut the cost without losing correctness?Design

Use It

In production, the script gets wired in three places:

  • Claude Code hooks. A pre-task hook calls the init script and refuses to launch the agent if it fails.
  • GitHub Actions. A setup-agent job runs the init script, and the agent job depends on it.
  • Docker entrypoint. The agent container runs the init script before exec-ing the agent runtime, so logs surface on failure.

The script ports easily because it calls no specific framework. Bash, Make, or a tasks file can all wrap it.

Check: Use It0/2
1. Which integration points does the lesson name for wiring in the init script?Define
2. You want the same checks to run when developers launch the agent locally, in CI, and in containers, without tying them to one framework. Which design fits?Design

Ship It

outputs/skill-init-script.md is a skill that interviews the project, sorts its setup work into probes, and emits a project-specific init_agent.py plus a CI workflow that runs it before any agent step.

Author's Note

I treat init as the one place where the workbench gets to say no. When a probe fails, I want the session to halt and a human to look, even if the failure seems small, because a missing env var or a stale state file is exactly the kind of thing an agent will paper over if you let it. My bet is that a script which stops the session at the start is a better trade than an agent that limps through ten thousand tokens of guessing and then edits the wrong thing. If a check can't give a plain pass or fail without a network call or an LLM, I'd keep it out of init and run it as a separate workflow.

Exercises

  1. Add a probe that diffs the current commit against the last-known-good commit and refuses to start if more than 50 files changed.
  2. Wire the script to write a prereqs.lock file and refuse to start if the lock is older than seven days. This is a hard expiry that forces a human to look, separate from the 24-hour window in which a fresh lock lets probes be skipped.
  3. Add a --fix flag that auto-installs missing dev dependencies but never modifies runtime dependencies without approval.
  4. Move the probes from hardcoded functions to a YAML registry, then defend the trade-off.
  5. Add a timing budget per probe. A probe that runs longer than three seconds is a workbench smell.

Key Terms

TermWhat people sayWhat it actually means
Probe"A check"A deterministic function returning (name, status, detail)
Init report"Setup output"JSON written next to state holding the probe results
Idempotent"Safe to re-run"Two runs in a row produce identical reports, timestamp aside
Fail loud"Don't swallow"Halt and surface to the human, with no silent fallback
Setup tax"Bootstrap cost"The tokens the agent spends each session rediscovering the obvious

Further Reading

Code

Ships With