feat(kyberforge): primitive-author and factory-audit support for apm hooks, instructions and prompts #144
38
CONTEXT.md
38
CONTEXT.md
@@ -48,7 +48,31 @@ _Avoid_: agent hygiene
|
||||
A reusable slash command defined as a `SKILL.md` file following the
|
||||
[Agent Skills open standard](https://agentskills.io), authored at
|
||||
`plugins/<plugin>/.apm/skills/<skill>/SKILL.md`.
|
||||
_Avoid_: command, prompt, macro
|
||||
_Avoid_: command, macro; and "prompt" for a skill — a **Prompt** is a different artifact
|
||||
|
||||
**Prompt**:
|
||||
A single-intent, user-triggered message with parameters, authored as
|
||||
`plugins/<plugin>/.apm/prompts/<name>.prompt.md` — the text a user would otherwise type repeatedly.
|
||||
It carries no procedure beyond steering existing skills or agents by name; once it holds reusable
|
||||
know-how, bundled files, or anything the model should find on its own, it is a **Skill** in the
|
||||
wrong container. This is a house rule, stricter than apm, which frames a prompt as a full workflow.
|
||||
_Avoid_: command (the Claude-side deployed form), workflow, macro
|
||||
|
||||
**Instruction**:
|
||||
A scoped rule authored as `plugins/<plugin>/.apm/instructions/<name>.instructions.md`, applied when
|
||||
the agent touches files matching its `applyTo` glob. Omitting `applyTo` makes it always-on in every
|
||||
session of every repo that installs the package — a legitimate way for a package to ship guidance to
|
||||
consumers, but a deliberate choice, never a default. A rule for this repo alone belongs in
|
||||
**AGENTS.md**, not in an instruction.
|
||||
_Avoid_: rule (the Claude-side deployed form under `.claude/rules/`), guideline, standard
|
||||
|
||||
**Hook**:
|
||||
A runtime callback a harness fires inside its own tool loop, authored as one JSON file per concern
|
||||
under `plugins/<plugin>/.apm/hooks/` in apm's canonical shape — nested entries, PascalCase events,
|
||||
`${PLUGIN_ROOT}` script paths — which apm renders per target. Reach is narrowed in the package's
|
||||
`apm.yml` `targets:`, never by filename. The last resort among apm primitives: procedure belongs in
|
||||
a **Skill**, and a hook is only for "this must always happen at this event".
|
||||
_Avoid_: trigger, callback script (the script is the hook's payload, not the hook)
|
||||
|
||||
**apm package**:
|
||||
The deployable unit apm builds and installs — one or more skills, agents, hooks, commands, and MCP
|
||||
@@ -58,6 +82,13 @@ _Avoid_: bundle, module, source tree; and bare "plugin" for the *installable art
|
||||
ADR-0024 is an apm package and not a Claude Code plugin. "Plugin" stays correct as a modifier in the
|
||||
repo's settled compounds — **Plugin marketplace**, "plugin units", `plugins/`.
|
||||
|
||||
**apm primitive**:
|
||||
Any content type authored under `plugins/<name>/.apm/` — skills, agents, hooks, instructions, and
|
||||
prompts. Skills and agents each have their own author skill; `primitive-author` covers the other
|
||||
three, so its name is narrower in practice than the term. `factory-audit` audits all five.
|
||||
_Avoid_: component, asset, artifact (unqualified); bare "primitive" when only the three
|
||||
non-skill, non-agent types are meant — say "hook, instruction, or prompt"
|
||||
|
||||
**Output profile**:
|
||||
A named ecosystem format `apm pack` can compile the marketplace manifest into, declared per profile
|
||||
under root `apm.yml`'s `marketplace.outputs:`. apm defines `claude` and `codex`; each writes to its
|
||||
@@ -193,3 +224,8 @@ _Avoid_: namespace, category
|
||||
an audit running in the same context as the work it checks shares that work's blind spots. The
|
||||
recheck belongs to `factory-audit`, not to `forge`, which routes only to the author
|
||||
skills and never to an audit.
|
||||
- "prompt" meant both apm's `.prompt.md` primitive and, loosely, any slash command or a skill — resolved:
|
||||
a **Prompt** is the `.prompt.md` primitive under the house rule above. apm calls a prompt "a
|
||||
callable program for an LLM", but on Claude it deploys as a model-invocable command with fewer
|
||||
frontmatter keys than a skill, and Codex receives nothing. A fat prompt is a worse skill on every
|
||||
harness, so the procedure goes in the skill and the prompt only steers it.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Prompts are thin, user-triggered steering messages; procedure belongs in a skill
|
||||
|
||||
**Status: accepted (2026-09-28).** Refs #94. Sets the house rule that `primitive-author` enforces
|
||||
when it writes a `.prompt.md`, and that `factory-audit` checks in its prompt flow.
|
||||
|
||||
A **Prompt** (the term is in `CONTEXT.md`) is a single-intent, user-triggered message with
|
||||
parameters. It is the text a user would otherwise type again and again. It carries no procedure
|
||||
beyond steering existing skills or agents by name. Once it holds reusable know-how, bundled files,
|
||||
or anything the model should find on its own, it is a **Skill** in the wrong container.
|
||||
|
||||
This is stricter than apm. apm's docs call a prompt "a callable program for an LLM", and 0.28.0
|
||||
scaffolds one as a numbered-steps workflow (`apm_cli/workflow/discovery.py`). On every harness this
|
||||
repo targets, a prompt with a full workflow in it is a worse skill:
|
||||
|
||||
- **Claude Code.** Custom commands have been merged into skills. "Both create `/deploy` and work
|
||||
the same way", and both are model-invocable by default (code.claude.com/docs/en/skills.md).
|
||||
apm 0.28.0 keeps only `description`, `allowed-tools`, `model`, `argument-hint` and `input` for
|
||||
Claude (`_PRESERVED_COMMAND_KEYS`) and drops `disable-model-invocation`. A deployed prompt is
|
||||
therefore a model-visible skill with fewer frontmatter keys, and it cannot be made user-only.
|
||||
- **Copilot / VS Code.** Prompt files are marked deprecated for Agent Host, and VS Code offers a
|
||||
migration to agent skills.
|
||||
- **Codex.** Codex receives no prompts at all.
|
||||
|
||||
The one job a prompt does better than a skill is a short, parameterised "do this now, using X and Y"
|
||||
message, where `input:` → `$arguments` is the whole point.
|
||||
|
||||
## Description contract
|
||||
|
||||
A prompt's `description` is one plain, human-facing sentence that names the skills or agents it
|
||||
steers. For example: "Review the current PR with `gitea-prs` and `factory-audit`, then summarise."
|
||||
It has no "Use when…" trigger clause and no `Not X -> Y` boundary clause. This is the same shape
|
||||
`factory-audit` already applies to `disable-model-invocation: true` skills. Without a trigger clause,
|
||||
the router has little reason to pick the prompt over the skills it wraps. So when the model routes,
|
||||
it tends to reach the real capability.
|
||||
|
||||
**Unverified:** how strongly Claude avoids routing to a description with no trigger clause. The
|
||||
contract lowers the chance of the model invoking the prompt, but does not prevent it. If it does
|
||||
happen, the cost is bounded: the prompt is a thin wrapper that calls the right skills anyway.
|
||||
|
||||
## Enforcement
|
||||
|
||||
- **`primitive-author`.** The prompt reference opens with a boundary gate. A request that carries
|
||||
procedure is redirected to `skill-author`.
|
||||
- **`factory-audit`, script checks.**
|
||||
- `description` is present and non-empty (FAIL).
|
||||
- It is 250 characters or fewer (SUGGESTION).
|
||||
- It has no "Use when" trigger clause (SUGGESTION).
|
||||
- **`factory-audit`, judgment step.** A body that clearly carries reusable procedure is a FAIL. A
|
||||
borderline body is a SUGGESTION. There is deliberately no line-count or heading heuristic: the
|
||||
call is made by reading the content, because any threshold misfires.
|
||||
|
||||
## Considered options
|
||||
|
||||
- **Fat workflow prompts as peers of skills (rejected).** This follows apm's framing. But every
|
||||
"make me a command" request becomes a coin flip between two near-identical containers. The prompt
|
||||
also carries worse metadata on Claude and does not arrive on Codex.
|
||||
- **The full ADR-0020 description contract for prompts (rejected).** A trigger clause and a boundary
|
||||
clause would make prompts route well. That actively invites the model to invoke the prompt, which
|
||||
contradicts "user-triggered".
|
||||
- **Skill-by-default with prompts as a grudging exception (superseded during the grill).** This
|
||||
framed a prompt as a weaker skill competing for the same job. Giving it a distinct role, a thin
|
||||
caller over skills, is a boundary that can be checked, which "prefer skills" is not.
|
||||
Reference in New Issue
Block a user