Thomas Piacentini
← Workshop

Cheat sheet

Repo Memory, Init Scripts and Handoffs

Original text · English

Frame the task, keep state in schema-checked files, probe before start, and end each session with a handoff packet.

One workbench session

  1. Frame write goal, facts, paths, acceptance and unknowns before any edit.
  2. Init script probe the workbench. On a fail, halt and surface to the human.
  3. Read load the scope contract and agent_state.json.
  4. Edit change allowed files only.
  5. Prove run the acceptance command through the feedback runner.
  6. Judge run the verification gate, then the reviewer.
  7. Clean up clean_state.json must hold an empty list.
  8. Hand off generate handoff.md and handoff.json.
Sources

Commands

CommandWhat it does
python3 code/main.pyRuns the lesson demo. Each one writes its files next to the script
python3 code/main.py in lesson 34Writes workdir/agent_state.json and workdir/task_board.json
python3 code/main.py --no-cacheLesson 35: ignore prereqs.lock and run every probe
python3 code/main.py --write-lkgLesson 35: pin current HEAD as last-known-good
git diff --name-only <baseline> HEADThe command behind the lkg_diff probe
python3 code/main.py in lesson 40Writes handoff.md and handoff.json
python3 -m unittest discover code/tests -vLesson 43: tests the task frame validator
python3 -m unittest tests.test_accountsAcceptance command of the example frame
Sources

Schema-first state

"required": ["schema_version", "active_task_id", "touched_files", "next_action"]
"active_task_id": {"type": ["string", "null"], "pattern": r"^(T-\d{3,}|)$"}
"status": {"enum": ["todo", "in_progress", "done", "blocked"]}
mgr = StateManager(state_path, STATE_SCHEMA)
state = mgr.load()read, then validate
mgr.commit(state)validate, then atomic_write
fd, tmp_name = tempfile.mkstemp(prefix=path.name + ".", dir=path.parent)
os.fsync(fh.fileno())flush to disk first
os.replace(tmp_name, path)atomic rename
Sources

Init probes

probe_runtime
fails below REQUIRED_PYTHON.
probe_dependencies
importlib.util.find_spec for each dep.
probe_test_command
shutil.which on the test command.
probe_env
fails on a missing env var.
probe_state_freshness
warn when the state is old or absent.
probe_lkg_diff
fail above the file diff budget.
Probe
returns (name, status, detail).
Report
init_report.json. Exit 1 on any fail.
Sources

Defaults and numbers

SettingDefaultNote
REQUIRED_PYTHON(3, 10)Runtime probe floor
STATE_FRESHNESS_SECONDS24 60 60Older state gives a warn
LOCK_TTL_SECONDS24 60 60Fresh lock skips the probes
PROBE_BUDGET_SECONDS3.0Slower pass becomes warn
LKG_FILE_DIFF_BUDGET50More changed files is a fail
schema_version1Only allowed enum value
TAIL_K5Feedback records kept in the handoff
Review total risk7Below it the handoff adds a warn
Sources

Seven handoff fields

summary
one paragraph of what was done.
changed_files
the diff at a glance.
commands_run
what was actually executed.
failed_attempts
what was tried and why it failed.
open_risks
what can bite next session, with severity.
next_action
the first concrete step. The load-bearing field.
verdict_pointer
outputs/verification/<task_id>.json and the review.
feedback_tail
last TAIL_K records plus every nonzero exit.
Sources

Prompt-only versus workbench

OutcomePrompt-onlyWorkbench-guided
tests_actually_runFalseTrue
acceptance_metFalseTrue
files_outside_scope20
handoff_qualitymissingfull packet
reviewer_total39
StepsRead, edit, claim done8 steps, init to handoff
Sources

Task frame

frame = TaskFrame(
goal="Prevent duplicate email addresses during signup",
allowed_paths=["app/accounts.py", "tests/test_accounts.py"],
forbidden_paths=["migrations/**", "deploy/**"],
acceptance=["python3 -m unittest tests.test_accounts"],
facts=[RepositoryFact("Duplicate errors use status 409",
"tests/test_accounts.py:44")],receipt
unknowns=["Whether email comparison is case-insensitive"])
issues = validate(frame)empty list means READY
render(frame)Markdown, Status: READY or BLOCKED
Sources

