PetV3 Agent Factory 4: Budgets and Checkpoints for Autonomous Work
PetV3 automation system, part 4 of 4
Telling an automated system to “keep going” creates two problems. It may continue spending on expensive models because work remains, and a later conversation may mistake an old report for current state after the original context is gone.
PetV3 handles those problems with a usage governor and append-only checkpoints.
Separate model intensity from product performance
The dashboard modes from small to max are not CPU or RSS budgets for the pet. They are orchestration policies: which model class to use, how many workers to admit, and how much verification a wave requires.
These are not official provider limits or current account limits. They are a PetV3 internal policy snapshot dated September 1, 2026, using ChatGPT Pro 20x as its planning assumption.
| Mode | Dated UTC daily policy cap | Derived five-hour value |
|---|---|---|
| small | 1%p | 0.208333%p |
| med | 2%p | 0.416667%p |
| high | 3%p | 0.625000%p |
| xhigh | 4%p | 0.833333%p |
| max | 5%p | 1.041667%p |
The five-hour values are not observed provider limits; they are the daily policy values multiplied by 5/24. Code and tests verified the router and pause-state transition when a controller supplies usage readings. Message count is not a hard budget because model, context, reasoning, and tool use make messages consume different amounts.
Luna or Haiku handles small deterministic work. Terra or Sonnet handles intermediate judgment. Sol or Opus is reserved for control review of state machines, security boundaries, and conflict mediation. The max queue is not a concurrency target: closed tasks, non-conflicting files, available usage, and local resources all reduce it.
Unknown is not zero
The repository has no stable API that directly reads every Codex or Claude subscription gauge. A controller must obtain values from /status or /usage and pass them to the router before and after each wave. Here, automatic stopping means the router is designed to write a pause when supplied values reach the policy threshold—not that an unattended detector reads usage on its own.
By design, missing data is never converted to zero. With unknown usage, only one small calibration wave is allowed; if usage remains unknown, no next wave is created. stop_on_cap=true is the default. A human pause is recorded as manual, while a budget stop is usage_cap.
In the actual record, a controller reviewed an approximate gauge supplied by the user and explicitly wrote a usage_cap pause at control revision 43. The exact daily baseline time and the five-hour window's start and current values were unavailable. A later Claude conversation found that pause already set and honored it. This demonstrates pause enforcement and handoff continuity, not autonomous usage detection by either provider.
Autonomy does not mean never stopping. It means encoding the conditions under which the system must stop.
Send nothing when control did not change
The source of truth for control state is an untracked control.ini. Before and after waves, commits, and landings, the controller polls for changes since a revision.
scripts/dash-control.sh poll --since 42
An unchanged revision returns zero bytes. A change returns one small JSON snapshot. Rendering cost and model-token cost are separate problems, and the system reduces each at a different boundary.
Audit the need for refactoring every six landings
Continuous execution also creates pressure for files and boundaries to grow. Every sixth landing opens a diff-only audit.
The audit worker reads only the diff bundle since the previous baseline and returns none, inspect, or refactor. A seventh landing is refused until the verdict is recorded. Only inspect or refactor allows the controller to inspect narrow definitions and callers.
The audit does not automatically create a refactoring task. Necessity assessment and authorization for new work remain separate decisions.
A handoff is more than one summary
A pause checkpoint contains two files with the same identifier.
YYYY-MM-DDTHHMMSSZ-slug.worklog.md
YYYY-MM-DDTHHMMSSZ-slug.resume.md
The work log records user-visible outcomes, exact command results, decisions, and approvals. The resume file records an unverified snapshot, next actions, the usage ledger, and stop conditions. Both are append-only, and the newest pair is selected by name.
Publishing does not modify the current main branch. The script opens the tip of handoff/petv3 in a detached worktree, stages exactly the two checkpoint files, and pushes without force. It rejects unfinished markers and strings that resemble API keys.
A new Codex or Claude conversation resumes in this order:
AGENTS.md
→ model-routing.md
→ checkpoint worklog
→ checkpoint resume
→ backlog.md
→ remeasure current Git and control state
The checkpoint HEAD and dirty state are historical observations. They are not facts until the new controller verifies the current repository.
The final conclusion
PetV3 automation is not one large agent loop. It is a composition of small contracts.
- Worktrees isolate changes.
- The ledger exposes overlap.
- The landing gate moves only a verified snapshot to main.
- The TUI collects evidence and control in one place.
- The usage governor decides whether another wave may start.
- Checkpoints transfer facts and next actions between Codex and Claude.
The central lesson is this:
Good automation is not a system that runs longer. It is a system that can explain when it runs, when it stops, and what it accepts as true.
Start from the beginning: PetV3 Agent Factory 1: Designing the Failures Before the Automation