# Meta Skill Writer

> Use when creating new skills, editing existing skills, structuring SKILL.md files, writing skill frontmatter, testing skills with subagents, deploying skills, or verifying skills work before deployment

- Skill: `bitranox/meta-skill-writer` (Agent Skill, multi-file: 23 files)
- Install (CLI): `npx skillmds@latest add bitranox/meta-skill-writer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bitranox/meta-skill-writer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: bitranox (https://skillmd.com/u/bitranox)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bitranox/meta-skill-writer

---


# Writing Skills

> Adapted from the superpowers plugin (MIT).

## Overview

**Writing skills IS Test-Driven Development applied to process documentation.**

**Personal skills live in agent-specific directories (`~/.claude/skills` for Claude Code, `~/.agents/skills/` for Codex)** 

**if there is a skill in the current directory, work on AND test THAT skill, not `~/.claude/skills` or `~/.agents/skills/`**

You write test cases (pressure scenarios with subagents), watch them fail (baseline behavior), write the skill (documentation), watch tests pass (agents comply), and refactor (close loopholes).

**Core principle:** If you didn't watch an agent fail without the skill, you don't know if the skill teaches the right thing.

**REQUIRED BACKGROUND:** You MUST understand bitranox:process-test-driven-development before using this skill. That skill defines the fundamental RED-GREEN-REFACTOR cycle. This skill adapts TDD to documentation.

**Official guidance:** For Anthropic's official skill authoring best practices, see anthropic-best-practices.md. This document provides additional patterns and guidelines that complement the TDD-focused approach in this skill.

## Reference Files

| Topic                                                                      | File                             |
|----------------------------------------------------------------------------|----------------------------------|
| Testing methodology - pressure scenarios, RED/GREEN/REFACTOR, meta-testing | testing-skills-with-subagents.md |
| Persuasion principles - authority, commitment, scarcity, social proof      | persuasion-principles.md         |
| Anthropic official best practices - conciseness, freedom, structure        | anthropic-best-practices.md      |
| Graphviz conventions - node shapes, edge labels, naming patterns           | graphviz-conventions.dot         |
| Flowchart rendering - SVG output from dot diagrams                         | render-graphs.js                 |
| Worked example - full test campaign testing CLAUDE.md variants             | examples/CLAUDE_MD_TESTING.md    |

Use the Read tool to load referenced files identified as relevant for full details.

## What is a Skill?

A **skill** is a reference guide for proven techniques, patterns, or tools. Skills help future Claude instances find and apply effective approaches.

**Skills are:** Reusable techniques, patterns, tools, reference guides

**Skills are NOT:** Narratives about how you solved a problem once

## TDD Mapping for Skills

| TDD Concept             | Skill Creation                                   |
|-------------------------|--------------------------------------------------|
| **Test case**           | Pressure scenario with subagent                  |
| **Production code**     | Skill document (SKILL.md)                        |
| **Test fails (RED)**    | Agent violates rule without skill (baseline)     |
| **Test passes (GREEN)** | Agent complies with skill present                |
| **Refactor**            | Close loopholes while maintaining compliance     |
| **Write test first**    | Run baseline scenario BEFORE writing skill       |
| **Watch it fail**       | Document exact rationalizations agent uses       |
| **Minimal code**        | Write skill addressing those specific violations |
| **Watch it pass**       | Verify agent now complies                        |
| **Refactor cycle**      | Find new rationalizations -> plug -> re-verify   |

The entire skill creation process follows RED-GREEN-REFACTOR.

## When to Create a Skill

**Create when:**
- Technique wasn't intuitively obvious to you
- You'd reference this again across projects
- Pattern applies broadly (not project-specific)
- Others would benefit

**Don't create for:**
- One-off solutions
- Standard practices well-documented elsewhere
- Project-specific conventions (put in project instructions file: CLAUDE.md / AGENTS.md)
- Mechanical constraints (if it's enforceable with regex/validation, automate it - save documentation for judgment calls)

## Skill Types

### Technique
Concrete method with steps to follow (condition-based-waiting, root-cause-tracing)

### Pattern
Way of thinking about problems (flatten-with-flags, test-invariants)

### Reference
API docs, syntax guides, tool documentation (office docs)

## Directory Structure


```
skills/
  skill-name/
    SKILL.md              # Main reference (required)
    supporting-file.*     # Only if needed