Classify each unknown

Discoverable
the repository or running system can answer it. Continue.
Decidable
the task contract gives the agent authority. Continue.
Human
changes product behavior, cost, risk or public compatibility. Pause.
Deferred
outside this slice. Put it in non-goals.
Sources

Troubleshooting

SymptomCauseFix
Next session redoes finished workState lived in chat, not the repoKeep state in schema-checked JSON in the repo
Session resumes on corrupt statewrite_text() with silenced exceptionsUse atomic_write: mkstemp, fsync, os.replace
does not match on T-bogusTask id breaks the patternUse ids like T-001
init failed; refuse to launch agentA probe has status failFix the probe. Do not start the agent
lkg_diff fail, files changed above budgetMore than 50 files since the last-known-goodA human ratifies the baseline with --write-lkg
Probe pass turns to warn with slow:Probe above the 3 second budgetMove it out of init or cache its result
no next_action recorded; needs humanState has no next_actionSet next_action before the handoff
Frame Status: BLOCKEDvalidate found issuesFix each line under Blocking issues
Sources

Questions

What goes in repo memory?
Data useful three months from now in a CI rerun.
Two init runs in a row?
The second is a no-op except a fresh timestamp.
Handoff without next_action?
It is a status report, not a handoff.
handoff.md and handoff.json differ?
The JSON wins.
When to stop reading the repo?
When each decision has evidence, is delegated, or is an unknown.
When to wrap up?
At 50-75% of the context budget, not at 95%.
Sources

Tips

  • Keep large artifacts out of agent_state.json. Store only the path.
  • Log each tool call ID to pending_calls.jsonl before execution.
  • Append each mutation to state.events.jsonl. Snapshot to state.json.
  • Keep one active handoff per branch and topic.
  • Give each fact a receipt: a file path and line.
  • Init has no network and no LLM calls.
Sources
Original source
---
id: agent-workbench-ops
title: Repo Memory, Init Scripts and Handoffs
code: RM
subtitle: Frame the task, keep state in schema-checked files, probe before start, and end each session with a handoff packet.
hue: 30
sources: phases/14-agent-engineering/34-repo-memory-and-state, phases/14-agent-engineering/35-initialization-scripts, phases/14-agent-engineering/40-multi-session-handoff, phases/14-agent-engineering/41-workbench-for-real-repos, phases/14-agent-engineering/43-frame-the-task-before-code
source_hash: 02eda89dcde22cea
---

## One workbench session
type: flow
from: phases/14-agent-engineering/41-workbench-for-real-repos/docs/en.md, phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md, phases/14-agent-engineering/35-initialization-scripts/docs/en.md, phases/14-agent-engineering/40-multi-session-handoff/docs/en.md

1. **Frame**: write goal, facts, paths, acceptance and unknowns before any edit.
2. **Init script**: probe the workbench. On a fail, halt and surface to the human.
3. **Read**: load the scope contract and `agent_state.json`.
4. **Edit**: change allowed files only.
5. **Prove**: run the acceptance command through the feedback runner.
6. **Judge**: run the verification gate, then the reviewer.
7. **Clean up**: `clean_state.json` must hold an empty list.
8. **Hand off**: generate `handoff.md` and `handoff.json`.

## Commands
type: command-table
span: 2
from: phases/14-agent-engineering/34-repo-memory-and-state/docs/en.md, phases/14-agent-engineering/35-initialization-scripts/code/main.py, phases/14-agent-engineering/40-multi-session-handoff/docs/en.md, phases/14-agent-engineering/41-workbench-for-real-repos/docs/en.md, phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md, phases/14-agent-engineering/43-frame-the-task-before-code/code/main.py

