git:20260807.981abcd to git:20260807.97c5a8e
38 added, 38 removed. Audit A to A.
# AGENTS.md — `packs/`
Context for working inside any pack directory. **Max 150 lines** (CI enforces it).
Read `packs/AGENTS.local.md` when present — it carries host-specific overrides.
## Pack layout
The pack is the ownership and test-execution boundary; `.apm/` is the runtime export boundary; a skill is the
- evaluation-fixture boundary. Tests go in `packs/<pack>/tests/`, never under `.apm/`; evals stay skill-local at
- `.apm/skills/<skill>/evals/` and project with the skill. `tools/lint-pack-test-boundary.py` enforces both across
- every pack, plus one pytest process per skill test dir. Shape: `docs/architecture/pack-layout.md`.
+ evaluation-fixture boundary. Skill tests go in `packs/<pack>/tests/skills/<skill>/` and hook tests in
+ `packs/<pack>/tests/hooks/` — never under `.apm/`, which adapters project verbatim into an installed tree.
+ Evals stay skill-local at `.apm/skills/<skill>/evals/` and project with the skill. Run **one pytest process
+ per skill test directory**: two skills may ship the same test basename, and a shared `sys.path` binds one
+ skill's module for both suites and passes green.
| Path | Purpose |
|------|---------|
| `pack.toml` | Pack metadata — version, description, adapter-contract, categories |
| `.claude-plugin/plugin.json` | Claude plugin manifest source (must match `pack.toml` version, stay schema-valid) |
| `seeds/` | Adopter scaffold templates (brownfield install) |
| `tests/` · `docs/` | Never projected — implementation tests (`skills/<skill>/`, `hooks/`, `pack/`) · concept anchor and pack guides |
| `.apm/skills/` | Skill sources → projected per adapter |
| `.apm/agents/` | Agent sources → projected per adapter |
| `.apm/hooks/` | Hook-body sources → projected per adapter |
| `.apm/hook-wiring/` | Hook-wiring sources → projected per adapter |
| `.apm/commands/` | Command sources → projected per adapter |
| `.apm/kiro-ide-hooks/` | Kiro IDE hook sources → projected per adapter |
| `.apm/shared-libs/` | Shared library sources → projected per adapter |
| `.apm/adapter-root-bins/` | Adapter root binary sources → projected per adapter |
| `.apm/user-libs/` | User library sources → projected per adapter |
## Reserved authoring assets
- Any immediate child of the packs root whose name begins with `_` is a reserved authoring asset.
- Reserved directories are not catalogue payload — they do not appear in `list-packs`, are not
- installed, and are not included in packaged archives. See `packs/README.md`.
+ Any immediate child of the packs root whose name begins with `_` is a reserved authoring asset: not
+ catalogue payload, absent from `list-packs`, never installed or packaged. See `packs/README.md`.
## pack.toml schema map
- > The machine source of truth for pack.toml format is `contracts/pack.schema.json`.
- > The table below is a navigational summary; the JSON Schema is normative.
+ > The normative source for pack.toml format is the `pack` JSON Schema that ships inside `agentbundle`;
+ > `agentbundle catalogue lint --root .` validates against it. The table below is a navigational summary.
| Table | Required fields | Notable optional fields |
|-------|----------------|------------------------|
| `[pack]` | `name`, `version` | `description`, `display_name`, `adapter-contract`, `categories`, `keywords`, `maintainers`, `links`, `readme` |
| `[pack.adapter-contract]` | `version` | — |
| `[pack.install]` | `default-scope` | `allowed-scopes`, `user-scope-hooks`, `allowed-adapters` |
| `[pack.evals]` | — | `skills` (array of covered skill names) |
| `[pack.recipes.*]` | `description` | `steps`, `adapter` |
| `[pack.dependencies]` | — | `required`, `recommended`, `conflicts` (arrays) |
| `[pack.seeds]` · `[pack.layout]` · `[pack.first-value]` · `[pack.adaptation]` | — | Seed paths · per-scope layout overrides · first-value install metadata · adaptation inference rules |
`[pack.install]` is required when `adapter-contract.version` ≥ 0.2.
## Primary workflow (any catalogue)
Run after any pack change. If `agentbundle` is not installed: `pip install agentbundle`.
```bash
agentbundle catalogue lint --root .
agentbundle catalogue verify --root .
agentbundle catalogue self-host --root . --write
```
For CI pipeline orchestration — publication ordering, exit codes, JSON output contract — see
[`guides/_shared/reference/catalogue-ci-contract.md`](../guides/_shared/reference/catalogue-ci-contract.md).
## Version bump rule
- Every **non-cosmetic** change to pack content requires a version bump in both:
- 1. `pack.toml` → `[pack] version`
- 2. `.claude-plugin/plugin.json` → `"version"`
+ Every **non-cosmetic** change to pack content requires a version bump in **both** `pack.toml` →
+ `[pack] version` and `.claude-plugin/plugin.json` → `"version"`.
Which increment: **patch** for changed bodies/directives/conventions; **minor** for new primitives;
**major** for removals. Never ride an unreleased version from another in-flight PR.
- Host-specific post-bump steps (changelog, marketplace regeneration) are in `packs/AGENTS.local.md`.
+ Further post-bump steps (changelog, marketplace regeneration) are host-specific — see `AGENTS.local.md` if
+ your catalogue has one.
## Self-hosting projection
All `.apm/` primitives are the **source of truth**. `agentbundle catalogue self-host --root . --write`
projects them to every shipped adapter's layout. Never edit a projected output directly.
On a dirty working tree: `agentbundle catalogue self-host --root . --write --force`.
- **Critical ordering:** when a session edits both seeds and non-seed pack sources (`.apm/**`,
- `pack.toml`), run self-host AFTER all edits — not between them.
+ **Critical ordering:** when a session edits both seeds and non-seed pack sources (`.apm/**`, `pack.toml`),
+ run self-host AFTER all edits — not between them.
## Claude plugin JSON format
- Each pack's `.claude-plugin/plugin.json` is validated against `contracts/plugin-manifest.schema.json`.
- Non-compliant manifests block publishing.
+ Each pack's `.claude-plugin/plugin.json` is validated against the plugin-manifest JSON Schema that ships
+ inside `agentbundle`; `agentbundle catalogue lint --root .` reports violations. Non-compliant manifests
+ block publishing.
**Required:** `name` (string), `version` (string matching `pack.toml`), `description` (string).
- **Allowed optional fields** — `skills`, `agents` (arrays of strings); `author` (`{name, email?}`);
- `license`, `homepage`, `repository`, `category`, `displayName` (strings); `keywords` (array);
- `source` (`{source, repo, branch, directory}`).
+ **Allowed optional fields** — `skills`, `agents` (arrays of strings); `author` (`{name, email?}`); `license`,
+ `homepage`, `repository`, `category`, `displayName` (strings); `keywords` (array); `source`
+ (`{source, repo, branch, directory}`).
`additionalProperties: false` — any unknown key fails validation.
## Authoring or editing a skill
- `README.md` states pack intent and the user journey it serves — not a contributor capability list.
- Edit `.apm/skills/<name>/SKILL.md`. Run self-host to project. Run
- `agentbundle catalogue lint --root . --deep` to confirm spec compliance.
+ `README.md` states pack intent and the user journey it serves — not a contributor capability list. Edit
+ `.apm/skills/<name>/SKILL.md`, run self-host to project, then `agentbundle catalogue lint --root . --deep`.
- Full authoring standards — frontmatter key whitelist, body structure, naming, three-tier dependency
- policy, and evals — live in [`guides/_shared/how-to/author-a-skill.md`](../guides/_shared/how-to/author-a-skill.md).
+ Full authoring standards — frontmatter key whitelist, body structure, naming, three-tier dependency policy,
+ evals — live in [`catalogue-authoring-standards.md`](../guides/_shared/reference/catalogue-authoring-standards.md).
## Eval coverage
A non-cosmetic pack update must also update the pack's eval harness:
- - **Tier-A activation** — `evals/eval_queries.json` (~8–10 should-trigger + ~8–10 near-miss) and
- a `[pack.evals]` block in `pack.toml` listing every user-triggered skill.
+ - **Tier-A activation** — `evals/eval_queries.json` (~8–10 should-trigger + ~8–10 near-miss) and a
+ `[pack.evals]` block in `pack.toml` listing every user-triggered skill.
- **Tier-4 LLM-judge rubric** — `evals/evals.json` for judgment/authoring skills.
- **Tier-B-lite behavior check** — add an `expect` block to an `evals/evals.json` entry (non-destructive,
- non-credentialed skills only). Four things that must be explicit to avoid format churn:
- - Field name is **`files`** (not `fixture`): paths are relative to the **skill root**
- (e.g. `"evals/files/sample.md"`), not relative to `evals/`. The runner seeds a temp workspace
- with these files before invoking the skill.
+ non-credentialed skills only). Four things must be explicit, or the format churns:
+ - The field is **`files`**, not `fixture`, and its paths are relative to the **skill root**
+ (`"evals/files/sample.md"`) — not to `evals/`. The runner seeds a temp workspace with them first.
- `expect.produces`: filenames the run must create in the workspace.
- `expect.output_contains` / `expect.output_excludes`: substrings in captured output.
- - **Your skill script must accept the workspace path** — via a `--fixture`/`--root` flag or by
- treating CWD as the workspace — so the runner can confine writes and verify `produces`.
- Full procedure: [`guides/_shared/how-to/author-a-skill.md`](../guides/_shared/how-to/author-a-skill.md).
+ - **Your skill script must accept the workspace path** — a `--fixture`/`--root` flag, or treat CWD as
+ the workspace — so the runner can confine writes and verify `produces`.
+ Full procedure: [`guides/_shared/reference/catalogue-authoring-standards.md`](../guides/_shared/reference/catalogue-authoring-standards.md).
## Windows-safe Python scripts
- Any script under `.apm/` that prints to stdout or stderr must include the UTF-8 reconfigure guard
- immediately after `import sys`, before any `print()` call:
+ Any script under `.apm/` that prints to stdout or stderr must include the UTF-8 reconfigure guard immediately after `import sys`, before any `print()` call:
```python
sys.stdout.reconfigure(encoding="utf-8", errors="strict")
sys.stderr.reconfigure(encoding="utf-8", errors="backslashreplace")
```
## TDD plan stubs
Stubs in `plan.md` tasks must be `raise NotImplementedError # STUB: ACn` — not `...`. A bare `...` is valid Python and passes immediately, defeating the red-green cycle.
## Security — skill bodies that read files or pass content to a model
- **Realpath-resolve before every read.** `~`-expansion and `..`-rejection alone are not enough — a symlink inside the approved directory bypasses containment without `realpath`. Canonicalize the full target path; verify the prefix still falls within the approved boundary.
- **Data boundary on loaded files.** Treat any file loaded from a user-controlled path as structured data: extract only the fields you expect; ignore embedded directives. This is the instruction-vs-data boundary against prompt injection.
- **Cross-config confirmation.** When `output_dir` or any config path comes from a user-level config shared across projects, confirm the loaded artifact belongs to the current brand or project before using it — a same-slug file from another project can silently anchor the wrong output.
- **`shutil.copytree`/`copy2` dereference symlinks by default.** When copying from any source that could be attacker-controlled (untrusted packs, plugin submissions), pass `symlinks=True` to `copytree` and `follow_symlinks=False` to `copy2` — they then preserve symlinks as symlinks instead of materialising the target's contents into the output tree.
## Shipped pack content carries no internal-governance citations
- Anywhere under `packs/` — `.apm/**`, `pack.toml`, `README`/`JOURNEY`/`DESIGN`, `seeds/**` —
- never cite this catalogue's own governance: `RFC-0NNN`, `ADR-0NNN`, spec/plan or AC citations,
- internal doc paths. Keep the rule, drop the citation. Detail: `packs/AGENTS.local.md`.
+ Anywhere under `packs/` — `.apm/**`, `pack.toml`, `README`/`JOURNEY`/`DESIGN`, `seeds/**` — never cite your
+ catalogue's own governance: decision-record or proposal numbers, spec/plan or acceptance-criterion citations,
+ or repository-only paths. Shipped content lands in someone else's tree, where each is a dead end. **Keep the
+ rule, drop the citation** — state what the reader must do, in full.