Sentinel configuration reference

A sentinel observes a running codon from the outside. While a codon (a sealed unit of agent work) executes, the sentinel watches the event stream, batches matching events into language-model calls, and writes a report. This page is the lookup reference for the JSON configuration that controls all of that: which events to match, when to call the model, what the prompt sees, and where the output goes.

The configuration lives in a hank directory, the folder containing the hank's JSON configuration, hank.json, including its meta, overrides, and hank array. Sentinel configurations are attached to individual codons from that file, either as file references or inline objects; the attachment mechanics are at the end of this page, after the field reference.

The sections follow the order you will usually need them: the top-level fields first, then triggers and execution strategies, then prompts, output, error handling, and finally the codon wrapper and the list of events that can actually reach a sentinel. Each table is generated from the published schema; the prose around it records the runtime behavior that a schema cannot express, including several checks that happen at load time or first call rather than at validation.

Schema fields and runtime checks#

Use the tables to look up types, required fields, and bounds from the published sentinel.schema.json (docs.lock hash 63b4609a…). The text below them covers runtime checks that JSON Schema cannot express. Version markers identify when a field changed.

Two publication details are worth knowing before you rely on the schema in an editor. The published schema description still links to https://hankweave.dev/reference/sentinel-config; use Sentinel configuration instead. And the runtime sentinel parser is strict and does not declare a $schema property, so a $schema key inside a sentinel configuration file is rejected at load. The published $schema row is editor metadata: associate the schema in the editor rather than placing $schema inside the configuration.

Define top-level fields#

A sentinel configuration is a strict object with 18 published properties. id, name, trigger, execution, and model are required, and the configuration must also define at least one of userPromptFile or userPromptText – a load-time check rejects any sentinel that defines neither, with the message "Each sentinel must have at least one of userPromptFile or userPromptText defined." Other keys are rejected.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
idstringyesminLength 1; pattern ^[a-z0-9-]+$
namestringyesminLength 1
descriptionstringno
triggerevent | sequenceyes
executionimmediate | debounce | count | timeWindowyes
systemPromptFilestring | array<string>noSystem prompt file(s), relative to this config file's directory (or the hank directory for inline configs), using '/' separators. Must stay inside the hank directory; absolute paths and symlinks are rejected.
systemPromptTextstringno
userPromptFilestring | array<string>noUser prompt file(s), relative to this config file's directory (or the hank directory for inline configs), using '/' separators. Must stay inside the hank directory; absolute paths and symlinks are rejected.
userPromptTextstringno
conversationalobjectno
errorHandlingobjectno
llmParamsobjectno
modelstringyesThe full model ID to use (e.g., "anthropic/claude-3-5-sonnet-20241022", "openai/gpt-4-turbo").
structuredOutputobjectno
joinStringstringnoString to join entries in text-based log file. Only valid for text output. Supports escape sequences: \n (newline), \t (tab), \r (carriage return), \ (backslash). Defaults to '\n---\n' for visual separation.
reportToWebsocketobjectno
outputobjectno
$schemastringnoJSON Schema URL for editor support

Most of these fields behave as the table suggests; id and model carry extra runtime rules. id uses lowercase letters, numbers, and hyphens, and duplicate IDs in one codon are rejected at load ("Duplicate sentinel ID '<id>' in codon <codon>").

The model field decides whether the sentinel can run at all, and the check depends on the form of the ID. The schema describes model as a full model ID. For a slash-containing model, a missing registry entry, an unconfigured provider, or a failed or pending provider health check causes the sentinel to be skipped with an info-level Skipping sentinel <id>: … message; the codon proceeds. A slash-less short name such as haiku skips that load-time gate: the sentinel loads and fires, then always fails its first LLM call with llm-call-failed: No LLM provider available, because only slash-containing full model IDs receive a provider call.

The anchor hank's quality-observer sentinel runs with anthropic/claude-haiku-4-5 and writes the report below. It is an event-observer result: it summarizes the completion event it received – status, cost, duration, and the budget fields present in that event – and states plainly that artifact existence, row coverage, and correctness cannot be verified from that input. Use a rig or an artifact-reading codon for filesystem assertions; a sentinel is an event observer.