```

**Flat namespace** - all skills in one searchable namespace

**Separate files for:**
1. **Heavy reference** (100+ lines) - API docs, comprehensive syntax
2. **Reusable tools** - Scripts, utilities, templates

**Keep inline:**
- Principles and concepts
- Code patterns (< 50 lines)
- Everything else

**Hub skills with supporting files:** When SKILL.md is an index that points to separate reference files, add an explicit instruction in the body (not frontmatter) telling Claude to use the Read tool. This prevents Claude from trying to answer from summary tables alone instead of loading the detailed file.

```markdown
Use the Read tool to load referenced files identified as relevant for full details.
```

Only add this for hub/reference skills with supporting files. Self-contained skills (where everything is in SKILL.md) don't need it - the full body is already loaded when invoked.

**Upstream doc linking:** When hub skills consolidate content from original source documents in subdirectories (e.g., `docs/`, `tutorial/`, `api/`), include a two-tier routing table in SKILL.md:
1. **Tier 1** - distilled reference files (same directory as SKILL.md)
2. **Tier 2** - original upstream docs (subdirectory paths for deeper detail)

Don't rely on passive `> Source:` annotations in supporting files - agents treat these as attribution, not as actionable links. The hub must provide an explicit routing table for upstream docs.

```markdown
## Reference Files

| Topic                                           | Distilled reference | Upstream source (full detail)      |
|-------------------------------------------------|---------------------|------------------------------------|
| Core API - Client, Session, request(), stream() | api-reference.md    | docs/api/full-reference.md         |
| Config - Settings, env vars, pyproject section  | configuration.md    | docs/guides/configuration.md       |
| Tutorials - quickstart, first app, deployment   | quick-start.md      | tutorial/getting-started/README.md |

Use the Read tool to load a distilled reference first.
If it lacks the detail you need, load the upstream source.
```

The upstream table should work as a comprehensive index so an agent can jump straight to the right file for any specific class, function, or method. Each row needs to list the concrete API symbols the file covers, not just a prose summary.

```markdown
| Topic                                                   | Upstream source |
|---------------------------------------------------------|-----------------|
| NO Widgets                                              | docs/widgets.md |
| OK Widgets - DataTable, Tree, OptionList, Select, Input | docs/widgets.md |
```

**Building routing tables:** For each supporting file, use `grep -E '^#{2,3} ' filename.md` to extract H2/H3 headings. For tier 1 (distilled reference) rows, list the 3-5 most important headings, class names, or function names as the topic description. If a file covers more than 5 key terms, pick the ones an agent is most likely to search for and add an "etc." or "and more" suffix. For tier 2 (upstream source) rows, list all classes, functions, API endpoints, and key concepts - the upstream column serves as a comprehensive index, so every symbol must be findable through the topic description.

**Evaluating routing descriptions:** For each row in a routing table, ask: given a realistic user query, could Claude pick the right file from the topic description alone? If two rows sound equally plausible for the same query, the descriptions need more differentiation.

Common failures:
- Topic names too generic ("API", "Config") - Claude can't distinguish files
- Overlapping scope - two files both sound relevant for the same query
- Missing subtopics - key content buried inside a file isn't mentioned in the description

Fix: expand topic descriptions to include 2-3 disambiguating subtopics or keywords.

```markdown
| Topic                                         | Distilled reference |
|-----------------------------------------------|---------------------|
| NO API                                        | api-reference.md    |
| NO Config                                     | configuration.md    |
| OK Core API - endpoints, auth, rate limits    | api-reference.md    |
| OK Config - env vars, CLI flags, config files | configuration.md    |
```

**Verifying routing tables:** After writing a routing table, run two checks to confirm it is both complete and accurate. For two-tier tables, run both checks independently for the distilled reference column and the upstream source column:

1. **Coverage check (file -> table):** For each referenced file, run `grep -E '^#{2,3} ' filename.md` to extract its headings. For each heading or key term, confirm it appears in the routing table's topic description for that file. Missing terms = content agents can't find through the table.
2. **Accuracy check (table -> file):** For each search term listed in the routing table, run `grep -i "term" filename.md` to confirm the file actually contains it. Mismatches = stale entries that route agents to the wrong file.

A routing table passes when every key term in the file appears in the table (coverage) and every term in the table appears in the file (accuracy). For tier 1 rows with 10+ headings, coverage is sufficient if the top 5 most-queried terms are represented. For tier 2 rows, coverage requires all classes, functions, and API endpoints - no "top 5" shortcut. Both columns must pass independently.

Example:

```markdown
Referenced file `widgets.md` contains headings: DataTable, Tree, Select, Input, OptionList

OK Coverage check passes - routing table says:
| Widgets - DataTable, Tree, OptionList, Select, Input | widgets.md |

