Install the frontmatter hooks¶
Every note an agent writes with its file tools is checked right after the write, and the failures go back to the agent so it can fix the note in the same turn. The write itself is never undone. Two checks run:
- Schema (
validate_frontmatter.py): type and required fields, checked against the generated JSON Schema for the note'stype:. - Data model guard (
datamodel_guard.py --no-report): links resolve, lifecycle rules, the note's location, and uniqueness ofclient_idand email. A hook run does not rewritedatamodel-guard-latest.md. Run the guard by hand, or on a schedule, when you want that report.
Hooks are per machine. They live in dot-folders (.claude/, .codex/, .agents/), which
Obsidian Sync does not sync, so every new laptop needs this page once. Scripts that write notes
themselves (uv run …) are not seen by any hook. They must self-validate, as
CONVENTIONS requires.
What runs¶
One adapter serves all three agents:
{skills.root}/vault-validator/scripts/agent_hook.py --agent <claude|codex|antigravity>.
It takes the written Markdown paths from the agent's payload and runs both sibling checks on
them. It then returns any failure in the form that agent reads.
| Agent | Config file (vault root) | Event | Matcher | How a failure reaches the model |
|---|---|---|---|---|
| Claude Code | .claude/settings.json |
PostToolUse |
Write\|Edit\|MultiEdit |
exit 2, message on stderr |
| Codex | .codex/hooks.json |
PostToolUse |
apply_patch\|Edit\|Write |
{"decision":"block","reason":…} |
| Antigravity | .agents/hooks.json |
PreToolUse + PostInvocation |
write_to_file\|replace_file_content\|multi_replace_file_content |
injectSteps ephemeral message |
Antigravity sends no file path to PostToolUse. The adapter therefore records the target at
PreToolUse and validates once the tool calls finish (PostInvocation). The pending list lives
in $XDG_RUNTIME_DIR/vault-validator-agent-hook/.
Agent shells do not expand key notation, so the config files carry literal absolute paths.
Below, <vault> is the vault root and <skills> is <vault>/00-CORE/Agents/skills on the
default layout. Use resolve_context.py --key skills.root to find the real path.
Prerequisites¶
uvonPATHin the shell the agent uses (uv --version).- The vault schema tree exists:
<vault>/00-CORE/Schemas/json-schema/. Without it the validator prints aSKIPline and passes. That is expected in an MCP-only workspace.
Claude Code¶
Create or merge <vault>/.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "uv run --quiet \"$CLAUDE_PROJECT_DIR/00-CORE/Agents/skills/vault-validator/scripts/agent_hook.py\" --agent claude --vault \"$CLAUDE_PROJECT_DIR\"",
"timeout": 60
}
]
}
]
}
}
Restart Claude Code in the vault. Run /hooks and check that the PostToolUse entry is listed.
Codex¶
- The
hooksfeature must be on.codex features list | grep hooksshould showstable true. Older builds need[features] codex_hooks = truein~/.codex/config.toml. - The vault must be a trusted project in
~/.codex/config.toml([projects."<vault>"] trust_level = "trusted"). - Create
<vault>/.codex/hooks.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "apply_patch|Edit|Write",
"hooks": [
{
"type": "command",
"command": "uv run --quiet <skills>/vault-validator/scripts/agent_hook.py --agent codex --vault <vault>",
"timeout": 60,
"statusMessage": "Validating frontmatter"
}
]
}
]
}
}
- Trust the hook. Start
codexin the vault, run/hooksand mark the new hook trusted. Codex skips new or changed hooks until they are trusted, and it records trust against the hook's hash. Editing the command means trusting it again.
Antigravity CLI (agy)¶
The vault must be listed under trustedWorkspaces in ~/.gemini/antigravity-cli/settings.json.
Then create <vault>/.agents/hooks.json:
{
"vault-frontmatter": {
"PreToolUse": [
{
"matcher": "write_to_file|replace_file_content|multi_replace_file_content",
"hooks": [
{
"type": "command",
"command": "uv run --quiet <skills>/vault-validator/scripts/agent_hook.py --agent antigravity --event pre --vault <vault>",
"timeout": 30
}
]
}
],
"PostInvocation": [
{
"type": "command",
"command": "uv run --quiet <skills>/vault-validator/scripts/agent_hook.py --agent antigravity --event postinv --vault <vault>",
"timeout": 60
}
]
}
}
Run /hooks in agy and check that vault-frontmatter is listed. The PreToolUse handler
returns {"decision": "ask"}, which defers to the normal approval flow. It must not return {}:
agy 1.2.13 treats that as a hard deny and blocks every file write (found 2026-09-29).
Verify¶
The adapter's own tests need no agent:
cd <skills>/vault-validator && uv run scripts/test_agent_hook.py
A live check per agent: ask it to create 00-CORE/TaskNotes/Tasks/zz-hook-probe.md with the
frontmatter type: task and nothing else. A working hook reports 'status' is a required
property back to the agent in the same turn. Delete the probe note afterwards. Do not probe in
99-TMP/, because the validator skips that folder.
A dry run of the Claude Code path, without an agent:
echo '{"tool_input":{"file_path":"<vault>/<some typed note>.md"}}' \
| uv run --quiet <skills>/vault-validator/scripts/agent_hook.py --agent claude --vault <vault>; echo "exit=$?"
Exit 0 means pass (or skip). Exit 2 means the note failed, and the reason is on stderr.
Troubleshooting¶
- Nothing happens on write. Check that the config file sits at the vault root the agent was
started in. For Codex, check that the hook is trusted in
/hooks. For Antigravity, check that the workspace is trusted. - Every write takes a few seconds. Both checks start through
uv. On this laptop (WSL, vault on DrvFs) a passing write measured 6.7 s on 2026-09-26. The first run is slower still, because it builds the environments. Keepuv's cache on the Linux filesystem (the default~/.cache/uv). SKIP: … no generated JSON Schemas. This workspace has no schema tree, for example when skills are served over MCP. Validation is unavailable there by design.- Temporarily silence failures.
SCHEMA_VALIDATION=warnin the agent's environment prints violations without failing.
Related¶
- vault-validator skill: the validator, the adapter, and the foreground
frontmatter_watcher.pybackstop for writes that no hook sees. - hook-authoring skill: authoring and porting hooks between agents.
- FRONTMATTER-REQUIRED: required fields per
type:.