Inspect the minimal single-provider hank

Use this fixture for a first useful hank#

A hank is the configuration for a unit of work; a codon is one sealed agent task in its sequence. This page walks through the smallest useful hank: one codon, one provider key (ANTHROPIC_API_KEY), and one output file. By the end you will have read every file in the fixture, validated the configuration, run it headless, watched it fail safely without a key, and checked the evidence a good run leaves behind.

We follow one successful path and one authentication-failure capture, pausing at each field in hank.json to explain what it controls. If you would rather begin with a guided validate, run, break, and resume walkthrough, start with start/quickstart and come back here when you want the fixture itself explained.

Find the pieces before you run#

Work from the minimal-single-provider/ directory. Before running anything, look at what the fixture ships: the configuration, the prompt, the input data, a README, and a set of captured expected outputs you can compare your own run against.

FIG. 1 Find the pieces before you run
Read the diagram as text
Output
minimal-single-provider/
├── hank.json
├── prompts/
│   └── summarize.md
├── data/
│   └── notes.txt
├── expected/
│   ├── MANIFEST.md
│   ├── receipt.json
│   ├── validate-output.txt
│   ├── run-transcript.txt
│   ├── final-events.txt
│   ├── fail-no-key.txt
│   └── summary-example.md
└── README.md

The input, data/notes.txt, is fictional meeting material. It names the Calloway vendor decision, Devon as owner, and a Friday-the-12th deadline, and it carries a training-fixture watermark so it cannot be mistaken for real data:

Output
FICTIONAL TRAINING FIXTURE — invented content for documentation testing.

Meeting notes, Tuesday.
Attendees: Mara, Devon, Sam.
We compared the two vendor quotes for the packaging line refit.
Decision: go with the Calloway quote (lower total, includes installation).
Devon owns the purchase order and vendor follow-up.
Deadline: PO issued by Friday the 12th.
Sam raised spare-parts stocking; parked for next week.

The expected/ directory holds captures from one complete live execution. Its manifest dates the set to 2026-09-27, identifies the published hankweave@0.11.0 artifact, and records a tracked codon cost of $0.02690865. The capture set uses normalized placeholders for changing execution values, while final-events.txt retains some per-run process and session fields; diff-clean recapture therefore depends on the normalizer covering those fields too. The manifest marks the execution as actual service output. Note what is absent: there is no committed out/ directory, because out/summary.md is created by the run command below.

Output
# Capture manifest
- Captured: 2026-09-27T07:55:01.007247+00:00 · runtime: hankweave@0.11.0
- actual_service_output: true
- Scope: one complete live execution
- Raw execution (path normalized): ~/.hankweave-executions/minimal/1790495675081-16mt-8591f9
- Complete codons: 1/1
- Tracked codon cost: $0.02690865 (provider health checks and sentinel calls are separate)
- Input scope: notes only

Read what hank.json controls#

The configuration is a single file. Its hank array holds one codon, and its $schema URL pins this fixture to a versioned schema rather than the @latest spelling used by the initialization fixture.

JSON
{
  "$schema": "https://unpkg.com/hankweave@0.11.0/schemas/hank.schema.json",
  "meta": {
    "name": "Minimal single provider",
    "version": "1.0.0",
    "description": "The smallest useful hank: one codon, one provider key, one output file. The quickstart's fixture."
  },
  "hank": [
    {
      "id": "summarize-notes",
      "name": "Summarize the notes",
      "model": "haiku",
      "continuationMode": "fresh",
      "promptFile": "./prompts/summarize.md",
      "checkpointedFiles": ["summary.md"],
      "outputFiles": [ { "copy": ["summary.md"] } ]
    }
  ]
}

The optional meta block carries a name, version, and description; it has no runtime effect. The summarize-notes codon declares id, name, model, and continuationMode, plus its prompt, checkpointed file, and output copy. Each of those fields does something specific:

haiku is a registry shortcut. When authentication fails, the capture prints its runtime-resolved display name: Claude Haiku 4.5 (latest) (anthropic/claude-haiku-4-5). That is a registry resolution result, not a pinned artifact string. Bare shortcuts such as haiku, sonnet, and opus are valid only where the registry defines them; use a full pi/<provider>/<model> or provider-prefixed slug for another model. See model resolution for the resolution phases.

