Skip to content

Sentinel Blocks

Sentinel blocks are the bridge between human-written governance documents and the directives Claude reads. Every ADR, Invariant Record, and guideline can contain one. The compile pipeline writes them; Claude reads them; you can extend them.

What a sentinel block looks like

markdown
[edikt:directives:start]: #
source_hash: "a3b2c1d0..."
directives_hash: "9f8e7d6c..."
compiler_version: "0.3.0"
paths:
  - "**/*.go"
  - "**/repository/**"
scope:
  - implementation
  - review
directives:
  - "Every SQL query MUST include `tenant_id`. No exceptions. (ref: INV-012)"
  - "NEVER write raw SQL outside `internal/repository/`. (ref: INV-012)"
reminders:
  - "Before writing SQL → MUST include `tenant_id` in WHERE clause (ref: INV-012)"
verification:
  - "[ ] Every SQL query references `tenant_id` (ref: INV-012)"
manual_directives:
  - "All new tables MUST include a `created_at` timestamp column (ref: team convention)"
suppressed_directives: []
[edikt:directives:end]: #

The block uses Markdown link reference definitions ([edikt:directives:start]: #) as the sentinel markers — chosen in ADR-006 because they are valid Markdown that renders as nothing (invisible to readers, parseable by tools), unlike HTML comments which Claude Code v2.1.72+ hides from the model.

The three-list schema is defined in ADR-008.

The five lists

Compile-owned (read-only for users)

ListWhat it contains
directives:MUST/NEVER rules extracted from the source document
reminders:Pre-action interrupts: "Before X → check Y"
verification:Grep-verifiable checklist items

These are regenerated every time the source body changes. If you hand-edit directives:, compile detects it via hash comparison and runs an interactive interview to resolve.

User-owned (never touched by compile)

ListWhat it contains
manual_directives:Rules compile missed or couldn't infer. Always ship into governance.md.
suppressed_directives:Auto-generated rules you want to reject. Always filtered out by gov:compile.

These survive every recompilation. Compile never reads, modifies, or deletes them. See Extensibility for usage examples.

The three metadata fields

FieldPurpose
source_hashSHA-256 of the document body (excluding the sentinel block). Detects when the human content changes, triggering recompilation.
directives_hashSHA-256 of the directives: list. Detects when you hand-edit auto-generated directives (triggers the interview flow).
compiler_versionWhich edikt version wrote this block. Used to detect algorithm drift across upgrades.

Path and scope routing

FieldHow it's used
paths:Glob patterns. Claude Code auto-loads the governance topic file when editing matching files. Derived by compile from the document's domain or pinned by the author.
scope:Activity tags (planning, design, review, implementation). Used by the routing table in governance.md to match the current task. Invariants scope to all activities by default.

The merge formula

When /edikt:gov:compile assembles the final governance.md, it reads all lists from every source and merges:

effective_rules = (directives - suppressed_directives) ∪ manual_directives
  • Your manual_directives: always ship — compile can't override them
  • Your suppressed_directives: always filter — compile can't un-suppress them
  • The merge is exact string match — a suppression must match the directive text exactly

This is locked by ADR-008.

Hash-based caching

Compile doesn't call Claude when nothing changed:

StateConditionWhat happens
CleanBoth hashes match stored valuesSkip — no Claude call, no writes
Body changedsource_hash doesn't matchRegenerate directives from new body
Hand-editedsource_hash matches but directives_hash doesn'tInteractive interview to resolve
FreshNo sentinel block existsFirst-time generation
Forced--regenerate flag passedRegenerate regardless of hashes

The interview flow for hand-edits gives you five options per line: move to manual, suppress, delete, edit the source, or skip. In headless/CI mode, use --strategy=regenerate (discard edits) or --strategy=preserve (skip the file).

Where sentinels live

Sentinels are embedded in the source documents themselves — not in separate files:

docs/architecture/decisions/ADR-003-hexagonal.md
  ├── ## Context (human)
  ├── ## Decision (human — compile reads this)
  ├── ## Consequences (human)
  └── [edikt:directives:start/end] (Claude — compile writes this)

One file, two audiences, clearly separated by the sentinel markers.

Commands that interact with sentinels

CommandReads sentinelsWrites sentinels
/edikt:adr:compileYes (hashes)Yes (directives, reminders, verification)
/edikt:invariant:compileYes (hashes)Yes (directives, reminders, verification)
/edikt:guideline:compileYes (hashes)Yes (directives, reminders, verification)
/edikt:gov:compileYes (all five lists)No (writes governance.md, not sentinels)
/edikt:gov:reviewYes (staleness check)No
/edikt:gov:scoreIndirectly (scores compiled output)No

Next steps

Released under the Elastic License 2.0. Free to use, not for resale.