Files
holocron/plugins/kyberforge/.apm/skills/factory-audit/references/hook-flow.md
Defame1297 df28351d3e fix(kyberforge): resolve PR #144 review and audit round 1
- factory-audit: no-op hooks, ./ after interpreters, split-quote and
  spaced ${PLUGIN_ROOT} paths, camelCase events in Claude-targeted flat
  files, case-insensitive routing stems, and non-string YAML keys are
  now caught; input: forms and prompt boundary clauses align with
  primitive-author; bats 347 -> 367
- primitive-author: routing forms, quoting guidance, install exit on
  hidden Unicode, argument-hint exception
- forge: drop duplicated gotcha, fit description and body budgets (#143)
- skill-author: primitive-author boundary, Claude-only env vars
- hook: exit unless CLAUDE_PROJECT_DIR is set, so Copilot/Codex never
  run apm update; ADR-0019 correction, ADR-0025 amendment, docs fixes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkT7RSDwDbmrM9T34b6sTi
2026-09-28 20:13:43 +00:00

5.2 KiB

source_keys
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 and never fires, 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 scripts/validate.sh <hook-file>

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, 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, including after an interpreter. 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.

There is no provenance and no Vale step: a hook carries no source_keys and no prose.

Four 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/ passes. apm discovers both .apm/hooks/ and hooks/, and this audit may target a third-party package; primitive-author authors only in .apm/hooks/.
  • 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.
  • A split-quoted "${PLUGIN_ROOT}"/x.sh or a script path containing a space is a FAIL, stricter than the author's Should 10: apm leaves the first unrewritten and cuts the second at the space, so either deploys a hook that 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:

Checked: structure · purpose · handlers