Output

---
# Codon Completion Summary

**Status:** Success

**Cost:** $<cost>

**Duration:** 117,863 ms (~1m 58s)

---

## Verification Limitations

Artifact existence, row coverage, and correctness **cannot be verified** from this input. No file-update events were provided. The absence of file-update events does not indicate missing files.

Model shortcuts and model-selection planes are documented in model resolution and authentication and models. AWS Bedrock covers sentinel credentials. The general path/ref contract belongs to hank.json; this page only states sentinel-specific reference bases.

Match events with triggers#

The required trigger field decides which events the sentinel cares about. It is a discriminated union with an event form (match individual events, optionally filtered by conditions) and a sequence form (match an ordered pattern across the event history).

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
eventobjecttype = event
↳ onarray<string>yesminItems 1
↳ conditionsarray<object | object | contains | matches | object>no
sequenceobjecttype = sequence
↳ interestFilterobjectyes
↳ ↳ onarray<string>yesminItems 1
↳ patternarray<object>yesminItems 1
↳ optionsobjectno
↳ ↳ consecutivebooleanno

Both forms are validated against the live runtime, not just the schema. Every named event type in on, in a sequence's interestFilter.on, and in a pattern step's type must be * or an exact name the runtime knows; anything else, including prefix globs such as codon.* and misspellings, fails at load with "Invalid event type". Some valid names still never fire: the sentinel.* events pass validation but are never routed (see Which events reach sentinels).

Condition paths use dot notation into event.data, such as exitStatus.type, and are checked against the named event type's data shape; that path check is skipped when on contains *. Declared sentinel-virtual fields such as content on file.updated are also accepted – the sentinel's view resolves them at trigger time, separately from the event's wire payload. Conditions in one array are AND-ed, and a missing path evaluates to undefined and does not match. The eight operators are equals, notEquals, in, notIn, contains, matches, greaterThan, and lessThan; equals and notEquals take a string, number, boolean, or null; in and notIn take a non-empty array of strings or numbers; contains and matches take strings; and greaterThan and lessThan take numbers.

The sequence form adds state. A sequence keeps an in-memory history of interest-filtered events capped at 1000 per sentinel (the oldest is dropped) and tracks the last trigger position to avoid duplicate matches. options.consecutive defaults to true: consecutive matching must reach the exact history tail, while false matches pattern steps greedily with gaps allowed. Wildcards are accepted in the interest filter and pattern-step types. This history cap is separate from the template and queue caps below.

Batch events with execution strategies#

Matching an event does not call the model directly. The required execution field controls when matched events become LLM calls, so a sentinel can react to every match or accumulate matches into batches.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
immediateobjectstrategy = immediate
debounceobjectstrategy = debounce
↳ millisecondsintegeryes> 0; max 300000
countobjectstrategy = count
↳ thresholdintegeryes> 0; max 1000
timeWindowobjectstrategy = timeWindow
↳ millisecondsintegeryes> 0; max 3600000

immediate queues one batch per match. debounce accumulates matches until a quiet-period timer fires. count queues at its threshold. timeWindow runs a fixed window timer but queues a trigger only when that window buffered events – it is not an unconditional periodic LLM job.

Batching interacts with a bounded queue. Each sentinel processes its trigger queue serially, so new matches wait while an item is processed. The trigger queue has a hard cap of 100: when it already holds 100, the oldest is dropped before the new trigger is added. The queued-event guard is a 10,000 threshold checked before enqueue that drops only the oldest existing trigger, so the total can exceed 10,000 after a new batch lands. Once the queue passes 70% of the trigger cap, the runtime logs [Sentinel:<id>] Queue backpressure: <n>/100 triggers queued at info level. At codon completion, pending accumulated events are flushed and the queue drains before the codon is considered done. Sentinel concepts covers the observer lifecycle and drain semantics.

Prompts and template context#

