AGENTS.md · git:20260923.ae41833 · 2026-09-23 · sha256 6da4f85644bfa572

AGENTS.md git:20260923.ae41833A

Immutable. This exact content never changes and is served at /api/v1/blob/6da4f85644bfa572.

# AGENTS.md - Instructions for LLMs Working in This Repo

## Project Purpose

The Writing Style Library is a composable catalog of writing instructions organized along four orthogonal axes: Voice, Tone, Style/Mode/Genre, and Format/Output Structure. The goal is to allow any combination of axis values to be assembled into a structured prompt prefix that shapes LLM writing output toward a specific register, rhetorical pattern, and layout. The library is packaged as a Claude Code plugin with three skills today - `writing-instruction-builder` (compose a prompt from axis values you already know you want), `style-profile` (capture a personal default style once, for reuse), and `entry-recommender` (recommend a combination for a described situation, composing the prompt in the same step) - plus any further skill a maintainer-approved spec adds; check `docs/internal/` for in-flight proposals. A TypeScript/Python SDK and a Composer SPA were considered and deliberately deferred indefinitely (see `ROADMAP.md` - "Deliberately deferred").

---

## The Four-Axis Taxonomy Model

Four axes, one directory each, one selection each. Composition applies them in the precedence
order below (ADR 0016). Named "four-axis" per ADR 0018; earlier docs called this a three-axis
model and grouped Voice with Tone, so treat any surviving "Axis 1/2/3" numbering as stale.

**Axis 1 - Voice** (`taxonomy/voices/`): The persistent identity of the writer. Voice captures worldview, characteristic concerns, and professional archetype. It is stable across contexts. Example seed entries: `pragmatic-architect`, `product-thinker`, `operator`.

**Axis 2 - Tone** (`taxonomy/tones/`): Situational register layered on top of voice. Tone is orthogonal to voice and can be mixed freely. Example seed entries: `candid`, `encouraging`, `confident`.

> Voice and Tone are the most confusable pair in the catalog. The rule: **voice is what does
> not change; tone is what changes per piece.** They have separate directories, separate
> schemas with different required fields, and separate composer parameters. When triaging
> which directory a new entry belongs in, ask whether a writer could hold it constant across
> a year of writing.

**Axis 3 - Style** (`taxonomy/styles/`): The cognitive and rhetorical pattern of the writing. How ideas are sequenced and structured. Example seed entries: `problem-solution`, `layered-disclosure`, `decision-log`.

**Axis 4 - Format** (`taxonomy/formats/`): The visual and structural container. Defines headings, bullet depth, table layouts, section templates. Example seed entries: `adr`, `daily-standup`, `slack-message`.

---

## Key Conventions

### Atomic-Folder Pattern

Each taxonomy entry lives in its own folder: `taxonomy/<axis>/<entry-id>/`. The folder contains `ENTRY.md` (the entry body and frontmatter) and nothing else. Worked examples do not live inside the entry folder: they are separate files under the top-level `examples/` tree (`examples/vertical-slices/<topic-slug>/<axis>-<entry-id>.md` - see `examples/README.md` and CONTRIBUTING.md Path 2).

### Frontmatter-First

Every entry file must begin with a YAML frontmatter block. The block must include at minimum: `id`, `axis`, `name`, `version`, `review_status`, `tags`, `created`, `author`. Validate against the axis-specific schema in `schemas/` before committing.

### No Em-Dashes, No En-Dashes

This is a hard rule with no exceptions. Do not use U+2014 (em-dash) or U+2013 (en-dash) anywhere in any file you create or modify - including entry bodies, documentation, commit messages, and comments. Use " - " (space-hyphen-space) as the substitute. A pre-commit hook enforces this rule; do not attempt to disable it.

### Review Status Progression

New entries must start at `draft`. The progression is: `draft` -> `machine-verified` -> `stable` -> `reference-quality` (ADR 0021). Tooling sets `machine-verified` once an entry passes every enforced check (`python tools/promote.py`); only the maintainer sets `stable` or `reference-quality`, after reading the entry (`python tools/promote.py --reviewed <id>`, recorded in `docs/internal/review-ledger.md`). `reviewed` is unused for taxonomy entries. Do not set an entry to `stable` or `reference-quality` without maintainer approval.

The 60 entries shipped in v0.1.0 were set to `stable` as the initial baseline, and later Format entries followed through the agentic factory. None of those 97 had been read one by one by the maintainer, so ADR 0021 moved all of them to `machine-verified`; each returns to `stable` as it is read. The catalog currently carries 97 `machine-verified` entries and 21 `draft` entries (the Hold-20 Formats, staged for a future audience-expansion release, plus the `marketer` voice), and any new entry starts at `draft`.

---

## How to Add an Entry

1. Create a new folder: `taxonomy/<axis>/<kebab-case-id>/`
2. Create `ENTRY.md` inside that folder
3. Add the required frontmatter fields (see CONTRIBUTING.md for the full list)
4. Write the entry body: definition sentence, "When to use" bullets, "Instruction block" section, optional "Do not use when" section
5. Add at least one example file (vertical slice preferred)
6. Run `python tools/validate.py` and confirm it passes
7. Commit with a Conventional Commits message: `feat(taxonomy): add <entry-id> <axis> entry`

---

## Generating and Promoting Content at Scale (the Agentic Factory)