NO Coverage check fails - routing table says:
| Widgets                                              | widgets.md |
(agent searching for "DataTable" won't find the right file)

OK Accuracy check passes - grep widgets.md for "DataTable" -> found
NO Accuracy check fails - routing table lists "TreeView" but file only contains "Tree"
```

Tier 2 example (upstream source column):

```markdown
Routing table row:
| Core API - Client, Session, request(), stream() | api-reference.md | docs/api/full-reference.md  |

Upstream file `docs/api/full-reference.md` contains headings: Client, Session, request, stream, Connection, Retry

NO Tier 2 coverage check fails - topic lists 4 of 6 symbols; missing Connection and Retry -> add them to topic description
NO Tier 2 accuracy check fails - topic lists "stream()" but upstream file heading is "Streaming" -> fix topic or confirm alias

Fixed topic: Core API - Client, Session, request(), Streaming, Connection, Retry
```

**Presence gate:** Before checking coverage and accuracy, first confirm a tier 2 table exists at all. Run `find <upstream-dirs> -name '*.md'` to list all upstream files. If any exist but the routing table has no tier 2 (upstream source) column, the skill is incomplete - add the column and populate it before proceeding with coverage/accuracy checks.

## SKILL.md Structure

**Frontmatter (YAML):**
- Only two fields supported: `name` and `description` (custom fields are stripped by Claude Code)
- Caps are PER FIELD, not combined: `name` 64 characters, `description` 1024
- **Over the cap is not refused, it is silently truncated.** No warning, no error, and nothing in the
  file to see: the SKILL.md still reads perfectly while the injected available-skills listing cuts
  the description mid-word and ends it with an ellipsis, so every trigger past the cut is invisible
  to the router. The skill then behaves as if its newest triggers were never written - documented,
  tested, listed in its own table, and never suggested.
  - **Measure the field, do not eyeball it:** extract the frontmatter `description` and take `len()`.
    This repo's commit gate measures it too and fails over the cap, so the rule is mechanical rather
    than remembered.
  - **The observed cut falls LATER than 1024, not earlier.** A 1060-character description was
    delivered whole; a 2124-character one lost its last 597 characters. So nothing under the
    documented cap is at risk, and the reason to stay under it is that the real boundary is a
    harness detail that can move - not that your 900-character description is in danger. Do not
    probe for the knife; the 500-character target below is the margin worth aiming at.
  - **A bloated description is a REWRITE, not an append.** An appended trigger lands in the dead
    tail, and inserting one early only pushes a different one out.
  - To see where a live session actually cut, ask a running agent to quote its own available-skills
    entry for that skill verbatim, and diff it against the field in the file. The listing is
    injected into the system prompt, so it cannot be read off disk.
- `name`: Use letters, numbers, and hyphens only (no parentheses, special chars)
- `description`: Third-person, describes ONLY when to use (NOT what it does)
  - Start with "Use when..." to focus on triggering conditions
  - Include specific symptoms, situations, and contexts
  - **NEVER summarize the skill's process or workflow** (see CSO section for why)
  - Keep under 500 characters if possible

```markdown
---
name: skill-name-with-hyphens
description: Use when [specific triggering conditions and symptoms]
---

# Skill Name

## Overview
What is this? Core principle in 1-2 sentences.

## When to Use
[Small inline flowchart IF decision non-obvious]

Bullet list with SYMPTOMS and use cases
When NOT to use

## Core Pattern (for techniques/patterns)
Before/after code comparison

## Quick Reference
Table or bullets for scanning common operations

## Implementation
Inline code for simple patterns
Link to file for heavy reference or reusable tools

## Common Mistakes
What goes wrong + fixes

## Real-World Impact (optional)
Concrete results
```


## Claude Search Optimization (CSO)

**Critical for discovery:** Future Claude needs to FIND your skill

### 1. Rich Description Field

**Purpose:** Claude reads description to decide which skills to load for a given task. Make it answer: "Should I read this skill right now?"

**Format:** Start with "Use when..." to focus on triggering conditions

**CRITICAL: Description = When to Use, NOT What the Skill Does**

The description should ONLY describe triggering conditions. Do NOT summarize the skill's process or workflow in the description.

**Why this matters:** Testing revealed that when a description summarizes the skill's workflow, Claude may follow the description instead of reading the full skill content. A description saying "code review between tasks" caused Claude to do ONE review, even though the skill's flowchart clearly showed TWO reviews (spec compliance then code quality).

When the description was changed to just "Use when executing implementation plans with independent tasks" (no workflow summary), Claude correctly read the flowchart and followed the two-stage review process.

**The trap:** Descriptions that summarize workflow create a shortcut Claude will take. The skill body becomes documentation Claude skips.

```yaml
# NO BAD: Summarizes workflow - Claude may follow this instead of reading skill
description: Use when executing plans - dispatches subagent per task with code review between tasks

# NO BAD: Too much process detail
description: Use for TDD - write test first, watch it fail, write minimal code, refactor

# OK GOOD: Just triggering conditions, no workflow summary
description: Use when executing implementation plans with independent tasks in the current session

# OK GOOD: Triggering conditions only
description: Use when implementing any feature or bugfix, before writing implementation code
```

**Content:**
- Use concrete triggers, symptoms, and situations that signal this skill applies
- Describe the *problem* (race conditions, inconsistent behavior) not *language-specific symptoms* (setTimeout, sleep)
- Keep triggers technology-agnostic unless the skill itself is technology-specific
- If skill is technology-specific, make that explicit in the trigger
- Write in third person (injected into system prompt)
- **NEVER summarize the skill's process or workflow**

```yaml
# NO BAD: Too abstract, vague, doesn't include when to use
description: For async testing

# NO BAD: First person
description: I can help you with async tests when they're flaky

# NO BAD: Mentions technology but skill isn't specific to it
description: Use when tests use setTimeout/sleep and are flaky

# OK GOOD: Starts with "Use when", describes problem, no workflow
description: Use when tests have race conditions, timing dependencies, or pass/fail inconsistently

# OK GOOD: Technology-specific skill with explicit trigger
description: Use when using React Router and handling authentication redirects
```

### 2. Keyword Coverage

Use words Claude would search for:
- Error messages: "Hook timed out", "ENOTEMPTY", "race condition"
- Symptoms: "flaky", "hanging", "zombie", "pollution"
- Synonyms: "timeout/hang/freeze", "cleanup/teardown/afterEach"
- Tools: Actual commands, library names, file types

### 3. Descriptive Naming

**Use active voice, verb-first:**
- OK `creating-skills` not `skill-creation`
- OK `condition-based-waiting` not `async-test-helpers`

**If the target marketplace defines a naming registry, follow it.** Some marketplaces enforce a
category-prefix scheme (e.g. `<category>-[<sub>-]<name>` like `coding-python-...`, `marketing-...`)
via a registry file and a contribution gate. When contributing there, pick the category per that
repo's rules and keep the verb-first/descriptive style for the `<name>` part. The scheme itself is
repo-specific - see that repo's `CONTRIBUTING.md` (for this marketplace, `skill-taxonomy.json`).

### 4. Token Efficiency (Critical)

**Problem:** getting-started and frequently-referenced skills load into EVERY conversation. Every token counts.

**Target word counts (tiered by skill type):**
- getting-started workflows: <150 words each
- Frequently-loaded / process skills: <200 words total
- Other process/technique skills: <500 words (still be concise)
- Reference/hub skills (routing tables + supporting files): MAY exceed 500 words, but keep the SKILL.md lean - push detail into reference files and let the body stay an index. This skill-writer is itself a hub skill and legitimately runs long.

**Techniques:**

**Move details to tool help:**
```bash
# NO BAD: Document all flags in SKILL.md
search-conversations supports --text, --both, --after DATE, --before DATE, --limit N

# OK GOOD: Reference --help
search-conversations supports multiple modes and filters. Run --help for details.
```

**Use cross-references:**
```markdown
# NO BAD: Repeat workflow details
When searching, dispatch subagent with template...
[20 lines of repeated instructions]

# OK GOOD: Reference other skill
Always use subagents (50-100x context savings). REQUIRED: Use [other-skill-name] for workflow.
```

**Compress examples:**
```markdown
# NO BAD: Verbose example (42 words)
your human partner: "How did we handle authentication errors in React Router before?"
You: I'll search past conversations for React Router authentication patterns.
[Dispatch subagent with search query: "React Router authentication error handling 401"]

# OK GOOD: Minimal example (20 words)
Partner: "How did we handle auth errors in React Router?"
You: Searching...
[Dispatch subagent -> synthesis]
```

**Eliminate redundancy:**
- Don't repeat what's in cross-referenced skills
- Don't explain what's obvious from command
- Don't include multiple examples of same pattern

**Verification:**
```bash
wc -w skills/path/SKILL.md
# getting-started workflows: aim for <150 each
# Other frequently-loaded: aim for <200 total
```

**Name by what you DO or core insight:**
- OK `condition-based-waiting` > `async-test-helpers`
- OK `using-skills` not `skill-usage`
- OK `flatten-with-flags` > `data-structure-refactoring`
- OK `root-cause-tracing` > `debugging-techniques`

**Gerunds (-ing) work well for processes:**
- `creating-skills`, `testing-skills`, `debugging-with-logs`
- Active, describes the action you're taking

### 5. Cross-Referencing Other Skills

**When writing documentation that references other skills:**

Use skill name only, with explicit requirement markers:
- OK Good: `**REQUIRED SUB-SKILL:** Use bitranox:process-test-driven-development`
- OK Good: `**REQUIRED BACKGROUND:** You MUST understand bitranox:process-debug-systematic`
- NO Bad: `See skills/testing/test-driven-development` (unclear if required)
- NO Bad: `@skills/testing/test-driven-development/SKILL.md` (force-loads, burns context)

**Why no @ links:** `@` syntax force-loads files immediately, consuming 200k+ context before you need them.

**Referencing a SCRIPT is the opposite of referencing a skill: state the home path.** A skill name
resolves through the skill system; a script filename resolves through nothing. A bare
`helper.py --check` in prose is unrunnable for a reader whose context lacks the owning skill's base
directory, and it reads as NOT SHIPPED to anyone auditing the plugin (they search the path-qualified
locations, e.g. `hooks/`, and conclude the file is missing).

- OK Good: `` `reconcile_memory_index.py --check` (home: `<plugin>/skills/meta-self-improve/`, launch via `hooks/run-python.sh`) ``
- NO Bad: `` run `reconcile_memory_index.py --check` `` (bare filename - where? how?)

Rules: state the home (`skills/<owner>/<script>`) and the launch shim at the point of use; when a
family of skills shares a core reference file, single-source the homes there (a "Script homes"
section) and let the members cite it; a script referenced from OUTSIDE its owning skill is the case
that always needs the path - within its own skill's SKILL.md the announced base directory resolves a
bare name.

**Referencing external DOCS must be INSTALL-REACHABLE (the same rule, applied to docs).** A skill
teaching a tool or library usually points at that project's fuller docs (its `README`, a `docs/`
folder). But the skill ships ALONE - only its own directory - and a package's `README`/`docs/` are
usually STRIPPED from the installed wheel (the wheel ships `src/` + `py.typed`, not docs). So a bare
`see docs/api.md` or "the package's README" is a DANGLING pointer on any machine that installed the
skill: it resolves to nothing. This shipped for real in more than one skill. Every reference you send
a reader to must resolve FROM AN INSTALL, by one of:

- **Install-local discovery FIRST - the installed code's own truth (zero rot, no network, always the
  reader's version).** For CLI options, the skill must direct the reader/LLM to RUN `<tool> --help`
  (and `<tool> <subcommand> --help`) instead of freezing a flag list that goes stale; for the API,
  `python -c "import mod; help(mod)"` / docstrings. When a command's flags or an API signature are in
  question, running `--help`/`help()` beats reading any doc - it is the authoritative, always-current
  source. Author every tool/library skill this way: do NOT enumerate every flag in the SKILL.md when
  `--help` covers it - point the reader at `--help` and keep the body to the judgment `--help` cannot
  give.
- **For narrative docs not in `--help`, link the LATEST docs (the default branch).** Tools here are
  installed/updated via `uv`/`uvx`, so the reader is normally on the latest release and latest docs
  match: `https://github.com/<owner>/<repo>/blob/<default-branch>/docs/x.md` (use the repo's real
  default branch - `master` or `main`; `blob/HEAD/...` is branch-name-agnostic and survives a
  default-branch rename). The one fragility: a deep file link 404s if that file is later renamed or
  moved - mitigate by linking the `docs/` dir or repo root when the file set is unstable. Do NOT
  hard-code a version tag as the default - it silently goes STALE as the tool moves past it. Use a
  tag-anchored URL plus a "swap `<tag>` for your installed version" hint ONLY when the skill is pinned
  to one library version or version drift would actively mislead.