Once a batch fires, the prompts decide what the model actually sees. A sentinel must define at least one of userPromptFile and userPromptText. Supplying both is accepted: file content is assembled first and text is appended, joined with a blank line, so these fields are not an XOR pair. Prompt-file fields accept a string or an array of strings; array entries concatenate in order. [since 0.10.0] File-based sentinel configs resolve their own refs (systemPromptFile, userPromptFile, structuredOutput.schemaFile) from the config file's directory, while inline configs resolve from the hank directory – previously all refs resolved from the first config's directory. Escape and symlink checks on those refs run when the codon starts and again on cache hits, because sentinels load at codon start.

Prompts are Eta templates with caching enabled and autoEscape: false, so rendered text is raw. The available context is:

Scroll to explore the table →
Context propertyMeaning
it.eventsTriggering events, capped at the first 1000 events
it.codon.idCurrent codon ID
it.codon.nameThe sentinel configuration's name
it.codon.descriptionThe sentinel configuration's optional description
it.codon.startTimeTime the sentinel run started
it.world.currentTimeTrigger queue time

Three properties of this context trip people up. it.codon.name and it.codon.description come from the sentinel configuration's own name and optional description, not from the observed codon; only it.codon.id is the observed codon's runtime ID. it.codon.startTime is supplied by the runtime when it loads sentinels for the codon, so it is the time that sentinel run started. And it.world.currentTime is the trigger's queue time (trigger.queuedAt), not template-execution time, keeping timestamps consistent when LLM execution is delayed by the queue. Templates execute synchronously and cannot be interrupted, so keep them lightweight and avoid complex computation or infinite loops. it.timestamp and it.context are not part of the context.

Most importantly, event data reaches the model only when the template interpolates it; a prompt without <%= it.events %> receives no event data. Events passed to a template are capped at the first 1000 to bound template cost.

The sweep fixture demonstrates both cases across two separate captured runs of the same two-codon hank, both taken against hankweave@0.10.0 on 2026-09-04. In the first run the prompt had no event interpolation, and the model replied that no event data had been provided. In the later run the prompt carried <%= it.events %>, and the model wrote a report from the one codon.completed event it received. The captured report is shown first; the event stream from the first run follows. Neither capture is evidence that the observer inspected every earlier codon or the whole filesystem.

Templated sweep report (later, templated run; hankweave@0.10.0, 2026-09-04).

Output

---
# Sweep Report

1. Received 1 event of type `codon.completed`.

2. Codon `step-two` completed successfully with a cost of $<cost> and duration of <ms>ms.

3. Sweep: clean

Captured sentinel event stream (first, untemplated run; hankweave@0.10.0, 2026-09-04).

Output
{"id":"<id>","timestamp":"<ts>","type":"codon.completed","data":{"codonId":"step-one","success":true,"cost":"<n>","duration":"<n>","exitStatus":{"type":"success"}}}
{"id":"<id>","timestamp":"<ts>","type":"codon.completed","data":{"codonId":"step-two","success":true,"cost":"<n>","duration":"<n>","exitStatus":{"type":"success"}}}
{"id":"<id>","timestamp":"<ts>","type":"sentinel.triggered","data":{"sentinelId":"sweep-observer","codonId":"step-two","triggerNumber":1,"strategy":"immediate","eventCount":1,"queueSize":0}}
{"id":"<id>","timestamp":"<ts>","type":"sentinel.output","data":{"sentinelId":"sweep-observer","codonId":"step-two","triggerNumber":1,"outputType":"text","content":"I don't see any events in your message. You've described a scenario involving codon completion events and asked me to write a sweep report, but no actual event data has been provided for me to analyze.\n\nTo write the three-line report you've requested, I would need to see the events themselves. Could you please share:\n\n- The event log or event stream from the two-codon run\n- Details about each `codon.completed` event (success/failure status, costs if applicable)\n\nOnce you provide the events, I can generate:\n1. Count of codon.completed events and their success status\n2. Total cost calculation\n3. Sweep status assessment\n\nPlease paste the events and I'll write your report.","cost":"<n>","tokens":{"input":102,"output":158},"eventCount":1}}

Hank JSON owns the general reference-path contract. Environment variables lists sentinel-scoped provider keys.

Bound conversational history#

By default each trigger is an independent LLM call. Conversational mode instead retains history across triggers, and because that history grows, it requires a trimming strategy.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
conversationalobjectno

