From 1b033a5d81dc92f0aec714bc47e5aeaa585a8b1a Mon Sep 17 00:00:00 2001 From: Richard Wooding Date: Tue, 28 Apr 2026 13:15:53 +0200 Subject: [PATCH] Add skill-authoring agent skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repo-local skill that codifies the authoring discipline for Claude Code agent skills: progressive disclosure (metadata → instructions → resources), ≤500-line SKILL.md, domain-split references, scripts over prose for deterministic checks, and specific third-person descriptions. Layout under .claude/skills/skill-authoring/: - SKILL.md — the entry point loaded when the skill triggers - references/patterns.md — common patterns and anti-patterns - references/review-checklist.md — review checklist for new/changed skills - scripts/lint_skill.py — deterministic structural lint - templates/SKILL.md.tmpl — starter template - templates/description-examples.md — good vs bad description examples Mirrors the same skill landed in SPANDigital/cel2sql (commit abbbfdd, PR #118) and originally transferred from SPANDigital/ai#16. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/skill-authoring/SKILL.md | 96 +++++++++ .../skill-authoring/references/patterns.md | 136 ++++++++++++ .../references/review-checklist.md | 46 ++++ .../skill-authoring/scripts/lint_skill.py | 204 ++++++++++++++++++ .../skill-authoring/templates/SKILL.md.tmpl | 46 ++++ .../templates/description-examples.md | 60 ++++++ 6 files changed, 588 insertions(+) create mode 100644 .claude/skills/skill-authoring/SKILL.md create mode 100644 .claude/skills/skill-authoring/references/patterns.md create mode 100644 .claude/skills/skill-authoring/references/review-checklist.md create mode 100644 .claude/skills/skill-authoring/scripts/lint_skill.py create mode 100644 .claude/skills/skill-authoring/templates/SKILL.md.tmpl create mode 100644 .claude/skills/skill-authoring/templates/description-examples.md diff --git a/.claude/skills/skill-authoring/SKILL.md b/.claude/skills/skill-authoring/SKILL.md new file mode 100644 index 0000000..b1bf742 --- /dev/null +++ b/.claude/skills/skill-authoring/SKILL.md @@ -0,0 +1,96 @@ +--- +name: skill-authoring +description: Authors and reviews agent skills for this repo, enforcing progressive disclosure (metadata → instructions → resources), ≤500-line SKILL.md files, domain-split references, scripts over prose for deterministic work, and specific third-person descriptions. Use when creating a new skill, splitting an oversized SKILL.md, or reviewing an existing skill for structure. +--- + +# Skill Authoring + +This skill codifies the authoring discipline so every skill loads cheaply and triggers accurately. + +## Why progressive disclosure + +An agent skill is a folder with `SKILL.md` at its root. The agent loads content across three levels — each step costs +more context, so keep lower levels lean. + +| Level | When loaded | Budget | Content | +|-----------------|---------------------------------------------------|----------------------------|-----------------------------------------| +| 1. Metadata | Always, at startup | ~100 tokens per skill | `name` + `description` from frontmatter | +| 2. Instructions | When the skill triggers | <5,000 tokens (≤500 lines) | `SKILL.md` body | +| 3. Resources | On demand, via explicit links or script execution | Effectively unlimited | `references/`, `scripts/`, `assets/` | + +Scripts are especially efficient: the source never enters context — only the **output** does. + +## Authoring a new skill + +1. **Copy the template.** Copy `templates/SKILL.md.tmpl` from this skill into your new skill's folder as `SKILL.md`, + then fill it in. Place the skill wherever skills live in the current repo (e.g. `skills//`). +2. **Write the description first.** Third person, include *what* the skill does AND *when* to use it. + See [templates/description-examples.md](templates/description-examples.md) for good vs bad examples. +3. **Draft 3 evaluation scenarios before prose.** Capture concrete prompts the skill should handle (and ones it should + decline), alongside the expected behaviour. Real failures drive what you document — this keeps the skill lean. +4. **Write the minimum SKILL.md needed to pass the evals.** Resist documenting edge cases you haven't seen fail. +5. **Split early if content grows.** See [references/patterns.md](references/patterns.md) for the four split patterns. +6. **Lint before committing.** **Run** the linter from this skill against your new skill directory — see the + Resources section below for the exact script path. +7. **Walk the review checklist.** See [references/review-checklist.md](references/review-checklist.md). + +## Hard authoring rules + +- `SKILL.md` ≤ 500 lines. Target ≤ 200. +- Description is third person, specific, includes what + when. Vague descriptions don't trigger. +- References are one level deep — `SKILL.md` links to reference files, references do NOT link to further references. + Agents often preview links with `head -100` and miss content past that on chained links. +- Reference files over ~100 lines start with a Contents / TOC block. +- Name files by content (`references/finance.md`), not position (`docs/file2.md`). +- Use forward slashes in paths — backslashes break on Unix. +- Avoid time-sensitive wording (`"after August 2025..."`) in the main flow. Move legacy content into a collapsed "old + patterns" section. +- Deterministic work belongs in `scripts/`, not Markdown. + +## Execution intent: be explicit + +When referencing a script, state whether the agent should **run** it or **read** it: + +- **Run** `python scripts/fetch_osm.py --bbox ...` — produces GeoJSON on stdout. (Agent executes.) +- **See** `scripts/fetch_osm.py` for the Overpass query it issues. (Agent reads as reference.) + +Execution is almost always what you want — scripts are more reliable than generated code, cheaper in context, and +consistent across runs. + +## When to split SKILL.md + +Three signals: + +1. **Length.** File is pushing 500 lines. +2. **Mutually-exclusive contexts.** Two user tasks never need the same sections. Example: a Bo-Kaap question never needs + Camps Bay lore. +3. **Code vs prose.** Long inline code blocks the agent will re-type anyway — move to `scripts/`. + +The four split patterns, with worked examples specific to this repo's skills, live +in [references/patterns.md](references/patterns.md). + +## Folder layout + +``` +/ +├── SKILL.md # Required +├── references/ # Optional — loaded on demand via explicit link +├── scripts/ # Optional — executed, source never loaded +├── templates/ # Optional — starter files users copy +└── assets/ # Optional — images, palettes, map files +``` + +## Review and maintenance + +- Before merging a new or edited skill: run the linter and walk the checklist. +- If a reference file grows past ~100 lines, add a TOC. +- If `SKILL.md` grows past ~300 lines, plan a split before it hits 500. +- If a skill stops triggering when expected, tighten its description — add concrete keywords from real user prompts. + +## Resources + +- [references/patterns.md](references/patterns.md) — the four split patterns with repo-specific examples. +- [references/review-checklist.md](references/review-checklist.md) — pre-merge checklist. +- [templates/SKILL.md.tmpl](templates/SKILL.md.tmpl) — starter template to copy. +- [templates/description-examples.md](templates/description-examples.md) — good vs bad description examples. +- **Run** `python scripts/lint_skill.py ` — lints a skill directory for the rules above. diff --git a/.claude/skills/skill-authoring/references/patterns.md b/.claude/skills/skill-authoring/references/patterns.md new file mode 100644 index 0000000..a02134f --- /dev/null +++ b/.claude/skills/skill-authoring/references/patterns.md @@ -0,0 +1,136 @@ +# Skill Split Patterns + +## Contents + +- Pattern 1: High-Level Guide with References +- Pattern 2: Domain-Specific Organisation +- Pattern 3: Conditional Detail +- Pattern 4: Scripts Instead of Prose +- Choosing a pattern +- Combining patterns + +--- + +## Pattern 1: High-Level Guide with References + +Keep `SKILL.md` as a lightweight overview and link out to deeper material. The agent loads additional files only when the specific feature is needed. + +``` +pdf-processing/ +├── SKILL.md # Overview + quick start +├── FORMS.md # Form-filling guide +├── REFERENCE.md # Full API reference +└── EXAMPLES.md # Common patterns +``` + +In `SKILL.md`: + +```markdown +## Advanced features + +**Form filling**: see FORMS.md +**API reference**: see REFERENCE.md +**Examples**: see EXAMPLES.md +``` + +**When to use:** the skill covers one cohesive domain but has distinct deep-dive features most users won't need. + +**Example:** a game skill's `SKILL.md` links out to `references/tilemap-loading.md`, `references/player-movement.md`, `references/npc-dialogue.md`, `references/camera.md`. A question about camera bounds pulls in `SKILL.md` + `camera.md` only. + +--- + +## Pattern 2: Domain-Specific Organisation + +Split references by domain so the agent only pulls in the relevant slice. + +``` +bigquery-skill/ +├── SKILL.md +└── reference/ + ├── finance.md # Revenue, ARR, billing + ├── sales.md # Pipeline, opportunities + ├── product.md # API usage, features + └── marketing.md # Attribution, campaigns +``` + +A question about sales pipeline reads `SKILL.md` + `reference/sales.md`. The other three files never enter context. + +**When to use:** the skill spans domains where one user's questions are irrelevant to another's. + +**Example:** a city-lore skill with one reference file per neighbourhood (`bo-kaap.md`, `sea-point.md`, `camps-bay.md`, …). Bo-Kaap content never loads for a Camps Bay question, and vice versa. The SKILL.md acts as a navigation map. + +--- + +## Pattern 3: Conditional Detail + +Show the common path in `SKILL.md`; link out to rare-but-important paths. + +```markdown +## Editing documents + +For simple edits, modify the XML directly. + +**For tracked changes**: see REDLINING.md +**For OOXML internals**: see OOXML.md +``` + +**When to use:** there's a clear 80/20 split between the common path and rare edge cases. Keeping edge cases inline bloats every activation for the 80% who don't need them. + +**Example:** a tiled-map authoring skill's `SKILL.md` covers the happy path for adding a tile layer; `references/collision-authoring.md` covers the less common collision-shape authoring; `references/event-triggers.md` covers POI interaction objects. + +--- + +## Pattern 4: Scripts Instead of Prose + +Deterministic logic belongs in a script, not in Markdown. The script's source never enters context — only its output does. + +``` +pdf-skill/ +├── SKILL.md +└── scripts/ + ├── analyze_form.py + ├── fill_form.py + └── validate.py +``` + +In `SKILL.md`, be explicit about execution intent: + +```markdown +Run: `python scripts/analyze_form.py input.pdf > fields.json` +``` + +versus + +```markdown +See `scripts/analyze_form.py` for the extraction algorithm. +``` + +The first tells the agent to execute. The second tells it to read as reference. **Execution is almost always what you want** — pre-made scripts are more reliable than generated code, cheaper in context, and consistent across runs. + +**When to use:** the work is deterministic (parsing, querying, projecting, validating). If a prompt would ask the agent to re-type the same code every time, it belongs in a script. + +**Example:** a map-data skill's `scripts/fetch_osm.py` issues an Overpass API query and emits GeoJSON. The agent runs it, consumes the output, never reads the script body. + +--- + +## Choosing a pattern + +| Signal in the content | Pattern | +|-----------------------|---------| +| SKILL.md has distinct feature deep-dives | 1 | +| Content splits cleanly by audience or sub-domain | 2 | +| 80/20 happy path vs rare edge cases | 3 | +| Same deterministic code re-typed across uses | 4 | + +--- + +## Combining patterns + +A mature skill often uses all four at once: + +- `SKILL.md` is a thin overview (Pattern 1). +- References are split by sub-domain (Pattern 2). +- Rare edge cases are linked-out from each section (Pattern 3). +- Deterministic steps live in `scripts/` with explicit "Run:" instructions (Pattern 4). + +A map-data skill is a natural candidate for all four once it grows — thin SKILL.md, references split by feature type (e.g. `overpass-queries.md`, `projection.md`), edge cases for unusual POI types linked out, and all fetch/convert work in scripts. diff --git a/.claude/skills/skill-authoring/references/review-checklist.md b/.claude/skills/skill-authoring/references/review-checklist.md new file mode 100644 index 0000000..abb7665 --- /dev/null +++ b/.claude/skills/skill-authoring/references/review-checklist.md @@ -0,0 +1,46 @@ +# Skill Review Checklist + +Walk this list before merging any new skill or edit. Lint runs first; the checklist catches things the linter can't. + +## Frontmatter + +- [ ] `name` present, lowercase-hyphenated, matches the folder name exactly. +- [ ] `description` present, third person, ≤ 2 sentences. +- [ ] Description includes **what** the skill does AND **when** to use it. +- [ ] Description includes concrete keywords an agent can match on (tool names, formats, file types, domain terms) — not just vague categories. + +## SKILL.md body + +- [ ] `wc -l SKILL.md` ≤ 500. Target ≤ 200. +- [ ] No time-sensitive wording in the main flow ("after August 2025", "until we migrate"). Move legacy content to a collapsed section. +- [ ] All relative links use forward slashes. +- [ ] All relative links stay within the skill folder (no `../` into other skills). Duplicate a short snippet if needed; let the other skill trigger separately for longer content. +- [ ] Every script reference is explicit about intent — **Run** (execute) vs **See** (read as reference). + +## References + +- [ ] References are one level deep — SKILL.md links to them; references do NOT link to further reference files. +- [ ] Reference files over ~100 lines open with a Contents / TOC block. +- [ ] Filenames describe content (`collision-authoring.md`, `finance.md`), not position (`doc2.md`, `ref1.md`). + +## Scripts + +- [ ] Each script starts with a docstring or one-line comment stating its purpose. +- [ ] SKILL.md shows the exact invocation (`python scripts/foo.py `) and the expected output shape. +- [ ] Scripts take inputs from args or stdin — no paths hardcoded to the author's machine. +- [ ] Scripts exit with non-zero on failure; success is silent or produces the documented output. + +## Assets + +- [ ] Binary assets are small enough to ship in the repo (target ≤ 100 KB each). +- [ ] Asset filenames describe content. + +## Evaluation + +- [ ] At least one concrete scenario (a real prompt + expected behaviour) exercises this skill's main path. +- [ ] Scenarios were written **before** SKILL.md prose, or at minimum drove the prose content. + +## Lint + +- [ ] `python /skill-authoring/scripts/lint_skill.py /` exits 0. +- [ ] Any warnings are either fixed or explicitly justified in the PR description. diff --git a/.claude/skills/skill-authoring/scripts/lint_skill.py b/.claude/skills/skill-authoring/scripts/lint_skill.py new file mode 100644 index 0000000..2504b9b --- /dev/null +++ b/.claude/skills/skill-authoring/scripts/lint_skill.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +"""Lint an agent skill directory against this repo's authoring rules. + +Usage: python lint_skill.py + +Checks: +- /SKILL.md exists +- Frontmatter has name (matches folder) and description +- Description is <= 2 sentences, third person, mentions "when" to use +- SKILL.md body <= 500 lines (warn at > 300) +- Reference files > 100 lines have a Contents/TOC block near the top +- SKILL.md relative links use forward slashes and stay inside the skill dir +- Reference files do not link to further reference files (one-level rule) +- Filenames are not positional (file1.md, doc2.md, ref3.md) +- SKILL.md body has no time-sensitive wording ("after ", etc.) + +Exits 0 on clean run, 1 if any ERROR, prints WARNINGs either way. +Templates (files ending with .tmpl) are skipped. +""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +ERRORS: list[str] = [] +WARNINGS: list[str] = [] + + +def err(msg: str) -> None: + ERRORS.append(msg) + + +def warn(msg: str) -> None: + WARNINGS.append(msg) + + +FRONTMATTER_RE = re.compile(r"\A---\r?\n(.*?)\r?\n---\r?\n", re.DOTALL) + + +def parse_frontmatter(text: str): + match = FRONTMATTER_RE.match(text) + if not match: + return None, text + fm_block = match.group(1) + body = text[match.end():] + meta: dict[str, str] = {} + current_key = None + for line in fm_block.splitlines(): + if re.match(r"^[A-Za-z_][A-Za-z0-9_-]*:\s", line) or line.endswith(":"): + key, _, value = line.partition(":") + meta[key.strip()] = value.strip() + current_key = key.strip() + elif current_key and line.startswith(" "): + meta[current_key] = (meta[current_key] + " " + line.strip()).strip() + return meta, body + + +SENTENCE_SPLIT = re.compile(r"(?<=[.!?])\s+") +PERSON_WORDS = re.compile(r"\b(you|your|yours|we|our|ours|us|i|me|my)\b", re.I) +WHEN_PHRASE = re.compile(r"\b(use when|used when|used to|used for|use for|use this when)\b", re.I) +TIME_SENSITIVE = re.compile(r"\b(after|until|before)\s+(19|20)\d{2}\b", re.I) +LINK_RE = re.compile(r"\]\(([^)]+)\)") +POSITIONAL = re.compile(r"^(file|doc|page|ref|part|section)\d+\.(md|py|mjs|js|ts)$", re.I) + + +def lint_skill_md(path: Path, skill_dir: Path) -> None: + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError as exc: + err(f"{path}: could not be decoded as UTF-8: {exc}") + return + meta, body = parse_frontmatter(text) + + if meta is None: + err(f"{path}: missing YAML frontmatter") + return + + expected_name = path.parent.name + name = meta.get("name", "").strip() + if not name: + err(f"{path}: frontmatter missing 'name'") + elif name != expected_name: + err(f"{path}: frontmatter name {name!r} does not match folder {expected_name!r}") + + desc = meta.get("description", "").strip().strip('"').strip("'") + if not desc: + err(f"{path}: frontmatter missing 'description'") + else: + sentences = [s for s in SENTENCE_SPLIT.split(desc) if s.strip()] + if len(sentences) > 2: + warn(f"{path}: description is {len(sentences)} sentences (target <= 2)") + if PERSON_WORDS.search(desc): + warn(f"{path}: description appears to use first/second person; prefer third person") + if not WHEN_PHRASE.search(desc): + warn(f"{path}: description should include 'when' to use the skill (e.g. 'Use when ...')") + + lines = body.splitlines() + n = len(lines) + if n > 500: + err(f"{path}: SKILL.md body is {n} lines (hard limit 500)") + elif n > 300: + warn(f"{path}: SKILL.md body is {n} lines (target <= 200, warn >= 300)") + + if TIME_SENSITIVE.search(body): + warn(f"{path}: body contains time-sensitive wording; move to a legacy section") + + skill_dir_resolved = skill_dir.resolve() + for m in LINK_RE.finditer(body): + link = m.group(1).strip() + if link.startswith(("http://", "https://", "mailto:", "#")): + continue + if "\\" in link: + err(f"{path}: link {link!r} uses backslashes; use forward slashes") + target = link.split("#", 1)[0] + if not target: + continue + if target.startswith("/"): + err(f"{path}: link {link!r} is absolute; use a relative path inside the skill directory") + continue + resolved = (path.parent / target).resolve() + try: + resolved.relative_to(skill_dir_resolved) + except ValueError: + err(f"{path}: link {link!r} escapes the skill directory") + + +TOC_HEADING_RE = re.compile(r"^#{1,3}\s+(contents|table of contents|toc)\b", re.I | re.M) + + +def lint_reference(path: Path) -> None: + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError as exc: + err(f"{path}: could not be decoded as UTF-8: {exc}") + return + lines = text.splitlines() + n = len(lines) + + if n > 100: + head = "\n".join(lines[:40]) + if not TOC_HEADING_RE.search(head): + warn(f"{path}: {n} lines without a TOC (add a Contents block for files over 100 lines)") + + for m in LINK_RE.finditer(text): + link = m.group(1).strip() + if link.startswith(("http://", "https://", "mailto:", "#")): + continue + target = link.split("#", 1)[0] + if not target: + continue + if target.endswith(".md") and not target.startswith("../"): + warn(f"{path}: reference links to another file {link!r}; references should be one level deep") + + +def lint_filenames(skill_dir: Path) -> None: + for p in skill_dir.rglob("*"): + if not p.is_file(): + continue + if p.suffix == ".tmpl": + continue + if POSITIONAL.match(p.name): + err(f"{p}: positional filename; rename to describe content") + + +def lint_directory(skill_dir: Path) -> None: + if not skill_dir.is_dir(): + err(f"{skill_dir}: not a directory") + return + + skill_md = skill_dir / "SKILL.md" + if not skill_md.exists(): + err(f"{skill_dir}: missing SKILL.md") + return + + lint_skill_md(skill_md, skill_dir) + lint_filenames(skill_dir) + + for ref_dir_name in ("references", "reference"): + ref_dir = skill_dir / ref_dir_name + if ref_dir.exists(): + for p in sorted(ref_dir.rglob("*.md")): + lint_reference(p) + + +def main() -> None: + if len(sys.argv) != 2: + print(__doc__, file=sys.stderr) + sys.exit(2) + + target = Path(sys.argv[1]) + lint_directory(target) + + for w in WARNINGS: + print(f"WARN {w}") + for e in ERRORS: + print(f"ERROR {e}") + + print(f"\n{len(ERRORS)} error(s), {len(WARNINGS)} warning(s)") + sys.exit(1 if ERRORS else 0) + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/skill-authoring/templates/SKILL.md.tmpl b/.claude/skills/skill-authoring/templates/SKILL.md.tmpl new file mode 100644 index 0000000..85bb1cf --- /dev/null +++ b/.claude/skills/skill-authoring/templates/SKILL.md.tmpl @@ -0,0 +1,46 @@ +--- +name: REPLACE-with-folder-name-lowercase-hyphenated +description: REPLACE with one or two third-person sentences. Describe WHAT this skill does and WHEN to use it. Include concrete keywords the agent will match on (tool names, file types, domains). Example shape — "Fetches OpenStreetMap data for Cape Town's City Bowl and converts it to Phaser-ready tile coordinates. Use when generating the base map, adding a neighbourhood, or placing real-world POIs onto the tile grid." +--- + +# REPLACE with Skill Title + +REPLACE with a one-paragraph overview: what this skill lets the agent do, and why it's a skill (progressive disclosure benefit) rather than inline prose in a CLAUDE.md. + +## Quick start + +REPLACE with the happy-path workflow — the single most common thing a user will ask. Show commands inline. Keep it to 5–10 lines. + +## REPLACE with feature / section 2 + +Cover the next most common path here. If any subsection has deep detail, link out instead of inlining: + +For REPLACE-topic, see [references/REPLACE-topic.md](references/REPLACE-topic.md). + +## Scripts + +List every script with exact invocation and expected output. Be explicit about execution intent. + +- **Run** `python scripts/REPLACE-script.py ` — REPLACE with what it does and what it emits. +- **See** `scripts/REPLACE-script.py` — REPLACE (use only when the agent should read, not execute). + +## References + +One-line description per file. The agent uses these lines to decide whether to open a reference. + +- [references/REPLACE-topic-a.md](references/REPLACE-topic-a.md) — REPLACE with one-line description. +- [references/REPLACE-topic-b.md](references/REPLACE-topic-b.md) — REPLACE with one-line description. + +## Conventions + +REPLACE with any hard rules this skill enforces — naming, layer structure, format requirements, palette constraints. Keep this section as a bulleted list of imperatives. + + diff --git a/.claude/skills/skill-authoring/templates/description-examples.md b/.claude/skills/skill-authoring/templates/description-examples.md new file mode 100644 index 0000000..bc7db2b --- /dev/null +++ b/.claude/skills/skill-authoring/templates/description-examples.md @@ -0,0 +1,60 @@ +# Descriptions: Good vs Bad + +The `description` is the **only** signal the agent uses to decide whether to trigger a skill. A vague description → skill never fires. A specific description with concrete keywords → skill fires at the right moment. + +## Rules + +1. **Third person.** "Authors skills..." not "You author skills...". +2. **What + when.** Both must be present. +3. **Concrete keywords.** Names of tools, formats, file types, domains the user is likely to mention. +4. **≤ 2 sentences.** + +## Side-by-side examples + +### Too vague +> Helps with documents. + +Never triggers reliably. "Documents" is too generic. + +### Better +> Extracts text and tables from PDF files, fills forms, merges documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction. + +Concrete keywords ("PDF", "forms", "extraction"); what + when both present. + +--- + +### Too vague +> Cape Town stuff. + +### Better +> Reference material on Cape Town's City Bowl and Atlantic Seaboard neighbourhoods — real landmarks, history, culture, typical activity — used to inform NPC dialogue, quest design, building names, and atmosphere. Use when writing dialogue or quests, naming buildings, or choosing which POIs are interactive in a given area. + +--- + +### Wrong person (second) +> Use this skill when you want to add a Phaser scene. + +### Right (third) +> Phaser 3 patterns for this game: scene lifecycle, Tiled map loading, player movement with arrow/WASD keys, camera follow, layer-based collision, NPC interaction, and dialogue. Use when wiring new game code, adding a scene, debugging movement or collision, or integrating a new interactive object type. + +--- + +### Missing "when" +> Defines the game's pixel-art conventions. + +### Complete +> Defines the game's pixel-art conventions — 32×32 tile grid, 3/4 top-down perspective, palette, walk-cycle frame counts, building-facade construction — and guides authoring or commissioning new sprites. Use when adding a character, building, prop, or tileset art, or when reviewing art for consistency. + +--- + +### Missing "what" (just categories) +> For game evaluation tasks. + +### Complete +> Evaluation scenarios that exercise the game end-to-end — walk-route coverage, collision correctness, POI trigger firing, visual consistency, dialogue flow. Use when adding a feature, before merging map or engine changes, or when building a new skill. + +## The trigger test + +Ask: "If the agent had 100 skills listed by description alone, and a user said ``, would it pick this one over the others?" + +If the answer isn't obvious, make the description more specific — usually by naming the tool, format, or domain term the user is most likely to say.