- **A DISTILLED copy bundled in the skill dir** (it then ships with the skill) - use when offline use
  matters or the skill is pinned to one library version; stamp it with the source URL + version so it
  can be refreshed, and keep it distilled so drift stays bounded.
- **NEVER a bare package-local path** (`docs/api.md`, "its `docs/`") that is neither shipped in the
  skill dir nor a URL.

First make the SKILL.md SELF-SUFFICIENT for the common path, so any external reference is for the
deep minority of cases only: then a rotted link or a stale bundle degrades gracefully instead of
breaking the skill. Neither a URL (network + link-rot) nor a bundle (drift) is free - the
self-sufficient body is what makes either safe.

Verify before shipping: `grep -nE '(docs/|README|see [^ ]*\.md|its .docs.)' SKILL.md | grep -v http`
should surface only references that (a) ship as files in the skill dir (`ls` them) or (b) are
install-local `--help`/`help()`. Any remaining bare package-local path is the bug.

## Flowchart Usage

```dot
digraph when_flowchart {
    "Need to show information?" [shape=diamond];
    "Decision where I might go wrong?" [shape=diamond];
    "Use markdown" [shape=box];
    "Small inline flowchart" [shape=box];

    "Need to show information?" -> "Decision where I might go wrong?" [label="yes"];
    "Decision where I might go wrong?" -> "Small inline flowchart" [label="yes"];
    "Decision where I might go wrong?" -> "Use markdown" [label="no"];
}
```

