Hook chains in Verboo Code: automatic recovery when a task fails
Back to the blog
Articledev toolshow toverboo codetutorialautomação

Hook chains in Verboo Code: automatic recovery when a task fails

MafraSeptember 11, 20265 min read

A task fails in the middle of the night, with nobody watching the terminal: today, in Verboo Code, it just stays failed until someone comes back and runs it again. Hook chains solve exactly that. It is an event-driven recovery layer that, when a failure hook fires, evaluates declarative rules and executes an action, such as recovering the task with a fresh agent or notifying the team. It is a real feature, off by default. This article shows the exact command to turn it on and the full configuration file, verified straight from the verbeux-ai/code source code (src/utils/hookChains.ts, docs/hook-chains.md).

What triggers a hook chain?

Today, two events. PostToolUseFailure, when a tool fails during execution, and TaskCompleted with outcome: "failed", when a task ends without success. Each rule declares which event and which outcome it reacts to; the outcome accepts a single value in outcome or a list in outcomes (never both at once, the schema itself rejects that).

How do I turn on hook chains in Verboo Code?

Two things. First, the environment variable that turns on the whole feature, because it ships off by default even if a config file exists:

export CLAUDE_CODE_ENABLE_HOOK_CHAINS=1
# also accepts: true, yes, on

Second, the rules file. The default path is .verboo/hook-chains.json, at the project root. To use a different path:

export CLAUDE_CODE_HOOK_CHAINS_CONFIG_PATH=/path/to/hook-chains.json

Without the activation variable, Verboo Code does not even read the file: the enabled check happens before any attempt to load the configuration from disk.

What is the format of the configuration file?

An object with four general control fields and a list of rules. It can sit directly at the root of the JSON or be wrapped in a hookChains key, both forms are accepted.

FieldTypeDefault
versionnumber1
enabledbooleantrue
maxChainDepthinteger, 1 to 102
defaultCooldownMsinteger30000
defaultDedupWindowMsinteger30000
ruleslist[]

A minimal example that recovers a failed task by running a fresh agent:

{
  "version": 1,
  "enabled": true,
  "maxChainDepth": 2,
  "defaultCooldownMs": 30000,
  "defaultDedupWindowMs": 30000,
  "rules": [
    {
      "id": "retry-task-via-fallback",
      "trigger": { "event": "TaskCompleted", "outcome": "failed" },
      "cooldownMs": 60000,
      "actions": [
        {
          "type": "spawn_fallback_agent",
          "id": "spawn-retry-agent",
          "description": "Retry failed task with fallback agent",
          "promptTemplate": "A task failed. Recover it safely.\nTask=${TASK_SUBJECT}\nError=${ERROR}",
          "agentType": "general-purpose",
          "model": "sonnet"
        }
      ]
    }
  ]
}

How do I scope a rule to specific errors only?

With the condition block, inside the rule. It is optional, but without it the rule reacts to any occurrence of the declared event and outcome.

FieldWhat it does
toolNameslist of tool names the rule accepts (matches the event's tool_name/toolName)
taskStatuseslist of accepted task statuses
errorIncludescase-insensitive substring match against the error message
eventFieldEqualsexact equality by field path, for example "meta.source": "scheduler"
"condition": {
  "toolNames": ["Edit", "Write", "Bash"],
  "errorIncludes": ["timeout", "permission denied"]
}

Can I just notify the team, without running any agent?

Yes, with the notify_team action instead of spawn_fallback_agent. It reads the team configured in the project and writes the message without needing to spawn a new agent:

{
  "type": "notify_team",
  "id": "notify-ops",
  "recipients": ["*"],
  "summary": "Hook chain ${RULE_ID} fired",
  "messageTemplate": "Event=${EVENT_NAME} outcome=${OUTCOME}\nTask=${TASK_ID}\nError=${ERROR}"
}

If there is no team file configured, the action does not break the chain: it is skipped, with the reason logged. Both actions, and a third one (warm_remote_capacity, which tells the runtime to warm up remote execution capacity ahead of time), accept the same text placeholders:

PlaceholderFilled with
${EVENT_NAME}the event that triggered the rule
${OUTCOME}the event's outcome
${RULE_ID}the id of the rule that matched
${TASK_SUBJECT}the task's subject
${TASK_DESCRIPTION}the task's description
${TASK_ID}the task's identifier
${ERROR}the error message
${PAYLOAD_JSON}the full event payload, as JSON

warm_remote_capacity is safe to leave configured even without using remote execution: it is skipped without error whenever the project's policy blocks remote sessions, or when there is no active remote session to warm up.

What stops an endless recovery loop?

Three guards, and they stack. maxChainDepth cuts the dispatch when the current chain depth has already reached the limit (default 2, cap 10): a hook chain action that triggers another event cannot spiral into an infinite chain. cooldownMs stops the same rule from firing again before the configured time passes (30 seconds by default, adjustable per rule). dedupWindowMs suppresses the repetition of the same event and action combination within the window, so a burst of the same error does not fire the same agent several times in a row. On top of that, safe-by-default behavior: if the current signal has already been aborted, the action is skipped instead of trying to run against a state that no longer exists.

Flowchart of a hook chain in Verboo Code: failure event, rule matching via condition, action dispatched, and the safety guard that prevents repeated firing
From event to action, with the guard that prevents a loop. Verified in verbeux-ai/code, src/utils/hookChains.ts, 2026-09-11.

Every time a rule fires spawn_fallback_agent, a fresh agent runs from scratch to try to recover the task. On a night when the same error repeats a few times before cooldownMs quiets the rule down, that means several recovery agents in a row. In Verboo Code, each one of them runs with unlimited tokens, so the fifth recovery attempt costs the same as the first.

Enjoyed this article?
Share knowledge with your network.
// Read also

Related articles