continuationMode: "fresh" starts the codon in a new agent session. It is a required field with no default: the value fresh is an explicit choice the fixture writes, and the alternative is continue-previous. checkpointedFiles: ["summary.md"] makes that file watched, streamed to the client, and sealed into the git-based checkpoint store after the codon. outputFiles: [{"copy": ["summary.md"]}] copies summary.md from the execution directory to the output directory when the codon completes, gated on that codon's own completed status, its outputFiles, and an output directory set by -o or by outputDirectory in hankweave.json (or HANKWEAVE_RUNTIME_OUTPUT_DIRECTORY). See concepts/codons, concepts/checkpoints, reference/hank-json, and operate/runbook for those mechanics.

Equally important is what this hank does not declare: no budget, no onFailure (so the failure policy is the default abort), no rig setup, no sentinels, and no requirements.env. The fixture's lesson is that everything else defaults. Because it declares no requirements.env, a missing ANTHROPIC_API_KEY is caught by the per-model harness self-test's credential-presence check, which makes no model call; --validate runs the same self-test, so a missing key fails validation too. See authentication and models.

Keep the data path in mind#

The prompt is the other half of the codon. It asks the agent to read notes.txt from read_only_data_source in its working directory, then write summary.md with one sentence and exactly three bullets: one decision, one owner, and one deadline. The prompt preserves the deadline wording from the notes and forbids inferring a month, year, calendar date, or other detail the notes do not give. The file must stay under 15 lines.

Output
# Summarize the meeting notes

Read the file `notes.txt` inside the `read_only_data_source` directory in your current working directory (use shell commands like `cat` — the directory may be a symlink).

Write `summary.md` in the current working directory containing:

1. A one-sentence summary of what the meeting decided.
2. Exactly three bullet points: one decision, one owner, one deadline — taken from the notes.

Keep the deadline's wording from the notes. Do not infer a month, year, calendar date, or other detail that the notes do not give.

Nothing else. Keep it under 15 lines.

One detail in that prompt deserves attention. The runtime links the source data to agentRoot/read_only_data_source – a symlink to the data directory, falling back to a copy – whatever its source name; here the source is data/, and the startup capture reports Source → data. The name is a convention, not a read-only mount, so use the linked path in the prompt rather than depending on the source name. The detail is owned by designing codons and handoffs.

This is the cheapest first run the fixture set offers: one mechanical summarization task on the cheapest model tier, one key, and one sealed output. It is the small version of the shape used by quote-template-unification at production scale.

Validate, run, and break safely#

Enter the minimal-single-provider/ directory – the directory containing hank.json – before running these commands. The runtime resolves a relative data positional against the working directory and defaults the config to hank.json there. The shown bunx launcher invokes the package's Node entry point, so use Node >=22.19.0; Bun/bunx is the package launcher here. If you use npm instead, substitute npx once for bunx in each command. Set ANTHROPIC_API_KEY for the successful run: a local Claude Code login alone is not the default direct-Anthropic credential for this fixture.

Validate before the run#

Run the checked validation command from the fixture root:

⌁ Terminal
bunx hankweave@0.11.0 hank.json data/ --validate

With two positionals the first is the hank path and the second the data path; the .json (or remote-URL) test applies only when a single positional is given, where it is the hank path if it ends in .json and the data path otherwise. The command grammar belongs to reference/cli. Validation initializes the registry with the billable provider health checks disabled, but it still runs the same per-model harness self-test as startup (SDK import, credential presence, and for Pi models the catalog), so a missing key fails validation with exit 1. What validation does not prove is that the key is valid or the provider healthy: the credential check tests presence only. A plain run separately performs the provider health checks, which can be billable and are tracked outside the codon total.

Check-it: the command prints ✓ Configuration is valid! and the GOOD TO RUN! box with 1 codons • 1 prompts • 0 system prompts • 0 rigs • 1 checkpoints. The full captured output looks like this:

Output

