- name
- ad-hooks
- description
- Scaffold deterministic quality gates: pre-commit (lint, format, secret-scan) and pre-push (build, tests) with a hook runner fitted to the stack, plus a PostToolUse artifact-validator gate on both hosts and the Stop nudge and workflow checkpoint on Claude Code. Use to wire hooks, set up quality gates, or prevent --no-verify bypass.
- summary
- Scaffold deterministic quality gates per WORKFLOW §11 — pre-commit + pre-push, runner detected from stack signals — plus a session-lifecycle tier (Stop handoff-nudge and UserPromptSubmit workflow checkpoint on Claude Code today, and a dual-host PostToolUse artifact-validator gate).
<background_information>
Scaffolds the deterministic gates `WORKFLOW.md` §11 names. The skill writes config files for a hook runner and updates `AGENTS.md` Quality Gates; it does not execute install scripts. The user runs the runner's one-time bootstrap (`npx husky init`, `lefthook install`, `pre-commit install`) — the skill names the exact command.
Codex auto-trigger on description keywords is less mature than Claude Code's. If auto-invocation does not fire when the user asks about hooks, pre-commit, or quality gates, invoke the skill manually.
</background_information>
<instructions>
Step 0 — confirm the gates the user wants. WORKFLOW §11 names two tiers:
- Pre-commit (fast): lint, format, secret-scan. Runs on every commit. Keep under ~5s; slow pre-commits push devs to `--no-verify`.
- Pre-push (thorough): build, unit tests, integration tests. Runs on every push. Acceptable to be slow.
- Pre-commit review gate (opt-in third tier — ADR-0047): wire `/ad-review` — or `/ad-audit` for team-bound work — over the staged work before it commits, confirmed findings blocking the commit. Trade-off belongs to the user: front-loads fixing (fits parallel workflows) but bottlenecks a frequent committer. Offer it; never scaffold unasked, never as a default.
- Advisory-first debut for heuristic gates: deterministic toolchain gates (lint, format, build, test) may block from day one — reproducible verdicts. Heuristic or novel gates (secret-scan patterns, the review gate) debut warn-only, with an explicit flip-to-block criterion stated at scaffold time. A gate that blocks before earning trust trains the user to bypass gates in general.
Confirm both tiers are in scope. If the user wants only one, scaffold only that tier.
Visual / E2E for UI projects (Cypress, Playwright) live in CI, not pre-push. Out of scope.
Step 1 — detect the runner. Run the deterministic detector from the consumer repository root:
```bash
node .agents/skills/ad-hooks/scripts/detect-hooks.mjs
```
If this skill was loaded from a different base directory, substitute that base; the bundled script is `scripts/detect-hooks.mjs`. Execute it; do not read it as prose. Its JSON reports `stacks`, `runners`, `prePush` (`files`, gate `commands`), `ci` (`files`, gate `commands`, `matrices`), raw textual `drift.ciOnlyCommands`, and `unreadable`. A non-empty `unreadable` means the scan is partial; name those paths instead of treating them as absent.
Use its facts in this order:
1. Existing runner. `.husky/` → Husky. `lefthook.yml` or `.lefthook.yml` → lefthook. `.pre-commit-config.yaml` → pre-commit. `.git/hooks/` with non-sample scripts → native hooks.
2. Stack signals (if no runner present). `package.json` → recommend Husky or lefthook. `pyproject.toml` → recommend pre-commit. `go.mod` → recommend lefthook. `Cargo.toml` → recommend lefthook. Multiple stacks → recommend lefthook (cross-language by default).
3. No signals. Recommend native `.git/hooks/` only as fallback. Warn the user that native hooks are not portable across clones.
If multiple runners are present, surface the conflict and ask the user before scaffolding. Never silently pick.
Step 2 — recommend the per-stack commands. For the chosen runner, propose the per-tier command set from the command catalog in [references/hook-commands.md](references/hook-commands.md) (Node / Python / Go / Rust). For a mixed / other stack not in the catalog, ask for the per-tier command list. Do not invent.
Offer to swap any default. Confirm before writing.
Step 3 — scaffold the runner config. Write the runner-specific config file using the config shapes in [references/hook-commands.md](references/hook-commands.md) (Husky / lefthook / pre-commit / native — each with its file paths and bootstrap command).
Step 4 — update `AGENTS.md` Quality Gates. Append or refresh the section with: pre-commit gate list, pre-push gate list, runner name + config path, bootstrap command, CI status if known, no-bypass policy. Honor existing managed markers if `ad-bootstrap` already wrote Quality Gates.
Step 5 — mirror CI locally. WORKFLOW §11: "CI failure is a local gate gap." The detector already performed the deterministic scan; do not re-derive it in prose.
1. CI surface and commands. `ci.files` is the detected CI surface; when it is empty, note the gap and stop this step. `ci.commands` contains test / lint / typecheck / build-like `run:` / `script:` commands; `prePush.commands` contains the corresponding local gate commands.
2. Matrix. `ci.matrices` contains inline CI matrix dimensions. Compare them with the actual local runtime the pre-push hook uses; a script cannot infer a developer's installed version. For each meaningful missing dimension, warn: `CI matrix <dim>=<values>, pre-push runs <value>. Failures under <missing-value> will only surface in CI.`
3. Command drift. `drift.ciOnlyCommands` is an exact-text diff. For each command with no wrapper evidence, warn: `CI runs <cmd> — pre-push does not. Add to pre-push or CI will catch what local won't.` A wrapper may be semantically equivalent while spelling the command differently; inspect and state that evidence rather than claiming a gap from raw text alone.
4. Offer to close the gap. Propose specific edits to the runner config; ask the user before writing (matrix mirroring can be expensive).
Step 6 — tell the user the bootstrap command. Output the exact one-line command the user runs. The skill does not execute it.
</instructions>
<output_contract>
Filesystem changes:
- The runner's config file (`.husky/pre-commit`, `lefthook.yml`, `.pre-commit-config.yaml`, or `.git/hooks/`).
- An updated `AGENTS.md` Quality Gates section.
- For native-hooks fallback: a `setup-hooks.sh` script.
The skill does not execute the runner's install command. The skill does not write CI config.
Session-lifecycle hooks are agent-side session events wired in the host's hook configuration. Codex documents lifecycle hooks with the same event vocabulary as Claude Code (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, and others), enabled by default, configured in `<repo>/.codex/hooks.json` or an inline `[hooks]` table in `.codex/config.toml`, and run only after the operator reviews and trusts the exact hook definition through `/hooks`. The tier has three members:
- The Stop handoff nudge (ADR-0055) and the UserPromptSubmit workflow checkpoint (ADR-0074) are wired on Claude Code today; their Codex wiring is a follow-up under ADR-0083. Their scripts ship in both host trees for byte-parity discipline.
- The **artifact-validator `PostToolUse` gate** (ADR-0083, Spec 0008, GROUND-0027) is wired on both hosts. After a skill writes a record under `doc/research/`, the gate routes the file by its first heading (`GROUND-NNNN` to `ad-ground/scripts/validate-record.mjs`, `PRISM-NNNN` to `ad-prism/scripts/validate-plan.mjs`) and, on a validator failure, exits 2 with the validator's message and a reproduction command on stderr, which Codex records as feedback in place of the tool result. A pass, an unowned file, a missing path, or malformed stdin is silent. On Codex the written paths are recovered from the `apply_patch` headers (`*** Add File:`, `*** Update File:`, `*** Move to:`) in `tool_input.command`; the matcher `Edit|Write` is a documented alias for `apply_patch`. Every governed firing appends one JSON line to `<tmpdir>/agentic-artifact-gate/<session_id>.jsonl`, outside the working tree (`AD_ARTIFACT_GATE_EVIDENCE_DIR` redirects it); `AD_ARTIFACT_GATE=0` silences the gate; `AD_ARTIFACT_GATE_SKILLS_ROOT` names the skills root when the sibling skills are installed elsewhere. It is feedback, not enforcement: no decision object, no deny, no `Stop` continuation.
Wiring, merged into `<repo>/.codex/hooks.json`, with `<ad-hooks-dir>` resolved to the directory this skill was loaded from:
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "node \"<ad-hooks-dir>/scripts/artifact-gate.mjs\"", "timeout": 30 }
]
}
]
}
}
```
Scaffold a session-lifecycle hook only when the user asked for it: show the exact merged JSON and the target file, write only after the user approves, then tell the operator to run `/hooks` to review and trust the new definition. A `PreToolUse` guard and any `Stop`-based repair loop remain future scope behind their own decisions (ADR-0083).
Documentation discipline rules apply at write time:
- No emoji anywhere in scaffolded config or AGENTS.md update.
- No version stamps or DRAFT markers.
- Quality Gates section opens with the operational rule (gates are deterministic) before listing the gates.
- One scope: Quality Gates. No duplication of ARCHITECTURE.md or ADR rationale.
- No commented-out scripts. No orphan TODO / FIXME.
</output_contract>
## Next
- Run the runner's bootstrap command (cited in Step 6 — e.g., `npm install`, `lefthook install`, `pre-commit install`).
- Verify a deliberately-failing edit (e.g., a known lint violation) gets blocked at commit.
- Add a redundant CI gate so contributors cannot bypass via `--no-verify`. WORKFLOW §11 binding.
- `/ad-drift` periodically to confirm hooks stay wired as the project evolves.
Ver en GitHub