- 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
6.1 KiB
source_keys
| source_keys | ||
|---|---|---|
|
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 — soapm installexiting 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
matcheris 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'sapm.yml— passes. apm discovers both.apm/hooks/andhooks/, and this audit may target a third-party package;primitive-authorauthors only in.apm/hooks/. Any otherhooks/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 owntargets:, 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 numerictimeoutin seconds. - SUGGESTION: a tool event (
PreToolUse,PostToolUse) orSessionStartwith nomatcher— Claude receives"*". Amatcheron 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/timeoutSeckeys in a Claude-shaped file — they render, but leave stray keys insettings.json. - SUGGESTION: a helper
.jsonfile in the hook directory without ahookskey — 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