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
This commit is contained in:
@@ -15,8 +15,8 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `apm compile --validate` is not a gate: it only warns on instructions, exits 0, and never reads prompts. `apm install` exits 1 only on a hook payload Copilot would reject or on critical hidden Unicode, which also blocks the package's deployment; bad prompt input names and dropped keys merely warn — `/factory-audit` is the only check that fails on the rest.
|
||||
- Never draft with the real suffix outside `.apm/<type>/`. apm's local discovery globs `**/*.instructions.md` across the whole tree, so a draft or template named that way anywhere in the repo is picked up as a real instruction. The templates carry a trailing `.template` for this reason; drop it only on the final path.
|
||||
- `apm compile --validate` is not a gate: it only warns on instructions, exits 0, and never reads prompts. `apm install` exits 1 only on a hook payload Copilot would reject or on critical hidden Unicode; bad prompt input names and dropped keys merely warn — `/factory-audit` is the only check that fails on the rest.
|
||||
- Never draft with the real suffix outside `.apm/<type>/`. apm's local discovery globs `**/*.instructions.md` across the whole tree, so a draft named that way anywhere in the repo becomes a real instruction. The templates carry a trailing `.template` for this reason; drop it only on the final path.
|
||||
- Never hand-write `.claude/settings.json`, even to test a hook. apm owns that file (ADR-0019), overwrites it outright when it is malformed, and `apm audit --ci` fails on anything it would not have written.
|
||||
|
||||
## Step 1 — Dispatch
|
||||
@@ -26,13 +26,12 @@ metadata:
|
||||
| A hook — `.apm/hooks/<name>.json`, or "run X whenever Y happens" | hook | `references/hook.md` |
|
||||
| An instruction — `.apm/instructions/<name>.instructions.md`, or a rule for files matching a pattern | instruction | `references/instruction.md` |
|
||||
| A prompt — `.apm/prompts/<name>.prompt.md`, or a reusable message the user types to kick off work | prompt | `references/prompt.md` |
|
||||
| A skill or an agent | — | stop: route to `skill-author` or `agent-author` |
|
||||
|
||||
Read only the reference matching the resolved type — each is self-contained. If the target sits inside a git worktree, capture `rtk git log --oneline -1` before touching the filesystem; Step 4 needs it.
|
||||
|
||||
## Step 2 — Boundary gate
|
||||
|
||||
Run the reference's **Gate** section before writing anything. A failed gate stops this skill: name the owner it points to — `skill-author` for procedure, `agentsmd-author` for a repo-only rule, `apm-workflow` for reach or `targets:` — and hand over. Never bend the artifact to pass the gate.
|
||||
Run the reference's **Gate** section before writing anything. A failed gate stops this skill: hand over to the owner the Gate names. Never bend the artifact to pass the gate.
|
||||
|
||||
## Step 3 — Create or improve
|
||||
|
||||
@@ -42,11 +41,11 @@ Run the reference's **Gate** section before writing anything. A failed gate stop
|
||||
| File exists, at least one signal | Improve: read the whole file, then apply each signal against the reference's checklist |
|
||||
| File exists, no signal | Stop and ask whether the user meant a new file or has feedback to apply |
|
||||
|
||||
Signals: grill output, `/factory-audit` findings, inline feedback, session context describing what went wrong. Group findings by root cause and fix the cause once.
|
||||
Signals: grill output, `/factory-audit` findings, inline feedback, session context. Group findings by root cause and fix the cause once.
|
||||
|
||||
## Step 4 — Validate and close
|
||||
|
||||
1. Run `/factory-audit` on the file, inline in this context; resolve every FAIL before reporting done, including the `### Prose` FAILs Vale raises on an instruction or prompt body.
|
||||
2. Run `rtk apm install --dry-run` from the repo root and read what each target will receive. On a feature branch, discard `apm.lock.yaml` churn afterwards (`rtk git checkout -- apm.lock.yaml`).
|
||||
3. Bump the owning package's `apm.yml` `version:` — minor for a new hook, instruction or prompt, patch for a fix — unless this branch already bumped it for unreleased work. None of these has a version of its own.
|
||||
4. **Commit verification.** Inside a git worktree, once the audit is clean, run `rtk git add` and `rtk git commit`, then re-run `rtk git log --oneline -1` and confirm the hash changed from Step 1's. Staged-but-uncommitted work is silently lost if the tree is cleaned up. Outside a worktree, report done on a clean audit and name that as the reason.
|
||||
1. Run `/factory-audit` on the file, inline in this context; resolve every FAIL before reporting done, including the `### Prose` FAILs Vale raises on an instruction or prompt body or description.
|
||||
2. Render it: in a fresh `mktemp -d` directory, run `rtk apm install <absolute path to the owning package> --target <its targets:>`, then read what each target received — `.claude/settings.json` and `.github/hooks/`, `.claude/rules/` and `.github/instructions/`, or `.claude/commands/` and `.github/prompts/`. A local path deploys the working tree; `--dry-run` renders nothing, and a repo-root install resolves the remote's `main`, so never use either.
|
||||
3. Bump the owning package's `apm.yml` `version:` — minor for a new hook, instruction or prompt, patch for a fix — unless this branch already bumped it for unreleased work. None of these is released on its own version — bump the package even if an instruction carries an optional `version:` key.
|
||||
4. **Commit verification.** Inside a git worktree, once the audit is clean, run `rtk git add` and `rtk git commit`, then confirm `rtk git log --oneline -1` changed from Step 1's hash: staged-but-uncommitted work is lost if the tree is cleaned up. Outside a worktree, report done on a clean audit and name that as the reason.
|
||||
|
||||
@@ -48,7 +48,7 @@ target:
|
||||
- **`${PLUGIN_ROOT}`** is the target-neutral token; apm rewrites it per target
|
||||
(`"${CLAUDE_PROJECT_DIR}/.claude/hooks/<pkg>/…"` on Claude, repo-relative elsewhere).
|
||||
`${CLAUDE_PLUGIN_ROOT}` is rewritten identically, so it is valid, but it ties the source to one
|
||||
harness's name (Should 12).
|
||||
harness's name (Should 11).
|
||||
- **Claude is the verified target.** apm 0.28.0 passes this nested shape to Copilot without
|
||||
reshaping it, and whether Copilot CLI runs nested entries or honours `matcher` is unverified. That
|
||||
gap is apm's to close. Per-file target routing is deprecated, so a Copilot-native flat hook
|
||||
@@ -65,15 +65,25 @@ Must:
|
||||
2. Use the wrapped shape `{"hooks": {Event: [...]}}`. If a naked settings slice is used instead,
|
||||
every top-level value must be a list, with no stray scalar keys anywhere.
|
||||
3. Every event value is a list of objects, and every nested `hooks` is a list of objects. Anything
|
||||
else fails the Copilot install outright. The file contributes at least one entry: an empty one
|
||||
deploys nothing, with only a warning.
|
||||
else fails the Copilot install outright. The file contributes at least one entry, and every
|
||||
entry carries at least one handler: an empty list or a handler-less entry deploys nothing, with
|
||||
only a warning.
|
||||
4. Event names are PascalCase (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `SessionStart`,
|
||||
`Stop`, …). An all-lowercase name (`stop`) never warns and never fires; a camelCase name outside
|
||||
apm's rename map (`userPromptSubmit`) deploys verbatim to Claude and never fires.
|
||||
`Stop`, …). An all-lowercase name never warns: only Kiro's rename map covers one (`stop` →
|
||||
`Stop`), and every other target deploys it verbatim, where it never fires. A camelCase name
|
||||
outside apm's rename map (`userPromptSubmit`) likewise deploys verbatim to Claude and never
|
||||
fires.
|
||||
5. The script is referenced as `${PLUGIN_ROOT}/…` (or `${CLAUDE_PLUGIN_ROOT}/…`, see Shape) for
|
||||
the package root, or `./…` for the hook directory, and exists inside the package. No absolute
|
||||
path and no bare relative path (`scripts/x.sh`): apm bundles and rewrites neither. No `$` or
|
||||
backtick in the path itself. A missing script is only a warning at install time.
|
||||
the package root, or `./…` for the hook directory, and exists inside the package. The script is
|
||||
the command's first token or the first argument after an interpreter (`bash`, `sh`, `zsh`,
|
||||
`python`, `python3`, `node`, `pwsh`, `ruby`, `perl`); in either position, no absolute path and
|
||||
no bare relative path (`scripts/x.sh`): apm bundles and rewrites neither. No `$` or backtick in
|
||||
the path itself, and no space. When quoting, quote the whole token —
|
||||
`"${PLUGIN_ROOT}/scripts/my-hook.sh"`, never `"${PLUGIN_ROOT}"/scripts/x.sh`: apm rewrites
|
||||
`${PLUGIN_ROOT}` only when a path separator follows it directly, and only up to the next space
|
||||
or quote, so a split quote is left unrewritten and a spaced path is cut short. This is stricter
|
||||
than the research's Should, as with Must 6: either defect fails every time the hook fires. A
|
||||
missing script is only a warning at install time.
|
||||
6. A script run directly as the command's first token is executable. This is stricter than the
|
||||
research's Should: without it the hook fails every time it fires. A script passed to an
|
||||
interpreter (`bash ${PLUGIN_ROOT}/x.sh`) needs no executable bit.
|
||||
@@ -86,14 +96,10 @@ Should:
|
||||
Claude receives `"*"`.
|
||||
9. Do not author Copilot's flat `bash` / `powershell` / `timeoutSec` keys in a Claude-shaped file;
|
||||
they render onto Claude as stray keys.
|
||||
10. Keep script paths free of spaces, and when quoting, quote the whole token:
|
||||
`"${PLUGIN_ROOT}/scripts/my-hook.sh"`. apm rewrites `${PLUGIN_ROOT}` only when a path separator
|
||||
follows it directly, and only up to the next space or quote — so `"${PLUGIN_ROOT}"/scripts/x.sh`
|
||||
is left unrewritten and a spaced path is cut short.
|
||||
11. Keep helper files in the hook directory non-JSON. Copilot's loader rejects any bundled `.json`
|
||||
10. Keep helper files in the hook directory non-JSON. Copilot's loader rejects any bundled `.json`
|
||||
without a `hooks` key.
|
||||
12. Prefer `${PLUGIN_ROOT}` over `${CLAUDE_PLUGIN_ROOT}`.
|
||||
13. No filename that routes by target. Case-insensitively, apm routes a stem of exactly
|
||||
11. Prefer `${PLUGIN_ROOT}` over `${CLAUDE_PLUGIN_ROOT}`.
|
||||
12. No filename that routes by target. Case-insensitively, apm routes a stem of exactly
|
||||
`hooks-<target>` and any stem ending `<target>-hooks` — bare (`claude-hooks`), prefixed
|
||||
(`x-claude-hooks`) or combined (`claude-codex-hooks`, the union). That routing is deprecated and
|
||||
reach belongs to `targets:` (see Gate); the research allows it only when deprecated routing is
|
||||
|
||||
@@ -18,8 +18,8 @@ glob. On Claude it deploys to `.claude/rules/<stem>.md` with `applyTo` renamed t
|
||||
always-on source. Stop and hand to `agentsmd-author`.
|
||||
- **No file pattern fits** → an instruction without `applyTo` is always-on in every session of
|
||||
every repo that installs this package, and `apm compile` can fold it into the global sections of
|
||||
`AGENTS.md` and `CLAUDE.md` (skipped when `.github/instructions/` or `.claude/rules/` is already
|
||||
populated, unless `--force-instructions`). Say exactly that to the user and continue only on an explicit yes.
|
||||
`AGENTS.md` and `CLAUDE.md` (CLAUDE.md is skipped when `.claude/rules/` is populated, AGENTS.md
|
||||
when `.github/instructions/` is, unless `--force-instructions`). Say exactly that to the user and continue only on an explicit yes.
|
||||
Legitimate when a package deliberately ships guidance to its consumers; never a default.
|
||||
- **Procedure the agent follows step by step** → a skill. Stop and hand to `skill-author`.
|
||||
- **A rule scoped to a file pattern** → continue.
|
||||
@@ -47,8 +47,8 @@ Should:
|
||||
its handling of a list is unverified.
|
||||
7. Keep frontmatter to `description` and `applyTo`, plus optional `author` and `version`. No target
|
||||
consumes other keys, and Claude drops them.
|
||||
8. Put any rationale Claude needs in the body. `description` never reaches Claude — it survives only
|
||||
for Copilot and as Cursor's index text.
|
||||
8. Put any rationale Claude needs in the body. `description` never reaches Claude — it survives for
|
||||
Copilot and as index text in Cursor rules and compiled AGENTS.md/CLAUDE.md.
|
||||
9. Keep relative markdown links resolvable from the source file.
|
||||
10. Check the glob against the tree: one that matches nothing here fires only in consumer repos
|
||||
that have such files, and one broader than the rule's real scope spends context on every file
|
||||
|
||||
@@ -56,6 +56,8 @@ Should:
|
||||
5. Frontmatter keys stay within `description`, `allowed-tools`, `model`, `argument-hint` and
|
||||
`input`. Claude drops everything else with only a warning. The exception: a Copilot-only key
|
||||
(`agent`, `tools`, …) that is intended, with its Claude drop accepted and said so.
|
||||
The research files this as a Must; it is a Should here because only the author can say a
|
||||
Copilot-only key is intended.
|
||||
6. The description follows the contract above: one plain sentence, no trigger or boundary clause,
|
||||
naming the skills or agents it steers.
|
||||
7. Spell keys in kebab-case — `allowed-tools`, `argument-hint` — not the camelCase aliases.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- **URL:** file:///root/.local/pipx/venvs/apm-cli/lib/python3.11/site-packages/apm_cli/
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md
|
||||
- **Description:** Installed apm-cli 0.28.0 source — ground truth for what apm deploys from a hook, instruction or prompt file and what it silently skips or only warns on; every Must/Should checklist item traces to the research docs' Authoring checklists, and the `**/*.instructions.md` local-discovery glob behind the template-suffix Gotcha is `primitives/discovery.py` `LOCAL_PRIMITIVE_PATTERNS`
|
||||
- **Description:** Installed apm-cli 0.28.0 source — ground truth for what apm deploys from a hook, instruction or prompt file and what it silently skips or only warns on; each Must/Should traces to the research docs' Authoring checklists or audit-only lists, or to ADR-0029, with tier moves annotated inline, and the `**/*.instructions.md` local-discovery glob behind the template-suffix Gotcha is `primitives/discovery.py` `LOCAL_PRIMITIVE_PATTERNS`; the per-target event rename maps are `integration/hook_integrator.py` `_HOOK_EVENT_MAP`; and Step 4.2's render relies on `apm install <local path>` deploying the working tree to every `--target`, verified against 0.28.0
|
||||
- **Contributing files:** SKILL.md, references/hook.md, references/instruction.md, references/prompt.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user