**Use flowcharts ONLY for:**
- Non-obvious decision points
- Process loops where you might stop too early
- "When to use A vs B" decisions

**Never use flowcharts for:**
- Reference material -> Tables, lists
- Code examples -> Markdown blocks
- Linear instructions -> Numbered lists
- Labels without semantic meaning (step1, helper2)

**REFERENCE:** See graphviz-conventions.dot for graphviz style rules.

**Visualizing for your human partner:** Use `render-graphs.js` in this directory to render a skill's flowcharts to SVG:
```bash
./render-graphs.js ../some-skill           # Each diagram separately
./render-graphs.js ../some-skill --combine # All diagrams in one SVG
```

## Code Examples

**One excellent example beats many mediocre ones**

Choose most relevant language:
- Testing techniques -> TypeScript/JavaScript
- System debugging -> Shell/Python
- Data processing -> Python

**Good example:**
- Complete and runnable
- Well-commented explaining WHY
- From real scenario
- Shows pattern clearly
- Ready to adapt (not generic template)

**Don't:**
- Implement in 5+ languages
- Create fill-in-the-blank templates
- Write contrived examples

You're good at porting - one great example is enough.

### An example from a real scenario is not a real address

"From a real scenario" means the SHAPE is real, never that the values are. A skill ships to every
install and to a public repo, so an address, MAC, hostname, user or path copied out of the machine
you developed on is published with it - and it reads as authoritative, so readers paste it back.
Substitute the reserved documentation values, which look real and name nothing:

| For               | Use                                                 | Reserved by |
|-------------------|-----------------------------------------------------|-------------|
| IPv4              | `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24` | RFC 5737    |
| IPv6              | `2001:db8::/32`                                     | RFC 3849    |
| MAC               | `00:00:5e:00:53:00` .. `:ff`                        | RFC 7042    |
| IPv6 link-local   | the EUI-64 form of one, `fe80::200:5eff:fe00:53af`  | (derived)   |
| Hostname / domain | `example.com`, `host.example`                       | RFC 2606    |
| Path              | `/path/to/project`, `~/project`                     | -           |

Vendored upstream documentation keeps its own examples - rewriting them would make the copy
disagree with its source. This rule is about values YOU add.

### Write the skill and its artifacts in the present tense, not as a story

A skill says what is true now. A review artifact says what was verified. Neither is a session log,
and the reader has neither your transcript nor your machine.

**Keep out of both:** what the operator instructed, permitted or changed their mind about; which
tool or agent you reached for and when; scratch and temp paths; how the skill looked before this
edit; who caught what. **Keep in the artifact:** the claim tested, how it was tested, and the
outcome - a verbatim line of agent output earns its place when it IS the evidence, a paragraph of
narrative around it does not.

The trap is that provenance feels like rigour while you are writing it, because it is what you
just lived through. Test it by reading a line back and asking what a reader DOES differently
knowing it. "The operator lifted a restriction mid-session" changes nothing for anyone; "a
subagent on the old text wrote the call the library refuses" is the test result.

<Bad>
```markdown
- [x] Subagent scenarios run, both arms, after the operator lifted the standing no-dispatch
      instruction mid-session. Verified with a script at /tmp/scratch-9f2/verify.py on the dev
      box, whose LAN address is 192.0.2.139.
```
</Bad>

<Good>
```markdown
- [x] RED: a subagent given the pre-change text wrote `ping(entry.ip)`, which the library refuses.
      GREEN: given the new text, `ping(entry.scoped)`.
- [x] Every code block executed against the real library, not reviewed.
```
</Good>

## File Organization

### Self-Contained Skill
```
defense-in-depth/
  SKILL.md    # Everything inline
```
When: All content fits, no heavy reference needed

