A hank is a JSON program that arranges codons, a codon is a single agent task in that sequence, and a checkpoint is a recorded completion boundary that lets an execution resume where it stopped. Those three pieces are what make a long agent run operable: instead of treating a multi-hour pipeline as something that either finishes or is lost, we can interrupt it, restart it, step it back, and audit what it produced.
This chapter puts those operations to work on the seven-codon chapter-5 hank. We run it once to completion, interrupt it after a checkpoint seals and resume the same execution, roll back one codon and regenerate the downstream artifacts, trace a cited price in the final award brief back to its digitized source, and compare a fresh lower-tier model run against the mixed-model baseline. Each section ends with a check-it block so we verify behavior rather than assume it.
The environment needs a few things in place before any paid run: Node.js 22.19.0 or later (the package's entrypoint is a Node script, so Node runs beside the launcher), Python 3 (the python3 interpreter, used by the preparation and verification recipes below), Git, and a non-root user with writable execution and output directories. The commands use Bun's bunx launcher; the equivalent Node/npm spelling is npx hankweave@0.11.0. Export both ANTHROPIC_API_KEY and BASETEN_API_KEY – chapter 5's two judgment codons run on the Baseten route and need the latter.
Before starting, download the fixture bundle. Extract it from its parent directory, then prepare the chapter's task data:
tar -xzf hankweave-fixtures-0.11.0.tar.gz
cd hankweave-fixtures-0.11.0
cd chapters/ch5
python3 ../../verify.py prepare-data ../../quote-template-unification task-data
The prepared task-data directory under chapters/ch5 contains task inputs only; the held-out truth and generator remain in the bundle.
Complete the hank, then operate it#
We start from the complete chapter-5 hank rather than retyping a program, because the exercises that follow depend on its exact shape. It has seven fresh codons: three normalizers, survey-and-extracts, validate-and-repair, reconcile, and award-brief. The five mechanical codons use haiku; the two judgment codons use pi/baseten/deepseek-ai/DeepSeek-V4-Pro. The configured per-codon caps add to $9.00; the accepted chapter capture records $0.80 in tracked codon cost for one complete execution, not a provider-billing promise.
The hank reconciles eight suppliers – AST, BCN, CDR, DVR, EMB, FJR, HBR, and IRS – across five BOM parts, producing 40 data rows, or 41 CSV lines including the header. The unknown GRN intake is quarantined rather than counted as a ninth quoting supplier. Read through the definition once before running it; the sections below operate its checkpoints, inspect its chain-of-custody fields, and measure the effect of changing the judgment tier.
{
"$schema": "https://unpkg.com/hankweave@0.11.0/schemas/hank.schema.json",
"meta": {
"name": "quote-template-unification",
"version": "1.0.0",
"description": "Normalize eight suppliers across three digitizer dialects and five native extracts against RFQ NC-RFQ-0042, quarantine GRN, reconcile 40 supplier-part rows, and publish a cited award brief plus exception queue."
},
"hank": [
{
"id": "normalize-aster",
"name": "Normalize Aster (Datalab dialect)",
"model": "haiku",
"continuationMode": "fresh",
"description": "Mechanical extraction of both Aster submissions (original + rev2) into canonical envelope objects. No judgment: dedup, alias resolution, and validity checks happen downstream.",
"rigSetup": [
{
"type": "command",
"command": {
"run": "mkdir -p pipeline"
}
},
{
"type": "copy",
"copy": {
"from": "rigs/preflight.ts",
"to": "pipeline/preflight.ts"
}
},
{
"type": "command",
"command": {
"run": "bun pipeline/preflight.ts"
}
}
],
"promptFile": "prompts/normalize-aster.md",
"checkpointedFiles": [
"envelope-aster.json"
],
"outputFiles": [
{
"copy": [
"envelope-aster.json"
]
}
],
"onFailure": "abort",
"budget": {
"maxTimeSeconds": 240,
"maxDollars": 0.5,
"onExceeded": "fail"
}
},
{
"id": "normalize-beacon",
"name": "Normalize Beacon (Reducto dialect)",
"model": "haiku",
"continuationMode": "fresh",
"description": "Mechanical extraction of Beacon's PDF-derived Reducto-shaped submission, including its pack pricing, part substitution, and one-time tooling charge, into a canonical envelope object.",
"promptFile": "prompts/normalize-beacon.md",
"checkpointedFiles": [
"envelope-beacon.json"
],
"outputFiles": [
{
"copy": [
"envelope-beacon.json"
]
}
],
"onFailure": "abort",
"budget": {
"maxTimeSeconds": 240,
"maxDollars": 0.5,
"onExceeded": "fail"
}
},
{
"id": "normalize-cedar",
"name": "Normalize Cedar (generic-OCR dialect)",
"model": "haiku",
"continuationMode": "fresh",
"description": "Mechanical extraction of Cedar's skewed-scan OCR submission, including its OCR-confusable part id, NO_BID line, handwritten lead-time annotation, and near-term expiry date, into a canonical envelope object.",
"promptFile": "prompts/normalize-cedar.md",
"checkpointedFiles": [
"envelope-cedar.json"
],
"outputFiles": [
{
"copy": [
"envelope-cedar.json"
]
}
],
"onFailure": "abort",
"budget": {
"maxTimeSeconds": 240,
"maxDollars": 0.5,
"onExceeded": "fail"
}
},
{
"id": "survey-and-extracts",
"name": "Prepare native exports and review raw intake",
"model": "haiku",
"continuationMode": "fresh",
"promptFile": "./prompts/survey-and-extracts.md",
"rigSetup": [
{
"type": "copy",
"copy": {
"from": "rigs/native-inputs.ts",
"to": "pipeline/native-inputs.ts"
}
},
{
"type": "command",
"command": {
"run": "bun pipeline/native-inputs.ts"
}
}
],
"onFailure": "retry",
"retryConfig": {
"maxAttempts": 3,
"delayMs": 20000,
"maxDelayMs": 120000
},
"budget": {
"maxDollars": 1.5,
"maxTimeSeconds": 1800,
"onExceeded": "complete"
},
"checkpointedFiles": [
"envelope-dover.json",
"envelope-embar.json",
"envelope-fjord.json",
"envelope-harbor.json",
"envelope-iris.json",
"survey-notes.json"
],
"outputFiles": [
{
"copy": [
"envelope-dover.json",
"envelope-embar.json",
"envelope-fjord.json",
"envelope-harbor.json",
"envelope-iris.json",
"survey-notes.json"
]
}
]
},
{
"id": "validate-and-repair",
"name": "Validate & Repair Envelopes",
"model": "pi/baseten/deepseek-ai/DeepSeek-V4-Pro",
"continuationMode": "fresh",
"description": "Reads all eight supplier envelopes plus survey notes, the RFQ contract and lookups. Resolves aliases, substitutions, duplicate submissions and line extensions; quarantines GRN and routes unresolved hazards to the exception ledger.",
"promptFile": "prompts/validate-and-repair.md",
"rigSetup": [
{
"type": "copy",
"copy": {
"from": "rigs/native-inputs.ts",
"to": "pipeline/native-inputs.ts"
}
},
{
"type": "command",
"command": {
"run": "bun pipeline/native-inputs.ts --check"
}
}
],
"checkpointedFiles": [
"validated-records.json",
"exception-ledger.json"
],
"outputFiles": [
{
"copy": [
"validated-records.json",
"exception-ledger.json"
]
}
],
"sentinels": [
{
"sentinelConfig": "sentinels/quality-observer.json"
}
],
"onFailure": "abort",
"budget": {
"maxDollars": 3.0,
"maxTimeSeconds": 2400,
"onExceeded": "complete"
}
},
{
"id": "reconcile",
"name": "Reconcile to Unified Records",
"model": "pi/baseten/deepseek-ai/DeepSeek-V4-Pro",
"continuationMode": "fresh",
"description": "Joins validated records against the RFQ's 5-part BOM across all 8 quoting suppliers, guarantees exactly 40 data rows, and keeps GRN only in the exception ledger.",
"promptFile": "prompts/reconcile.md",
"checkpointedFiles": [
"unified-records.csv",
"exception-ledger.json"
],
"outputFiles": [
{
"copy": [
"unified-records.csv",
"exception-ledger.json"
]
}
],
"onFailure": "abort",
"budget": {
"maxDollars": 2.0,
"maxTimeSeconds": 1800,
"onExceeded": "fail"
}
},
{
"id": "award-brief",
"name": "Award Brief & Exception Queue",
"model": "haiku",
"continuationMode": "fresh",
"description": "Renders cited financial recommendations deterministically from reconciled rows; the agent completes only a qualitative assessment. Source-based pre-copy checks protect the financial block and the lossless exception CSV.",
"promptFile": "prompts/award-brief.md",
"rigSetup": [
{
"type": "copy",
"copy": {
"from": "rigs/render-exceptions.ts",
"to": "pipeline/render-exceptions.ts"
}
},
{
"type": "copy",
"copy": {
"from": "rigs/render-award.ts",
"to": "pipeline/render-award.ts"
}
},
{
"type": "command",
"command": {
"run": "bun pipeline/render-exceptions.ts && bun pipeline/render-award.ts"
}
}
],
"checkpointedFiles": [
"award-brief.md",
"exceptions.csv"
],
"outputFiles": [
{
"beforeCopy": [
{
"type": "command",
"command": {
"run": "bun pipeline/render-exceptions.ts --check && bun pipeline/render-award.ts --check"
}
}
],
"copy": [
"award-brief.md",
"exceptions.csv",
"unified-records.csv"
]
}
],
"onFailure": "abort",
"budget": {
"maxDollars": 1.0,
"maxTimeSeconds": 1200,
"onExceeded": "complete"
}
}
]
}
With the definition in place, run the anchor once with a fresh execution and an output copy:
bunx hankweave@0.11.0 hank.json task-data --headless --start-new --execution exec --max-cost 9 --shim-idle-timeout 1800 --overwrite-output -o out
Check-it – anchor contract:
python3 ../../verify.py chapter 5 out
bun rigs/render-exceptions.ts out --check && bun rigs/render-award.ts out --check
These checks assert the eight-supplier, 40-row contract, the source references, the award brief, and the survey notes. They are stronger evidence than a zero process exit alone, and every later section builds on this baseline output.
How resume actually works#
Before we interrupt anything, the resume model needs to be precise, because there is no --resume flag. Resume is the default: when an execution directory already exists for the same data hash, running the hank without --start-new reuses that execution. Use --start-new (or -n/--new) when we need a new execution directory and a newly resolved plan.
A resumed execution keeps its .hankweave/state.json plan verbatim. Every planned entry – failed, interrupted, or not yet started – runs on the model and per-codon configuration stored when the plan was built, so neither an edited hank nor a --model override re-resolves that plan. The startup line Using global model override: <model> (applies to all codons) prints on every launch, but on a continuation it does not mean the resumed codons use the override. To change or compare a model, build a fresh plan: --start-new with a new execution and output directory.
Two scoping rules are worth knowing before they surprise us. First, the changed-hank check runs only when we resume an explicit -e/--execution directory: if the recorded hank hash differs, a non-interactive run (for example --headless) refuses with hank.json does not match the configuration recorded for this execution … refusing to resume in non-interactive mode. unless we add -y (or --force), and an interactive run asks Continue with modified config? [y/N]. A managed resume – no -e, no --start-new – never reads the hank hash at all: it takes the newest execution whose data hash matches and keeps the persisted plan, so --start-new is how to run an edited hank. Second, if the newest execution for the data hash is already complete, the runtime resumes it, starts a continuation from its newest completed codon, finds no codon left to run, and announces it: Resumed completed execution <id> — nothing to do. All codons already completed. Use --start-new to run fresh. It runs no codon and exits 0 two seconds later.
Check-it – reuse choice: keep the same data source and omit --start-new when we are deliberately resuming; use --start-new for an independent plan. A completed reuse is confirmed by the Resumed completed execution … notice on stdout (under --headless) and zero new codon events, not by assuming new work ran.
Kill the run, then resume it#
With that model in mind, we can test it. We start a fresh headless run in one terminal, then watch its checkpoint repository from another. The checkpoint store is a non-bare shadow repository, not the project .git. Launch the run first; startup creates the repository and its initial checkpoint:
bunx hankweave@0.11.0 hank.json task-data --headless --start-new --execution exec-kill --max-cost 9 --shim-idle-timeout 1800 --overwrite-output -o out-resume
After the run has started, wait for the completed:validate-and-repair checkpoint commit while the first terminal is still running:
git --git-dir=exec-kill/.hankweave/checkpoints/.hankweavecheckpoints --work-tree=exec-kill/agentRoot log --oneline
Once that commit appears, press Ctrl-C in the first terminal and wait for the process to exit before resuming. The graceful shutdown keeps the sealed checkpoint; because the shutdown reason is neither all codons completed nor a codon failure, state.json records the interrupted run as failed, with the in-flight codon still marked running. Resume with the same execution and data, without --start-new:
bunx hankweave@0.11.0 hank.json task-data --headless --execution exec-kill --max-cost 9 --shim-idle-timeout 1800 --overwrite-output -o out-resume
The startup information identifies the matched execution ID, source path, and execution path, so we can verify which execution was matched. Only reconcile and award-brief should run after validate-and-repair has sealed; the resumed codons use their persisted models and budgets. A second Ctrl-C during shutdown escalates to force shutdown: it aborts the running harness session, skips checkpoints, telemetry, and state transitions, and exits 1 under the shutdown watchdog.
The captured two-codon scenario below demonstrates the same checkpoint rollback and resume mechanics on a smaller timeline, so the sequence is easier to read. Its assertion is deliberately about write-line and write-second, not about the chapter-5 codon names. In the log, the runtime rolls back to write-line's completed checkpoint, checks it out (detached HEAD), then resumes at write-second and finishes with exit code 0:
[<ts>] [INFO] [DEBUG] Initializing checkpoints...
[<ts>] [INFO] Fresh run <id> - branch will be created on first checkpoint
[<ts>] [DEBUG] Built execution thread: 0 codons across 1 runs, next codon: write-line
[<ts>] [INFO] Added checkpoint patterns: out.txt
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-line with status completed
[<ts>] [INFO] [CHECKPOINT-DEBUG] Created checkpoint: <sha> (completed) on branch run-<id>
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: write-second
[<ts>] [DEBUG] Built execution thread: 1 codons across 1 runs, next codon: write-second
[<ts>] [INFO] Added checkpoint patterns: out.txt, second.txt
[<ts>] [INFO] Shutting down server: SIGTERM
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-second with status exit
[<ts>] [INFO] [CHECKPOINT-DEBUG] Created checkpoint: <sha> (exit) on branch run-<id>
[<ts>] [INFO] Shutting down sentinel manager...
[<ts>] [INFO] Shutdown: SIGTERM (exit code: 0)
[<ts>] [INFO] [DEBUG] Initializing checkpoints...
[<ts>] [DEBUG] Built execution thread: 2 codons across 1 runs, next codon: none
[<ts>] [DEBUG] Built execution thread: 2 codons across 1 runs, next codon: none
[<ts>] [INFO] Rolling back to completed checkpoint of codon write-line (run <id>): {"codon":{"codonId":"write-line","startTime":"<ts>","status":"completed","claudePid":"<pid>","claudeLogPath":".hankweave/runs/<id>/write-line-claude.log","claudeSessionId":"<session>","currentCost":0.01039995,"currentTokens":{"inputTokens":"<n>","outputTokens":"<n>","cacheCreationTokens":"<n>","cacheReadTokens":"<n>"},"assistantMessageCount":3,"extensionCount":0,"endTime":"<ts>","exitCode":0,"finalCost":"<n>","finalTokens":{"inputTokens":"<n>","outputTokens":"<n>","cacheCreationTokens":"<n>","cacheReadTokens":"<n>"},"resultMessageReceived":true,"completionCheckpoint":"<sha>"},"runId":"<id>","runStatus":"failed","runStartTime":"<ts>","runEndTime":"<ts>","gitBranch":"run-<id>","globalIndex":1,"runIndex":0,"codonIndexInRun":0,"validatedCheckpoints":[{"type":"completed","sha":"<sha>","message":"completed:write-line [run:<id>] Write one line","timestamp":"<ts>","branch":"run-<id>"}],"continuationSessionId":null}
[<ts>] [INFO] Starting codon-by-codon rollback to completed checkpoint <sha> in codon write-line (Write one line)
[<ts>] [INFO] Checked out checkpoint <sha7> (detached HEAD)
[<ts>] [DEBUG] Built execution thread: 1 codons across 2 runs, next codon: write-second
[<ts>] [DEBUG] Built execution thread: 1 codons across 2 runs, next codon: write-second
[<ts>] [DEBUG] [getNextCodonToExecute] Execution thread determined next codon: write-second
[<ts>] [INFO] Added checkpoint patterns: out.txt, second.txt
[<ts>] [DEBUG] Built execution thread: 2 codons across 2 runs, next codon: none
[<ts>] [INFO] [CHECKPOINT-DEBUG] Creating checkpoint for codon write-second with status completed
[<ts>] [INFO] [CHECKPOINT-DEBUG] Created checkpoint: <sha> (completed) on branch run-<id>
[<ts>] [DEBUG] Built execution thread: 2 codons across 2 runs, next codon: none
[<ts>] [DEBUG] Built execution thread: 2 codons across 2 runs, next codon: none
[<ts>] [INFO] Shutting down server: all codons completed
[<ts>] [INFO] Shutting down sentinel manager...
[<ts>] [INFO] Shutdown: all codons completed (exit code: 0)
The companion assertion counts start events across both runs: the sealed codon started exactly once, and only the unsealed one re-ran:
codon.started events across both runs — write-line: 1 · write-second: 2 (codon 1 was sealed before the kill, so it must start exactly once)
Roll back one codon#
Resume moves an execution forward from its last sealed checkpoint; rollback moves it backward to an earlier one. Rollback is an interactive TUI (terminal user interface) or WebSocket-client operation, not a CLI flag. The earlier runs were headless, so start a separate interactive execution for this exercise; the TUI is the default when we omit --headless:
bunx hankweave@0.11.0 hank.json task-data --start-new --execution exec-rollback --max-cost 9 --shim-idle-timeout 1800 --overwrite-output -o out-rollback
With the default autostart, a finished run announces completion and shuts the process down about two seconds later, so the rollback menu is not there after the run ends. Press r while the run is still live (or start with --no-autostart to keep the server up after the last codon). In the rollback menu, select the validate-and-repair checkpoint whose type is completed and confirm. The TUI sends rollback.toCheckpoint with the selected checkpoint SHA and autoRestart: true; the raw WebSocket rollback.toCodon command is for a client that sends the command directly, not a literal TUI menu selection. The protocol reference owns the complete command signatures. The runtime journals each stage of the operation, and the emitted events are:
| id | category | journaled | sentinelRouted | payloadFields | receipts |
|---|---|---|---|---|---|
| rollback.codonCheckpoint | server-state | true | true | codonId, codonName, checkpoint, checkpointType, message | schemas/event-schemas.ts:759, schemas/event-schemas.ts:1085, schemas/event-schemas.ts:1366, hankweave-runtime.ts:5048 |
| rollback.completed | server-state | true | true | fromRun, toRun, checkpoint, codonId, codonName, checkpointType, autoRestart | schemas/event-schemas.ts:774, schemas/event-schemas.ts:1086, schemas/event-schemas.ts:1369, hankweave-runtime.ts:5075 |
| rollback.progress | server-state | true | true | currentStep, totalSteps, message | schemas/event-schemas.ts:769, schemas/event-schemas.ts:1084, schemas/event-schemas.ts:1368, hankweave-runtime.ts:5469 |
| rollback.started | server-state | true | true | fromRun, fromCodon, toCodon, toCheckpoint, checkpointType, codonsToProcess | schemas/event-schemas.ts:754, schemas/event-schemas.ts:1083, schemas/event-schemas.ts:1365, hankweave-runtime.ts:5010 |
Rollback restores the working directory to the selected checkpoint (a forced checkout to detached HEAD in the shadow repository), then the auto-restarted continuation re-runs reconcile and award-brief, regenerating unified-records.csv and award-brief.md. The checkpoint before validate-and-repair remains untouched, and the old timeline remains inspectable because the resumed run creates a new branch from the detached checkpoint state.
The checkpoint repository has an explicit layout: GIT_DIR=exec-rollback/.hankweave/checkpoints/.hankweavecheckpoints and GIT_WORK_TREE=exec-rollback/agentRoot. Inspect the shadow repository directly – a plain git log answers from the project .git instead – and identify checkpoints by their commit subjects, which begin error:<codonId> or completed:<codonId>; these are commit subjects, not git tags:
git --git-dir=exec-rollback/.hankweave/checkpoints/.hankweavecheckpoints --work-tree=exec-rollback/agentRoot log --oneline --decorate
checkpoint_sha="$(git --git-dir=exec-rollback/.hankweave/checkpoints/.hankweavecheckpoints --work-tree=exec-rollback/agentRoot log --format='%H %s' | awk '$2 ~ /^(error:|completed:)/ { print $1; exit }')"
git --git-dir=exec-rollback/.hankweave/checkpoints/.hankweavecheckpoints --work-tree=exec-rollback/agentRoot checkout --detach "$checkpoint_sha"
Check-it – rollback: after the TUI operation and the auto-restarted run, check the regenerated output copy and the checkpoint history:
python3 ../../verify.py chapter 5 out-rollback
bun rigs/render-exceptions.ts out-rollback --check && bun rigs/render-award.ts out-rollback --check
git --git-dir=exec-rollback/.hankweave/checkpoints/.hankweavecheckpoints --work-tree=exec-rollback/agentRoot log --oneline --decorate
Confirm that unified-records.csv and award-brief.md were regenerated in out-rollback and that both the pre-rollback and post-rollback timelines are present in the shadow-repository log.
Trace one dollar figure to its source#
The operations so far prove the pipeline can stop and restart; this section checks whether its output can be believed. We follow one award amount instead of trusting a final paragraph. The award brief cites a source document and block; the unified-records row repeats the document, quote, line, and block identifiers; the validated record and envelope lead back to the digitized source. Here, source evidence means the input files under task-data/digitized/ and task-data/digitized-extracts/: normalize codons copy those fields without judgment, while validate-and-repair applies the RFQ contract to derived records and exception decisions without altering those input files. award-brief renders only what upstream stages settled. The excerpt below shows the award table and the first rows of the unified records it projects from; note the [doc:block] citations attached to every figure.
Projection of unified-records.csv only. Rank QUOTED, ranking_eligible=true rows by line_extension_minor; break equal-price ties by supplier_code ascending. Unit prices are already normalized per each upstream. No eligible bid means no recommendation; unsupported data stops export rather than inventing a price or citation.
| Buyer part | Selected supplier | Unit price (USD per each) | Line extension (USD) | Tie / no-bid decision | Ineligible QUOTED suppliers |
| --- | --- | --- | --- | --- | --- |
| NC-1001-A | BCN | $3.95 [doc-beacon-8821:doc-beacon-8821-b1] | $1,975.00 [doc-beacon-8821:doc-beacon-8821-b1] | Unique lowest eligible bid | CDR [doc-cedar-cw-77:doc-cedar-cw-77-b1] |
| NC-1002-A | AST | $0.79 [doc-aster-qb-1047-rev2:doc-aster-qb-1047-rev2-b2] | $1,580.00 [doc-aster-qb-1047-rev2:doc-aster-qb-1047-rev2-b2] | Unique lowest eligible bid | CDR [doc-cedar-cw-77:doc-cedar-cw-77-b2] |
| NC-1003-B | EMB | $1.05 [doc-embar-quote:doc-embar-quote-b3] | $1,050.00 [doc-embar-quote:doc-embar-quote-b3] | Unique lowest eligible bid | CDR [doc-cedar-cw-77:doc-cedar-cw-77-b3] |
| NC-1004-A | AST | $6.40 [doc-aster-qb-1047-rev2:doc-aster-qb-1047-rev2-b4] | $160.00 [doc-aster-qb-1047-rev2:doc-aster-qb-1047-rev2-b4] | Unique lowest eligible bid | None |
| NC-1005-A | BCN | $0.19 [doc-beacon-8821:doc-beacon-8821-b5] | $950.00 [doc-beacon-8821:doc-beacon-8821-b5] | Unique lowest eligible bid | CDR [doc-cedar-cw-77:doc-cedar-cw-77-b5] |
supplier_code,buyer_part_id,rfq_rev,qty,unit_price_minor,price_basis,line_extension_minor,currency,status,ranking_eligible,source_document_id,source_quote_id,source_line_id,source_block_id
AST,NC-1001-A,B,500,410,PER_EACH,205000,USD,QUOTED,true,doc-aster-qb-1047-rev2,q-aster-qb-1047-rev2,doc-aster-qb-1047-rev2-L1,doc-aster-qb-1047-rev2-b1
AST,NC-1002-A,B,2000,79,PER_EACH,158000,USD,QUOTED,true,doc-aster-qb-1047-rev2,q-aster-qb-1047-rev2,doc-aster-qb-1047-rev2-L2,doc-aster-qb-1047-rev2-b2
AST,NC-1003-B,B,1000,115,PER_EACH,115000,USD,QUOTED,true,doc-aster-qb-1047-rev2,q-aster-qb-1047-rev2,doc-aster-qb-1047-rev2-L3,doc-aster-qb-1047-rev2-b3
Choose the NC-1001-A Beacon amount. Follow its [doc-beacon-8821:doc-beacon-8821-b1] citation to the Beacon row with the same supplier_code and buyer_part_id, then inspect validated-records.json and the matching envelope-beacon.json in the execution's agentRoot; the cited source block is under task-data/digitized/ or task-data/digitized-extracts/. The output copy holds the files this walk reads (out/award-brief.md, out/unified-records.csv, out/validated-records.json, and out/envelope-beacon.json, since the run's outputFiles are all copied to out), and the execution's own working copies sit under exec/agentRoot. The four source columns are source_document_id, source_quote_id, source_line_id, and source_block_id; they are the chain-of-custody fields to inspect.
Check-it – citation chain: run the chapter check, then verify that the chosen amount has a [doc:block] citation and that its source reference reaches a digitizer block without a broken link:
python3 ../../verify.py chapter 5 out
bun rigs/render-exceptions.ts out --check && bun rigs/render-award.ts out --check
render-exceptions.ts serializes the exception ledger; the financial projection is derived from unified-records.csv, while the agent supplies only the qualitative assessment. These checks therefore verify exported structure and source-based projections, not the semantic truth of the agent's narrative.
What happens at a cheaper model tier#
The last exercise changes one variable: the model behind the judgment codons. Because a persisted plan freezes every codon's model, the comparison needs a fresh plan: --start-new with its own execution and output directories. --model haiku replaces every codon's model, so the five mechanical codons are unchanged and only validate-and-repair and reconcile move from the Baseten model to haiku; the quality-observer sentinel keeps its configured model.
bunx hankweave@0.11.0 hank.json task-data --headless --start-new --model haiku -y --execution exec-haiku --max-cost 9 --shim-idle-timeout 1800 --overwrite-output -o out-haiku
Check the fresh run independently, then compare its records and exception ledger with the mixed-model output. The hypothesis is that the judgment-heavy artifacts differ: some calls the Baseten model made correctly may be wrong or missed entirely by the cheaper model. Do not treat a difference as proof that the cheaper model is always wrong; inspect which judgment-heavy artifact changed and why. The mixed-model chapter receipt records $0.80 in tracked codon cost for one complete execution; configured caps total $9.00, and neither number predicts the cost or accuracy of this new run.
Check-it – model comparison:
python3 ../../verify.py chapter 5 out-haiku
diff -u out/unified-records.csv out-haiku/unified-records.csv || true
diff -u out/exception-ledger.json out-haiku/exception-ledger.json || true
Record the artifact-level differences, rather than a blanket model-quality verdict. The fresh --start-new and separate paths are what make this a comparison instead of a re-plan of the existing execution.
Account for all eight suppliers#
One structural detail of the hank deserves a closer look, because it explains where the full supplier count comes from. The three hand-digitized dialects cover 15 rows from the earlier chapters. To account for the remaining suppliers, we add five native exports – Dover, Embar, Fjord, Harbor, and Iris – through the pre-built survey-and-extracts codon at position four. rigs/native-inputs.ts deterministically parses those exports into envelopes and starts survey-notes.json with complete: false; the agent inspects unmatched intake and records quarantine decisions. It does not retype or re-parse the native exports. The broader contract is eight suppliers and 40 data rows, with GRN quarantined.
Check-it – coverage: run the deterministic chapter check after the run and inspect survey-notes.json; confirm that the structured output covers the eight named suppliers and that the unknown intake is represented in the exception path rather than guessed into a ninth supplier.
What can go wrong#
The failure modes in this chapter are mostly about timing and plan reuse. If we interrupt before any checkpoint seals, resume has no checkpoint to restore and starts again from the first codon – wait for a completed: entry before pressing Ctrl-C. If we omit --start-new after a failed attempt, the existing execution's frozen plan wins, models and all. And a changed hank.json is checked only on an explicit -e resume: a managed resume keeps the persisted plan and never re-plans, so --start-new is the way to run an edited hank.
After checking a model-tier difference, we can return to the same evidence: roll back to validate-and-repair and run reconcile and award-brief again.
Check-it – chapter recap: confirm the checkpoint log, the structured output checks, the rollback history, and the side-by-side artifact comparison before repeating the rollback-and-rerun path.