The steps above add ONE entry by hand. To generate or promote content at scale - new
candidate entries, whole-corpus de-duplication, or rendering and promoting drafts to
`machine-verified` - use the **agentic generation factory** in `tools/agentic/`. It is the
catalog's production engine: isolated subagents do the writing, layered gates do the
checking, and it all runs free in-session (no paid CI).

Key rules when operating it:

- **New entries always start `review_status: draft`.** Leaving `draft` for
  `machine-verified` earns a hard cost (rendering on all 12 anchor topics); `stable`
  is a maintainer decision made after reading the entry (ADR 0021).
- **Gate 2 is atomic.** An admitted entry (`machine-verified` or above) must render on all 12 anchor topics
  (`tools/anchor_topics.py`). Render while still draft (drafts are exempt), then flip
  with `python tools/promote.py` - it is guarded and will refuse to leave the build red.
- **Gate every batch.** New entries get a cross-vendor distinguishability gate; the
  corpus gets a family-cluster de-dup audit; dated samples get a calendar gate.

Start at [`tools/agentic/README.md`](tools/agentic/README.md). The design and contracts
are in [`docs/internal/agentic-generation-spec.md`](docs/internal/agentic-generation-spec.md);
the step-by-step promotion and release steps are in
[`docs/internal/release-plans/promotion-and-release-runbook.md`](docs/internal/release-plans/promotion-and-release-runbook.md).

---

## Validation

Always run validation before committing:

```bash
python tools/validate.py
```

This checks frontmatter completeness, schema conformance, no em-dash/en-dash characters, valid `review_status` values, and resolving cross-references.

---

## Schema Safety

The six contributor-facing schemas (`entry.universal`, `voice`, `tone`, `style`, `format`,
`example`) are **frozen** as of
[ADR 0019](docs/internal/adr/0019-schema-freeze-and-change-policy.md). `experimental/diff-pair.schema.json`
is not frozen and is served from `/schemas/experimental/` so its URL says so, but the
classes below still govern it.

The test: **would this edit change the verdict on any conceivable document, not just the
files in this repo?** Checking only the current corpus is the trap this policy exists to
avoid, because `additionalProperties: false` or a narrowed `$ref` can leave every entry here
passing while rejecting documents the published schema used to accept.

| Class | What it covers | Requires |
| ------ | --------------- | --------- |
| **A - Annotation** | An allowlist, nothing else: `title`, `description`, `$comment`, `examples`, plus unobservable reordering | Normal PR. No ADR, no version bump, no entry migration. Say "annotation-only, class A" in the PR body. |
| **B - Additive optional** | New **optional** property; widened `enum`, `pattern`, or bound | Minor version bump + `CHANGELOG.md` note |
| **C - Breaking, or unclear** | New required property, removal, rename, narrowing, optional -> required, **and every keyword not listed in A or B** - including `$id`, `$schema`, `$ref`, `$defs`, `additionalProperties`, `unevaluatedProperties`, `format`, and the combinators | ADR + **major** version bump + migrate every affected file in the same change + `validate.py` green before merge |

**The default is C.** Anything not on the class A allowlist or squarely class B is class C;
unclear resolves to the most expensive class, not to a fourth one. A change to
`entry.universal` or a shared `$def` is class C for every schema that `$ref`s it.

ADR 0019 is the authority; this table is a summary. If still unsure, open a draft PR and ask
rather than committing directly.

---

## Skill Frontmatter Convention

Each `skills/*/SKILL.md` includes a `metadata: version:` block in its YAML frontmatter:

```yaml
metadata:
  version: "0.3.0"
```

This is a project-private convention - it is not part of the Claude Code skills specification. The value must be a SemVer `X.Y.Z` string and must match the `version` field for that skill in `library.json` under `components.skills`. The validator at `scripts/validate-plugin-manifest.mjs` enforces this agreement: it will fail if the SKILL.md version and the library.json version drift. When bumping a skill version, update both `SKILL.md` and `library.json` in the same commit.

---

## Paths to Know

| Path | What it contains |
| ------ | --------------- |
| `taxonomy/` | All axis entries |
| `examples/` | Worked output examples |
| `schemas/` | JSON Schema definitions for entry types |
| `skills/writing-instruction-builder/` | Compose a prompt from axis values you already know you want |
| `skills/style-profile/` | Capture a personal default style once, for reuse |
| `skills/entry-recommender/` | Recommend a combination for a described situation |
| `tools/validate.py` | Validation script |
| `tools/agentic/` | The agentic generation factory (generate, audit, remediate, render harnesses) |
| `tools/promote.py` | Guarded, atomic promotion: draft -> machine-verified; `--reviewed`: machine-verified -> stable |
| `site/` | The Astro Starlight documentation site (authored pages in `src/content/docs/`; catalog pages generated at build time) |
| `docs/` | Internal governance only (see the two rows below) - not built by Astro; the site lives in `site/` |
| `docs/internal/` | Living planning docs - ADRs, release-plan trackers, specs, the backlog - maintained under direct maintainer direction as work proceeds. Demonstrated by this repo's own history: `agentic-generation-spec.md`, the promotion-and-release runbook, and every `stream-b-*` tracker were all produced and merged here. |
| `docs/internal/_working/` | Frozen historical planning snapshots - read-only, do not modify |
| `_LOCAL/` | Source research - read-only, do not modify |