fix(kyberforge): resolve clean-context audit findings on primitive support

primitive-author:
- description excludes read-only review (-> factory-audit)
- validation Gotcha now matches the research: compile never reads
  prompts, install fails only on a bad Copilot hook payload and warns on
  prompt input names and dropped keys
- instruction fold-in into AGENTS.md/CLAUDE.md stated as conditional on
  dedup and --force-instructions
- hook checklist gains the wrapped-shape Must, drops hardlinks, notes
  why executable is stricter than the research, and states the
  separate Copilot-targeted package route instead of a blanket "don't"
- prompt Must 5 keeps the research's Copilot-only-key exception; adds
  model-slug and 250-char Shoulds; descriptions name skills or agents
- placeholder instruction covers both FILL IN and FILL_IN_ tokens

factory-audit: hardlink FAIL scoped to instructions and prompts
(find_hook_files skips symlinks only), with bats cases; prompt-flow
description rubric names skills or agents.

forge: version-bump, apm-routes and sources references updated for the
primitive route.

Refs #94

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 17:40:12 +00:00
parent 0d96dc8282
commit 9ac5340e15
11 changed files with 76 additions and 36 deletions

View File

@@ -46,7 +46,7 @@ Cite file and line for every finding.
**description**
- SUGGESTION: the description does not read as one user-facing action, or does not name the skills the prompt steers. On Claude the description is model-visible and apm drops `disable-model-invocation`, so naming the steered skills keeps the router pointed at the capability rather than the wrapper.
- SUGGESTION: the description does not read as one user-facing action, or does not name the skills or agents the prompt steers. On Claude the description is model-visible and apm drops `disable-model-invocation`, so naming what it steers keeps the router pointed at the capability rather than the wrapper.
Then return to `SKILL.md` Step 4, opening the report with this coverage line:

View File

@@ -99,12 +99,15 @@ def read_text(path):
return None
def check_not_linked():
# apm's find_files_by_glob rejects symlinks and hardlinks (link count > 1),
# so a linked file is silently never deployed.
def check_not_linked(hardlinks=True):
# apm's find_files_by_glob (instructions, prompts) rejects symlinks and
# hardlinks (link count > 1); find_hook_files skips symlinks only, so hooks
# pass hardlinks=False. A rejected file is silently never deployed.
if os.path.islink(target):
fail(f"is a symlink — apm's discovery skips symlinks, so it is never deployed — {fname}")
return
if not hardlinks:
return
try:
if os.stat(target).st_nlink > 1:
fail(f"is a hardlink (link count > 1) — apm's discovery rejects hardlinks, so it is never deployed — {fname}")
@@ -231,7 +234,7 @@ def check_script(kind_, rel, first, pkg_root, where):
def audit_hook():
check_not_linked()
check_not_linked(hardlinks=False)
stem = fname[:-len('.json')]
hooks_dir = os.path.basename(parent_dir)
if hooks_dir != 'hooks':

View File

@@ -165,6 +165,14 @@ teardown() {
assert_output --partial "is a symlink"
}
@test "hook: a hardlinked hook file is not a FAIL (find_hook_files skips symlinks only)" {
write_hook real.json '{"hooks":{"Stop":[{"hooks":[{"type":"command","command":"true"}]}]}}'
ln "$PKG/.apm/hooks/real.json" "$PKG/.apm/hooks/linked.json"
run bash "$SCRIPT" "$PKG/.apm/hooks/linked.json"
assert_success
refute_output --partial "hardlink"
}
# ---------------------------------------------------------------------------
# Instructions
# ---------------------------------------------------------------------------
@@ -227,6 +235,15 @@ applyTo: "**/*.py"' 'body'
assert_output --partial "also exists at the package root"
}
@test "instruction: a hardlinked instruction file is a FAIL" {
write_instruction python 'description: x
applyTo: "**/*.py"' 'body'
ln "$PKG/.apm/instructions/python.instructions.md" "$TMPDIR/python.instructions.md"
run bash "$SCRIPT" "$PKG/.apm/instructions/python.instructions.md"
assert_failure
assert_output --partial "is a hardlink"
}
# ---------------------------------------------------------------------------
# Prompts
# ---------------------------------------------------------------------------
@@ -314,3 +331,11 @@ $(printf 'line\n%.0s' $(seq 1 80))
assert_success
refute_output --partial "SUGGESTION"
}
@test "prompt: a hardlinked prompt file is a FAIL" {
write_prompt review-pr 'description: Review a pull request with gitea-prs.' 'Review the PR with gitea-prs.'
ln "$PKG/.apm/prompts/review-pr.prompt.md" "$TMPDIR/review-pr.prompt.md"
run bash "$SCRIPT" "$PKG/.apm/prompts/review-pr.prompt.md"
assert_failure
assert_output --partial "is a hardlink"
}