How the hook decides
Generated hooks check a tool call before it leaves the coding harness.
Each bundle contains one decision core: plugin/hookcore.mjs.
It reads catalog.json and returns one of three results:
allow: continue;deny: block the call; orask: require human approval.
Harness-specific shims translate that result into the format expected by Antigravity, Claude Code, or Codex.
The runtime remains authoritative
Section titled “The runtime remains authoritative”The hook applies the same checks as the runtime executor, in the same order:
- unknown operation;
- approval state;
- confirmation; and
- idempotency.
The hook improves feedback time. It is not the trust boundary.
Remove the hook and runtime policy still applies.
This matters in two cases:
- Missing confirmation or idempotency is rejected inside the harness. The model can repair the call without a failed network path.
- Human approval remains human approval. A model-supplied argument cannot satisfy that gate.
tests/conformance.test.ts checks that hookcore.decide() and the runtime executor agree. If the hook allows a call the executor would refuse, conformance fails.
How ask maps to each harness
Section titled “How ask maps to each harness”allow and deny map directly in all supported harnesses. ask differs because not every harness exposes the same approval primitive.
| Harness | ask becomes | Reason |
|---|---|---|
| Antigravity | force_ask | Forces a user prompt and ignores cached Always Allow state |
| Claude Code | ask | Uses the harness permission dialog |
| Codex | deny | PreToolUse has no interactive ask, so the hook blocks the call and leaves approval to the harness flow |
Antigravity runs hooks for built-in tools as well as Anvil operations. Its shim passes through tools that are not in this bundle’s catalog. The deny-unknown rule applies only to operations owned by the bundle.