| Command | What it does |
|---|---|
| `python3 code/main.py` | Runs the lesson demo. Each one writes its files next to the script |
| `python3 code/main.py` in lesson 34 | Writes `workdir/agent_state.json` and `workdir/task_board.json` |
| `python3 code/main.py --no-cache` | Lesson 35: ignore `prereqs.lock` and run every probe |
| `python3 code/main.py --write-lkg` | Lesson 35: pin current HEAD as last-known-good |
| `git diff --name-only <baseline> HEAD` | The command behind the `lkg_diff` probe |
| `python3 code/main.py` in lesson 40 | Writes `handoff.md` and `handoff.json` |
| `python3 -m unittest discover code/tests -v` | Lesson 43: tests the task frame validator |
| `python3 -m unittest tests.test_accounts` | Acceptance command of the example frame |

## Schema-first state
type: annotated-code
lang: python
from: phases/14-agent-engineering/34-repo-memory-and-state/code/main.py

```python
"required": ["schema_version", "active_task_id", "touched_files", "next_action"]
"active_task_id": {"type": ["string", "null"], "pattern": r"^(T-\d{3,}|)$"}
"status": {"enum": ["todo", "in_progress", "done", "blocked"]}
mgr = StateManager(state_path, STATE_SCHEMA)
state = mgr.load()                          # read, then validate
mgr.commit(state)                           # validate, then atomic_write
fd, tmp_name = tempfile.mkstemp(prefix=path.name + ".", dir=path.parent)
os.fsync(fh.fileno())                       # flush to disk first
os.replace(tmp_name, path)                  # atomic rename
```

## Init probes
type: concepts
from: phases/14-agent-engineering/35-initialization-scripts/code/main.py, phases/14-agent-engineering/35-initialization-scripts/docs/en.md

- **`probe_runtime`**: fails below `REQUIRED_PYTHON`.
- **`probe_dependencies`**: `importlib.util.find_spec` for each dep.
- **`probe_test_command`**: `shutil.which` on the test command.
- **`probe_env`**: fails on a missing env var.
- **`probe_state_freshness`**: warn when the state is old or absent.
- **`probe_lkg_diff`**: fail above the file diff budget.
- **Probe**: returns `(name, status, detail)`.
- **Report**: `init_report.json`. Exit 1 on any fail.

## Defaults and numbers
type: defaults
from: phases/14-agent-engineering/35-initialization-scripts/code/main.py, phases/14-agent-engineering/34-repo-memory-and-state/code/main.py, phases/14-agent-engineering/40-multi-session-handoff/code/main.py

| Setting | Default | Note |
|---|---|---|
| `REQUIRED_PYTHON` | (3, 10) | Runtime probe floor |
| `STATE_FRESHNESS_SECONDS` | 24 * 60 * 60 | Older state gives a warn |
| `LOCK_TTL_SECONDS` | 24 * 60 * 60 | Fresh lock skips the probes |
| `PROBE_BUDGET_SECONDS` | 3.0 | Slower pass becomes warn |
| `LKG_FILE_DIFF_BUDGET` | 50 | More changed files is a fail |
| `schema_version` | 1 | Only allowed enum value |
| `TAIL_K` | 5 | Feedback records kept in the handoff |
| Review total risk | 7 | Below it the handoff adds a warn |

## Seven handoff fields
type: concepts
from: phases/14-agent-engineering/40-multi-session-handoff/docs/en.md, phases/14-agent-engineering/40-multi-session-handoff/code/main.py

- **`summary`**: one paragraph of what was done.
- **`changed_files`**: the diff at a glance.
- **`commands_run`**: what was actually executed.
- **`failed_attempts`**: what was tried and why it failed.
- **`open_risks`**: what can bite next session, with severity.
- **`next_action`**: the first concrete step. The load-bearing field.
- **`verdict_pointer`**: `outputs/verification/<task_id>.json` and the review.
- **`feedback_tail`**: last `TAIL_K` records plus every nonzero exit.

## Prompt-only versus workbench
type: comparison
from: phases/14-agent-engineering/41-workbench-for-real-repos/code/main.py, phases/14-agent-engineering/41-workbench-for-real-repos/docs/en.md

| Outcome | Prompt-only | Workbench-guided |
|---|---|---|
| `tests_actually_run` | False | True |
| `acceptance_met` | False | True |
| `files_outside_scope` | 2 | 0 |
| `handoff_quality` | missing | full packet |
| `reviewer_total` | 3 | 9 |
| Steps | Read, edit, claim done | 8 steps, init to handoff |