trimmingStrategy is required: maxTurns is an integer greater than 0 and at most 100, while maxTokens is an integer greater than 0 and at most 100000. continueOnError defaults to false. A conversational sentinel also requires systemPromptFile or systemPromptText. maxTurns prunes the oldest complete user-and-assistant turns. maxTokens prunes the oldest messages using exact token counts when available and a text.length / 4 estimate for older messages; that mode may break turn pairing.

History persists through atomic writes at .hankweave/sentinels/history/{sentinel-id}-codon-{codon-id}.json, so state resumes after a server restart. The consecutive-failure threshold does not apply to conversational sentinels; category-specific fatal policy still applies, so a corruption error unloads a conversational sentinel unless continueOnError is true.

Constrain structured output#

Structured output selects object, array, or enum output and constrains which schema material may accompany it.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
structuredOutputobjectno

The modes have complementary requirements. Enum mode requires a non-empty enumValues array and forbids schema fields. Object and array modes require exactly one of schemaStr and schemaFile and forbid enumValues. Schema code is evaluated with the Zod z object already in scope; neither inline code nor a schema file needs an import, and the result must be a Zod schema or loading fails.

Structured output also adds a model-capability requirement, checked late rather than at load. A structured-output sentinel whose full slash model's registry entry lacks tool-call capability loads and fires, then fails its first structured-output call with a fatal configuration error ("doesn't support structured output (no tool_call capability)"), which unloads it. The capability check sits inside the concrete-LLM-object-call closure, constructed at load but invoked only when a trigger fires and real providers exist with a full slash model ID. Slash-less short names follow the separate load-and-first-call behavior described under top-level fields. With structuredOutput, output.format is rejected ("output.format is ignored when structuredOutput is configured") and joinString is invalid.

Set LLM parameters#

llmParams bounds response-generation settings and merges defaults when fields are absent.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
llmParamsobjectno

temperature is a number from 0 through 2, maxOutputTokens is a positive integer at most 100000, and maxRetries is an integer from 0 through 5. Missing fields use temperature: 0, maxOutputTokens: 8192, and maxRetries: 2; temperature 0 is the deterministic default for consistent sentinel output. Authentication and models covers provider access and model-selection planes.

Error handling and unloading#

Error handling counts consecutive failures and applies category-specific unloading rules.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
errorHandlingobjectno

maxConsecutiveFailures is an integer of at least 1 and defaults to 3. The threshold is checked only for regular errors of a non-conversational immediate sentinel: when consecutive failures reach it, the sentinel unloads, and one success resets the counter. A debounce, count, or timeWindow sentinel logs a failed call and continues without unloading, so repeated failed LLM calls never unload it. Fatal policy is category-based: template and configuration errors always unload (they recur every execution); resource errors never unload (transient); an error explicitly requesting unload wins first. A corruption error unloads a conversational sentinel unless continueOnError is true; for a non-conversational sentinel it is kept and never compared with the threshold, so repeated corruption errors never unload it either.

One schema field is misleading here. unloadOnFatalError is present in the schema with a stated default of true, but the runtime never reads it. It is metadata, not an effective switch. See errors and exit codes for the failure taxonomy and exit behavior.

Write output files#

A sentinel writes text or JSONL output. Structured output is always NDJSON, one JSON object per line, regardless of output.format.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
outputobjectno
joinStringstringnoString to join entries in text-based log file. Only valid for text output. Supports escape sequences: \n (newline), \t (tab), \r (carriage return), \ (backslash). Defaults to '\n---\n' for visual separation.

The two formats differ in how entries are stored. Text output (the default) appends joinString + text to an append-only log. Auto-named text logs use a .md extension; a configured output.file keeps its configured name. JSONL wraps each text output as one JSON line containing text, timestamp, and sentinelId, with a .jsonl extension. joinString defaults to \n---\n, supports \n, \t, \r, and \\ escapes, and is only valid for text output.

