--- source_keys: - apm-cli-installed-source - apm-docs-llms-full --- # Hook Flow Steps 1 to 3 for an apm hook — the target Step 0 matched as a `.json` file directly under a `hooks/` directory. Work them in order, then return to `SKILL.md` Step 4 to report. ## Gotchas - apm checks almost nothing here. Invalid JSON is skipped without a word, an all-lowercase event deploys verbatim and never fires on every target but Kiro (whose map alone renames `stop`), and a missing script only warns — so `apm install` exiting 0 says nothing about whether the hook works. Never cite a clean install as evidence against a finding. - Copilot receiving a Claude-shaped file is not a finding. apm renders one source for every target and documents that it owns the per-target shape; whether Copilot CLI honours a nested entry or `matcher` is unverified upstream, not a defect in the file. - Quoting is not the fix for a script path with a space. apm rewrites a whole-token-quoted `"${PLUGIN_ROOT}/scripts/x.sh"`, but still stops reading the path at the space; the only fix is a path without one. ## Step 1 — Deterministic checks Resolve the path against this skill's own directory. Run exactly: ```bash bash scripts/validate.sh ``` Its findings become the `### Structure` dimension, FAILs and SUGGESTIONs both, at the tier the script assigned: JSON validity, the wrapped-or-naked shape, event lists and nested handler lists (the checks whose failure makes the Copilot install fail), a file contributing no entries (no events, only empty event lists, or an entry with no handler), event names that never fire, unfilled `FILL IN` or `FILL_IN_` template placeholders, a file under apm's deployed output rather than package source, referenced scripts that are missing, outside the package, not executable when run directly, or referenced by an absolute, bare relative, `../`, split-quoted or space-containing path apm will not bundle correctly, deprecated filename routing, and `${CLAUDE_PLUGIN_ROOT}` where `${PLUGIN_ROOT}` would do. Script references are read with apm 0.28.0's own patterns: `${PLUGIN_ROOT}/…` only when the path follows the token directly, up to the first whitespace or quote, and `./…` anywhere in the command. A `./` or `../` match is held to the script rules — a FAIL when missing — only in command position (the first token, or the first argument after `bash`, `sh`, `zsh`, `python`, `python3`, `node`, `pwsh`, `ruby` or `perl`), when it ends in a script extension, or when it names a package entry that is not a file; any other match (`npx prettier --check ./src`, `printf '.\n'`) is at most a SUGGESTION, because apm only warns and it runs against the consumer's working directory as meant. Absolute and bare relative script paths are checked in the same command positions. An all-lowercase event FAILs only when no target the package deploys to renames it, and is a SUGGESTION when only some do. A camelCase event outside Claude's rename map FAILs in a Claude-shaped file, and in a flat Copilot-shaped file too whenever the nearest `apm.yml` above the file targets Claude — no `target:`/`targets:` means every target. It exits **0** with no FAIL, **1** on real findings, **2** when it never ran — report that as `### Structure` unverified, quoting the stderr reason. An INFO line is observational: report it under `### Structure` and count it in `· P info`. There is no provenance and no Vale step: a hook carries no `source_keys` and no prose. Three tiers deliberately differ from `primitive-author`'s checklist or the research's. Do not re-tier them by judgment: - A hook file directly under a package-root `hooks/` — beside the package's `apm.yml` — passes. apm discovers both `.apm/hooks/` and `hooks/`, and this audit may target a third-party package; `primitive-author` authors only in `.apm/hooks/`. Any other `hooks/` directory (`.github/hooks/`, `.cursor/hooks/`, …) is apm's deployed output and FAILs. - Deprecated filename routing is a SUGGESTION, matching the author's Should: the research allows it when deprecated routing is intended. - A non-executable script run as the command's first token is a FAIL, stricter than the research's Should, because it fails every time it fires. ## Step 2 — Read the hook and its scripts Read the hook file, every script it references, and the package's `apm.yml` `targets:` — reach is narrowed there, never in the hook file. ## Step 3 — Qualitative audit Cite file and line for every finding. **purpose** — apm's own rule is to reach for a skill, instruction or prompt first; a hook is for "this must always happen at this event". - FAIL: the script carries procedure the agent should follow — instructions printed to the model, a multi-step workflow — rather than a runtime callback. That is a skill. - SUGGESTION: the behaviour is harness-specific (a Claude-only event, a Claude-only matcher value) in a package whose `targets:` includes other harnesses, and nothing records that the other targets receiving it was accepted. The apm-native fix is a separate package with its own `targets:`, not a routing filename. **handlers** — the research checklist's Should and audit-only items, which apm never checks: - SUGGESTION: a handler without `"type": "command"` or an explicit numeric `timeout` in seconds. - SUGGESTION: a tool event (`PreToolUse`, `PostToolUse`) or `SessionStart` with no `matcher` — Claude receives `"*"`. A `matcher` on an event Claude ignores it for (`Stop`, `UserPromptSubmit`) is inert, not wrong. - SUGGESTION: a PascalCase event name that is not a real Claude Code event (a misspelling deploys verbatim and never fires; the script cannot tell a typo from an event it does not know). - SUGGESTION: `bash`/`powershell`/`timeoutSec` keys in a Claude-shaped file — they render, but leave stray keys in `settings.json`. - SUGGESTION: a helper `.json` file in the hook directory without a `hooks` key — Copilot's loader scans the bundled scripts directory and rejects it. Keep helper configuration non-JSON. Then return to `SKILL.md` Step 4, opening the report with this coverage line: ```text Checked: structure · purpose · handlers ```