The five-layer governance stack from CLAUDE.md Is Not Enough: The Governance Stack for Agentic Development, as a drop-in starter kit. Copy the templates below into your project, customize the placeholders, and you have the document foundation, the runtime-enforcement hook, and the external-validation CI workflow described in the article. These are starting points, not policy — read them, edit them, and treat the placeholders as prompts for project-specific decisions.
Install order
CONSTITUTION.md— Defines the project's governing principles and the decision order that resolves conflicts between them.DIRECTIVES.md— Converts the constitution's principles into enforceable rules at three severity levels: Critical, Important, Recommended.SECURITY.md— Defines vulnerability scope, reporting, severity, and response targets — including agent-specific concerns like prompt injection and hook bypass.AGENTS.md— Open-standard project orientation for AI coding agents — file map, commands, working rules, precedence.CLAUDE.md— Claude-specific orientation that supplements AGENTS..claude/hooks/pre-tool-use.js— PreToolUse:Bash hook that blocks direct commits and pushes to the protected branch, including nested shell bypasses..claude/settings.json— Registers the pre-tool-use hook against PreToolUse:Bash and declares the project's Bash permission allow/deny rules..githooks/pre-push— Git pre-push hook that closes the bare-push gap the Claude PreToolUse hook cannot see..github/workflows/governance.yml— Three-job CI workflow — lint and test, static analysis, dependency scan — with every third-party action pinned to a commit SHA to defend against mutable-tag supply-chain risk.agentic-governance-stack/README.md— Step-by-step install instructions written for a coding agent.
Quick start: hand this prompt to your agent
Paste the prompt below into a fresh Claude Code (or compatible agent) session inside the project you want to govern. The agent will fetch this page, walk through the templates in order, customize the placeholders with you, and open a pull request with the stack installed.
You are installing the EthereaLogic Agentic Governance Stack into the current project. Templates and instructions live at https://etherealogic.ai/agentic-governance-stack-templates/
Steps:
1. Read the article first: https://etherealogic.ai/claude-md-is-not-enough-the-governance-stack-for-agentic-development/
2. Open https://etherealogic.ai/agentic-governance-stack-templates/. Each template on that page is wrapped in <figure class="codeblock"> with data-template-path indicating where it belongs in the project tree.
3. For each template (in the order they appear on the page), copy the exact contents of the inner <pre><code> element into a file at the path given by data-template-path, relative to the project root. Mark .claude/hooks/pre-tool-use.js and .githooks/pre-push as executable.
4. Wire the git pre-push hook:
git config --local core.hooksPath .githooks
5. Customize the placeholders in CONSTITUTION.md, DIRECTIVES.md, SECURITY.md, AGENTS.md, and CLAUDE.md for this project. Do not leave the contact email or stack tables as-is.
6. Replace the SHA placeholders in .github/workflows/governance.yml with current SHAs from each action's release page.
7. Verify the Claude hook is wired by running:
echo '{"tool_name":"Bash","tool_input":{"command":"git push origin main"}}' \
| node .claude/hooks/pre-tool-use.js
The expected exit code is 2 with a BLOCKED message on stderr.
8. Verify the git pre-push hook by running:
printf 'refs/heads/feature abc123 refs/heads/main 000\n' \
| bash .githooks/pre-push
The expected exit code is 1 with a BLOCKED message on stderr.
9. Confirm with the operator that server-side branch protection is configured on the remote (e.g., GitHub branch protection on main).
10. Open a pull request with the templates installed. Do not push directly to main; the hooks you just installed will block it, and branch protection will reject it on the remote regardless.
11. Report back to the operator with the PR URL and a one-paragraph summary of what was installed and what was customized.
One-shot installation prompt. Paste into a fresh agent session inside your project root and the agent will install the full stack from the templates below.
The templates
Each template below is a self-contained file. The figure caption shows the destination path relative to your project root. The Copy button copies the body to your clipboard; Download saves it as a file with the correct extension. Both pull from the same source — what you see is exactly what your agent or you will install.
CONSTITUTION.md
# Constitution
The constitution defines the governing principles of this project and the
**decision order** in which conflicts between them are resolved.
When two principles disagree, the higher-numbered one wins. This is the
statement; the principles below are scoped to it.
## Article I — Decision Order
When principles conflict, the agent and the team apply them in this order,
top to bottom:
1. **Safety.** No action that risks data loss, credential exposure, or
destructive change to shared systems is acceptable, regardless of
downstream cost.
2. **Evidence traceability.** A claim must be verifiable from artifacts the
reviewer can re-run. Speed gains do not justify unverified claims.
3. **Correctness.** A working incorrect answer is worse than a slower correct
one.
4. **Determinism.** Repeated runs from the same starting state should
produce the same result.
5. **Throughput.** Where the four principles above are satisfied, prefer the
shortest path to a working change.
6. **Aesthetics.** Naming, formatting, and layout matter, but not at the cost
of any principle above.
## Article II — Principles
The decision order operates on the following principles. Each principle is
brief on purpose; specific enforcement lives in `DIRECTIVES.md`.
- **Reproducibility.** Every result a contributor reports must be reproducible
from the repository state at that moment, by another contributor or by CI,
without privileged access.
- **Evidence over assertion.** A test report that says "PASS" without machine-
verifiable output and a human-readable artifact does not satisfy any review
requirement of this project.
- **Production-first.** The default state of any file shipped from this
repository is production-ready. Placeholder content, mocked values, and
TODOs are not acceptable in the production scan root.
- **Least privilege.** Tools and humans operate with the minimum permissions
required for the task.
- **Reviewability.** Changes are introduced in increments small enough to be
reviewed end-to-end by another contributor.
- **Documented intent.** A change that is not obvious from the diff is
explained in the commit message, the PR description, or both.
## Article III — Scope
This constitution governs work performed inside this repository, by humans,
agents, automated tooling, and CI. It does not bind external systems or
upstream dependencies.
## Article IV — Amendment
The constitution may be amended only by a pull request that:
1. Modifies this file.
2. Includes a written rationale in the PR description.
3. Receives review from at least one human maintainer.
Agents may propose amendments. Agents may not merge them.
---
*Adapted from the Agentic Governance Stack templates published at
[etherealogic.ai](https://etherealogic.ai/). Customize for your project's
constraints — the decision order matters more than the specific principles.*
Defines the project's governing principles and the decision order that resolves conflicts between them.
DIRECTIVES.md
# Directives
Directives convert the principles in `CONSTITUTION.md` into enforceable rules
at three severity levels. The constitution states what we believe; this file
states what we will and will not do.
Severity:
- **Critical** — blocking. Violations stop the change from landing. No bypass
without an explicit waiver recorded in the PR.
- **Important** — requires written justification to bypass, in the PR
description.
- **Recommended** — good practice. Skipping is allowed without justification
but should be rare.
Each directive carries an ID. Cite the ID in commit messages or PR comments
when a directive is involved.
## Critical
- **CRIT-001 — No direct commits or pushes to `main` / `master`.** All
changes land via pull request. Enforced by **three independent layers**,
none of which is sufficient on its own:
1. The Claude Code `PreToolUse:Bash` hook
(`.claude/hooks/pre-tool-use.js`) catches Bash invocations whose
refspec explicitly names a protected branch — including nested
shell wrappers (`bash -c "git push origin main"`) and compound
commands. It cannot catch bare `git push` or `git commit`,
because the hook surface does not expose the working-tree branch.
2. The local git pre-push hook (`.githooks/pre-push`, wired with
`git config --local core.hooksPath .githooks`) catches every push
that resolves to a protected branch on the remote, including the
bare-push case the Claude hook misses.
3. Server-side branch protection rules on the hosted remote
(e.g., GitHub branch protection on `main`) is the final
authoritative gate. Configure it on the remote and require pull
requests for the protected branch.
- **CRIT-002 — No fabricated metrics.** A reported number must trace to a
reproducible artifact in the repository or in CI. "Approximately X"
language is not a substitute for measurement.
- **CRIT-003 — No placeholder content in production paths.** Files in the
production scan root must not contain `TODO`, `FIXME`, `XXX`, lorem-ipsum
text, or stubbed function bodies that return constants.
- **CRIT-004 — Dual evidence for PASS claims.** Any test or check claimed as
passing must produce both a machine-verifiable artifact (exit code, JSON
report, log file) and a human-readable summary. One without the other does
not satisfy this directive.
- **CRIT-005 — No secrets in the repository.** Credentials, tokens, API
keys, and private keys are not committed. Detected secrets must be
rotated, not just removed from history.
- **CRIT-006 — No bypassing pre-commit / pre-push hooks.** `--no-verify`,
`--no-gpg-sign`, and equivalent flags are not used unless the operator has
explicitly authorized it for a single commit, with the rationale recorded
in the commit message.
## Important
- **IMP-001 — Conventional commit format.** Commit subjects use the
`type(scope): summary` form. Types: `feat`, `fix`, `chore`, `docs`,
`test`, `refactor`, `perf`, `build`, `ci`.
- **IMP-002 — Tests live alongside the code they validate.** Each new
module ships with at least one test exercising its public surface.
- **IMP-003 — Dependencies are pinned.** Lockfiles are committed. Floating
version specifiers (`^`, `~`, `*`) require justification.
- **IMP-004 — GitHub Actions are pinned to a commit SHA.** Version tags are
mutable and a documented supply-chain attack surface. See
`.github/workflows/governance.yml` for the pinning convention.
- **IMP-005 — Public-facing code passes static analysis.** Lint, type
check, and security scan must pass on every PR.
## Recommended
- **REC-001 — Prefer pure functions and explicit dependencies.** Side effects
are isolated at boundaries.
- **REC-002 — Document the WHY in commit messages, not the WHAT.** The diff
shows what changed; the message explains why.
- **REC-003 — Small, focused PRs.** A single PR addresses a single concern.
- **REC-004 — Update documentation in the same PR as the change.** Stale
docs are worse than no docs.
- **REC-005 — Annotate non-obvious code with one-line comments only when
the WHY is non-obvious.** Avoid restating the WHAT.
## Bypass and waiver
A directive may be waived for a single change by:
1. Citing the directive ID in the PR description.
2. Stating the rationale.
3. Receiving review from a human maintainer who explicitly approves the
waiver.
Waivers are not retroactive. A change that violates a directive without a
recorded waiver is reverted, not patched.
---
*Adapted from the Agentic Governance Stack templates published at
[etherealogic.ai](https://etherealogic.ai/). Tune severity to your context —
some teams promote `CRIT-006` to a hard policy enforced by branch
protection, others keep it advisory.*
Converts the constitution's principles into enforceable rules at three severity levels: Critical, Important, Recommended.
SECURITY.md
# Security Policy
This file defines what counts as a security vulnerability in this project,
how to report one, how the project will respond, and what is out of scope.
## Reporting a vulnerability
Email **security@example.com** with:
1. A clear description of the issue.
2. Reproduction steps or a proof of concept.
3. The affected version, branch, or commit.
4. Whether the issue is currently being exploited.
Do not file public issues for vulnerabilities. We will acknowledge receipt
within two business days and provide a status update within five.
If you prefer encrypted communication, our PGP key is at:
**https://example.com/.well-known/security.pgp**
## What is in scope
The following are treated as vulnerabilities subject to this policy:
- Remote code execution against the build, deploy, or runtime surface.
- Credential leakage — committed secrets, secrets exposed via logs, or
secrets exfiltrable via crafted input.
- Authentication or authorization bypass.
- Injection (SQL, command, prompt, template) into any code path that
reaches a privileged operation.
- Supply-chain compromise — including dependency tampering, registry
takeover, and unverified action invocation.
- Denial of service against shared infrastructure operated by the project.
- **Prompt injection that causes an agent to violate a CRIT-level
directive.** Agentic projects treat prompt-injection attacks that bypass
governance as in-scope security issues, not feature requests.
- **Hook bypass.** Any input or shell construction that allows a tool call
to land despite a registered enforcement hook (e.g., `PreToolUse:Bash`)
is in scope.
## What is out of scope
- Theoretical issues without a working proof of concept.
- Findings against unsupported or end-of-life versions.
- Self-XSS that requires the victim to paste the payload into their own
console.
- Rate-limiting and brute-force issues against public, unauthenticated
endpoints (these are addressed at infrastructure layers).
- Issues in third-party dependencies that have not yet been published. We
monitor dependency advisories and will respond once an upstream fix is
available.
## Severity
Severity is assigned at triage by the maintainer who acknowledges the
report. Targets below are best-effort — high-volume periods or upstream
dependencies may extend them.
| Severity | Definition | Acknowledgement | Initial response | Patched in supported branches |
|------------|--------------------------------------------------------|-----------------|------------------|-------------------------------|
| Critical | Active exploitation possible against production users | 24 hours | 72 hours | 7 days |
| High | Exploitable with low complexity, no active exploitation| 48 hours | 7 days | 30 days |
| Medium | Exploitable with elevated complexity or preconditions | 5 days | 14 days | 60 days |
| Low | Limited impact, defense-in-depth improvement | 5 days | 30 days | next release |
## Disclosure
We coordinate disclosure with the reporter. The default is:
1. Reporter notifies the project privately.
2. Project triages, assigns a severity, and produces a fix.
3. Fix is released; an advisory is published referencing the reporter (with
their consent) and the affected versions.
4. Public disclosure occurs after the advisory, typically 7 to 30 days
after the fix lands, depending on severity.
If the reporter requires a faster public timeline, we will negotiate in
good faith. We do not pursue legal action against good-faith researchers
who comply with this policy.
## Hall of fame
Researchers whose reports lead to a published advisory are credited in the
project's `THANKS.md` file (with their consent). This project does not
operate a paid bounty program at this time.
---
*Adapted from the Agentic Governance Stack templates published at
[etherealogic.ai](https://etherealogic.ai/). Replace the contact, response
targets, and the supported-version table with values that match your
team's actual capacity.*
Defines vulnerability scope, reporting, severity, and response targets — including agent-specific concerns like prompt injection and hook bypass.
AGENTS.md
# AGENTS.md
This file is the project orientation layer for AI coding agents. It covers
where things live, what commands run, and how the project expects agents to
behave. It is the open-standard counterpart to `CLAUDE.md`.
## Read order
Before substantive work, read in this order:
1. `CONSTITUTION.md` — governing principles and decision order.
2. `DIRECTIVES.md` — enforceable rules at three severity levels.
3. `SECURITY.md` — what counts as a vulnerability and how to report one.
4. This file — orientation, commands, conventions.
5. `CLAUDE.md` (Claude only) — Claude-specific notes that supplement this
file rather than replace it.
When this file and a higher-priority file disagree, the higher-priority
file wins.
## File map
| Path | Purpose |
|-----------------------------|--------------------------------------------------|
| `src/` | Production source code |
| `tests/` | Test suite |
| `docs/` | Documentation, including ADRs |
| `scripts/` | Build, verify, and deploy scripts |
| `.claude/` | Claude-specific configuration (if applicable) |
| `.github/workflows/` | CI workflows |
| `CONSTITUTION.md` | Governing principles |
| `DIRECTIVES.md` | Enforceable rules |
| `SECURITY.md` | Vulnerability policy |
| `AGENTS.md` | This file |
| `CLAUDE.md` | Claude-specific orientation |
## Tech stack
Replace this section with your project's stack. Keep it brief — agents need
the headline, not a tutorial.
| Layer | Technology |
|-------------|----------------|
| Runtime | (e.g., Node 20)|
| Language | (e.g., TS 5.4) |
| Framework | (e.g., Next 15)|
| Test runner | (e.g., Vitest) |
| Lint | (e.g., ESLint) |
## Commands
| Command | Purpose |
|-------------------|----------------------------------------|
| `make install` | Install dependencies (or `npm ci`) |
| `make test` | Run the full test suite |
| `make lint` | Lint and type-check |
| `make build` | Produce a deployable artifact |
| `make verify` | Run the local guardrails check |
Replace these with the actual commands for your project. Agents will assume
the entries here are authoritative.
## Working rules
These rules apply to every change, by humans and agents alike.
- Edit existing files in preference to creating new ones.
- Do not introduce a new dependency without justifying it in the PR.
- Do not regenerate lockfiles unless the dependency change is intentional.
- All PASS claims require dual evidence (`DIRECTIVES.md` CRIT-004).
- All commits use conventional commit format (`DIRECTIVES.md` IMP-001).
- Run `make verify` before submitting a PR.
## Agent specialization
If the project uses scoped sub-agents (e.g., a test automator, a security
reviewer, a UX specialist), document each one's scope and evidence
requirements here. The default is to use the general-purpose agent.
## Precedence
When patterns conflict, resolve in this order:
1. `CONSTITUTION.md`
2. `DIRECTIVES.md`
3. `SECURITY.md`
4. This file
5. `CLAUDE.md` and other agent-specific files
6. Inline code comments
7. Commit history
## Ground rules for agents
- An agent may propose changes to any file in this list. An agent may not
merge changes to `CONSTITUTION.md`, `DIRECTIVES.md`, or `SECURITY.md`
without human review.
- An agent that produces an output it cannot verify must say so explicitly
rather than claiming success.
- An agent that hits a runtime barrier (a hook exit, a CI failure, a
permission denial) investigates the underlying cause rather than working
around the barrier.
---
*Adapted from the Agentic Governance Stack templates published at
[etherealogic.ai](https://etherealogic.ai/). The AGENTS.md format is an
open standard governed by the Linux Foundation's Agentic AI Foundation;
this file is compatible with that standard.*
Open-standard project orientation for AI coding agents — file map, commands, working rules, precedence.
CLAUDE.md
# CLAUDE.md
This file is the Claude-specific orientation layer. It supplements
`AGENTS.md` with Claude-specific notes — slash commands, hook reference,
sub-agent definitions — without replacing the open-standard content there.
If a Claude session sees both files, read `AGENTS.md` first, then this one.
When the two disagree, `AGENTS.md` wins.
## Slash command catalog
The project ships the following slash commands under `.claude/commands/`.
Each is a policy-encoded workflow, not a shortcut.
| Command | Purpose |
|-----------------|-----------------------------------------------------------------|
| `/prime` | Read the governance files and orient before substantive work |
| `/implement` | Implement a specified change with required evidence |
| `/review` | Review a change against the constitution and directives |
| `/verify` | Run the full verification suite and report dual evidence |
| `/audit` | Independent compliance audit of the most recent change |
| `/commit` | Stage and commit using the project's conventional format |
| `/pull-request` | Open a PR with the structured description the project requires |
## Hook reference
The project registers the following hooks in `.claude/settings.json`. Each
hook either blocks an action with a non-zero exit, or passes the action
through unchanged.
| Hook event | Script | Scope |
|----------------------|-------------------------------------|-----------------------------------------------------------|
| `PreToolUse:Bash` | `.claude/hooks/pre-tool-use.js` | Catch Bash calls that explicitly name a protected branch |
A blocked tool call exits with status 2 and prints a one-line explanation.
The agent does not retry the same call after a hook block — it investigates
the cause.
The `PreToolUse:Bash` hook covers explicit-refspec pushes
(`git push origin main`) and nested shell wrappers
(`bash -c "git push origin main"`). It does **not** cover bare pushes
(`git push`, `git push origin`) or commits on the protected branch,
because the working-tree branch is not visible in the Bash payload. Those
cases are covered by the companion `.githooks/pre-push` git-side hook
shipped alongside this template kit. See `DIRECTIVES.md` CRIT-001 for the
full three-layer enforcement contract.
## Sub-agents
Replace this section with your project's sub-agent definitions. The
default Claude Code installation does not require any.
If you use sub-agents, document each one's:
- **Scope** — what work it owns.
- **Evidence requirement** — what artifacts it must produce.
- **Forbidden actions** — what it cannot do regardless of prompt.
## Context conventions
- The `.claude/` directory is Claude-specific. Agents that are not Claude
should ignore it.
- The `.claude/agents/`, `.claude/commands/`, and `.claude/hooks/`
subdirectories are read by Claude Code automatically. Do not move or
rename them.
- The `.claude/settings.json` file is the single source of truth for which
hooks and permissions are active. Hooks present in `.claude/hooks/` but
not registered in `settings.json` do **not** run.
## When in doubt
A Claude session that finds itself uncertain should:
1. Stop and read `CONSTITUTION.md` again.
2. Identify which decision-order principle is in tension.
3. Apply the higher-priority principle.
4. Document the resolution in the commit message or PR description.
Asking the human operator is preferable to guessing in any case where the
constitution does not produce a clear answer.
---
*Adapted from the Agentic Governance Stack templates published at
[etherealogic.ai](https://etherealogic.ai/). This file is intentionally
short — Claude-specific knowledge belongs here, project-wide knowledge
belongs in `AGENTS.md`.*
Claude-specific orientation that supplements AGENTS.md with the slash command catalog and hook reference.
.claude/hooks/pre-tool-use.js
#!/usr/bin/env node
/**
* PreToolUse:Bash guard
*
* Scope: catches Bash tool calls that *explicitly name* a protected branch
* in the push refspec, including nested shell wrappers. Specifically:
*
* blocks (exit 2):
* - git push origin main
* - git push origin HEAD:main
* - git push --force origin master
* - bash -c "git push origin main" (and sh / zsh variants)
* - git push origin main && echo done (and other compound forms)
* - git reset --hard origin/main
*
* does NOT block on its own (out of scope for a Bash-payload hook):
* - git push (no remote / refspec — current branch is unknown
* from inside this hook)
* - git push origin (no refspec)
* - git commit -m "..." (the hook cannot tell which branch HEAD is on)
*
* The unhandled cases are covered by the companion git-side pre-push hook
* shipped in `.githooks/pre-push` of this template kit. The two layers
* compose: the Claude hook closes the explicit-refspec class (which can
* arrive via any agent-driven Bash invocation), and the git pre-push
* hook closes the bare-push class (which depends on the working-tree
* state). Install both for end-to-end coverage of `DIRECTIVES.md`
* CRIT-001.
*
* Exits 2 when blocked, 0 when allowed. Reads the tool payload as JSON
* from stdin. The Claude Code runtime provides the payload in this shape:
*
* {
* "tool_name": "Bash",
* "tool_input": { "command": "<the shell string>", ... }
* }
*
* Register this hook in `.claude/settings.json` under
* `hooks.PreToolUse.Bash`. See the companion `settings.json` template for
* the exact shape.
*
* Adapted from the Agentic Governance Stack templates published at
* https://etherealogic.ai/agentic-governance-stack-templates/
*/
'use strict';
const PROTECTED_BRANCHES = ['main', 'master'];
function readStdin() {
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => { data += chunk; });
process.stdin.on('end', () => resolve(data));
process.stdin.on('error', reject);
});
}
function block(reason) {
process.stderr.write(`[pre-tool-use] BLOCKED: ${reason}\n`);
process.exit(2);
}
function allow() {
process.exit(0);
}
/**
* Recursively unwrap nested shell invocations of the form
* bash -c "<inner>"
* sh -lc '<inner>'
* zsh -c $'<inner>'
* so we evaluate the actual command being run, not the wrapper.
*/
function unwrapShellWrappers(command) {
let current = command;
for (let i = 0; i < 8; i++) {
const m = current.match(
/^\s*(?:bash|sh|zsh|dash|ash)\s+(?:-[a-z]*c|--command)\s+(['"$])([\s\S]+)\1\s*$/,
);
if (!m) return current;
current = m[2];
}
return current;
}
function splitCompound(command) {
// Split on shell separators that introduce another command in the same
// invocation: ;, &&, ||, |, & (background). Quotes inside the command
// are not perfectly respected here — we err on the side of evaluating
// every fragment.
return command
.split(/(?:;|&&|\|\||(?<!\|)\|(?!\|)|(?<!&)&(?!&))/)
.map((fragment) => fragment.trim())
.filter(Boolean);
}
function isProtectedRef(ref) {
if (!ref) return false;
const bare = ref.replace(/^origin\//, '').replace(/^refs\/heads\//, '');
return PROTECTED_BRANCHES.includes(bare);
}
function checkGitFragment(fragment) {
// Match `git commit ... -m ...` or `git push origin main` patterns.
const tokens = fragment.match(/("[^"]*"|'[^']*'|\S+)/g) || [];
if (tokens[0] !== 'git') return null;
const sub = tokens[1];
if (sub === 'push') {
// Look for an explicit refspec on the push line.
const refTokens = tokens.slice(2).filter((t) => !t.startsWith('-'));
// refTokens may be [], [remote], [remote, ref], or [remote, src:dst]
if (refTokens.length === 0) {
// `git push` with no args targets the current branch. We do not know
// the current branch from this hook surface, so we fall back to a
// conservative warning rather than a hard block.
return null;
}
const refSpec = refTokens[1];
if (!refSpec) return null;
const dst = refSpec.includes(':') ? refSpec.split(':').pop() : refSpec;
if (isProtectedRef(dst)) {
return `git push to protected branch "${dst}" — open a pull request instead.`;
}
return null;
}
if (sub === 'commit') {
// Detect commit-on-protected-branch by reading HEAD if available.
// We cannot reliably know the current branch from inside this hook,
// so the strict guard is on the push step. A separate pre-commit
// hook in the repository should reject commits authored on main.
return null;
}
if (sub === 'reset' || sub === 'rebase') {
if (fragment.includes('--hard') && /\borigin\/(main|master)\b/.test(fragment)) {
return `destructive ${sub} against protected branch — investigate before forcing.`;
}
return null;
}
if (sub === 'checkout' && /\b(--|-)\s*$/.test(fragment)) {
// `git checkout --` discards uncommitted changes. Warn aggressively.
return 'git checkout -- discards local changes; confirm intent.';
}
return null;
}
async function main() {
const raw = await readStdin();
if (!raw) allow();
let payload;
try {
payload = JSON.parse(raw);
} catch (err) {
// If we cannot parse the payload, fail open with a warning so the
// agent is not blocked by a runtime contract change.
process.stderr.write(`[pre-tool-use] could not parse payload: ${err.message}\n`);
allow();
}
if (payload.tool_name !== 'Bash') allow();
const command = (payload.tool_input && payload.tool_input.command) || '';
if (!command) allow();
const inner = unwrapShellWrappers(command);
const fragments = splitCompound(inner);
for (const fragment of fragments) {
const reason = checkGitFragment(fragment);
if (reason) block(reason);
}
allow();
}
main().catch((err) => {
process.stderr.write(`[pre-tool-use] hook error: ${err.message}\n`);
// Fail open on hook crash so a misconfigured hook does not lock the
// agent out of every Bash call.
process.exit(0);
});
PreToolUse:Bash hook that blocks direct commits and pushes to the protected branch, including nested shell bypasses. Exits with status 2 to halt the tool call before it lands.
.claude/settings.json
{
"$schema": "https://www.claude.com/schemas/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(make:*)",
"Bash(npm:*)",
"Bash(pnpm:*)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git branch*)"
],
"deny": [
"Bash(git push origin main*)",
"Bash(git push origin master*)",
"Bash(git push --force*)"
]
},
"hooks": {
"PreToolUse": {
"Bash": [
{
"command": "node",
"args": [".claude/hooks/pre-tool-use.js"]
}
]
}
}
}
Registers the pre-tool-use hook against PreToolUse:Bash and declares the project's Bash permission allow/deny rules. The hook is inert without this registration.
.githooks/pre-push
#!/usr/bin/env bash
# Git pre-push hook — protected-branch guard.
#
# Rejects any push that lands on a protected branch on the remote,
# including bare pushes (`git push`, `git push origin`) where the
# Claude Code Bash hook cannot determine the destination branch.
#
# The git hook system passes the local and remote refs being pushed on
# stdin, one push per line:
#
# <local ref> <local sha> <remote ref> <remote sha>
#
# Exit non-zero to abort the push.
#
# Adapted from the Agentic Governance Stack templates published at
# https://etherealogic.ai/agentic-governance-stack-templates/
#
# Install:
# chmod +x .githooks/pre-push
# git config --local core.hooksPath .githooks
#
# Verify:
# git push origin HEAD:main # should be rejected
# git push origin HEAD:feature # should be allowed
set -euo pipefail
PROTECTED_REGEX='^refs/heads/(main|master)$'
while read -r local_ref local_sha remote_ref remote_sha; do
# Skip deletions — `git push --delete` arrives with a zero local sha.
# Deleting a protected branch is also a destructive op; block it too.
if [[ "$remote_ref" =~ $PROTECTED_REGEX ]]; then
branch="${remote_ref#refs/heads/}"
if [[ "$local_sha" =~ ^0+$ ]]; then
echo "[pre-push] BLOCKED: refusing to delete protected branch '$branch'." >&2
else
echo "[pre-push] BLOCKED: push to protected branch '$branch' refused." >&2
echo "[pre-push] Open a pull request instead." >&2
fi
exit 1
fi
done
exit 0
Git pre-push hook that closes the bare-push gap the Claude PreToolUse hook cannot see. Wire with `git config –local core.hooksPath .githooks`.
.github/workflows/governance.yml
# Governance CI workflow
#
# Runs three independent jobs on every push and pull request:
# 1. lint-and-test — quality (lint + unit tests)
# 2. security — static analysis
# 3. dependencies — vulnerability scan against the lockfile
#
# Every third-party action is pinned to a specific commit SHA, not a
# version tag, to defend against the mutable-tag supply-chain attack
# class. Replace each SHA below with the current pinned SHA from the
# action's release page when adopting this template.
#
# Adapted from the Agentic Governance Stack templates published at
# https://etherealogic.ai/agentic-governance-stack-templates/
name: governance
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-test:
name: Lint and test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
# actions/checkout@v4 — replace with the pinned SHA for v4
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# actions/setup-node@v4 — replace with the pinned SHA for v4
- name: Setup Node
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Unit tests
run: npm test -- --reporter=verbose
# codecov/codecov-action@v4 — replace with the pinned SHA for v4
- name: Upload coverage
if: success()
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238
with:
fail_ci_if_error: true
files: ./coverage/lcov.info
security:
name: Static analysis
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# github/codeql-action/init@v3 — replace with the pinned SHA for v3
- name: Initialize CodeQL
uses: github/codeql-action/init@4675d51de96d1d4dafabf81f93475a83fe7710e7
with:
languages: javascript-typescript
- name: Build
run: |
if [ -f package.json ]; then npm ci && npm run build --if-present; fi
# github/codeql-action/analyze@v3 — replace with the pinned SHA for v3
- name: Analyze
uses: github/codeql-action/analyze@4675d51de96d1d4dafabf81f93475a83fe7710e7
dependencies:
name: Dependency scan
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# snyk/actions/node@v0.4.0 — replace with the pinned SHA you intend to use
- name: Run Snyk
uses: snyk/actions/node@b98d498629f1c368650224d6d212bf7dfa89e4bf
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
Three-job CI workflow — lint and test, static analysis, dependency scan — with every third-party action pinned to a commit SHA to defend against mutable-tag supply-chain risk.
agentic-governance-stack/README.md
# Agentic Governance Stack — Templates
A drop-in starter kit for the five-layer governance pattern described in
[*CLAUDE.md Is Not Enough: The Governance Stack for Agentic
Development*](https://etherealogic.ai/claude-md-is-not-enough-the-governance-stack-for-agentic-development/).
These templates are starting points, not policy. Read them, customize them,
and treat the placeholders as prompts for project-specific decisions.
## What is in this directory
| File | Layer |
|-------------------------------------|-------------------------------|
| `CONSTITUTION.md` | 2 — Constitutional governance |
| `DIRECTIVES.md` | 2 — Constitutional governance |
| `SECURITY.md` | 2 — Constitutional governance |
| `AGENTS.md` | 1 — Navigation |
| `CLAUDE.md` | 1 — Navigation (Claude) |
| `.claude/hooks/pre-tool-use.js` | 4 — Runtime enforcement |
| `.claude/settings.json` | 4 — Runtime enforcement |
| `.githooks/pre-push` | 4 — Runtime enforcement (git) |
| `.github/workflows/governance.yml` | 5 — External validation |
## Install instructions for an AI coding agent
If you are an AI coding agent reading this README, your operator has asked
you to install this governance stack into the current project. Follow these
steps in order.
1. **Read the article first.** Open
<https://etherealogic.ai/claude-md-is-not-enough-the-governance-stack-for-agentic-development/>
and read it end to end before proceeding. The templates only make sense
in context.
2. **Confirm the project root.** Run `pwd` and verify with the operator
that you are in the correct directory before writing any files.
3. **Copy the document foundation** to the project root. Create or
overwrite, in this order:
- `CONSTITUTION.md`
- `DIRECTIVES.md`
- `SECURITY.md`
- `AGENTS.md`
- `CLAUDE.md`
If any of these already exist, do **not** silently overwrite. Surface
the conflict to the operator and ask which version to keep.
4. **Customize placeholders.** Replace, at minimum:
- The contact email in `SECURITY.md`.
- The tech-stack and command tables in `AGENTS.md`.
- The principle ordering in `CONSTITUTION.md` if it does not match your
project's actual priorities. Do not leave the ordering unread — the
order is the statement.
5. **Install the Claude runtime hook.** Create the directory and copy:
- `.claude/hooks/pre-tool-use.js`
- `.claude/settings.json` (merge with any existing `settings.json` —
do not overwrite a populated file)
6. **Install the git pre-push hook.** This is the companion guard that
covers the bare-push case the Claude hook cannot see. Copy
`.githooks/pre-push` into the project's `.githooks/` directory, mark
it executable, and point the local git config at it:
```sh
chmod +x .githooks/pre-push
git config --local core.hooksPath .githooks
```
7. **Install the CI workflow.** Copy
`.github/workflows/governance.yml` into the project's
`.github/workflows/` directory. Replace each pinned SHA with the
current pinned SHA for the matching action version. Run the workflow
on a draft PR before merging it to confirm it passes.
8. **Verify the Claude hook is wired.** From the project root:
```sh
cat .claude/settings.json | jq '.hooks.PreToolUse.Bash'
```
The output must list `pre-tool-use.js`. If it does not, the hook is
not active and CRIT-001's first layer is unenforced.
9. **Test the Claude hook.** Attempt a dry run that should be blocked:
```sh
echo '{"tool_name":"Bash","tool_input":{"command":"git push origin main"}}' \
| node .claude/hooks/pre-tool-use.js
echo "exit=$?"
```
Expected output:
```text
[pre-tool-use] BLOCKED: git push to protected branch "main" — ...
exit=2
```
If you see anything else, the hook is misconfigured. Stop and surface
the failure to the operator.
10. **Test the git pre-push hook.** Simulate a push payload to confirm
the hook rejects pushes to a protected branch. The git hook system
receives the local and remote refs on stdin:
```sh
printf 'refs/heads/feature abc123 refs/heads/main 000\n' \
| bash .githooks/pre-push
echo "exit=$?"
```
Expected output:
```text
[pre-push] BLOCKED: push to protected branch 'main' refused.
[pre-push] Open a pull request instead.
exit=1
```
11. **Commit the templates** as a single, focused commit:
```text
chore: install agentic governance stack templates
Adds CONSTITUTION, DIRECTIVES, SECURITY, AGENTS, CLAUDE, the
Claude PreToolUse:Bash hook, the git pre-push companion, and the
governance CI workflow.
Source: https://etherealogic.ai/agentic-governance-stack-templates/
```
Open a pull request rather than pushing directly to `main`. The
layered hooks will block a direct push regardless.
12. **Report dual evidence to the operator** once the PR opens:
- The PR URL (machine-verifiable artifact).
- A one-paragraph summary of what was installed and what was
customized (human-readable artifact).
## Install instructions for a human
If you are a human reading this:
1. Read the article: <https://etherealogic.ai/claude-md-is-not-enough-the-governance-stack-for-agentic-development/>.
2. Copy the files into your project root with the layout above.
3. Customize the placeholders. The decision order in `CONSTITUTION.md`
matters more than the specific principles.
4. Wire the git pre-push hook with
`git config --local core.hooksPath .githooks` and confirm it is
executable.
5. Pin the GitHub Actions in `governance.yml` to current SHAs from each
action's releases page.
6. Configure server-side branch protection on the remote (e.g., GitHub
branch protection rules requiring a pull request to `main`). The
hooks are best-effort local guards; branch protection is the
authoritative gate.
7. Open a draft PR with the templates and run CI before merging.
## Customizing the templates
The templates are intentionally generic. Common customizations:
- **Tighten or loosen severity in `DIRECTIVES.md`.** Some teams promote
`CRIT-006` (no `--no-verify`) to a hard branch-protection rule. Others
treat it as advisory.
- **Add domain-specific sub-agents in `CLAUDE.md`.** Teams shipping data
pipelines often add a data-quality reviewer. Teams shipping web apps
often add a UX reviewer.
- **Extend the protected-branch hook in `pre-tool-use.js`.** The default
guards `main` and `master`. Add your release branches if you have them.
- **Replace the CI scanner trio in `governance.yml`.** The template uses
Codecov, CodeQL, and Snyk as illustrative choices. Substitute the
scanners your organization already runs.
## License
These templates are released for unrestricted use. Attribution to
EthereaLogic is appreciated but not required.
Step-by-step install instructions written for a coding agent. Drop this into a vendor folder and point your agent at it to install the stack autonomously.
What to do once the templates are in place
The document foundation is necessary but not sufficient on its own. Once the files are in your repository, the next two steps move the stack from orientation to governance:
- Verify the hook is registered. The
pre-tool-use.jshook is inert unless registered in.claude/settings.jsonunderhooks.PreToolUse.Bash. Both files are above; install both. - Pin every GitHub Action to a SHA. The CI workflow above ships with placeholder SHAs. Replace each one with the current pinned SHA from the corresponding action’s release page before merging.
- Open a draft PR. The hook will block any direct push to
main; that’s the point. Open a draft PR with the templates installed and let CI confirm the workflow runs cleanly.
Found a bug, a missing case, or a refinement worth contributing back? The article includes a follow-up series; updates land at https://etherealogic.ai/agentic-governance-stack-templates/.