Where the file lands depends on a priority chain. The log-file priority chain is codon-level settings.outputPaths.logFile, then sentinel-level output.file, then the automatic path .hankweave/sentinels/outputs/{id}/{id}-{codonId}-{timestamp}.{md|jsonl|ndjson} (the extension map contains only jsonl; everything else, including text, falls to md). lastValueFile follows the same chain. A bare filename with no / resolves under the managed execution path .hankweave/sentinels/outputs/{id}/; a path containing /, including ./x.md, resolves relative to agentRoot. A resolved path that escapes both the execution directory and agentRoot is rejected as a fatal configuration error.

lastValueFile is atomically replaced for every output (write-temp-then-rename); the log is append-only. Initialization failures while creating the directory or file, or checking writability, are fatal configuration errors. Write failures after initialization are logged and do not fail the sentinel. The sentinel-sweep capture uses the bare name sweep-report.log, so its managed location is <exec>/.hankweave/sentinels/outputs/<sentinel-id>/sweep-report.log, not agentRoot.

Control WebSocket reporting#

reportToWebsocket.errors and .outputs gate sentinel.error and sentinel.output emission. lifecycle and triggers are declared client-visibility keys for events that are always emitted into the event stream.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
reportToWebsocketobjectno

errors and outputs emit unless their values are explicitly false, suppressing sentinel.error and sentinel.output respectively; the output file is written independently of the outputs switch. sentinel.loaded, sentinel.unloaded, and sentinel.triggered are emitted into the event stream whenever their lifecycle operation or trigger occurs and the event callback is available. lifecycle and triggers are accepted schema keys, but this runtime has no corresponding suppression gates, so neither hides an event from the TUI or any client.

Events owns event shapes; WebSocket protocol covers client delivery.

Attach sentinels to codons#

With the configuration defined, the last step is attaching it. A codon attaches sentinels through a per-codon wrapper containing sentinelConfig and optional settings.

Scroll to explore the table →
fieldtypedefaultrequiredconstraintsdescription
sentinelsarray<object>noSentinels to run during this codon. Sentinels are parallel observation agents that process the event stream. Each entry is a wrapper object with sentinelConfig (portable sentinel configuration, file or inline) and setti…

sentinelConfig is either a hank-directory-anchored file path or an inline configuration object. Inline references receive escape checks at the wrapper level, because their base (the hank directory) is knowable; file-based references receive them at the file's own directory. The wrapper's settings carries failCodonIfNotLoaded (default false), outputPaths.logFile, outputPaths.lastValueFile, and reportToWebsocket.

Sentinels are wired per codon as "sentinels": [{"sentinelConfig": "<path>"}]; a root-level sentinels array fails validation with Unrecognized field(s) at hank root: sentinels. failCodonIfNotLoaded: true makes only sentinel config-loading failures fatal for the codon: a missing, unparseable, or schema-invalid config file or ref, or an exception thrown out of the sentinel manager (fatal then only when every loaded config sets the flag). A sentinel skipped for an unknown model, an unconfigured or unhealthy provider, or a per-sentinel instantiation error is only logged, and the codon runs without it even with the flag set. On load, the runtime emits sentinel.loaded with sentinelId, codonId, model, triggerType, executionStrategy, conversational, source, and optional sourcePath.

Which events reach sentinels#

Only server-state and agentic-backbone event categories are routed to sentinels. Connection-state events are client-specific, and sentinel events are not routed, preventing self-observation loops. The table below is the authoritative list of what a trigger can actually match.