### Skill with Reusable Tool
```
condition-based-waiting/
  SKILL.md    # Overview + patterns
  example.ts  # Working helpers to adapt
```
When: Tool is reusable code, not just narrative

### Skill with Heavy Reference
```
pptx/
  SKILL.md       # Overview + workflows
  pptxgenjs.md   # 600 lines API reference
  ooxml.md       # 500 lines XML structure
  scripts/       # Executable tools
```
When: Reference material too large for inline

### Bundled scripts and hooks: keep them cross-platform

Any script you ship inside a skill or as a hook (a `.sh`, `.py`, `.js`, scripts in
`scripts/`, or a hook command) runs on a user's machine that may be Windows. Author it
so Windows does not silently break it:

- **Write the logic in Python, not bash or `jq`.** A script's or hook's real work belongs in a
  typed, testable language (Python with the standard library is the default); use bash only as a
  thin launcher shim. Bash/`jq` pipelines are hard to unit-test, behave differently across OSes,
  and are the usual source of silent Windows breakage.
- **Line endings: LF only.** Commit a `.gitattributes` that pins `*.sh text eol=lf`
  (and `*.py`/`*.json` for stable hashing). A CRLF `.sh` makes Git Bash on Windows fail
  with "cannot execute: required file not found" or `$'\r': command not found`, and a
  Stop/PreToolUse hook that exits cleanly on failure then disappears with no error.
- **Force UTF-8 in launched interpreters.** A non-UTF-8 Windows locale (e.g. German
  cp1252) corrupts IO. For Python set `PYTHONUTF8=1` and `PYTHONIOENCODING=utf-8`
  before exec; read/write files with explicit `encoding="utf-8"`.
- **Do not assume an interpreter name resolves.** On Windows `python3` is usually the
  Microsoft Store stub (exits non-zero in a subprocess), `python` may be Python 2, and
  `py -3` is Windows-only. Launch Python through a small bash shim that probes
  `python3 -> python -> py -3` and converts POSIX paths with `cygpath` when present.
  The plugin's `hooks/run-python.sh` is the working reference; reuse it.
- **Git Bash only on Windows.** Hooks run through Git Bash (Git for Windows), not WSL or
  Cygwin (those mount drives differently and resolve a Linux interpreter). Guard
  `uname -s` and skip loudly to stderr under an unexpected shell.
- **A hook must never wedge a turn.** Every failure path exits 0; degrade silently
  (with a one-line stderr note) rather than erroring.
- **Set the executable bit in git, not the working tree.** A file run directly (`./script`, or a
  hook invoked by path with a `#!` shebang) needs its exec bit recorded in git. A working-tree
  `chmod +x` does NOT persist when `core.fileMode = false`; set it in the index with
  `git update-index --chmod=+x <file>` and verify with `git ls-files -s` (mode `100755`).
  Interpreter-run files with no shebang (a `.py`/`.js` run as `python x.py`) are never `./`-run,
  so they stay `100644`.
- **Match signals to their source.** A gate that scans transcript text for a trigger
  must run intent/correction patterns against the *user* role and self-admission
  patterns against the *assistant* role; matching both over concatenated text fires on
  ordinary assistant phrasing.
