Files
holocron/plugins/kyberforge/.apm/skills/factory-audit/references/hook-flow.md
Defame1297 965208bddd fix(kyberforge): resolve PR #144 review and audit round 2
- factory-audit: ./ and bare/absolute script checks scoped to command
  position (no false FAILs on ./src or printf); hook sources limited to
  .apm/hooks or package-root hooks/; Kiro-aware lowercase events;
  unfilled template placeholders FAIL; repo-only instructions FAIL at
  any scope; Vale description FAIL documented; bats 367 -> 378
- primitive-author: split-quote/spaced paths and handler-less entries
  promoted to Must; Step 4.2 renders into a scratch consumer instead of
  a no-op dry run; dispatch and gate hand-off trimmed
- apm-workflow 1.0.2: mutual boundary with primitive-author
- forge: no double package bump; gotcha wording
- skill-author: create keeps seeded 0.1.0 (ADR-0022); portable,
  retry-safe new-skill.sh; template and flow consistency fixes
- hook docs: cite the ADR-0019 correction; guard caveat

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

6.1 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 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 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, 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:

Checked: structure · purpose · handlers