Scroll to explore the table →
idcategoryjournaledsentinelRoutedpayloadFieldsreceipts
archive.completedserver-statetruetruecodonId, archivedPathsschemas/event-schemas.ts:780, schemas/event-schemas.ts:1091, schemas/event-schemas.ts:1371, hankweave-runtime.ts:5784
archive.partialserver-statetruetruecodonId, archivedPaths, failedPathsschemas/event-schemas.ts:785, schemas/event-schemas.ts:1092, schemas/event-schemas.ts:1372, hankweave-runtime.ts:5769
assistant.actionagentic-backbonetruetruecodonId, action, content, toolName?, toolInput?, toolUseId?schemas/event-schemas.ts:684, schemas/event-schemas.ts:1100, schemas/event-schemas.ts:1351, hankweave-runtime.ts:3210
budget.summaryserver-statetruetrueceiling, allocation, rows, totalsschemas/event-schemas.ts:836, schemas/event-schemas.ts:1093, schemas/event-schemas.ts:1381, hankweave-runtime.ts:7086
checkpoint.listserver-statetruetruerunId, checkpoints, currentBranchschemas/event-schemas.ts:749, schemas/event-schemas.ts:1082, schemas/event-schemas.ts:1364, hankweave-runtime.ts:4612
codon.completedserver-statetruetruecodonId, success, cost, duration, exitStatus, failureReason?, failureIgnored?, budgetExceeded?schemas/event-schemas.ts:672, schemas/event-schemas.ts:1075, schemas/event-schemas.ts:1349, hankweave-runtime.ts:3792
codon.extendedserver-statetruetruecodonId, codonName, extensionNumber, exhaustWithPrompt, cumulativeTokens, cumulativeCostschemas/event-schemas.ts:677, schemas/event-schemas.ts:1076, schemas/event-schemas.ts:1350, hankweave-runtime.ts:3378
codon.startedserver-statetruetruecodonId, codonName, codonDescription?, sessionId, previousSessionId?, startTime, promptMetadata?schemas/event-schemas.ts:667, schemas/event-schemas.ts:1074, schemas/event-schemas.ts:1348, hankweave-runtime.ts:385
errorserver-statetruetruemessage, codon?, fatal, severity?, context?, code?schemas/event-schemas.ts:729, schemas/event-schemas.ts:1081, schemas/event-schemas.ts:1360, hankweave-runtime.ts:1214
file.updatedagentic-backbonetruetruepath, filename, action, sha256, bytes, sourceschemas/event-schemas.ts:699, schemas/event-schemas.ts:1102, schemas/event-schemas.ts:1354, hankweave-runtime.ts:3027
filetree.updatedagentic-backbonetruetruetreeschemas/event-schemas.ts:704, schemas/event-schemas.ts:1103, schemas/event-schemas.ts:1355, hankweave-runtime.ts:3039
infoserver-statetruetruemessageschemas/event-schemas.ts:739, schemas/event-schemas.ts:1080, schemas/event-schemas.ts:1362, hankweave-runtime.ts:2102
loop.iteration.completedserver-statetruetrueloopId, iteration, durationMs, costUsd, tokensUsed, isFinal, terminationReason?schemas/event-schemas.ts:724, schemas/event-schemas.ts:1090, schemas/event-schemas.ts:1359, hankweave-runtime.ts:6415
rig.outputagentic-backbonetruetruecodonId, stream, line, commandIndexschemas/event-schemas.ts:719, schemas/event-schemas.ts:1106, schemas/event-schemas.ts:1358, hankweave-runtime.ts:6489
rig.setup.completedagentic-backbonetruetruecodonId, rigType, commandCount, durationMs, createdCheckpointschemas/event-schemas.ts:709, schemas/event-schemas.ts:1104, schemas/event-schemas.ts:1356, hankweave-runtime.ts:2604
rig.setup.failedagentic-backbonetruetruecodonId, failureType, exitCode?, commandIndex?, ignoredschemas/event-schemas.ts:714, schemas/event-schemas.ts:1105, schemas/event-schemas.ts:1357, hankweave-runtime.ts:2213
rollback.archiveRestoreserver-statetruetruecodonId, restoredPaths, failedPaths?, statusschemas/event-schemas.ts:790, schemas/event-schemas.ts:1088, schemas/event-schemas.ts:1370, hankweave-runtime.ts:5925
rollback.codonCheckpointserver-statetruetruecodonId, codonName, checkpoint, checkpointType, messageschemas/event-schemas.ts:759, schemas/event-schemas.ts:1085, schemas/event-schemas.ts:1366, hankweave-runtime.ts:5048
rollback.completedserver-statetruetruefromRun, toRun, checkpoint, codonId, codonName, checkpointType, autoRestartschemas/event-schemas.ts:774, schemas/event-schemas.ts:1086, schemas/event-schemas.ts:1369, hankweave-runtime.ts:5075
rollback.progressserver-statetruetruecurrentStep, totalSteps, messageschemas/event-schemas.ts:769, schemas/event-schemas.ts:1084, schemas/event-schemas.ts:1368, hankweave-runtime.ts:5469
rollback.rigCleanupserver-statetruetruecodonId, codonName, directories, status, successfulCleanups?, failedCleanups?, error?schemas/event-schemas.ts:764, schemas/event-schemas.ts:1087, schemas/event-schemas.ts:1367, hankweave-runtime.ts:5998
rollback.startedserver-statetruetruefromRun, fromCodon, toCodon, toCheckpoint, checkpointType, codonsToProcessschemas/event-schemas.ts:754, schemas/event-schemas.ts:1083, schemas/event-schemas.ts:1365, hankweave-runtime.ts:5010
sentinel.errorsentineltruefalsesentinelId, codonId, errorType, message, retriable, consecutiveFailureCountschemas/event-schemas.ts:821, schemas/event-schemas.ts:1115, schemas/event-schemas.ts:1378, sentinels/sentinel.ts:576
sentinel.loadedsentineltruefalsesentinelId, codonId, model, triggerType, executionStrategy, conversational, source, sourcePath?schemas/event-schemas.ts:811, schemas/event-schemas.ts:1113, schemas/event-schemas.ts:1376, hankweave-runtime.ts:6201
sentinel.outputsentineltruefalsesentinelId, codonId, triggerNumber, outputType, content, cost, tokens, eventCountschemas/event-schemas.ts:826, schemas/event-schemas.ts:1116, schemas/event-schemas.ts:1379, sentinels/sentinel.ts:538
sentinel.triggeredsentineltruefalsesentinelId, codonId, triggerNumber, strategy, eventCount, queueSizeschemas/event-schemas.ts:831, schemas/event-schemas.ts:1117, schemas/event-schemas.ts:1380, sentinels/sentinel.ts:392
sentinel.unloadedsentineltruefalsesentinelId, codonId, reason, errorType?, finalCost, llmCallCountschemas/event-schemas.ts:816, schemas/event-schemas.ts:1114, schemas/event-schemas.ts:1377, sentinels/sentinel-manager.ts:908
server.idleserver-statetruetruereason, messageschemas/event-schemas.ts:744, schemas/event-schemas.ts:1078, schemas/event-schemas.ts:1363, hankweave-runtime.ts:1180
state.snapshotserver-statetruetruecurrentCodon?, completedCodons, fileTree, totalCost, totalTime, recentFileAccess?, isRollingBackschemas/event-schemas.ts:662, schemas/event-schemas.ts:1077, schemas/event-schemas.ts:1347, hankweave-runtime.ts:1676
state.transitionserver-statetruetruetransitionType, runId?, codonId?, transition, resultingStateschemas/event-schemas.ts:805, schemas/event-schemas.ts:1089, schemas/event-schemas.ts:1375, hankweave-runtime.ts:463
token.usageserver-statetruetruecodonId, inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens, totalCost, modelId?, modelUsage?schemas/event-schemas.ts:689, schemas/event-schemas.ts:1079, schemas/event-schemas.ts:1352, hankweave-runtime.ts:3075
tool.resultagentic-backbonetruetruecodonId, toolUseId, toolName, result, truncated, originalLength, executionTimeMs, isErrorschemas/event-schemas.ts:694, schemas/event-schemas.ts:1101, schemas/event-schemas.ts:1353, hankweave-runtime.ts:3338

The generated selection contains 27 routed event types, including codon.extended, rig.setup.completed, rig.setup.failed, rig.output, loop.iteration.completed, archive.completed, archive.partial, rollback.archiveRestore, and budget.summary. The assistant.action row lists toolName? and toolUseId? as optional, but the runtime refines that: when action is "tool_use", both are required (toolUseId a non-empty string); thinking and message actions may omit both. toolUseId is the key that joins the action to tool.result.toolUseId and file.updated.source.toolUseId. Events owns the complete payload contract.

Sentinel event names pass trigger schema validation but never reach a sentinel; a trigger on sentinel.output loads successfully and never fires. Sentinel concepts owns the observer semantics.