- **Do real work in this preference order; drop a level only when the one above cannot do it:**
  1. **A modern, well-maintained library for the job.** Consult the
     **`bitranox:coding-python-use-modern-libraries`** skill and use its pick (httpx2 for HTTP, orjson for
     JSON, rtoml for TOML, ruamel.yaml for YAML, and so on). Do not fall back to dated
     defaults (`urllib`/`requests`, stdlib `json`) when a clearly better library exists.
     Declare deps via `uv` so they land in an isolated, reproducible environment, never the
     user's system Python: a CLI tool via `uvx <tool>`, or a script with PEP-723 inline
     metadata run by `uv run script.py` (uv fetches the deps on run, no prompt).
     - **Libraries on the `bitranox:coding-python-use-modern-libraries` list are pre-approved** - use them and
       let `uv` fetch them; do NOT ask first.
     - For a library NOT on that list, first vet it: trustworthy (reputable maintainer or
       community, not a typo-squat), common (widely adopted), and modern (actively maintained,
       current releases). If it passes, propose adding it to the `bitranox:coding-python-use-modern-libraries`
       list so the curated set grows. Each new entry needs: a proper one-line description of
       what it is for, the older library/libraries it replaces, and why it is better. Then use
       it. If it fails vetting, prefer a curated alternative or stdlib. Either way surface the
       choice to the user (name it, say why, note it is new to the list).
     - `uv` itself is the one prerequisite: if it is not installed, STOP and ask permission to
       install it (explaining what for) rather than installing it silently.
  2. **Standard library when no third-party library is warranted** (small glue, no hot path,
     or the stdlib module is genuinely the best tool: `pathlib`, `dataclasses`, `zoneinfo`,
     `re`, `subprocess`). The goal is the best tool, not the most dependencies.
  3. **An external command (last resort).** Only when neither of the above fits. Shelling out
     is the least portable choice because the program and its flags differ per OS:
     - **It must EXIST on every target OS.** Windows usually has no `grep`, `sed`, `awk`,
       `curl`, `jq`, `timeout`, or `make`. Probe with `shutil.which(prog)` and fail with a
       clear message (or fall back to stdlib) instead of crashing with "file not found".
     - **Its flags must be valid for THAT OS's build.** The same tool name is often a
       different program with different options (GNU vs BSD/macOS `sed`/`date`/`stat`; the
       Windows port of a tool; PowerShell vs `cmd.exe` builtins). Do not assume a Linux flag
       works elsewhere; stick to the common documented subset or branch per platform.
     - **Pass an argv list, never a shell string.** Use `subprocess.run([...])`, not
       `shell=True`. A shell string (pipes, `||`, `$?`, redirects, `case`) is POSIX-only,
       breaks under Windows `cmd.exe`, and is a command-injection risk. Decide success from
       the return code plus an output-file or output-text check in your own code, not shell
       glue. If you must accept a user-supplied command template, parse it once with
       `shlex.split` and substitute placeholders into the list elements (no shell).

### Ship tests for every script (and they MUST pass)

A skill that bundles Python scripts MUST also ship a `tests/` directory with pytest tests, and
those tests MUST pass before the skill is considered done. This is not optional.

- **Cover every important/main function** - each script's public/main functions need a test
  with decent coverage (happy path plus the key edge cases), not just an import smoke test.
- **Tests must pass.** Run them (`uv run --with pytest pytest tests/` or the project's runner)
  and confirm green. A skill with failing or missing tests does not ship.
- **Test by behaviour with real inputs:** build a tiny fixture project/data, run the function,
  assert on the actual output (e.g. the analyzer flags the planted case and skips the
  decorated/compiled one). Writing the tests routinely surfaces real bugs (argv ignored,
  order-sensitive regex, wrong thresholds) - fix the script, do not weaken the test.
- **Keep scripts import-safe** so they can be tested: all run-time work behind
  `if __name__ == "__main__":`, never at module top level (a script that executes on import
  cannot be unit-tested and will run side effects when imported).
- **A bundled script must IMPORT in a BARE environment.** The contribution gate (and any plain
  `pytest`) imports your test modules with the interpreter as-is - it does NOT provision a script's
  PEP 723 `dependencies`; only `uv run <script>` does. So a hard top-level `import orjson` collects
  fine under `uv run` yet fails the gate with `ModuleNotFoundError` on a clean runner. Guard every
  third-party import with a stdlib fallback (`try: import orjson ... except ModuleNotFoundError:
  import json as ...`) - keep the fast path, import anywhere. VERIFY in a fresh venv that has ONLY
  pytest, not your deps (`uv venv x && uv pip install --python x pytest && x/bin/python -m pytest`);
  your own machine having the dep installed is exactly what hides this until CI.
- A `tests/conftest.py` that puts the skill dir on `sys.path` lets tests `import <script>` by
  module name.

### Universal rules vs. marketplace plumbing

Everything in this skill (CSO/description craft, structure, flowcharts, cross-platform scripts,
tests, library choices) applies to **any** skill you author - a standalone skill for your own
machine just as much as one you contribute to a marketplace. Follow it either way.

A skill you contribute to a **specific plugin marketplace** has EXTRA rules layered on top, and
those are repo-specific, so they live in that repo's `CONTRIBUTING.md` (and `CLAUDE.md`), not
here. Typically: a release/versioning policy, a contribution gate (pre-commit/CI check), an
index or registry to update when you add a skill, and a git-history policy. Read that repo's
`CONTRIBUTING.md` before contributing.

When you discover a NEW authoring rule: if it helps any skill author (a portability gotcha, a
testing habit, a structural pattern), it belongs **here** in `skill-writer`; if it only matters
for one repo's release machinery, it belongs in **that repo's** `CONTRIBUTING.md`.

### Persisting durable state: use the curated store, not a hand-rolled backend

If the skill you author persists durable facts, learnings, preferences, or state across sessions,
use the 

…(truncated)
