fix(kyberforge): resolve PR #144 review and audit round 3

- factory-audit: hook events judged per deployed target after apm's
  rename (Claude/Copilot event sets FAIL, others SUGGESTION); Claude
  plugin layouts accepted as hook sources; interpreter options and
  sh -c strings checked; bats 378 -> 386
- primitive-author: Must 4/5 match the audit; reference hand-back
  points at the right steps; Step 4.2 --target all fallback
- skill-author: new-skill.sh repair only on the template marker line,
  so complete skills stay a no-op; provenance and calibration text
- forge: restore "already named" qualifier; drop false HITL claim
- apm-workflow: token example uses an env var
- docs/hooks.md: the apm-hooks.json sidecar is committed, not ignored

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkT7RSDwDbmrM9T34b6sTi
This commit is contained in:
2026-09-28 21:32:18 +00:00
parent 965208bddd
commit 5d0f988ed8
18 changed files with 395 additions and 140 deletions

View File

@@ -95,36 +95,72 @@ teardown() {
assert_output --partial "naked settings-slice shape"
}
@test "hook: an all-lowercase event no target maps is a FAIL" {
@test "hook: an all-lowercase event no target fires is a FAIL naming the targets" {
write_hook hooks.json '{"hooks":{"pretooluse":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'pretooluse' is all-lowercase — no target this package deploys to maps it"
assert_output --partial "event 'pretooluse' never fires on claude, copilot"
}
@test "hook: lowercase stop is a SUGGESTION for every target, clean for Kiro only, a FAIL without Kiro" {
@test "hook: lowercase stop with no targets: is a FAIL — Claude and Copilot never fire it" {
write_hook hooks.json '{"hooks":{"stop":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
assert_output --partial "SUGGESTION event 'stop' is all-lowercase — only Kiro renames it"
printf 'name: test-package\nversion: 0.1.0\ntargets: [kiro]\n' > "$PKG/apm.yml"
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "all-lowercase"
assert_failure 1
assert_output --partial "event 'stop' never fires on claude, copilot"
printf 'name: test-package\nversion: 0.1.0\ntargets: [claude, copilot]\n' > "$PKG/apm.yml"
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'stop' is all-lowercase — no target this package deploys to maps it"
assert_output --partial "event 'stop' never fires on claude, copilot"
}
@test "hook: camelCase userPromptSubmit in a Claude-shaped file is a FAIL; mapped sessionStart is not" {
@test "hook: lowercase stop is clean for Kiro only (Kiro renames it to Stop)" {
printf 'name: test-package\nversion: 0.1.0\ntargets: [kiro]\n' > "$PKG/apm.yml"
write_hook hooks.json '{"hooks":{"stop":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "event 'stop'"
}
@test "hook: lowercase stop in a Cursor-only package passes, at most a SUGGESTION (no published Cursor event list)" {
printf 'name: test-package\nversion: 0.1.0\ntargets: [cursor]\n' > "$PKG/apm.yml"
write_hook hooks.json '{"hooks":{"stop":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "FAIL"
assert_output --partial "SUGGESTION event 'stop' reaches cursor verbatim"
}
@test "hook: a Windsurf-only snake_case event is a SUGGESTION, not a FAIL" {
printf 'name: test-package\nversion: 0.1.0\ntargets: [windsurf]\n' > "$PKG/apm.yml"
write_hook hooks.json '{"hooks":{"pre_run_command":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
assert_output --partial "SUGGESTION event 'pre_run_command' reaches windsurf verbatim"
}
@test "hook: a misspelled PascalCase event targeting Claude is a FAIL" {
printf 'name: test-package\nversion: 0.1.0\ntargets: [claude]\n' > "$PKG/apm.yml"
write_hook hooks.json '{"hooks":{"PreToolUSe":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'PreToolUSe' never fires on claude"
}
@test "hook: a real Claude event apm does not rename for Copilot FAILs on Copilot only when Copilot does not fire it" {
write_hook hooks.json '{"hooks":{"SubagentStop":[{"hooks":[{"type":"command","command":"true"}]}],"PostToolBatch":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
refute_output --partial "event 'SubagentStop'"
assert_output --partial "event 'PostToolBatch' never fires on copilot"
}
@test "hook: camelCase userPromptSubmit is a FAIL on Claude; mapped sessionStart is not" {
write_hook hooks.json '{"hooks":{"userPromptSubmit":[{"hooks":[{"type":"command","command":"true"}]}],"sessionStart":[{"hooks":[{"type":"command","command":"true"}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'userPromptSubmit' is camelCase"
refute_output --partial "event 'sessionStart'"
assert_output --partial "event 'userPromptSubmit' never fires on claude"
refute_output --partial "FAIL event 'sessionStart'"
}
@test "hook: a flat Copilot-shaped file may use camelCase events when the package does not target Claude" {
@@ -132,7 +168,7 @@ teardown() {
write_hook hooks.json '{"hooks":{"userPromptSubmit":[{"type":"command","bash":"true","timeoutSec":5}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "camelCase"
refute_output --partial "never fires"
}
@test "hook: camelCase in a flat file is a FAIL when the package targets Claude" {
@@ -140,17 +176,17 @@ teardown() {
write_hook hooks.json '{"hooks":{"userPromptSubmit":[{"type":"command","bash":"true","timeoutSec":5}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'userPromptSubmit' is camelCase and the package's apm.yml targets Claude"
assert_output --partial "event 'userPromptSubmit' never fires on claude —"
}
@test "hook: camelCase in a flat file is a FAIL when apm.yml declares no targets (every target)" {
write_hook hooks.json '{"hooks":{"userPromptSubmit":[{"type":"command","bash":"true","timeoutSec":5}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "event 'userPromptSubmit' is camelCase"
assert_output --partial "event 'userPromptSubmit' never fires on claude"
}
@test "hook: targets are read from the nearest apm.yml walking up, and target: all counts as Claude" {
@test "hook: targets are read from the package root's apm.yml, and target: all counts as Claude" {
mkdir -p "$PKG/sub/hooks"
printf 'name: sub\nversion: 0.1.0\ntarget: copilot\n' > "$PKG/sub/apm.yml"
printf '%s\n' '{"hooks":{"userPromptSubmit":[{"type":"command","bash":"true","timeoutSec":5}]}}' > "$PKG/sub/hooks/hooks.json"
@@ -160,7 +196,7 @@ teardown() {
printf 'name: sub\nversion: 0.1.0\ntarget: all\n' > "$PKG/sub/apm.yml"
run bash "$SCRIPT" "$PKG/sub/hooks/hooks.json"
assert_failure 1
assert_output --partial "is camelCase"
assert_output --partial "never fires on claude"
}
@test "hook: a referenced script that does not exist is a FAIL" {
@@ -443,6 +479,62 @@ teardown() {
assert_output --partial "is no package source"
}
@test "hook: a Claude-plugin layout (hooks/ beside .claude-plugin/plugin.json, no apm.yml) is package source" {
mkdir -p "$TMPDIR/cplug/.claude-plugin" "$TMPDIR/cplug/hooks" "$TMPDIR/cplug/scripts"
printf '{"name":"cplug"}\n' > "$TMPDIR/cplug/.claude-plugin/plugin.json"
printf '#!/usr/bin/env bash\nexit 0\n' > "$TMPDIR/cplug/scripts/ok.sh"
chmod +x "$TMPDIR/cplug/scripts/ok.sh"
printf '%s\n' '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"${PLUGIN_ROOT}/scripts/ok.sh","timeout":5}]}]}}' > "$TMPDIR/cplug/hooks/hooks.json"
run bash "$SCRIPT" "$TMPDIR/cplug/hooks/hooks.json"
assert_success
refute_output --partial "FAIL"
rm -rf "$TMPDIR/cplug/.claude-plugin"
printf '{"name":"cplug"}\n' > "$TMPDIR/cplug/plugin.json"
run bash "$SCRIPT" "$TMPDIR/cplug/hooks/hooks.json"
assert_success
refute_output --partial "no package source"
}
@test "hook: interpreter options are skipped to reach the script (bash -e, python3 -u)" {
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"bash -e scripts/check.sh","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "script 'scripts/check.sh' is a bare relative path"
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"python3 -u /opt/hook.py","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "script '/opt/hook.py' is an absolute path"
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"bash -e ./gone.sh","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "script 'gone.sh' does not exist"
}
@test "hook: sh -c checks the first token of its command string; inline code (python3 -c) is not a script" {
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"sh -c scripts/check.sh","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "script 'scripts/check.sh' is a bare relative path"
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"sh -c \"/opt/x.sh --flag\"","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_failure 1
assert_output --partial "script '/opt/x.sh' is an absolute path"
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"python3 -c \"import sys; sys.exit(0)\"","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "FAIL"
write_hook hooks.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"bash -e \"${PLUGIN_ROOT}/.apm/hooks/scripts/check.sh\"","timeout":5}]}]}}'
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"
assert_success
refute_output --partial "FAIL"
}
@test "hook: an unedited primitive-author hook template is an unfilled-placeholder FAIL" {
cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/primitive-author/assets/templates/hook.json.template" "$PKG/.apm/hooks/hooks.json"
run bash "$SCRIPT" "$PKG/.apm/hooks/hooks.json"