HDR

Concepts

Writing decisions

The decision format, the checks HDR can run, and how much one decision should cover.

The decision format

Store one decision per file in .hdr/decisions/*.json. The file holds the decision’s history; a new decision has a single entry.

{
  "schema_version": 2,
  "id": "HDR-004",
  "history": [
    {
      "date": "2026-10-21",
      "title": "Expense belongs to the event budget",
      "rationale": "Event travel must be charged to the event, not team budgets.",
      "check": {
        "kind": "json_equals",
        "path": "expense.json",
        "pointer": "/budget",
        "expected": "event"
      }
    }
  ]
}
FieldRequiredMeaning
schema_versionyesThe integer 2.
idyesHDR- followed by at least three digits. Unique across the repository.
historyyesEntries, oldest first. The last one is what applies now.

Each history entry is either a decision or a retirement:

Entry fieldMeaning
titleThe decision, stated as an outcome.
rationaleWhy the decision was made, or why it was retired.
checkHow the decision is verified. See below.
status"retired" in a retirement entry, which has only status, rationale and an optional date.
dateOptional, YYYY-MM-DD.

How entries are added, and who may add them, is covered in Decision history.

Validation is strict. Unknown fields, unknown check kinds, duplicate IDs, duplicate JSON keys, and paths that escape the repository (including through symlinks) are all errors, in every entry of a history. An invalid decision file stops every check, so the hook fails closed rather than silently skipping it.

Check kinds

Paths are relative to the repository root, not the working directory.

KindFieldsPasses when
json_equalspath, pointer, expectedThe JSON value at the RFC 6901 pointer equals expected. Booleans never equal numbers.
file_sha256path, sha256The file’s SHA-256 matches the pinned lowercase hex digest.
path_existspathThe path exists.
hdr_hooksharnesses, eventsThe HDR checker is registered for every listed event in every listed harness.
append_onlypathFiles under path are added but never edited, replaced or removed.
tool_deniedtoolsThe tool about to run is not in the list. Names are each harness’s own tool names.
claude_hookevents or eventLegacy form of hdr_hooks for Claude only. Still accepted so old records keep working.

hdr_hooks and tool_denied and append_only are evaluated against live hook events, so hdr check cannot show their outcome on its own. hdr explain labels them HOOK-TIME.

There is no way to run an arbitrary command, ask a model, or express an exception or override. That is deliberate: every outcome is reproducible from the files on disk.

Decision grain

One file holds one decision: the outcome a human would state and defend in one sentence, with one rationale. The check is how that decision is verified, not the unit of decision.

  • Group checks that share a rationale. “Every lifecycle event reaches the checker in every harness we use” is one decision, so it is one hdr_hooks decision listing all harnesses and events, not one decision per event.
  • Split when the parts could change independently. If you can imagine revising or retiring one part while keeping the other, they are two decisions.
  • Title the outcome, not the mechanism. “Expense belongs to the event budget”, not “/budget equals event”.
  • Prefer a coarser decision now. Histories are append-only. A decision that is too fine can only be tidied up by retiring it, and it stays in the repository for good.

Agents are given a short version of this guidance when a session starts, because they are allowed to add new decisions too.