╭────────────────────────────────────────────────────────────────────╮
│  Hankweave v0.11.0                                                 │
│  linux x64 • node v22.23.2                                         │
╰────────────────────────────────────────────────────────────────────╯

Calculating data signature for validation...

> Validating configuration: <workspace>/fixtures/minimal-single-provider/hank.json

  Data source:    <workspace>/fixtures/minimal-single-provider/data
  Execution path: ~/.hankweave-executions/validation-<id>

✓ Configuration is valid!

╭──────────────────────────────────────────────────────────────────────────────╮
│  Minimal single provider v1.0.0                                              │
│  1 codon • 0 loops                                                           │
╰──────────────────────────────────────────────────────────────────────────────╯

└─ [1] summarize-notes (Summarize the notes)
      model: haiku │ mode: fresh │ prompts: 1 (13 lines)
      checkpointedGlobs: 1

╭─ GOOD TO RUN! ─────────────────────────────────────────────────────╮
│  1 codons • 1 prompts • 0 system prompts • 0 rigs • 1 checkpoints  │
╰────────────────────────────────────────────────────────────────────╯

Run it:  hankweave hank.json <data_path>

Environment Variables:

  From System (HANKWEAVE_ prefixed):
    - CAPTURE_VERSION: 0.11.0
    - TELEMETRY: 0

exit=0

Run a fresh headless execution#

With validation passing, run without the terminal interface, force a fresh execution, and copy the result to out/:

⌁ Terminal
bunx hankweave@0.11.0 hank.json data/ --headless --start-new -o out

--headless runs without the terminal interface. --start-new (also -n) starts a fresh execution and never resumes one. -o out copies the output under the fixture root. The runtime creates a managed execution directory at ~/.hankweave-executions/<exec-id>/ and prints its path. The codon's working directory is that execution's agentRoot, where the linked data appears as read_only_data_source.

Without an output directory set – no -o, no outputDirectory in hankweave.json or HANKWEAVE_RUNTIME_OUTPUT_DIRECTORY – summary.md remains only in ~/.hankweave-executions/<exec-id>/agentRoot/; there is no default results directory. See operate/runbook for output-directory behavior.

Check-it: after this command, out/summary.md exists under the fixture root and the capture contains the Hankweave Server Started box and Running in headless mode on port <port>:

Output

╭────────────────────────────────────────────────────────────────────╮
│  Hankweave v0.11.0                                                 │
│  linux x64 • node v22.23.2                                         │
╰────────────────────────────────────────────────────────────────────╯

Created new execution directory: ~/.hankweave-executions/<exec-id>
New execution: <exec-id>
  Source → data
  Exec   → ~/.hankweave-executions/<exec-id>
  SDKs   → Claude node_modules ✓

╭──────────────────────────────────────────────────────────────────────────────╮
│  Minimal single provider v1.0.0                                              │
│  1 codon • 0 loops                                                           │
╰──────────────────────────────────────────────────────────────────────────────╯

└─ [1] summarize-notes (Summarize the notes)
      model: haiku │ mode: fresh │ prompts: 1 (13 lines)
      checkpointedGlobs: 1


┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│  Hankweave collects anonymous usage statistics to help          │
│  improve the tool. No personal information, file contents,      │
│  or prompts are collected.                                      │
│                                                                 │
│  Learn more: https://docs.hankweave.dev/reference/telemetry     │
│  Opt out:    export HANKWEAVE_TELEMETRY=0                       │
│                                                                 │
│  This notice won't be shown again.                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘


══════════════════════════════════════════════════
  Hankweave Server Started
  WebSocket: ws://localhost:<port>
══════════════════════════════════════════════════

Running in headless mode on port <port>
➜ Listening on: http://localhost:<port>/ (all interfaces)

exit=0

Break authentication before a codon runs#

To exercise the named failure fixture, unset the key and run the same command:

⌁ Terminal
env -u ANTHROPIC_API_KEY bunx hankweave@0.11.0 hank.json data/ --headless --start-new -o out

Startup creates the execution directory, then its per-model self-test fails before the codon runs. The diagnostic prints the resolved model name and a stack trace pointing into an installed hankweave@0.11.0 bundle – not a source checkout.