## Task frame
type: annotated-code
lang: python
from: phases/14-agent-engineering/43-frame-the-task-before-code/code/main.py, phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md

```python
frame = TaskFrame(
    goal="Prevent duplicate email addresses during signup",
    allowed_paths=["app/accounts.py", "tests/test_accounts.py"],
    forbidden_paths=["migrations/**", "deploy/**"],
    acceptance=["python3 -m unittest tests.test_accounts"],
    facts=[RepositoryFact("Duplicate errors use status 409",
                          "tests/test_accounts.py:44")],  # receipt
    unknowns=["Whether email comparison is case-insensitive"])
issues = validate(frame)          # empty list means READY
render(frame)                     # Markdown, Status: READY or BLOCKED
```

## Classify each unknown
type: concepts
from: phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md

- **Discoverable**: the repository or running system can answer it. Continue.
- **Decidable**: the task contract gives the agent authority. Continue.
- **Human**: changes product behavior, cost, risk or public compatibility. Pause.
- **Deferred**: outside this slice. Put it in non-goals.

## Troubleshooting
type: troubleshooting
span: 2
from: phases/14-agent-engineering/34-repo-memory-and-state/docs/en.md, phases/14-agent-engineering/34-repo-memory-and-state/code/main.py, phases/14-agent-engineering/35-initialization-scripts/docs/en.md, phases/14-agent-engineering/35-initialization-scripts/code/main.py, phases/14-agent-engineering/40-multi-session-handoff/code/main.py, phases/14-agent-engineering/43-frame-the-task-before-code/code/main.py

| Symptom | Cause | Fix |
|---|---|---|
| Next session redoes finished work | State lived in chat, not the repo | Keep state in schema-checked JSON in the repo |
| Session resumes on corrupt state | `write_text()` with silenced exceptions | Use `atomic_write`: `mkstemp`, `fsync`, `os.replace` |
| `does not match` on `T-bogus` | Task id breaks the pattern | Use ids like T-001 |
| `init failed; refuse to launch agent` | A probe has status fail | Fix the probe. Do not start the agent |
| `lkg_diff` fail, files changed above budget | More than 50 files since the last-known-good | A human ratifies the baseline with `--write-lkg` |
| Probe pass turns to warn with `slow:` | Probe above the 3 second budget | Move it out of init or cache its result |
| `no next_action recorded; needs human` | State has no `next_action` | Set `next_action` before the handoff |
| Frame `Status: BLOCKED` | `validate` found issues | Fix each line under Blocking issues |

## Questions
type: qa
from: phases/14-agent-engineering/34-repo-memory-and-state/docs/en.md, phases/14-agent-engineering/35-initialization-scripts/docs/en.md, phases/14-agent-engineering/40-multi-session-handoff/docs/en.md, phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md

- **What goes in repo memory?** Data useful three months from now in a CI rerun.
- **Two init runs in a row?** The second is a no-op except a fresh timestamp.
- **Handoff without `next_action`?** It is a status report, not a handoff.
- **`handoff.md` and `handoff.json` differ?** The JSON wins.
- **When to stop reading the repo?** When each decision has evidence, is delegated, or is an unknown.
- **When to wrap up?** At 50-75% of the context budget, not at 95%.

## Tips
type: tips
from: phases/14-agent-engineering/34-repo-memory-and-state/docs/en.md, phases/14-agent-engineering/40-multi-session-handoff/docs/en.md, phases/14-agent-engineering/43-frame-the-task-before-code/docs/en.md, phases/14-agent-engineering/35-initialization-scripts/docs/en.md

- Keep large artifacts out of `agent_state.json`. Store only the path.
- Log each tool call ID to `pending_calls.jsonl` before execution.
- Append each mutation to `state.events.jsonl`. Snapshot to `state.json`.
- Keep one active handoff per branch and topic.
- Give each fact a receipt: a file path and line.
- Init has no network and no LLM calls.
Code and artifacts 1
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