Check-it: the expected-failure capture says No authentication found (set ANTHROPIC_API_KEY) and Server startup failed!:

Output

╭────────────────────────────────────────────────────────────────────╮
│  Hankweave v0.11.0                                                 │
│  linux x64 • node v22.23.2                                         │
╰────────────────────────────────────────────────────────────────────╯

Created new execution directory: ~/.hankweave-executions/<exec-id>
New execution: <exec-id>
  Source → data
  Exec   → ~/.hankweave-executions/<exec-id>
  SDKs   → Claude node_modules ✓

[<ts>] [ERROR] Self-test completed: FAILED
[<ts>] [ERROR] Self-test FAILED: Some checks failed
[<ts>] [ERROR]   - authentication: ✗ No authentication found (set ANTHROPIC_API_KEY)
[ERROR] Server startup failed!
Error message: Self-test failed for 1 model(s):
  - Claude Haiku 4.5 (latest) (anthropic/claude-haiku-4-5): Some checks failed
      • authentication: No authentication found (set ANTHROPIC_API_KEY)
Stack trace:
Error: Self-test failed for 1 model(s):
  - Claude Haiku 4.5 (latest) (anthropic/claude-haiku-4-5): Some checks failed
      • authentication: No authentication found (set ANTHROPIC_API_KEY)
    at c$ (file://<workspace>/build/runtime/0.11.0/node_modules/hankweave/dist/index.js:337:5382)
    at async N1t (file://<workspace>/build/runtime/0.11.0/node_modules/hankweave/dist/index.js:873:567)
    at async file://<workspace>/build/runtime/0.11.0/node_modules/hankweave/dist/index.js:874:658

exit=1

Check the completion evidence#

After a successful run, inspect the normalized event capture. It records RunStarted, CodonStarted, the codon transitions preparing → starting → initializing → running, cost and assistant-message updates, CodonFinalCostSet, a completed CheckpointCreated, the codon transition running → completed with exit code 0, one successful codon.completed, and RunCompleted. The initializing transition records the agent log path.

Check-it: the capture contains CheckpointCreated, one successful codon.completed, and RunCompleted:

Output
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "RunStarted", "runId": "<id>", "transition": {"type": "RunStarted", "data": {"runId": "<id>", "runFolder": "~/.hankweave-executions/<exec-id>/.hankweave/runs/<id>", "gitBranch": "run-<id>", "startingConditions": {"type": "fresh"}, "serverPid": 2862}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonStarted", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonStarted", "data": {"runId": "<id>", "codonId": "summarize-notes"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonTransitioned", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonTransitioned", "data": {"runId": "<id>", "codonId": "summarize-notes", "from": "preparing", "to": "starting", "metadata": {}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonTransitioned", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonTransitioned", "data": {"runId": "<id>", "codonId": "summarize-notes", "from": "starting", "to": "initializing", "metadata": {"claudePid": 957441, "claudeLogPath": ".hankweave/runs/<id>/summarize-notes-claude.log"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonTransitioned", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonTransitioned", "data": {"runId": "<id>", "codonId": "summarize-notes", "from": "initializing", "to": "running", "metadata": {"claudeSessionId": "cc67c965-b0af-4cef-8242-550f56ef618e"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CostsIncremented", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CostsIncremented", "data": {"runId": "<id>", "codonId": "summarize-notes", "costDelta":"<n>", "tokensDelta": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "AssistantMessageCountUpdated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "AssistantMessageCountUpdated", "data": {"runId": "<id>", "codonId": "summarize-notes", "newCount":"<n>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonFinalCostSet", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonFinalCostSet", "data": {"runId": "<id>", "codonId": "summarize-notes", "finalCost":"<n>", "finalTokens": {"inputTokens":"<n>", "outputTokens":"<n>", "cacheCreationTokens":"<n>", "cacheReadTokens":"<n>"}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CheckpointCreated", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CheckpointCreated", "data": {"runId": "<id>", "codonId": "summarize-notes", "checkpointType": "completed", "sha": "<sha>", "branch": "run-<id>"}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "CodonTransitioned", "runId": "<id>", "codonId": "summarize-notes", "transition": {"type": "CodonTransitioned", "data": {"runId": "<id>", "codonId": "summarize-notes", "from": "running", "to": "completed", "metadata": {"exitCode": 0, "resultMessageReceived": true, "checkpointSha": "<sha>", "contextExceeded": false, "extensionCount": 0}}}, "resultingState": {"currentRunId": "<id>", "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "codon.completed", "data": {"codonId": "summarize-notes", "success": true, "cost":"<n>", "duration":"<n>", "exitStatus": {"type": "success"}}}
{"id": "<id>", "timestamp": "<ts>", "type": "state.transition", "data": {"transitionType": "RunCompleted", "runId": "<id>", "transition": {"type": "RunCompleted", "data": {"runId": "<id>"}}, "resultingState": {"currentRunId": null, "runCount": 1, "totalCost":"<n>", "currentRunCost":"<n>"}}}

Recognize a good run's evidence#

Each capture above answers a different question about the run, so read them as a set. Validation ends with ✓ Configuration is valid!, a one-codon structure box, the GOOD TO RUN! counts, a Run it: line, and an environment report. Its execution path is a synthetic validation path, not a real directory. The captured report is labeled From System (HANKWEAVE_ prefixed) and shows that scope rather than the whole machine environment; its captured values include a CAPTURE_VERSION entry and TELEMETRY: 0.

The run capture supplies the banner box, the New execution block with Source → data, the managed execution path, and the Hankweave Server Started box. The banner's second line (linux x64 • node v22.23.2 in the capture) shows the platform and runtime of the machine that ran the command, so yours will differ. --headless removes the terminal interface, but the runtime still starts this local server and reports its WebSocket (the local client connection); it shuts down after all codons complete. The event capture shows the completed checkpoint, successful codon completion, and RunCompleted. Its initializing metadata gives the agent log path as .hankweave/runs/<id>/summarize-notes-claude.log.

Finally, compare your summary with this captured codon output:

MARKDOWN
# Meeting Summary

The team decided to go with the Calloway quote for the packaging line refit.

- **Decision:** Go with the Calloway quote (lower total, includes installation)
- **Owner:** Devon owns the purchase order and vendor follow-up
- **Deadline:** PO issued by Friday the 12th

Model output is stochastic, so another run can differ in wording. Treat the contract as the shape – one sentence and three bullets taken from the notes – not identical output bytes.

Measure the run cost#

The full run cost measured $0.02690865 on 2026-09-27 against the published hankweave@0.11.0 artifact with the haiku model. Treat this as a dated capture, not an estimate. Provider health checks and sentinel calls are tracked separately from the codon cost.

The hank declares no budget block, but a run's global cost and time ceilings are still resolved from outside it: budget.maxDollars and maxTimeSeconds in hankweave.json and a hank's overrides.budget set the same global ceilings (the runtime config and hank overrides combine as the lower of the two), and the operator's --max-cost <dollars> and --max-time <seconds> override that ceiling as the highest priority. Both flags are parsed and listed in the shipped --help. See reference/cli for the flag reference.

How the example captures were recorded#

The published transcripts come from this fixture directory against the pinned runtime: the validation and headless commands above are rerun, then expected/ is recaptured. The fixture corpus records a passed recapture transaction for this minimal fixture (its capture attempt is logged in the corpus's recapture record) and a verification step. Its normalizer masks changing execution values. The maintenance scripts are not shipped in the fixture corpus; you do not need them to run the example.

The maintenance capture transaction reports a successful live minimal run, an explicit no-key refusal, and preserved expected bytes.

Adapt one piece at a time#

To make this example your own, change either prompts/summarize.md or data/notes.txt, then run with --start-new. Changing the data directory changes the data hash; use operate/runbook for the distinction between resuming and starting fresh.

For a Gemini version, swap the single model field to a spelling such as pi/google/gemini-2.5-flash and use GEMINI_API_KEY. Generate it from this same fixture rather than hand-forking it; start/quickstart owns that provider variant.

When this shape is no longer enough, add a codon budget, a second codon, or a sentinel. Follow concepts/budgets, concepts/codons, or concepts/sentinels for those mechanics. For the full-sized version of this shape, continue to quote-template-unification.