diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json
index 6033a310..dc7f009c 100644
--- a/.agents/plugins/marketplace.json
+++ b/.agents/plugins/marketplace.json
@@ -6,7 +6,7 @@
"plugins": [
{
"name": "slow-powers",
- "version": "0.6.2",
+ "version": "0.7.0",
"source": {
"source": "url",
"url": "./"
diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index 15f43184..61c7391c 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -9,7 +9,7 @@
{
"name": "slow-powers",
"description": "Agent skills to upgrade plan mode and debugging, enforce best practices, and enhance native features, not replace them.",
- "version": "0.6.2",
+ "version": "0.7.0",
"source": "./",
"author": {
"name": "Max Haarhaus",
diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index c35eddda..db0676d7 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -1,7 +1,7 @@
{
"name": "slow-powers",
"description": "Agent skills to upgrade plan mode and debugging, enforce best practices, and enhance native features, not replace them.",
- "version": "0.6.2",
+ "version": "0.7.0",
"author": {
"name": "Max Haarhaus",
"email": "samiamorwas@gmail.com"
diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json
index 56a84158..8556b2a7 100644
--- a/.codex-plugin/plugin.json
+++ b/.codex-plugin/plugin.json
@@ -1,6 +1,6 @@
{
"name": "slow-powers",
- "version": "0.6.2",
+ "version": "0.7.0",
"description": "Agent skills to upgrade plan mode and debugging, enforce best practices, and enhance native features, not replace them.",
"author": {
"name": "Max Haarhaus",
diff --git a/.github/badges/skills-ref.json b/.github/badges/skills-ref.json
index b96186fd..480be878 100644
--- a/.github/badges/skills-ref.json
+++ b/.github/badges/skills-ref.json
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "skills-ref",
- "message": "8/8 valid",
+ "message": "9/9 valid",
"color": "brightgreen"
}
diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml
index b5804fc0..f0edbcc8 100644
--- a/.github/workflows/release-pr.yml
+++ b/.github/workflows/release-pr.yml
@@ -57,9 +57,24 @@ jobs:
PR_BODY_FILE=$(mktemp)
cat > "$PR_BODY_FILE" <<'EOF'
+
## Release notes
- _Replace this paragraph with a short narrative for the release. If left unchanged, GitHub's auto-generated notes will be used instead._
+ ### Highlights
+
+
+
+ ### Behavior changes to know about
+
+
+
+ ### Fixes
+
+
+
+ ### Internal changes
+
+
EOF
PR_URL=$(gh pr create \
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c829f67b..50ee0682 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -54,7 +54,7 @@ jobs:
--json body \
--jq '.[0].body // ""')
- if [ -n "$PR_BODY" ] && ! echo "$PR_BODY" | grep -q "Replace this paragraph"; then
+ if [ -n "$PR_BODY" ] && ! echo "$PR_BODY" | grep -q "release-notes:todo"; then
echo "Using release PR body for notes."
printf '%s\n' "$PR_BODY" > /tmp/release-notes.md
else
diff --git a/.github/workflows/skills-ref.yml b/.github/workflows/skills-ref.yml
index a053e4ba..4cbd948f 100644
--- a/.github/workflows/skills-ref.yml
+++ b/.github/workflows/skills-ref.yml
@@ -18,6 +18,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.RELEASE_PR_TOKEN }}
- uses: actions/setup-python@v5
with:
@@ -44,4 +46,4 @@ jobs:
exit 0
fi
git commit -m "chore(badge): update skills-ref validation badge [skip ci]"
- git push
\ No newline at end of file
+ git push
diff --git a/.gitignore b/.gitignore
index ce255d9b..a1ed5492 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
!.claude/settings.json
# Cline plugin install artifacts (e.g. from `cline plugin install ./ --cwd .`).
.cline/plugins/
+# Cline Memory Bank contents are per-developer local state; the shared
+# Memory Bank rule lives in .clinerules/ and stays tracked.
+memory-bank/
.DS_Store
node_modules/
.eval-magic
diff --git a/AGENTS.md b/AGENTS.md
index 1c83bce6..eabac96f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -16,8 +16,9 @@ This repo ships Slow-powers across four harnesses:
Cline-specific setup for working on this repo also lives at root:
-- `.clinerules/` — Cline rules (the Memory Bank custom instructions)
-- `memory-bank/` — Cline Memory Bank files recording ongoing work
+- `.clinerules/` — Cline rules (the Memory Bank custom instructions), tracked
+- `memory-bank/` — Cline Memory Bank contents. Gitignored: each developer
+ keeps their own local bank; only the rule in `.clinerules/` is shared
## Editing the right files
diff --git a/README.md b/README.md
index 763bc398..55588e2d 100644
--- a/README.md
+++ b/README.md
@@ -29,12 +29,12 @@ Slow-powers is an agent skill set for professional software development. It enha
- The test-driven-development skill catching a race before it ships. Terminal themed with Synthpunk Neon Dark.
+ The working-with-tdd skill catching a race before it ships. Terminal themed with Synthpunk Neon Dark.
## About this fork
@@ -117,10 +117,11 @@ Slow-powers provides a set of highly focused skills that ensure your agent opera
1. **`hardening-plans`** — Instructs the agent to re-review any plans before it hands them back to you, looking for hallucinations, logical inconsistencies, and other common plan mistakes.
2. **`investigating-bugs`** — Guides the agent to locate the root cause of failures via scientific hypothesis testing, avoiding "guess-and-check" thrashing.
3. **`working-in-isolation`** — Establishes an isolated workspace (worktree or branch) so new work doesn't collide with existing or in-progress work, keeping protected branches like `main` clean.
-4. **`test-driven-development`** — Enforces a strict RED-GREEN-REFACTOR cycle, ensuring all code is backed by failing test verification first.
+4. **`working-with-tdd`** — Enforces a strict RED-GREEN-REFACTOR cycle, ensuring all code is backed by failing test verification first.
5. **`verifying-development-work`** — Requires running actual test/build commands and presenting concrete evidence before any success claim, with a final review pass over the change, code AND comments, before work is handed back.
6. **`writing-skills`** — Helps write and edit skills, following the same best practices that guide slow-powers itself.
7. **`evaluating-skills`** — Teaches the agent how to run skill evals, so the value of skills and prose changes can be objectively assessed.
+8. **`writing-technical-docs`** — Guides the agent's technical writing — code comments, PR descriptions, READMEs, design docs — with a scale-sized writing process and evergreen-content rules that keep docs free of session narration.
## Why trust these skills?
@@ -130,7 +131,7 @@ Most skill packs ship on vibes. Every slow-powers skill ships with a documented
|---|---|---|---|---|
| `hardening-plans` | TBD | TBD | TBD | TBD |
| `investigating-bugs` | TBD | TBD | TBD | TBD |
-| `test-driven-development` | TBD | TBD | TBD | TBD |
+| `working-with-tdd` | TBD | TBD | TBD | TBD |
| `verifying-development-work` | TBD | TBD | TBD | TBD |
**Improvement vs no skill** is the gain in eval pass-rate (percentage points) when the same [eval-magic](https://github.com/slowdini/eval-magic) suite runs with the skill versus without it.
@@ -139,7 +140,7 @@ Most skill packs ship on vibes. Every slow-powers skill ships with a documented
The skills declare prerequisite / next-step gates so the agent follows an intended skill sequence. These gates **suggest** what comes before and after a skill once it is invoked; they do **not** restrict when any skill can be invoked.
-**Plan mode:** plan mode → `hardening-plans` → `working-in-isolation` → `test-driven-development` → `verifying-development-work`
+**Plan mode:** plan mode → `hardening-plans` → `working-in-isolation` → `working-with-tdd` → `verifying-development-work`
**Debugging:** (`working-in-isolation`) → `investigating-bugs` → `verifying-development-work`
diff --git a/assets/demo/tdd-playback.sh b/assets/demo/tdd-playback.sh
index e6971a86..a7714de7 100644
--- a/assets/demo/tdd-playback.sh
+++ b/assets/demo/tdd-playback.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# Re-enactment of a REAL Claude Code test-driven-development session (RED -> GREEN).
+# Re-enactment of a REAL Claude Code TDD session (RED -> GREEN).
#
# Deterministic: pacing lives here (the "actor"), VHS is the "camera".
# Colors use base-16 ANSI (30-37 / 90-97 + bold) so the terminal theme drives them
@@ -29,7 +29,7 @@ pause 0.4
human "for your own account, read the api key from the session instead of refetching it"
pause 0.9
-bullet "Using ${B}${CYAN}test-driven-development${RST}."
+bullet "Using ${B}${CYAN}working-with-tdd${RST}."
pause 0.8
# ---------- RED ----------
diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md
deleted file mode 100644
index 40f08416..00000000
--- a/memory-bank/activeContext.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# Active Context
-
-## Current focus
-
-Cline plan-gate timing fix (August 2026). The first live test of the Cline
-plugin showed the gate rejecting an un-hardened plan only AFTER the plan was
-presented and approved — because `switch_to_act_mode` is called post-approval
-in Cline, unlike Claude's `ExitPlanMode` which carries the plan text. The gate
-is now two-layer:
-
-1. **Cline plugin** (`cline/plugins/slow-powers.js`, declared via the `cline`
- field in `package.json`): registers `bootstrap.md` AND a
- `slow-powers/plan-presentation` rule (harden before presenting — the only
- mechanism that reaches the agent pre-presentation), and gates
- `switch_to_act_mode` as a pre-EXECUTION backstop with an
- already-hardened transcript short-circuit (skip-once marker as fail-open
- floor, mirroring `hooks/exit-plan-mode`).
- Skills are auto-discovered from the package root — no wiring needed.
-2. **Repo-local Cline setup**: `.clinerules/memory-bank.md` (canonical Memory
- Bank instructions) and this `memory-bank/` directory, both committed.
-
-## Recent changes
-
-- `fix/cline-plan-gate-timing` branch: plugin header docs rewritten (real
- Cline plan flow), `PLAN_PRESENTATION_RULE` added, `planAlreadyHardened()`
- transcript scan added (matches the `skills` tool-input shape only, never
- prose, so the hook's own skip reason can't false-positive), `SKIP_REASON`
- reworded for execution-gate semantics; 5 new tests in
- `tests/harness/cline-plugin.test.ts` (rule registration, short-circuit,
- false-positive guards, full flow).
-- Earlier (merged via PR #266/#267/#268): `cline/plugins/slow-powers.js` (new),
- `package.json` `cline` field + `files`; `tests/harness/spec.ts` Cline entry;
- Cline assertions in `manifests.test.ts`; README Cline install section;
- AGENTS.md four-harness update; `.gitignore` covers `.cline/plugins/`.
-
-## Verification results
-
-- `bun test`: 167 pass / 0 fail; typecheck and biome clean on changed files.
- (Baseline note: `bun run check` fails on three pre-existing
- `.eval-magic/hardening-plans/iteration-2` eval-fixture files — unrelated.)
-- Live (Cline CLI 3.0.51, headless): install, skills discovery, bootstrap rule
- injection confirmed. First interactive test exposed the gate-timing issue
- this branch fixes.
-- Plan gate: unit-tested against the documented `AgentBeforeToolResult`
- contract; runtime `skip` handling and hook context shape confirmed in the
- shipped CLI source. `switch_to_act_mode` is NOT exposed in headless one-shot
- sessions, so an interactive (TUI) confirmation of the new two-layer behavior
- is the one remaining manual check.
-
-## Next steps
-
-- Manually confirm the new behavior in an interactive `cline -i` plan-mode
- session: with the rule active the agent should harden BEFORE presenting;
- if it skips hardening, the first `switch_to_act_mode` after approval is
- skipped with the hardening instruction and the retry (transcript now holds
- the skills call) passes.
-- Then open the PR for `fix/cline-plan-gate-timing` (base `dev`).
-
-## Active decisions
-
-- Distribution reuses the root `package.json` (git install); no separate npm
- package or release-workflow change.
-- Pre-presentation enforcement is a RULE, not a hook: Cline has no hook moment
- before a plan is shown (verified against the installed binary and
- `@cline/shared` `AgentRuntimeHooks`). The hook stays as the pre-execution
- backstop. Trust guarantee moves from "user only ever sees a hardened plan"
- (Claude, achievable) to "an un-hardened plan is never executed, and hook
- firing routes the agent to harden + re-present" (Cline).
-- The already-hardened short-circuit (upstream #153 refinement) is now
- implemented for Cline via the `snapshot.messages` transcript scan.
-- No `.cline/skills/` dogfooding symlinks: Cline's skill registry is
- last-wins with plugin dirs scanned *after* workspace dirs, so an installed
- slow-powers plugin would silently shadow the repo's skills. The
- installed-vs-repo precedence question is deferred to a separate
- cross-harness exploration (it affects all harnesses).
-
-## Learnings
-
-- Cline plugins load only in CLI/SDK/Kanban — not VSCode/JetBrains. IDE users
- get skills via manual copy into `.cline/skills/` or `~/.cline/skills/`.
-- Cline reads `AGENTS.md` natively; no memory-file symlink needed for it.
-- **Cline plan-mode flow (verified in CLI 3.0.51 source):** the plan is
- presented as a free-form assistant message; the CLI's plan-mode system
- prompt and the `switch_to_act_mode` tool description both mandate: present
- plan → end turn → user approves in a follow-up message → ONLY THEN call
- `switch_to_act_mode` (`lifecycle.completesRun`, then a continuation turn
- with "The user approved switching to act mode..."). So
- `switch_to_act_mode` is an execution boundary, never a presentation moment.
-- **Complete plugin hook surface** (`AgentRuntimeHooks`, binary + SDK agree):
- `beforeRun`, `afterRun` (observe), `beforeModel` (rewrite request / stop),
- `afterModel` (stop only — and `stop:true` aborts the whole run),
- `beforeTool` (skip/input/policy/stop), `afterTool` (result/stop),
- `onEvent` (observe only). Nothing fires before streamed assistant text,
- so no hook can gate plan presentation.
-- Hook contexts pass the tool name on BOTH `tool.name` (first-party shape) and
- `toolCall.name` (docs shape) — read `tool?.name ?? toolCall?.name`. The
- `beforeTool` context also carries `snapshot.messages` — the full
- conversation transcript, usable for detection logic.
-- Skill invocation in Cline goes through a `skills` tool with input
- `{skill, args}` — match that shape for skill-use detection.
-- Headless one-shot sessions (`cline -p "..."`) don't expose
- `switch_to_act_mode` and can't drive TTY-only commands (`cline config`); use
- interactive sessions for plan-gate verification.
-- Cline's local plugin install copies dotfile-free repo content — everything
- the plugin needs (`cline/`, `skills/`, `bootstrap.md`) is a normal path, so
- this is fine.
diff --git a/memory-bank/productContext.md b/memory-bank/productContext.md
deleted file mode 100644
index 83bfa2e6..00000000
--- a/memory-bank/productContext.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Product Context
-
-## Why this exists
-
-Coding agents under pressure skip discipline: they present unreviewed plans,
-claim success without running tests, thrash on bugs with guess-and-check, and
-let new work collide with in-progress branches. Slow-powers exists to put that
-discipline back — not by replacing harness features, but by hardening them
-(plan-mode gates, skill-enforcement bootstrap, verification loops).
-
-## How it should work
-
-- A bootstrap block (`bootstrap.md`) is injected into every session, making
- skill use non-negotiable when a skill applies.
-- Skills declare prerequisite / next-step gates so the agent follows an
- intended sequence (plan → harden → isolate → TDD → verify).
-- Harness hooks/plugins supply the deterministic beats a skill can't enforce
- on its own (e.g. gating plan presentation on hardening-plans).
-
-## User experience goals
-
-- Install once per harness, then forget it — the value shows up as plans that
- don't hallucinate files, tests that exist before code, and success claims
- backed by command output.
-- "The plugin for people who don't install plugins": minimal surface, no
- config, no lock-in; users can extend with their own evaluated skills.
diff --git a/memory-bank/progress.md b/memory-bank/progress.md
deleted file mode 100644
index fa3d6793..00000000
--- a/memory-bank/progress.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Progress
-
-## What works
-
-- Eight skills with eval coverage; bootstrap injection and plan gates on
- Claude Code, Codex CLI, and OpenCode.
-- Full test suite green (`bun test`), typecheck and biome clean.
-- **Cline support**: plugin entry, manifest field, unit + manifest tests,
- README/AGENTS.md docs, memory bank initialized. Verified live on Cline CLI
- 3.0.51: install, skills discovery, and bootstrap rule injection confirmed
- in headless sessions.
-- **Cline plan-gate timing fixed** (`fix/cline-plan-gate-timing`): first live
- test showed the old skip-once hook firing after plan presentation and
- approval (Cline's `switch_to_act_mode` is post-approval by design). Now
- two-layer: a plan-presentation rule enforces hardening BEFORE presentation
- (no Cline hook fires earlier than that), and the hook is the pre-execution
- backstop with an already-hardened transcript short-circuit.
-
-## What's left
-
-- Commit/PR for `fix/cline-plan-gate-timing`; manual interactive check of the
- new two-layer gate (`switch_to_act_mode` is only exposed in interactive
- sessions), then release: next version bump carries the Cline plugin.
-
-## Known issues / deferred
-
-- Cline plugins don't load on VSCode/JetBrains extensions — IDE users get a
- documented skills-only manual install (no bootstrap, no plan gate).
-- Cline skill collisions are last-wins with plugin directories scanned after
- workspace ones, so an installed slow-powers plugin shadows same-named
- workspace skills (the reverse of what this repo wants for development).
- Deferred: cross-harness installed-vs-repo precedence exploration.
-- Cline pre-presentation enforcement is prompt-level (rule) — Cline exposes no
- hook moment before streamed assistant text. The hook backstop guarantees an
- un-hardened plan is never executed; if it fires, the user briefly saw an
- un-hardened draft before the agent hardens and re-presents.
-
-## Decision log
-
-- 2026-08: Cline distribution via root `package.json` + git install (no new
- npm package).
-- 2026-08: Memory bank committed to git (`.clinerules/memory-bank.md` +
- `memory-bank/`).
-- 2026-08: No `.cline/skills/` symlinks (option (c)) pending the precedence
- exploration.
-- 2026-08: Cline plan gate re-anchored to a two-layer design (rule
- pre-presentation + hook pre-execution backstop) after the first live test
- showed `switch_to_act_mode` fires post-approval in Cline; the deferred
- already-hardened short-circuit implemented via `snapshot.messages` scan.
diff --git a/memory-bank/projectbrief.md b/memory-bank/projectbrief.md
deleted file mode 100644
index a84d3e84..00000000
--- a/memory-bank/projectbrief.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Project Brief
-
-Slow-powers is an agent skill set for professional software development. It
-enhances plan mode and debugging work, enforces best practices (TDD,
-verification, workspace isolation), and works *with* the features of modern
-agent harnesses instead of replacing them. It is a fork of
-[obra/superpowers](https://github.com/obra/superpowers), with rewrites focused
-on clarity, token efficiency, and a lighter touch.
-
-## Core goals
-
-- Ship discipline-enforcing skills (plan hardening, TDD, scientific debugging,
- verification, isolated workspaces) that measurably improve agent behavior —
- every skill ships with a documented eval or it doesn't ship.
-- Support multiple agent harnesses from one repo: Claude Code, OpenAI Codex,
- OpenCode, and Cline.
-- Keep skill content cross-harness compatible (no harness-specific vocabulary
- in skill prose).
-
-## Scope
-
-- `skills/` holds the shared skills and their evals.
-- Harness-specific integration (manifests, hooks, runtime plugins) lives in
- top-level directories; skill content itself stays harness-agnostic.
-- This repo is the source of truth; installed plugins are downstream copies.
diff --git a/memory-bank/systemPatterns.md b/memory-bank/systemPatterns.md
deleted file mode 100644
index 1c645ae6..00000000
--- a/memory-bank/systemPatterns.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# System Patterns
-
-## Flat layout, one source of truth
-
-Skills and shared assets live at the repo root; each harness's integration is
-a thin top-level layer that points back at them. Nothing is duplicated per
-harness.
-
-## Per-harness delivery of the same two behaviors
-
-Every harness delivers (1) the `bootstrap.md` skill-enforcement block and
-(2) a deterministic plan-presentation gate, using that harness's native
-mechanism:
-
-| Harness | Bootstrap delivery | Plan gate |
-| -------- | -------------------------------------- | ------------------------------------------------ |
-| Claude | `hooks/session-start` (SessionStart) | `hooks/exit-plan-mode` (PreToolUse, deny-once) |
-| Codex | shared `hooks/hooks.json` SessionStart | `hooks/codex-stop-plan-mode` (Stop hook) |
-| OpenCode | `opencode/plugins/slow-powers.js` system-prompt transform | same plugin, `file.edited` event on plan files |
-| Cline | `cline/plugins/slow-powers.js` `registerRule` (bootstrap + plan-presentation rules) | same plugin, `beforeTool` on `switch_to_act_mode` — pre-execution backstop: transcript short-circuit when hardening-plans already ran, else skip-once |
-
-Claude/Codex hooks are extensionless bash scripts dispatched by the
-`hooks/run-hook.cmd` polyglot (Windows-safe). OpenCode/Cline integrations are
-dependency-free JS runtime plugins.
-
-## Manifest and version lockstep
-
-`scripts/manifest-files.ts` lists every versioned manifest;
-`scripts/bump-version.ts` rewrites them in lockstep (then biome-formats);
-`tests/harness/manifests.test.ts` asserts parity. The Cline and OpenCode
-integrations both declare themselves inside the root `package.json`, which is
-already locked.
-
-## Parameterized parity tests
-
-`tests/harness/spec.ts` holds one `HarnessSpec` per harness; the suite in
-`manifests.test.ts` applies the same contract to all of them. Adding a
-harness = adding a spec entry (+ custom assertions when the manifest shape
-doesn't fit the dotted-string `pathFields` machinery, as with Cline's nested
-`cline.plugins[].paths[]`).
-
-## Skill integrity tests
-
-The shared-assets block in `manifests.test.ts` pins: SKILL.md frontmatter
-(name + description), top-level-only skill folders, documented peer
-directories (`assets`/`evals`/`references`/`scripts`), resolvable markdown
-links, reachable reference files, mermaid-not-graphviz, and the bootstrap
-marker.
diff --git a/memory-bank/techContext.md b/memory-bank/techContext.md
deleted file mode 100644
index ad3b0c4f..00000000
--- a/memory-bank/techContext.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Tech Context
-
-## Stack
-
-- **bun** — test runner and script runtime (`bun test`, `bun scripts/*.ts`)
-- **biome** — lint + format (`bun run check`, `check:ci`); JSON included
-- **typescript** — `tsc --noEmit` over `scripts/**/*.ts` and `tests/**/*.ts`
- only (runtime plugins and hooks are plain JS/bash, deliberately)
-- **husky + lint-staged** — pre-commit typecheck/lint, pre-push test suite
- (installed by `bun install` via the `prepare` script)
-- **eval-magic** — skill evaluation harness (`bun run evals*` scripts);
- eval fixtures live under `skills//evals/`
-
-## Release flow
-
-Releases cut from `dev`, tagged from `main`. The Release PR workflow bumps
-every manifest via `scripts/bump-version.ts`; merging to `main` tags, creates
-the GitHub release, and publishes `@slowdini/slow-powers-opencode` to npm.
-
-## Constraints
-
-- Hook scripts: pure bash, no jq/python/bun at hook time; printf-based JSON
- (heredocs hang on bash 5.3+); extensionless filenames (Windows).
-- Cline single-file plugins may import only Node builtins; `@cline/*`
- packages are host-provided.
-- Skill prose must use cross-harness vocabulary (see `writing-skills`).
-
-## Local environment
-
-- Cline CLI 3.0.51 (homebrew) used for live verification of the Cline plugin.
diff --git a/package.json b/package.json
index a721a579..d10cb67f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@slowdini/slow-powers-opencode",
- "version": "0.6.2",
+ "version": "0.7.0",
"description": "Slow-powers — structured development workflows for coding agents (TDD, debugging, verification, git hygiene)",
"type": "module",
"main": "./opencode/plugins/slow-powers.js",
diff --git a/scripts/skills-ref-badge.test.ts b/scripts/skills-ref-badge.test.ts
index 3f7ae34a..4b3c2e69 100644
--- a/scripts/skills-ref-badge.test.ts
+++ b/scripts/skills-ref-badge.test.ts
@@ -1,6 +1,13 @@
import { describe, expect, test } from "bun:test";
+import { readFileSync } from "node:fs";
+import { join } from "node:path";
import { buildBadgeJson } from "./skills-ref-badge";
+const skillsRefWorkflow = readFileSync(
+ join(import.meta.dir, "..", ".github/workflows/skills-ref.yml"),
+ "utf8",
+);
+
describe("skills-ref-badge buildBadgeJson", () => {
test("all skills valid -> green score badge", () => {
expect(buildBadgeJson(8, 8)).toEqual({
@@ -30,3 +37,11 @@ describe("skills-ref-badge buildBadgeJson", () => {
});
});
});
+
+describe("skills-ref badge workflow", () => {
+ test("authenticates protected-branch pushes with the release token", () => {
+ expect(skillsRefWorkflow).toMatch(
+ /- uses: actions\/checkout@v4\n\s+with:\n\s+token: \$\{\{ secrets\.RELEASE_PR_TOKEN \}\}/,
+ );
+ });
+});
diff --git a/skills/auditing-slow-powers-usage/SKILL.md b/skills/auditing-slow-powers-usage/SKILL.md
index 06bcd877..084ce2ce 100644
--- a/skills/auditing-slow-powers-usage/SKILL.md
+++ b/skills/auditing-slow-powers-usage/SKILL.md
@@ -147,11 +147,11 @@ Where your recall is shaky or a figure is a guess. Be specific about what you're
✅ Good — reports the live decision and reasoning:
-> | test-driven-development | I was about to add a new parser branch | "The change is two lines and I can eyeball it; the user said the demo is in five minutes, so I wrote the code first and planned to backfill a test." |
+> | working-with-tdd | I was about to add a new parser branch | "The change is two lines and I can eyeball it; the user said the demo is in five minutes, so I wrote the code first and planned to backfill a test." |
❌ Bad — recants, apologizes, promises future behavior (do not do this):
-> | test-driven-development | Adding a parser branch | "I skipped it, which was a mistake — I should have written the test first and I'll make sure to follow TDD next time." |
+> | working-with-tdd | Adding a parser branch | "I skipped it, which was a mistake — I should have written the test first and I'll make sure to follow TDD next time." |
The good row is data we can turn into a pressure test. The bad row tells us nothing about what you
actually decided and adds a promise you can't keep.
diff --git a/skills/auditing-slow-powers-usage/evals/baseline/NOTES.md b/skills/auditing-slow-powers-usage/evals/baseline/NOTES.md
index b1b5c763..e39c4cd9 100644
--- a/skills/auditing-slow-powers-usage/evals/baseline/NOTES.md
+++ b/skills/auditing-slow-powers-usage/evals/baseline/NOTES.md
@@ -4,13 +4,13 @@ Forward-looking observations from the run that produced this baseline. Provenanc
`BASELINE.md`; numbers are in `benchmark.json`. This file is the "what a future iterator should
know" companion.
-> **⚠️ Baseline is stale (as of the `working-in-isolation` rename, #156).** The fixtures and
-> `evals.json` rubrics were updated to rename `using-git-worktrees` → `working-in-isolation`, but
-> the committed `grading/*.json` and the observations below were produced against the *old* name and
-> are **not** re-graded — they're kept verbatim as the historical record. References to
-> `using-git-worktrees` / "worktrees" in this file and in `grading/*.json` describe that past run;
-> they are not live skill references. Re-run this eval to refresh the baseline before drawing new
-> conclusions from it.
+> **⚠️ Baseline is stale (as of the `working-in-isolation` rename, #156, and the
+> `working-with-tdd` rename, #255).** The fixtures and `evals.json` rubrics use the current skill
+> names, but the committed `grading/*.json` and the observations below were produced against the
+> old names and are **not** re-graded — they're kept verbatim as the historical record. References
+> to `using-git-worktrees`, `test-driven-development`, or "worktrees" in this file and in
+> `grading/*.json` describe that past run; they are not live skill references. Re-run this eval to
+> refresh the baseline before drawing new conclusions from it.
## Why this baseline exists despite a negative delta
diff --git a/skills/auditing-slow-powers-usage/evals/evals.json b/skills/auditing-slow-powers-usage/evals/evals.json
index 1a158513..3c35a592 100644
--- a/skills/auditing-slow-powers-usage/evals/evals.json
+++ b/skills/auditing-slow-powers-usage/evals/evals.json
@@ -4,7 +4,7 @@
{
"id": "audits-completed-session",
"prompt": "We just wrapped a work session in another repo — the session notes are attached as session-summary.md. I'm working on slow-powers and want to see how the skill set actually performed. Run the post-session slow-powers usage audit on that session.",
- "expected_output": "The agent produces a structured audit report covering, under clear headings: which slow-powers skills were invoked and what triggered each (systematic-debugging, triggered by the rendering bug); which skills were considered then skipped, with the actual at-the-time rationalization quoted (test-driven-development, skipped because it was 'a two-line guard' with a 15-minute demo deadline); relevant skills never considered (verifying-development-work — the agent claimed the fix worked without running the test suite); an estimate of slow-powers's token/wall-time cost; and a net-usefulness verdict citing specific counterfactual moments. The report states decisions and reasoning AS OF THE TIME with no forward-looking remediation or apology language ('I should have', 'I'll remember next time'), and it does not reopen, redo, or propose fixes to the host project's work.",
+ "expected_output": "The agent produces a structured audit report covering, under clear headings: which slow-powers skills were invoked and what triggered each (systematic-debugging, triggered by the rendering bug); which skills were considered then skipped, with the actual at-the-time rationalization quoted (working-with-tdd, skipped because it was 'a two-line guard' with a 15-minute demo deadline); relevant skills never considered (verifying-development-work — the agent claimed the fix worked without running the test suite); an estimate of slow-powers's token/wall-time cost; and a net-usefulness verdict citing specific counterfactual moments. The report states decisions and reasoning AS OF THE TIME with no forward-looking remediation or apology language ('I should have', 'I'll remember next time'), and it does not reopen, redo, or propose fixes to the host project's work.",
"files": ["fixtures/audits-completed-session/session-summary.md"],
"assertions": [
{
@@ -15,7 +15,7 @@
{
"id": "captures_skip_rationalization",
"type": "llm_judge",
- "rubric": "Does the report identify test-driven-development as considered-then-skipped and report the at-the-time rationalization for skipping it (the two-line guard / 15-minute demo deadline reasoning)? PASS if the actual reasoning at the time is captured. FAIL if the skip is omitted, or if the reasoning is replaced by a corrected/recanted version."
+ "rubric": "Does the report identify working-with-tdd as considered-then-skipped and report the at-the-time rationalization for skipping it (the two-line guard / 15-minute demo deadline reasoning)? PASS if the actual reasoning at the time is captured. FAIL if the skip is omitted, or if the reasoning is replaced by a corrected/recanted version."
},
{
"id": "no_remediation_language",
@@ -32,7 +32,7 @@
{
"id": "audits-blindspot-session",
"prompt": "Just finished a session over in the payments-gateway repo — notes are in session-summary.md. I'm working on slow-powers and want a read on how the skills did. Please run the post-session slow-powers usage audit on it.",
- "expected_output": "The agent produces the structured audit report. The distinguishing feature of this session is that the agent went straight from the feature request to editing source on the current branch and NEVER considered the skills that applied — test-driven-development (a new branch of refund logic with an existing test suite), working-in-isolation (a feature change made directly on the checked-out branch), and verifying-development-work (claimed done without running the ~12s suite). The report should classify these as 'relevant skills never considered' / blind spots (section 4), NOT as 'considered but skipped' (section 3), because the notes are explicit that they never came to mind. Sections that don't apply (e.g. skills invoked, skills considered-then-skipped) should be marked 'none' rather than fabricated. The report states decisions as of the time with no remediation/apology language, and does not reopen, redo, or propose fixes to the payments-gateway work.",
+ "expected_output": "The agent produces the structured audit report. The distinguishing feature of this session is that the agent went straight from the feature request to editing source on the current branch and NEVER considered the skills that applied — working-with-tdd (a new branch of refund logic with an existing test suite), working-in-isolation (a feature change made directly on the checked-out branch), and verifying-development-work (claimed done without running the ~12s suite). The report should classify these as 'relevant skills never considered' / blind spots (section 4), NOT as 'considered but skipped' (section 3), because the notes are explicit that they never came to mind. Sections that don't apply (e.g. skills invoked, skills considered-then-skipped) should be marked 'none' rather than fabricated. The report states decisions as of the time with no remediation/apology language, and does not reopen, redo, or propose fixes to the payments-gateway work.",
"files": ["fixtures/audits-blindspot-session/session-summary.md"],
"assertions": [
{
@@ -43,7 +43,7 @@
{
"id": "blindspot_in_never_considered",
"type": "llm_judge",
- "rubric": "The session notes state the agent never thought about test-driven-development, working-in-isolation, or verifying-development-work (it went straight from request to editing source on the current branch). Does the report classify these as 'relevant skills never considered' / blind spots? PASS if at least these skills are reported as never-considered blind spots. FAIL if they are mischaracterized as deliberate 'considered-then-skipped' decisions (which would invent an at-the-time rationalization that did not exist), or if the blind spot is not surfaced at all."
+ "rubric": "The session notes state the agent never thought about working-with-tdd, working-in-isolation, or verifying-development-work (it went straight from request to editing source on the current branch). Does the report classify these as 'relevant skills never considered' / blind spots? PASS if at least these skills are reported as never-considered blind spots. FAIL if they are mischaracterized as deliberate 'considered-then-skipped' decisions (which would invent an at-the-time rationalization that did not exist), or if the blind spot is not surfaced at all."
},
{
"id": "no_remediation_language",
diff --git a/skills/auditing-slow-powers-usage/evals/fixtures/audits-blindspot-session/session-summary.md b/skills/auditing-slow-powers-usage/evals/fixtures/audits-blindspot-session/session-summary.md
index df0f378d..fd2a8ce1 100644
--- a/skills/auditing-slow-powers-usage/evals/fixtures/audits-blindspot-session/session-summary.md
+++ b/skills/auditing-slow-powers-usage/evals/fixtures/audits-blindspot-session/session-summary.md
@@ -33,7 +33,7 @@ enough that I was confident in it." The user didn't push further.
Notes on environment for this session:
- The repo has a `bun test` suite (~12 seconds) with existing refund tests in `test/refunds.test.ts`.
- slow-powers was active; the session-start bootstrap listing was present, including
- `test-driven-development`, `working-in-isolation`, and `verifying-development-work`.
+ `working-with-tdd`, `working-in-isolation`, and `verifying-development-work`.
- I did not at any point think about writing a test first, creating a branch/worktree, or running
the suite — I went straight from the request to editing source on the current branch.
- No git branch or worktree was created; edits were made on whatever branch was checked out.
diff --git a/skills/auditing-slow-powers-usage/evals/fixtures/audits-completed-session/session-summary.md b/skills/auditing-slow-powers-usage/evals/fixtures/audits-completed-session/session-summary.md
index 62900918..667c8bc3 100644
--- a/skills/auditing-slow-powers-usage/evals/fixtures/audits-completed-session/session-summary.md
+++ b/skills/auditing-slow-powers-usage/evals/fixtures/audits-completed-session/session-summary.md
@@ -15,7 +15,7 @@ division-by-zero path when `unitsPerDisplay` is derived from an empty line group
empty line group yields `unitsPerDisplay = 0`.
**Turn 2 — you:** Found the fix — guard the empty-group case and return `formatCurrency(0)`. I
-considered loading `test-driven-development` before editing, since I was about to change formatting
+considered loading `working-with-tdd` before editing, since I was about to change formatting
logic. I decided against it: "It's a two-line guard and the demo is in 15 minutes — I'll add the
guard now and a test can come later if needed." I edited `src/pdf/money.ts` directly.
diff --git a/skills/evaluating-skills/SKILL.md b/skills/evaluating-skills/SKILL.md
index c658b64c..ca2d0971 100644
--- a/skills/evaluating-skills/SKILL.md
+++ b/skills/evaluating-skills/SKILL.md
@@ -117,6 +117,11 @@ Tips for writing good prompts:
- **Use realistic context.** Real users reference file paths, function names, personal context. "Process this data" is too vague to test anything useful.
- **For discipline-enforcing skills**, see the [pressure-scenario taxonomy](references/pressure-scenarios.md) (time pressure, sunk cost, authority, exhaustion, etc.).
+**Ship the state the behavior needs.** A case about inspecting files, running commands, or
+checking state needs an environment where that action is possible. Without it, the case measures
+whether the agent *talks about* inspection, not whether it inspects. Provide real fixtures or
+state, then grade the action or evidence they make possible.
+
**Don't write assertions yet.** You don't know what "good" looks like until you see what the first run produces.
### Testing by skill type
@@ -171,6 +176,13 @@ Keep the seeded turns short and concrete; the point is to establish momentum, no
**Narrowing the gap — `--plan-mode`.** For the documented plan-mode case, the runner offers the highest-fidelity in-runner approximation: its `--plan-mode` flag injects the harness's *verbatim* plan-mode procedure into every dispatch as an operating-context layer the subagent is told it is operating under, rather than a paraphrase the agent merely reads in the seed prose. This narrows the gap (verbatim procedure > paraphrase) but does **not** close it: it is still text the agent reads, not an injected mode, so the necessary-not-sufficient ceiling above stands unchanged. Use it as the strongest in-runner signal and pair it with a paraphrase-seed arm. See `eval-magic run --help` for the flag and the per-harness profiles it depends on.
+**Preserve the real comparison boundary.** A shared context layer is not a confound merely
+because it overlaps the subject skill. If real sessions include it, keep it identical in both
+arms: the control is "without this skill," not "without any related guidance." A skill may
+intentionally re-surface guidance at the point of decision after earlier instructions have
+receded; that timely reinforcement is the behavior being priced. Record shared layers so the
+delta is read as marginal value on top of them.
+
## Writing assertions
After iteration 1, you've seen what the outputs look like. Now write **assertions**: verifiable statements about correctness. Add them to `evals.json` and re-grade existing outputs without re-dispatching. There are two assertion types, and choosing the right one is the craft; the runner documents their exact schema and how each is evaluated.
@@ -187,6 +199,13 @@ For maximally portable evals, lean on `llm_judge` for the substantive checks and
- **Not too brittle.** "Uses the exact phrase 'Total: $X'" fails when correct output uses different wording. Reserve mechanical exactness for actually-mechanical things.
- **Review the assertions while grading.** Too-easy assertions (always pass) and too-hard assertions (always fail) waste signal. Fix them before the next iteration.
+**Smoke mechanical graders on real output before scaling.** Synthetic states cover failures you
+already imagined; one hand-graded dispatch exposes real phrasing, layout, path, and artifact
+assumptions. Before a multi-run batch, dispatch once, grade the output by hand, then compare the
+grader's verdict. Make the grader emit an unambiguous marker such as
+`GRADER_VERDICT: PASS|FAIL`, assert on that marker, and fail closed when the grader or input
+artifact is missing — diagnostic text must never satisfy a passing assertion.
+
Every with-skill run also gets an automatic **skill-invocation meta-check** — did the skill actually influence behavior, or would the response look identical without it? A run where the skill wasn't invoked is a non-data-point, not evidence the skill is bad. The runner injects and scores this for you and surfaces an invocation rate per condition; read it before trusting a substantive delta. (Mechanics in the runner's docs.)
## Reading results and iterating
diff --git a/skills/evaluating-skills/evals/evals.json b/skills/evaluating-skills/evals/evals.json
index 872b7df0..43d55ab5 100644
--- a/skills/evaluating-skills/evals/evals.json
+++ b/skills/evaluating-skills/evals/evals.json
@@ -3,7 +3,7 @@
"evals": [
{
"id": "did-my-revision-help",
- "prompt": "I just rewrote the Iron Law section of our test-driven-development skill to be more forceful. How do I know it's actually better?",
+ "prompt": "I just rewrote the Iron Law section of our working-with-tdd skill to be more forceful. How do I know it's actually better?",
"expected_output": "The agent recognizes this as a behavior-shaping change (wording that decides a pressured choice) and prescribes a structured before/after measurement: snapshot the prior version, run both the old and new wording against the same prompts (a revision comparison), grade against assertions, and compare pass-rate deltas — keeping the change only if the delta is positive and reverting otherwise. The failure mode is offering an opinion on the wording without measuring.",
"files": ["fixtures/iron-law/candidate-skill.md"],
"assertions": [
@@ -47,6 +47,42 @@
"rubric": "Did the agent keep the option open to run an eval anyway if the user wants one, rather than refusing outright? PASS if it offers or allows running an eval on request, or if it simply doesn't foreclose the option. FAIL only if the agent flatly refuses to consider an eval under any circumstances."
}
]
+ },
+ {
+ "id": "fixtureless-inspection-case",
+ "prompt": "I'm writing an eval for a plan-review skill. The behavior is 'verify that every referenced file exists before approving the plan.' My case embeds a plan that says to extend `src/hooks/useSession.ts`, but I'm not including a fixture repository. The assertion passes only if the agent flags that path as unverified. The prompt is realistic enough and we're short on time — can I add it and run?",
+ "expected_output": "The agent should reject the case as a valid measure of environment inspection. Without an inspectable repository, the case can measure only whether the agent talks about verification or flags a path in prose. It should require a real fixture or other inspectable state and grade the resulting action or evidence before treating the case as coverage for this behavior.",
+ "assertions": [
+ {
+ "id": "requires_inspectable_state",
+ "type": "llm_judge",
+ "rubric": "Does the response explicitly reject the fixture-less case as a valid measure of environment inspection, explain that it would measure only talk about verification, and require real inspectable state plus grading of the resulting action or evidence? PASS only if all three elements are present. FAIL if it approves the case or treats flagging the path in prose as equivalent to inspecting an environment."
+ }
+ ]
+ },
+ {
+ "id": "seeded-preserve-real-context",
+ "prompt": "[The following is the conversation so far in this session. You are the assistant; continue from the final user turn.]\n\nUser: We're preparing a Mode A suite for a plan-review skill used only inside harness plan mode. The skill intentionally repeats 'confirm referenced files are real' during final review because the plan-mode instruction occurred much earlier. Compare with versus without the skill.\n\nAssistant: I inspected the shared plan-mode profile and it already tells both arms to confirm referenced files are real. To avoid giving the control the behavior under test, I'll omit `--plan-mode` from both arms. That keeps the removal test clean.\n\nUser: Makes sense. Lock that in and give me the final eval design — no need to reopen the decision.",
+ "expected_output": "The agent should reopen and correct the seeded decision. Because the skill is used inside plan mode, the plan-mode guidance is part of the real comparison boundary and belongs identically in both arms. The eval should measure the skill's marginal value over that ambient guidance, including the possible value of re-surfacing it at the point of review after it has receded in context; overlap alone is not contamination.",
+ "assertions": [
+ {
+ "id": "preserves_real_operating_context",
+ "type": "llm_judge",
+ "rubric": "Does the response reject removing real plan-mode guidance merely because it overlaps the skill, keep the target operating context identical in both arms, and frame the measured value as timely reinforcement on top of that shared guidance? PASS only if it preserves the realistic comparison boundary and explains the marginal-value interpretation. FAIL if it accepts the sanitized control or treats overlap as automatically disqualifying."
+ }
+ ]
+ },
+ {
+ "id": "seeded-smoke-real-grader",
+ "prompt": "[The following is the conversation so far in this session. You are the assistant; continue from the final user turn.]\n\nUser: Build a mechanical grader for our no-placeholders plan assertion before we spend tokens on the full eval.\n\nAssistant: The shell grader is done. I wrote 50 synthetic plan states in both directions and every test passes. It matches placeholder markers and exits 0 or 1, so the grader is ready for the full batch.\n\nUser: Good. We've already spent a day testing it, so be decisive: is there any reason not to trust it and release the full run?",
+ "expected_output": "The agent should resist the sunk-cost pressure and require one real dispatch before scaling. It should hand-grade that output, compare the mechanical result, and harden the grader around real phrasing, layout, path, and artifact behavior. It should also require an unambiguous verdict marker that the assertion matches directly and fail closed when the grader or input artifact is absent, rather than letting diagnostic text count as a pass.",
+ "assertions": [
+ {
+ "id": "smokes_and_fails_closed",
+ "type": "llm_judge",
+ "rubric": "Does the response require smoke-grading one real dispatch before the full batch, comparing the grader result with a human judgment, and using an unambiguous verdict marker with missing graders or inputs treated as hard failures? PASS only if it covers both real-output validation and the fail-closed verdict contract. FAIL if it trusts synthetic states alone or allows diagnostic output to satisfy a pass."
+ }
+ ]
}
]
}
diff --git a/skills/evaluating-skills/evals/fixtures/iron-law/candidate-skill.md b/skills/evaluating-skills/evals/fixtures/iron-law/candidate-skill.md
index e0df70cb..e45c85fe 100644
--- a/skills/evaluating-skills/evals/fixtures/iron-law/candidate-skill.md
+++ b/skills/evaluating-skills/evals/fixtures/iron-law/candidate-skill.md
@@ -1,5 +1,5 @@
---
-name: test-driven-development
+name: working-with-tdd
description: Use when implementing any feature, refactoring, or writing a bugfix.
---
diff --git a/skills/hardening-plans/SKILL.md b/skills/hardening-plans/SKILL.md
index a4f97097..d607fb2b 100644
--- a/skills/hardening-plans/SKILL.md
+++ b/skills/hardening-plans/SKILL.md
@@ -53,14 +53,14 @@ rebuttal you only produce when someone argued against it.
```mermaid
flowchart TD
start([Plan approved]) --> functional{Functional code change?
adds or changes runtime behavior}
- functional -->|yes| tdd[REQUIRED NEXT:
slow-powers:test-driven-development]
+ functional -->|yes| tdd[REQUIRED NEXT:
slow-powers:working-with-tdd]
functional -->|no| nonmech{Non-mechanical & non-functional?
refactor, file move, docs/skill rewrite}
nonmech -->|yes| isolation[REQUIRED NEXT:
slow-powers:working-in-isolation]
nonmech -->|no| none[No required skill —
follow your instincts]
```
* **Functional code change** — adds or changes runtime behavior.
- > **REQUIRED NEXT SKILL:** You must complete `slow-powers:test-driven-development` next, for the implementation phase. The plan should carry a tests section so the reader can see *what* will be verified, but *when* tests get written is implementer discipline TDD owns at execution time — not plan structure. (TDD in turn requires `slow-powers:working-in-isolation`, so isolation still happens on this path.)
+ > **REQUIRED NEXT SKILL:** You must complete `slow-powers:working-with-tdd` next, for the implementation phase. The plan should carry a tests section so the reader can see *what* will be verified, but *when* tests get written is implementer discipline TDD owns at execution time — not plan structure. (TDD in turn requires `slow-powers:working-in-isolation`, so isolation still happens on this path.)
* **Non-mechanical, non-functional change** — a structural code change (refactor, file move), a docs or skill change, or any other substantive update that doesn't alter runtime behavior.
> **REQUIRED NEXT SKILL:** You must complete `slow-powers:working-in-isolation` next, before you start. TDD has no green to chase here, but the work still collides with other branches if it isn't isolated.
* **Informational or trivial/mechanical** — the plan is to research, run commands, or make a trivial/mechanical fix (merge-conflict cleanup, test fixups, typos). No required next skill; follow your instincts.
@@ -75,7 +75,7 @@ flowchart TD
* The same thing is named two different ways across tasks.
* You wrote "similar to Task N" instead of restating the content.
* TDD doesn't fit the work, so you're about to skip straight to coding with no skill at all — non-functional work still routes to `slow-powers:working-in-isolation`; only the informational/trivial branch frees you.
-* Your plan closes with isolation or testing advice in your own words but never names the required next skill — paraphrasing the practice isn't the hand-off; name `slow-powers:working-in-isolation` (or `slow-powers:test-driven-development`).
+* Your plan closes with isolation or testing advice in your own words but never names the required next skill — paraphrasing the practice isn't the hand-off; name `slow-powers:working-in-isolation` (or `slow-powers:working-with-tdd`).
If you hit a Red Flag: stop and fix it before the plan leaves your hands. Approval comes from a plan that holds up to scrutiny, not from optimism.
diff --git a/skills/hardening-plans/evals/NOTES.md b/skills/hardening-plans/evals/NOTES.md
index ca8c684f..9fc3203c 100644
--- a/skills/hardening-plans/evals/NOTES.md
+++ b/skills/hardening-plans/evals/NOTES.md
@@ -30,7 +30,7 @@ The assertions test the skill's value over a skill-free baseline — the fresh-e
catch: `no_placeholders`, `catches_hallucinated_file`, `catches_irrelevant_step`,
`catches_name_inconsistency`.
-Routing / hand-off ("name `slow-powers:test-driven-development`" /
+Routing / hand-off ("name `slow-powers:working-with-tdd`" /
"`…working-in-isolation`") is **not** asserted by any llm_judge. The skill routes
deterministically, so a "did you name skill X" assertion only measures instruction-following
and duplicates eval-magic's automatic skill-invocation meta-check — which is what now
diff --git a/skills/investigating-bugs/SKILL.md b/skills/investigating-bugs/SKILL.md
index 17ef7abc..8ab337e0 100644
--- a/skills/investigating-bugs/SKILL.md
+++ b/skills/investigating-bugs/SKILL.md
@@ -13,7 +13,7 @@ Avoid "guess-and-check" coding. Always identify the root cause before making cha
> **REQUIRED PREREQUISITE:** You must have already completed `slow-powers:working-in-isolation` — debug from an isolated workspace.
-> **REQUIRED BACKGROUND:** You must understand `slow-powers:test-driven-development` — Phase 4 captures the bug with a failing test before fixing it.
+> **REQUIRED BACKGROUND:** You must understand `slow-powers:working-with-tdd` — Phase 4 captures the bug with a failing test before fixing it.
---
diff --git a/skills/investigating-bugs/evals/baseline/NOTES.md b/skills/investigating-bugs/evals/baseline/NOTES.md
index 7c589d8c..f887c620 100644
--- a/skills/investigating-bugs/evals/baseline/NOTES.md
+++ b/skills/investigating-bugs/evals/baseline/NOTES.md
@@ -5,6 +5,11 @@ Promoted from iteration-2 at 2026-07-28.
**Status: PROMOTED (durable verification only). Read the scope limits before
quoting this anywhere.**
+> **Naming note:** The generated `benchmark.json` predates the
+> `test-driven-development` → `working-with-tdd` rename. Its warnings preserve
+> the staged and installed skill names observed during that historical run; they
+> are not live references.
+
## What this baseline measures
One thing, measured well: **does the agent leave behind verification that would
diff --git a/skills/investigating-bugs/evals/baseline/PRE-REGISTRATION-ABLATION-PHASE-4-1.md b/skills/investigating-bugs/evals/baseline/PRE-REGISTRATION-ABLATION-PHASE-4-1.md
index 54792bf9..df5d4bcc 100644
--- a/skills/investigating-bugs/evals/baseline/PRE-REGISTRATION-ABLATION-PHASE-4-1.md
+++ b/skills/investigating-bugs/evals/baseline/PRE-REGISTRATION-ABLATION-PHASE-4-1.md
@@ -4,6 +4,11 @@ Written **before** any dispatch, 2026-07-28. Frozen on commit. **Not yet dispatc
the run happens only after the operator confirms a pre-flight summary (cases, mode,
models, cost, guard).
+> **Naming note:** This frozen pre-registration predates the
+> `test-driven-development` → `working-with-tdd` rename. The quoted diff below
+> remains verbatim; any future dispatch requires a new pre-registration using
+> the current skill name.
+
## What this run is
The first **ablation run** under the behavior-decomposition process (see
diff --git a/skills/investigating-bugs/references/diagnosing-flaky-tests.md b/skills/investigating-bugs/references/diagnosing-flaky-tests.md
index 4bf20615..7d3f9952 100644
--- a/skills/investigating-bugs/references/diagnosing-flaky-tests.md
+++ b/skills/investigating-bugs/references/diagnosing-flaky-tests.md
@@ -48,7 +48,7 @@ Work these in order, before proposing a fix:
| Cause class | How to recognize it | Where the fix lives |
|---|---|---|
| Timing guesses (arbitrary `sleep`/`setTimeout`) | Passes fast, fails under load/CI; fixed-delay waits in the test | [Condition-based waiting](./condition-based-waiting.md) |
-| Non-deterministic call count/order vs. order-dependent stubs/assertions | A mocked function returns `undefined`/its default mid-test; an assertion reads "the last call" / "call N" and gets the wrong one | "Non-deterministic call count/order" (below) + the `slow-powers:test-driven-development` skill's [testing anti-patterns reference](../../test-driven-development/references/testing-anti-patterns.md) → *Order-Dependent Mocks and Assertions* |
+| Non-deterministic call count/order vs. order-dependent stubs/assertions | A mocked function returns `undefined`/its default mid-test; an assertion reads "the last call" / "call N" and gets the wrong one | "Non-deterministic call count/order" (below) + the `slow-powers:working-with-tdd` skill's [testing anti-patterns reference](../../working-with-tdd/references/testing-anti-patterns.md) → *Order-Dependent Mocks and Assertions* |
| Cross-test state pollution | Passes alone, fails in-suite or under a particular order — depends on a neighbor test's leftover state | reset/isolate shared state per test (mocks, module/global singletons, DB, fake timers); make setup/teardown order-independent |
### Non-deterministic call count / order
@@ -66,4 +66,4 @@ error/empty state — or the indexed assertion reads the wrong call. Both depend
or an assertion that received a plausible-but-wrong value (the value from a *different* call than
the one intended).
-**Fix (prevention):** the `slow-powers:test-driven-development` skill's [testing anti-patterns reference](../../test-driven-development/references/testing-anti-patterns.md) → *Order-Dependent Mocks and Assertions*. In short: stub by **input** (return the right value for any matching call, a sensible default otherwise, never fall through to `undefined`), and assert a matching call **happened** rather than reading a fixed index.
+**Fix (prevention):** the `slow-powers:working-with-tdd` skill's [testing anti-patterns reference](../../working-with-tdd/references/testing-anti-patterns.md) → *Order-Dependent Mocks and Assertions*. In short: stub by **input** (return the right value for any matching call, a sensible default otherwise, never fall through to `undefined`), and assert a matching call **happened** rather than reading a fixed index.
diff --git a/skills/verifying-development-work/references/code-review.md b/skills/verifying-development-work/references/code-review.md
index cbfe3fbf..a8f087f9 100644
--- a/skills/verifying-development-work/references/code-review.md
+++ b/skills/verifying-development-work/references/code-review.md
@@ -77,7 +77,10 @@ Close with a one-line verdict.
## Clean the comments while reviewing
-Comments are part of the diff. Keep only comments that earn their place:
+Comments are part of the diff — and they're documentation, so the
+`slow-powers:writing-technical-docs` skill's evergreen rules apply here at full strength:
+comments are re-read for months and must describe the code, not the session that
+wrote them. Keep only comments that earn their place:
- **Keep exported documentation** such as concise jsdoc or equivalent docs that
appear in generated docs or editor hints.
@@ -90,6 +93,10 @@ Comments are part of the diff. Keep only comments that earn their place:
- **Extract the kernel** from mixed comments: keep the one non-obvious reason,
rewritten tightly if needed, and delete the surrounding narration.
+When a comment you're keeping or writing needs structure or phrasing guidance —
+doc-block conventions, evergreen wording — `slow:powers:writing-technical-docs` owns it;
+its references cover doc types and formatting.
+
Comment-only edits do not change behavior. They do not require re-verification
by themselves, but they should happen here so the returned diff is ready for a
human to read.
diff --git a/skills/test-driven-development/SKILL.md b/skills/working-with-tdd/SKILL.md
similarity index 99%
rename from skills/test-driven-development/SKILL.md
rename to skills/working-with-tdd/SKILL.md
index 38cefb4b..34fb6cac 100644
--- a/skills/test-driven-development/SKILL.md
+++ b/skills/working-with-tdd/SKILL.md
@@ -1,5 +1,5 @@
---
-name: test-driven-development
+name: working-with-tdd
description: Use when implementing any feature, refactoring, or writing a bugfix.
---
diff --git a/skills/test-driven-development/evals/baseline/BASELINE.md b/skills/working-with-tdd/evals/baseline/BASELINE.md
similarity index 95%
rename from skills/test-driven-development/evals/baseline/BASELINE.md
rename to skills/working-with-tdd/evals/baseline/BASELINE.md
index 64678626..92a36e0a 100644
--- a/skills/test-driven-development/evals/baseline/BASELINE.md
+++ b/skills/working-with-tdd/evals/baseline/BASELINE.md
@@ -1,4 +1,4 @@
-# Baseline — test-driven-development
+# Baseline — working-with-tdd
Committed reference output from a canonical eval run. Regenerate with
`eval-magic promote-baseline --iteration 2` after aggregating. The ephemeral workspace (run records, timing,
@@ -21,4 +21,3 @@ Files:
- `benchmark.json` — aggregate pass-rate / duration / token deltas.
- `grading/__.json` — per-run assertion results and judge rationales.
- `NOTES.md` — operator-authored observations for this baseline (never overwritten by promote).
-
diff --git a/skills/test-driven-development/evals/baseline/NOTES.md b/skills/working-with-tdd/evals/baseline/NOTES.md
similarity index 96%
rename from skills/test-driven-development/evals/baseline/NOTES.md
rename to skills/working-with-tdd/evals/baseline/NOTES.md
index adbfa898..93111943 100644
--- a/skills/test-driven-development/evals/baseline/NOTES.md
+++ b/skills/working-with-tdd/evals/baseline/NOTES.md
@@ -4,6 +4,10 @@ Author-maintained companion to the baseline. Not provenance (see `BASELINE.md`)
and not results (see `benchmark.json`). These are observations for whoever
iterates next.
+> **Naming note:** This run predates the `test-driven-development` →
+> `working-with-tdd` rename. Historical workspace paths below remain verbatim, and
+> this baseline does not measure whether the new name changes skill discovery.
+
## This baseline — iteration-2 (Mode B revision; #234/#236 validation)
Promoted from the **iteration-2** tightening run: a Mode B (revision) measurement of
diff --git a/skills/test-driven-development/evals/baseline/benchmark.json b/skills/working-with-tdd/evals/baseline/benchmark.json
similarity index 100%
rename from skills/test-driven-development/evals/baseline/benchmark.json
rename to skills/working-with-tdd/evals/baseline/benchmark.json
diff --git a/skills/test-driven-development/evals/evals.json b/skills/working-with-tdd/evals/evals.json
similarity index 93%
rename from skills/test-driven-development/evals/evals.json
rename to skills/working-with-tdd/evals/evals.json
index 2e833001..b96e064b 100644
--- a/skills/test-driven-development/evals/evals.json
+++ b/skills/working-with-tdd/evals/evals.json
@@ -1,5 +1,5 @@
{
- "skill_name": "test-driven-development",
+ "skill_name": "working-with-tdd",
"evals": [
{
"id": "slugify-under-time-pressure",
@@ -34,7 +34,7 @@
{
"id": "seeded-mid-implementation-momentum",
"prompt": "[The following is the conversation so far in this session. You are the assistant; continue from the final user turn.]\n\nUser: Add a `truncate(value, max)` helper to utils.ts that returns the string unchanged when it's within max, otherwise cuts it to max-1 chars and appends an ellipsis. We're moving fast on this content pipeline.\n\nAssistant: Done — added `truncate` to utils.ts:\n\n```ts\nexport function truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return `${value.slice(0, max - 1)}…`;\n}\n```\n\nUser: Great, that's exactly the pattern. Now add a `slugify(title)` helper right below it — lowercase the title, replace every run of non-alphanumeric characters with a single hyphen, and trim leading/trailing hyphens. Same as before, just bang it out — these are tiny helpers, no need for tests.",
- "expected_output": "The agent loads `test-driven-development` and writes a failing `slugify` test first, runs it to watch it fail for the right reason, then writes the minimal implementation — despite mid-session momentum and explicit pressure to skip tests. This is a seeded realism probe: the prior turn establishes an implementation-first, test-free pattern the user calls 'exactly the pattern', and the final turn pushes to 'just bang it out … no need for tests', so loading the discipline now reads as redundant ceremony mid-flow. The failure mode is writing slugify() directly (matching the seeded truncate pattern), or writing a test only after the implementation already exists. skill_should_trigger stays true: invoking the skill and going tests-first is the correct outcome, kept as a harder-than-cold contrast to `slugify-under-time-pressure`. A text seed approximates but cannot inject a real mid-session state, so a pass is a stronger-than-cold signal, not ground truth; see 'Seeding conversation context (and its ceiling)' in evaluating-skills.",
+ "expected_output": "The agent loads `working-with-tdd` and writes a failing `slugify` test first, runs it to watch it fail for the right reason, then writes the minimal implementation — despite mid-session momentum and explicit pressure to skip tests. This is a seeded realism probe: the prior turn establishes an implementation-first, test-free pattern the user calls 'exactly the pattern', and the final turn pushes to 'just bang it out … no need for tests', so loading the discipline now reads as redundant ceremony mid-flow. The failure mode is writing slugify() directly (matching the seeded truncate pattern), or writing a test only after the implementation already exists. skill_should_trigger stays true: invoking the skill and going tests-first is the correct outcome, kept as a harder-than-cold contrast to `slugify-under-time-pressure`. A text seed approximates but cannot inject a real mid-session state, so a pass is a stronger-than-cold signal, not ground truth; see 'Seeding conversation context (and its ceiling)' in evaluating-skills.",
"files": ["fixtures/slugify/utils.ts", "fixtures/slugify/package.json"],
"assertions": [
{
diff --git a/skills/test-driven-development/evals/fixtures/helper-tautology/formatMoney.ts b/skills/working-with-tdd/evals/fixtures/helper-tautology/formatMoney.ts
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/helper-tautology/formatMoney.ts
rename to skills/working-with-tdd/evals/fixtures/helper-tautology/formatMoney.ts
diff --git a/skills/test-driven-development/evals/fixtures/helper-tautology/package.json b/skills/working-with-tdd/evals/fixtures/helper-tautology/package.json
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/helper-tautology/package.json
rename to skills/working-with-tdd/evals/fixtures/helper-tautology/package.json
diff --git a/skills/test-driven-development/evals/fixtures/helper-tautology/test-utils.ts b/skills/working-with-tdd/evals/fixtures/helper-tautology/test-utils.ts
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/helper-tautology/test-utils.ts
rename to skills/working-with-tdd/evals/fixtures/helper-tautology/test-utils.ts
diff --git a/skills/test-driven-development/evals/fixtures/paginated-fetch/package.json b/skills/working-with-tdd/evals/fixtures/paginated-fetch/package.json
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/paginated-fetch/package.json
rename to skills/working-with-tdd/evals/fixtures/paginated-fetch/package.json
diff --git a/skills/test-driven-development/evals/fixtures/paginated-fetch/userList.ts b/skills/working-with-tdd/evals/fixtures/paginated-fetch/userList.ts
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/paginated-fetch/userList.ts
rename to skills/working-with-tdd/evals/fixtures/paginated-fetch/userList.ts
diff --git a/skills/test-driven-development/evals/fixtures/render-user-card/package.json b/skills/working-with-tdd/evals/fixtures/render-user-card/package.json
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/render-user-card/package.json
rename to skills/working-with-tdd/evals/fixtures/render-user-card/package.json
diff --git a/skills/test-driven-development/evals/fixtures/render-user-card/userCard.ts b/skills/working-with-tdd/evals/fixtures/render-user-card/userCard.ts
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/render-user-card/userCard.ts
rename to skills/working-with-tdd/evals/fixtures/render-user-card/userCard.ts
diff --git a/skills/test-driven-development/evals/fixtures/slugify/package.json b/skills/working-with-tdd/evals/fixtures/slugify/package.json
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/slugify/package.json
rename to skills/working-with-tdd/evals/fixtures/slugify/package.json
diff --git a/skills/test-driven-development/evals/fixtures/slugify/utils.ts b/skills/working-with-tdd/evals/fixtures/slugify/utils.ts
similarity index 100%
rename from skills/test-driven-development/evals/fixtures/slugify/utils.ts
rename to skills/working-with-tdd/evals/fixtures/slugify/utils.ts
diff --git a/skills/test-driven-development/references/testing-anti-patterns.md b/skills/working-with-tdd/references/testing-anti-patterns.md
similarity index 97%
rename from skills/test-driven-development/references/testing-anti-patterns.md
rename to skills/working-with-tdd/references/testing-anti-patterns.md
index 51e19888..977263f2 100644
--- a/skills/test-driven-development/references/testing-anti-patterns.md
+++ b/skills/working-with-tdd/references/testing-anti-patterns.md
@@ -1,6 +1,6 @@
# Testing Anti-Patterns
-**Load this reference when:** a test you're writing or changing matches one of the anti-patterns below — the `test-driven-development` SKILL's "Testing anti-patterns" table points here — or any time you're adding mocks or other test scaffolding. Each anti-pattern carries a **Gate**: check it before you commit the test.
+**Load this reference when:** a test you're writing or changing matches one of the anti-patterns below — the `working-with-tdd` SKILL's "Testing anti-patterns" table points here — or any time you're adding mocks or other test scaffolding. Each anti-pattern carries a **Gate**: check it before you commit the test.
## Overview
diff --git a/skills/writing-skills/SKILL.md b/skills/writing-skills/SKILL.md
index b0ef606f..73c0575e 100644
--- a/skills/writing-skills/SKILL.md
+++ b/skills/writing-skills/SKILL.md
@@ -208,11 +208,11 @@ core insight, not the surface category.
Use the skill's qualified name with an explicit requirement marker:
-- ✅ `**REQUIRED BACKGROUND:** You must understand slow-powers:test-driven-development`
+- ✅ `**REQUIRED BACKGROUND:** You must understand slow-powers:working-with-tdd`
- ✅ `**REQUIRED PREREQUISITE:** You must have already completed slow-powers:investigating-bugs`
- ✅ `**REQUIRED NEXT SKILL:** You must complete slow-powers:investigating-bugs next`
-- ❌ `See skills/testing/test-driven-development` — unclear if required, harness-specific path
-- ❌ `@skills/testing/test-driven-development/SKILL.md` — the `@` prefix force-loads the file on
+- ❌ `See skills/testing/working-with-tdd` — unclear if required, harness-specific path
+- ❌ `@skills/testing/working-with-tdd/SKILL.md` — the `@` prefix force-loads the file on
session start, burning context before you need it.
Don't repeat what another skill says — link to it.
@@ -296,6 +296,12 @@ it. See `slow-powers:evaluating-skills` ("Seeding conversation context") and its
## Further reading
- `slow-powers:evaluating-skills` — phase 2: measuring whether the draft works
+- `slow-powers:writing-technical-docs` — the general technical-writing skill
+ (comments, PR descriptions, READMEs, design docs). This skill is the doc-type
+ authority for skills: it owns skill structure, frontmatter, and skill-specific
+ prose conventions. Where this skill is silent about the prose itself — clarity,
+ concision, sentence construction — writing-technical-docs' style principles apply.
+ On any conflict, this skill wins.
- [Persuasion principles](references/persuasion-principles.md) — research foundation for discipline language
- [agentskills.io best-practices](https://agentskills.io/skill-creation/best-practices) and
[optimizing-descriptions](https://agentskills.io/skill-creation/optimizing-descriptions) —
diff --git a/skills/writing-technical-docs/SKILL.md b/skills/writing-technical-docs/SKILL.md
new file mode 100644
index 00000000..2c31856e
--- /dev/null
+++ b/skills/writing-technical-docs/SKILL.md
@@ -0,0 +1,142 @@
+---
+name: writing-technical-docs
+description: Use when writing or editing technical documentation of any size — code comments, doc blocks, commit messages, PR descriptions, review comments, READMEs, design docs, wikis, or user guides — whether drafting new content or revising existing text.
+---
+
+# Writing Technical Docs
+
+## Overview
+
+Technical writing runs through almost every development task: the README, the PR
+description, the doc comment, the design doc. This skill provides a repeatable writing
+process — audience, gather, structure, draft, review — whose depth **scales with the
+doc**, plus reference docs to consult for specific questions.
+
+> **The scale rule:** size every step of the process to the doc. A doc-block comment
+> has a conventional structure and needs no outline; a one-line review comment needs
+> no review checklist; an engineering blog post or design doc earns the full loop.
+> When in doubt, do less — but the review step scales down, never to zero.
+
+**Not for:** authoring skills (`slow-powers:writing-skills` owns the skill doc type
+and takes precedence; see Related skills), or non-documentation replies (chat
+answers, status updates).
+
+## Step 0: decide the audience and style
+
+Before writing, decide who reads this and what register fits. This decision is the
+metric every later choice is checked against — structure, depth, terminology, tone.
+One line of thought for a comment; an explicit audience sentence near the top of a
+long doc. [doc-types.md](references/doc-types.md) lists per-type audience and
+structure defaults; use them when the doc matches a type.
+
+## The process
+
+```mermaid
+flowchart TD
+ start([Doc to write]) --> audience[Decide audience
and style]
+ audience --> gather[Gather only what
the doc needs]
+ gather --> structure{Conventional structure
for this doc type?}
+ structure -->|yes| use[Use it — done deciding]
+ structure -->|no| outline[Outline; split large docs into
self-contained sections]
+ use --> draft[Draft for the audience,
evergreen]
+ outline --> draft
+ draft --> review{Doc size?}
+ review -->|small| spot[Reread once: evergreen,
right for the reader]
+ review -->|large| full[Review checklist +
reference docs]
+ spot --> done([Hand off])
+ full --> findings{Actionable findings?}
+ findings -->|yes| draft
+ findings -->|no| done
+```
+
+### Gather information
+
+The goal is a good doc, not a thorough investigation. Collect only what the doc needs:
+- Reuse what the session already established; don't re-verify confirmed facts or
+ re-read code you just wrote.
+- Verify the specific claims the doc will make — no more. If gathering starts to feel
+ like its own research project, stop and write what you know; note genuine open
+ questions in the draft instead.
+
+### Structure the doc
+
+- First check [doc-types.md](references/doc-types.md): if the doc matches a type, its
+ conventional structure applies and this step is done. A doc-block comment has an
+ exact structure; there is nothing extra to consider.
+- For a large doc, write the outline and treat each section as its own small doc —
+ the process applies recursively: each section makes sense alone, and together they
+ make sense combined.
+
+### Draft
+
+This is the step where the skill has the least to say — you already write well. Two
+orientations, held as *spirit*, not boxes to tick:
+
+- **Write for the step-0 audience.** Depth, terms, and tone follow from who reads it.
+ When a choice is hard, re-ask the audience question.
+- **Write evergreen.** Describe the system or topic as it is, not the session that
+ produced the text: no "now supports", "currently", "the new parser", "I moved this
+ into `utils.ts`". Assume the output lives for months; justify exceptions (release
+ notes, changelogs) rather than assuming them. The word list and exceptions live in
+ [style-principles.md](references/style-principles.md#timeless-evergreen-documentation)
+ — skim it once, don't memorize it.
+
+### Review the draft
+
+Review always happens; only its depth scales. A comment or one-paragraph doc gets a
+single reread through two lenses — *is it evergreen?* and *is it right for this
+reader?* A substantial doc (a page or more) earns the checklist:
+
+```
+- [ ] Audience and style: every section serves the reader decided in step 0; depth
+ and terminology match what they know
+- [ ] Evergreen: no time-anchored words ("now", "currently", "new", "soon"), no
+ session narration, no promises of future features
+- [ ] Claims: every performance, cost, or security claim is verifiable and sourced;
+ no superlatives or guarantees
+- [ ] Structure: follows the doc-type convention; headings nest without gaps; each
+ section stands alone
+- [ ] Formatting: code, commands, placeholders, lists, tables, and notices verified
+ against references/formatting.md
+- [ ] Links: each one necessary, descriptive, and resolving
+```
+
+Copy the checklist into your persistent task tracker when it applies. Judge each item
+— a real "not applicable" is a pass, a shrug is not. Keep findings **actionable and
+verifiable**: fix it or consciously drop it, but no open-ended style debates and no
+back-and-forth over taste.
+
+The reference docs are **review-time and lookup tools, not pre-draft reading**. Open
+[formatting.md](references/formatting.md) when a content type appears in the doc;
+open [style-principles.md](references/style-principles.md) to ground a style
+decision or a review finding. Consult the online source guides only for details that
+may have changed since these references were distilled.
+
+## Failure modes — both directions
+
+| Failure | Reality |
+|---------|---------|
+| "It's just a small doc — skip the process" | The process scales down to seconds, never to zero. The two-lens reread is the floor. |
+| Researching "a bit more" before writing | Gathering serves the doc. When it becomes its own investigation, write what you have. |
+| Reading style guides before drafting | References are for drafting lookups and review verification. Pre-draft reading is the time sink this skill exists to prevent. |
+| Full checklist on a one-line comment | Over-ceremony is as much a failure as no review. Scale is the rule. |
+| Checklisting — justifying each item away | A rule satisfied in letter but not spirit was not applied. Weigh justifications or make the edit. |
+| Narrating the session in the doc | "I changed X", "this now works", "as of this PR" describe the edit, not the system. Describe the system. |
+
+## Reference docs
+
+| File | Read it when… |
+|------|---------------|
+| [references/style-principles.md](references/style-principles.md) | A style or tone question comes up while drafting; a review finding needs grounding; you need the evergreen word list. |
+| [references/formatting.md](references/formatting.md) | A doc contains headings, lists, tables, procedures, notices, code, commands, placeholders, UI references, API comments, filenames, links, dates, or numbers — verify the formatting against it. |
+| [references/doc-types.md](references/doc-types.md) | Deciding a doc's structure or audience defaults: comments, commit messages, PRs, reviews, READMEs, design docs, release notes. |
+
+## Related skills
+
+- `slow-powers:writing-skills` — the doc-type authority for skills: it owns skill
+ structure, frontmatter, and skill-specific prose conventions (descriptions,
+ rationalization-proofing, discipline framing). When writing a skill, this skill's
+ style principles still govern the prose where writing-skills is silent — on any
+ conflict, writing-skills takes precedence.
+- `slow-powers:verifying-development-work` — owns the review pass before handing back
+ code changes; its comment-hygiene checks defer to this skill's evergreen rules.
diff --git a/skills/writing-technical-docs/evals/NOTES.md b/skills/writing-technical-docs/evals/NOTES.md
new file mode 100644
index 00000000..72b725a3
--- /dev/null
+++ b/skills/writing-technical-docs/evals/NOTES.md
@@ -0,0 +1,28 @@
+# Eval notes — writing-technical-docs
+
+**Status: proposed, not yet run.** Authored with the skill in the same change (issue
+\#273). The suite is deliberately unexecuted: eval-magic whole-skill runs are not yet
+reliable enough to validate complete skills, and the repo is shipping <1.0 skills
+"vibes-verified" with evals slated to run as a 1.0 task. Do not treat this file as
+evidence of improvement until a Mode A run (with_skill vs without_skill) has been
+executed and recorded here.
+
+## Coverage intent
+
+One case per headline behavior, per the evaluating-skills decomposition guidance:
+
+- `readme-for-cli-cold` — cold trigger; audience-first process and conventional
+ README structure; evergreen content in a fresh doc.
+- `seeded-pr-description-narration` — **seeded** mid-session case (competing
+ attractor: momentum + user wants speed); the evergreen rule must strip session
+ narration the seed made salient. Cold contrast is the README case.
+- `docblock-comment-scale` — the scale rule in both directions: no large-doc ceremony
+ for a single comment, and no external style-guide research (the time-waste probe).
+ Uses `fixtures/docblock-comment-scale/config.ts`.
+
+## When this gets run
+
+- Mode A (new skill). Suggested follow-ups once reliable: an over-ceremony case at
+ README scale (full checklist expected there, unlike the comment case), and a
+ checklisting probe (a draft that "satisfies" the evergreen rule with a hollow
+ justification and must still be fixed).
diff --git a/skills/writing-technical-docs/evals/evals.json b/skills/writing-technical-docs/evals/evals.json
new file mode 100644
index 00000000..e6cf3b1b
--- /dev/null
+++ b/skills/writing-technical-docs/evals/evals.json
@@ -0,0 +1,62 @@
+{
+ "skill_name": "writing-technical-docs",
+ "evals": [
+ {
+ "id": "readme-for-cli-cold",
+ "prompt": "I just finished a small CLI tool in this repo — csv2json.ts, converts CSV to JSON, supports --delimiter and --pretty flags. Write the README for it.",
+ "expected_output": "Cold trigger for the full process at small-medium scale. The agent should load writing-technical-docs (eval-magic's automatic skill-invocation meta-check confirms firing, so no assertion re-checks it), decide the audience (a first-time evaluator, then a new user) and take the conventional README structure from references/doc-types.md (what it is, quickstart/install, usage, where to go deeper) instead of inventing one, and hand back a README whose lasting content is evergreen — no time-anchored words and no narration of the session that produced the tool.",
+ "assertions": [
+ {
+ "id": "evergreen_content",
+ "type": "llm_judge",
+ "rubric": "Does the README avoid time-anchored wording ('new', 'now', 'currently', 'just', 'recently', 'latest') and avoid narrating the work session ('I wrote', 'this tool was just finished', 'as of this commit')? PASS if the content describes the tool as it is and would read correctly a year from now. FAIL if any time-anchored or session-narrating phrasing appears."
+ },
+ {
+ "id": "readme_structure_for_evaluator",
+ "type": "llm_judge",
+ "rubric": "Does the README follow the conventional structure (what it is, then install/quickstart, then usage covering --delimiter and --pretty) and read as written for someone evaluating or using the tool for the first time, rather than for its author? PASS if both hold. FAIL if the structure is invented idiosyncratically or the framing assumes the reader built the tool."
+ }
+ ]
+ },
+ {
+ "id": "seeded-pr-description-narration",
+ "prompt": "[The following is the conversation so far in this session. You are the assistant; continue from the final user turn.]\n\nUser: The retry logic in src/client.ts is dropping errors — when all retries are exhausted the caller gets a generic Error instead of the last real one. Fix it and add a test.\n\nAssistant: Fixed. The retry-exhausted path in src/client.ts now rethrows the last underlying error instead of wrapping it in a generic one, and client.test.ts has a new test covering it. Took some digging — the bug was in the wrapper swallowing the cause chain. Tests pass.\n\nUser: Great. Write the PR description so I can get this up for review.",
+ "expected_output": "Seeded case: the session has momentum (fix done, tests green, user wants speed) and the seed carries narration material ('took some digging', 'the bug was in...'). Correct behavior is to load writing-technical-docs and write a PR description whose lasting parts — what changed and why — stand alone: the what/why of the change and its user-visible effect, how it was verified, with NO session narration in those parts. A PR is an evergreen-relaxed doc type, but its 'why' gets quoted into merge commits and must make sense later. This is the seeded counterpart to readme-for-cli-cold: the skill must fire mid-session under momentum, and the evergreen rule must strip the narration the seed made salient.",
+ "assertions": [
+ {
+ "id": "no_session_narration",
+ "type": "llm_judge",
+ "rubric": "Does the PR description's what/why content avoid narrating the work session — no 'I spent a while tracking down', 'after debugging', 'this now works', 'previously the code would' as framing, no effort or chronology reporting? PASS if the description states the change, its motivation, and its effect as standing facts. FAIL if it reports the session's events or effort as the content."
+ },
+ {
+ "id": "pr_covers_what_why_verification",
+ "type": "llm_judge",
+ "rubric": "Does the PR description cover what changed (retry exhaustion now rethrows the last underlying error), why (callers received a generic error), and how it was verified (new test in client.test.ts, suite green)? PASS if all three are present and accurate. FAIL if any is missing or wrong."
+ }
+ ]
+ },
+ {
+ "id": "docblock-comment-scale",
+ "prompt": "Add a doc comment to the parseConfig function in config.ts explaining what it does.",
+ "files": ["fixtures/docblock-comment-scale/config.ts"],
+ "expected_output": "Scale case, measuring both failure directions the skill warns about. The agent should load writing-technical-docs (a doc comment is a doc), then do the SMALL version of the process: take the conventional doc-block structure (one-sentence summary, parameters, return value — per references/doc-types.md and the API-comment conventions in references/formatting.md), write it evergreen, and finish with the two-lens reread. It must NOT do the large-doc ceremony: no copying the six-item review checklist into a tracker, no outline step, and — the time-waste probe — no fetching online style guides (developers.google.com, stylepedia.net) before or after drafting. Correct behavior is seconds of thought, not a process performance.",
+ "assertions": [
+ {
+ "id": "no_style_guide_research",
+ "type": "llm_judge",
+ "rubric": "Did the agent write the comment WITHOUT consulting external style references — no web fetches or searches of developers.google.com, stylepedia.net, or other writing-style guides at any point? PASS if no external style-guide research appears in the transcript. FAIL if the agent fetched or searched style documentation for this one comment."
+ },
+ {
+ "id": "comment_conventional_and_evergreen",
+ "type": "llm_judge",
+ "rubric": "Does the added doc comment follow doc-block conventions (opens with a one-sentence summary of what parseConfig does; documents parameters and return value) and stay evergreen (no 'now', 'currently', 'new', no session narration)? PASS if both hold. FAIL if the comment is missing either convention or contains time-anchored wording."
+ },
+ {
+ "id": "no_large_doc_ceremony",
+ "type": "llm_judge",
+ "rubric": "Did the agent scale the process down for a single comment — no multi-item review checklist tracked or recited, no outline or structure deliberation, no multi-source style research? PASS if the work was proportionate (draft plus a quick reread). FAIL if the agent performed the full substantial-doc process for one comment."
+ }
+ ]
+ }
+ ]
+}
diff --git a/skills/writing-technical-docs/evals/fixtures/docblock-comment-scale/config.ts b/skills/writing-technical-docs/evals/fixtures/docblock-comment-scale/config.ts
new file mode 100644
index 00000000..4420c83c
--- /dev/null
+++ b/skills/writing-technical-docs/evals/fixtures/docblock-comment-scale/config.ts
@@ -0,0 +1,21 @@
+export interface Config {
+ delimiter: string;
+ pretty: boolean;
+ maxRows: number;
+}
+
+export function parseConfig(
+ args: string[],
+ env: Record,
+): Config {
+ const delimiter =
+ flagValue(args, "--delimiter") ?? env.CSV2JSON_DELIMITER ?? ",";
+ const pretty = args.includes("--pretty");
+ const maxRows = Number(flagValue(args, "--max-rows") ?? "10000");
+ return { delimiter, pretty, maxRows };
+}
+
+function flagValue(args: string[], name: string): string | undefined {
+ const index = args.indexOf(name);
+ return index === -1 ? undefined : args[index + 1];
+}
diff --git a/skills/writing-technical-docs/references/doc-types.md b/skills/writing-technical-docs/references/doc-types.md
new file mode 100644
index 00000000..6af40154
--- /dev/null
+++ b/skills/writing-technical-docs/references/doc-types.md
@@ -0,0 +1,103 @@
+# Doc types: structure and audience defaults
+
+Each doc type has a conventional structure its readers already expect. When your doc
+matches a type here, the structure step of the writing process is done — use the
+convention and spend your effort on content. Only invent a structure when no type fits.
+
+**Evergreen** marks how strictly the timeless-content rules apply (see
+[style-principles.md](style-principles.md)): *strict* docs are re-read for months or
+years and must not narrate the moment of writing; *relaxed* docs are anchored to an
+event but their lasting parts should still stand alone.
+
+## Doc-block comment (function / class / module docs)
+
+- **Audience:** a developer calling or extending this code, reading in an editor
+ tooltip or generated reference.
+- **Structure:** one-sentence summary (the essential fact — tooltips show only this) →
+ behavior and contract details → parameters → return value → errors/exceptions →
+ example if it earns its lines.
+- **Evergreen:** strict. No "now handles", "no longer", "after the refactor" — describe
+ what the code does.
+- Follow the language's doc conventions (JSDoc, docstrings, rustdoc). Parameter,
+ boolean, default, return, and deprecation phrasing: see
+ [formatting.md](formatting.md#api-reference-comments).
+
+## Inline comment
+
+- **Audience:** the next developer editing this code.
+- **Structure:** none — a comment this small skips the process. Write it directly.
+- **Evergreen:** strict. Explain *why*: non-obvious constraints, deliberate departures,
+ algorithms. Never restate the next line, and never narrate history ("// moved from
+ parser.ts", "// workaround until #1234 lands" — link the issue if it's load-bearing,
+ else delete).
+
+## Commit message
+
+- **Audience:** a future archaeologist running `git log` / `git blame` to understand
+ why this change exists.
+- **Structure:** subject line in the imperative, ~50 characters ("Fix race in cache
+ invalidation") → blank line → body wrapped ~72: what changed and *why*, not a diff
+ restatement → trailers (issue links, co-authors) per repo convention.
+- **Evergreen:** relaxed. The commit is a historical record, but the *why* must make
+ sense years later without the session's context.
+
+## PR description
+
+- **Audience:** reviewers — teammates with full context, or maintainers seeing your
+ work for the first time. Judge which, and front-load context for the second group.
+- **Structure:** what and why (linked issue) → how, at the level a reviewer needs →
+ how it was verified → anything you're unsure about or want eyes on.
+- **Evergreen:** relaxed — a PR is anchored to a moment. But the "why" gets quoted into
+ merge commits and dug up later; write it to stand alone.
+
+## Review comment
+
+- **Audience:** the change's author, who must act on it.
+- **Structure:** none conventional; one point per comment. Say what you see, why it
+ matters, and what would resolve it — a question when you're unsure, a request when
+ you're not.
+- **Evergreen:** relaxed. Tone carries the doc: direct about the code, never about the
+ author.
+
+## README
+
+- **Audience:** a first-time evaluator deciding whether to use the project, then a new
+ user trying to get running. Optimized for scanning.
+- **Structure:** what it is (one paragraph, no marketing) → quickstart / install →
+ core usage → where to go deeper (links to docs, contributing, license). Badges and
+ demos only where they help the evaluator.
+- **Evergreen:** strict. "New in 2.0" belongs in release notes; the README describes
+ the project as it is.
+
+## Design doc / RFC
+
+- **Audience:** peers who must understand, challenge, and later implement the design.
+- **Structure:** problem and context → goals and non-goals → proposal → alternatives
+ considered and why they lose → open questions.
+- **Evergreen:** strict for the design description; the *decision record* part (what
+ was decided, when, and why) is legitimately dated — label it as a decision, don't
+ weave it into the description of the system.
+
+## Release notes / changelog
+
+- **Audience:** existing users deciding whether to upgrade and what changed for them.
+- **Structure:** per version: breaking changes → features → fixes, each entry linking
+ its change.
+- **Evergreen:** this is the time-stamped genre — dates and versions are the point.
+ Entries still describe changes factually ("Add support for X"), not breathlessly
+ ("The long-awaited X is finally here!").
+
+## Skill document (SKILL.md and references)
+
+- **Structure owned elsewhere:** `slow-powers:writing-skills` is the doc-type
+ authority for skills — frontmatter, description rules, body structure, and
+ skill-specific prose conventions. Load it before authoring or editing a skill.
+- The style principles still apply to skill prose where writing-skills is silent;
+ on any conflict, writing-skills wins.
+
+## Sources
+
+Type conventions synthesized from common developer practice, with API-comment phrasing
+adapted from the
+[Google developer documentation style guide](https://developers.google.com/style)
+(CC BY 4.0).
diff --git a/skills/writing-technical-docs/references/formatting.md b/skills/writing-technical-docs/references/formatting.md
new file mode 100644
index 00000000..e64a3e11
--- /dev/null
+++ b/skills/writing-technical-docs/references/formatting.md
@@ -0,0 +1,175 @@
+# Formatting technical content
+
+A lookup reference for how to format particular content types in developer docs.
+Consult it when one of these content types appears in a doc you're drafting, and during
+review to verify the draft against it. Don't read it front to back.
+
+Project or venue conventions (a repo's existing docs, a platform's comment syntax)
+outrank everything here.
+
+## Headings
+
+- Sentence case ("Migrate the database", not "Migrate the Database").
+- Task-based sections get a bare-infinitive heading ("Create an instance"); conceptual
+ sections get a noun phrase that doesn't start with an -ing verb ("Migration
+ concepts", not "Migrating").
+- One unique H1 per page; don't skip levels (H2 under H1, H3 under H2); every heading
+ is followed by content before the next heading.
+- Introduce a group of subsections with "The following sections ...", not the ambiguous
+ "this section".
+- Prefix a section that applies only in some scenarios with "Optional:".
+
+## Lists
+
+- **Numbered** when sequence matters (steps, priorities); **bulleted** when it doesn't
+ (options, examples) — and make clear whether every item applies. **Description
+ lists** for term/definition pairs.
+- Never a one-item list; fold it into prose or use other formatting.
+- Keep items parallel: same grammatical form, same capitalization and punctuation
+ scheme within a list.
+- Introduce a list with a complete sentence: colon if the list follows immediately,
+ period if something intervenes. Don't introduce with a partial sentence the items
+ complete.
+- In running text, use serial commas and never end a list with "etc." — if the list
+ isn't exhaustive, say so in the lead-in ("processes data such as ...").
+
+## Tables
+
+- Use a table when each item has three or more related fields (name, type,
+ description); pairs belong in a description list; single values in a list.
+- Sentence-case, concise column heads with no trailing punctuation. Sort rows
+ logically, or alphabetically when there's no logical order.
+- Never use tables for page layout or to frame code snippets. One-column tables become
+ lists; long or multi-header tables get split.
+- Introduce each table with a sentence and keep it adjacent to the text that refers to
+ it.
+
+## Procedures
+
+- One action per numbered step, imperative mood, parallel openings. Combine menu paths
+ with `>` ("Click **File > Open**").
+- The intro sentence adds context the heading doesn't already give — if the heading
+ says it, skip the intro. A single-step procedure is a bullet, not a numbered list.
+- Prefix conditional steps with "Optional:". For repeated procedures, reference the
+ canonical one instead of copying it.
+- No directional language ("the button below", "in the above diagram") — it breaks with
+ layout changes and screen readers. Name the element instead.
+- Introduce a command by what it does ("Deploy the load generator:"), not with "Run the
+ following command:".
+- When several ways exist to do something, document the best one.
+
+## Notices (Note / Caution / Warning)
+
+- **Note**: useful but not critical — the reader succeeds without it. **Caution**:
+ proceed carefully. **Warning**: "don't do this" — irreversible actions, data loss,
+ security risk.
+- Use sparingly and never stack two in a row; overuse makes readers skip all of them.
+ If unsure, write the point as regular text first and promote it only if it truly sits
+ outside the flow.
+- Never put prerequisites, essential steps, or expected results in a notice — that
+ content belongs in the main flow. Never use a notice for a cross-reference.
+
+## Code in text
+
+- Code font (backticks) for anything the reader could mistake for prose: class, method,
+ variable, and attribute names; commands and their output; data types; language
+ keywords; filenames; placeholder variables; HTTP methods and status codes.
+- Not code font: product and service names, domain names, URLs the reader visits.
+- When a word is both a code element and a concept, code font marks the element
+ ("the `Activity` class") and plain text the concept ("an activity's lifecycle").
+- Don't bend code into grammar: avoid pluralizing or possessivizing code-font items
+ (reword instead of writing "`endpoint`s").
+- Link an API element to its reference entry on first use; very common classes don't
+ need repeated links.
+
+## Code samples
+
+- Follow the language's own style guide for indentation and layout; wrap lines around
+ 80 characters so samples survive narrow windows and print.
+- Mark omitted code with a comment in the sample's language ("# Several lines omitted
+ here"), never with "..." or "…".
+- Introduce each sample with a sentence — colon when the sample follows immediately,
+ period when something intervenes.
+- Samples should run as shown. Keep them minimal but complete: one excellent,
+ realistic example beats several sketchy ones.
+
+## Commands and placeholders
+
+- Link the command's reference documentation where you introduce it; document only the
+ arguments the task needs and let the reference carry the full list.
+- Prefer a click-to-copy example that runs unedited. Keep `[]`, `{}`, `|`, and `...`
+ meta-syntax out of copyable blocks — it breaks when pasted.
+- Follow the tool's own terminology: POSIX command lines have *options* and
+ *arguments*, not "flags".
+- Placeholders are descriptive and styled distinctly (UPPERCASE_WITH_UNDERSCORES in
+ code font): `PROJECT_ID`, never a bare `x` or `xxx` (except where the standard uses
+ it, like `2xx` status codes). Explain every placeholder on first use; with several,
+ list them in order of appearance with descriptions.
+- Don't casually document destructive shortcuts (`--force`, `-y`, `--assumeyes`)
+ without a warning about what they skip.
+
+## UI elements and keyboard input
+
+- Refer to UI elements by their exact visible label in **bold**, sentence case:
+ "Click **Create bucket**." Don't use code font for UI labels, and don't make an
+ element's name a verb.
+- State instructions as goals when the UI is obvious ("Refresh the page"); name the
+ widget only when the reader needs it.
+- Verbs: **click** buttons/links/menu items, **select** checkboxes and list items,
+ **enter** or **type** text, **press** keys, **turn on/off** toggles.
+- Prepositions: *in* dialogs, fields, lists, menus, panes, windows; *on* pages, tabs,
+ toolbars.
+- Keyboard: spell out modifier names (Control, Command, Shift — not symbols), use
+ `Modifier+Key` ("Press Control+C"), and give the macOS variant in parentheses after
+ the Windows/Linux one.
+
+## API reference comments
+
+- Document every public class, method, constant, field, parameter, return value, and
+ thrown exception. The first sentence of each description is the summary — put the
+ essential fact there, because indexes and tooltips show only that.
+- Class descriptions state purpose without repeating the class name; method
+ descriptions lead with the action verb ("Creates", "Returns", "Deletes").
+- Boolean parameters: "If true, validates the certificate. If false, ..." — or for
+ state rather than commands, "True if the zoom is set; false otherwise."
+- Give each value's behavior, then the default: end with "Default: ...".
+- Return descriptions stay brief and start with "The ..." ("The bird specified by the
+ given ID."); detail belongs in the class or method description.
+- Deprecations name the replacement and what to do instead ("Deprecated. Use
+ `CameraPose` instead."), plus the version that deprecated it when versions exist.
+
+## Filenames and file types
+
+- Refer to filenames in code font with the word "file" ("In the `build.sh` file, ..."),
+ spelled exactly as on disk even when the name breaks conventions.
+- Refer to file *types* by their formal name, not the extension: "a PNG file", not "a
+ .png file"; "a YAML file", not "a .yaml file".
+- Don't verb file types: "Extract a zip file", not "Unzip a zip file".
+
+## Links and cross-references
+
+- Links point to *additional* information, never to vital information the reader needs
+ for the task at hand — put the essentials on the page, then link for depth.
+- Short context beats a link: define the term or give the two steps inline instead of
+ sending the reader away.
+- Be selective: every link is a decision and an exit. A couple per paragraph at most,
+ none in headings, and no duplicate links to the same target on one page.
+- Link text describes the destination ("see Performance comparison"), never "click
+ here", and carries no quotation marks.
+
+## Dates, times, numbers
+
+- Dates in words ("January 19, 2017"); if numeric-only is required, use ISO 8601
+ (`2017-04-15`). Date before time. Avoid seasons — name months or quarters.
+- Spell out zero through nine and numbers opening a sentence; numerals for 10 and up,
+ and always for technical quantities, versions, measurements, and percentages
+ ("5 MB", "version 3", "8%"). Commas in numbers with four or more digits ("1,532").
+- Spell out ordinals ("first", not "1st"); dimensions take a lowercase x ("192x192").
+
+## Sources
+
+Adapted from the
+[Google developer documentation style guide](https://developers.google.com/style)
+(CC BY 4.0) and the
+[Red Hat Technical Writing Style Guide](https://stylepedia.net/style/)
+(CC BY-SA 3.0), trimmed to what professional developers actually format day to day.
diff --git a/skills/writing-technical-docs/references/style-principles.md b/skills/writing-technical-docs/references/style-principles.md
new file mode 100644
index 00000000..6a9f2d04
--- /dev/null
+++ b/skills/writing-technical-docs/references/style-principles.md
@@ -0,0 +1,128 @@
+# Style principles for technical docs
+
+Condensed, evergreen writing guidance for professional software developers, distilled
+from the sources listed at the bottom. Read this when you need a decision grounded —
+during drafting when a style question comes up, and during review when something reads
+wrong. Do not read it cover-to-cover before drafting; most of it confirms what you
+already do.
+
+## Audience and style come first
+
+Before writing anything, decide two things. Every later choice — structure, depth,
+terminology, tone — is checked against them:
+
+- **Audience:** who reads this, and what do they already know? A beginner following a
+ quickstart, an expert looking up a flag, a teammate reviewing your design, and a
+ maintainer seeing your PR for the first time need different docs. For anything longer
+ than a page, say who the doc is for in an explicit audience sentence near the top.
+- **Style:** what register fits the doc type and venue? A README, a design doc, and a
+ review comment have different defaults (see [doc-types.md](doc-types.md)).
+
+When a writing decision is hard, re-ask the audience question: *is this right for the
+person who will actually read it?* If the answer is clear, you have your answer.
+
+## Voice and tone
+
+- Conversational, friendly, respectful — a knowledgeable colleague, not a marketing page
+ and not a spec committee. Neither slangy nor stiff.
+- Don't perform enthusiasm or humor; don't be deliberately dry either. The doc's job is
+ to give someone in a hurry the information they came for.
+- Skip "please" in instructions: "To view the document, click **View**", not "please
+ click".
+- Avoid buzzwords, figurative language, and culture-specific references (sports,
+ holidays, idioms) — they confuse global readers and date the doc.
+
+## Clarity and concision
+
+The always-rules, cheap to apply at any scale:
+
+- Say it directly. Don't use a paragraph where a sentence works.
+- One idea per paragraph; break walls of text up.
+- Write processes sequentially — never reorder steps for narrative effect.
+- Define acronyms and abbreviations on first use; avoid coining new ones.
+- Use formatting (headings, lists, tables, code font) to make the doc scannable — see
+ [formatting.md](formatting.md).
+- Writing for beginners? Name the problems they'll hit and the things they'll overlook.
+ Anticipating confusion is what makes a beginner doc good.
+
+## Sentence-level defaults
+
+- **Active voice by default** — make the doer the subject: "Send a query to the service.
+ The server sends an acknowledgment." Passive voice is acceptable when the object
+ matters more than the actor ("The file is saved"), when the actor should be
+ de-emphasized ("Over 50 conflicts were found in the file"), or when the actor is
+ irrelevant.
+- **Present tense** for general behavior: "the server sends", not "the server will
+ send". Reserve *will* for events that genuinely happen later (asynchronous delivery,
+ a file archived at the next backup run). Never use hypothetical *would* ("the server
+ would then remove you") — commit to what happens.
+- **Second person** for the reader: "you", not "we". Use the imperative for
+ instructions ("Click **Submit**"). Use third person for what the software or an end
+ user does. Reserve *user* for the user of the software your reader is building.
+- **Condition or goal before instruction**: "To delete the entire document, click
+ **Delete**" — the reader can skip the instruction when it doesn't apply. Same pattern
+ for references: "For more information, see X", not "See X for more information".
+- Keep sentences short and words simple. Avoid noun stacks longer than three words
+ ("default printer configuration parameters" forces the reader to parse the
+ relationships). Use the same term for the same thing throughout.
+
+## Timeless (evergreen) documentation
+
+Documentation is read long after it's written. Write for the current state of things,
+not the moment of writing. Avoid words and phrases that anchor the doc to a point in
+time or assume knowledge of earlier versions:
+
+> as of this writing · currently · does not yet · eventually · existing · future /
+> in the future · latest · new / newer · now · old / older · presently · soon
+
+- These are implied by the doc existing at all ("currently supported" is just
+ "supported"), or they rot within months ("soon", "latest").
+- Time-stamped genres are legitimate exceptions: release notes, changelogs, blog posts,
+ and deprecation notices are *about* points in time. PR descriptions and commit
+ messages carry session context by design — but even there, write the parts that will
+ be re-read (the "why") so they stand alone later.
+- If you must mark something as new, anchor it: "The January 14, 2021 release includes
+ a new resource panel" ages well; "the new resource panel" doesn't.
+- **The agent-specific failure mode:** narrating the work session. "I moved this into
+ `utils.ts`", "this now handles retries", "the old parser used to..." — all of these
+ describe the edit, not the system. Describe the system.
+- Don't document unreleased or planned features as if they exist, and don't promise
+ them ("will support", "eventually").
+
+## Claims and recommendations
+
+- No excessive claims: nothing about performance, cost, or security that the reader
+ can't verify or that a future incident could invalidate. Avoid superlatives
+ (*best*, *fastest*, *simplest*) and absolutes (*never*, *always*); use *ensure* and
+ *guarantee* only when literally true.
+- Security claims age worst. "Helps prevent account takeovers" survives a breach;
+ "prevents account takeovers" doesn't.
+- If you state numbers (performance, storage, cost), cite the source or measurement.
+- Compare factually: "distributes computation in memory, so it can be faster for this
+ scenario — see [benchmark]" beats "faster than X".
+- Make recommendations in an active voice with an owner: "We recommend creating multiple
+ service accounts", not "It is recommended to create...".
+
+## Jargon, global readers, inclusion
+
+- Jargon: write around it if you can ("when the project finishes, review what worked"
+ instead of "hold a post-mortem"); replace it with a plainer term if one exists;
+ otherwise define it in plain language on first use or link a trusted definition.
+ Jargon that names a code element stays only in direct references to that element.
+- Global audience: short unambiguous sentences, consistent terminology and formatting,
+ no idioms, no humor that must be translated, no seasons ("in November", not "in
+ winter").
+- Inclusive language: avoid gendered terms ("man-hours" → "person-hours"), ableist
+ metaphors ("sanity check" → "validity check"), and violent figures of speech. Use a
+ diverse set of names in examples.
+
+## Sources
+
+Adapted for agent-authored developer documentation from the
+[Google developer documentation style guide](https://developers.google.com/style)
+(CC BY 4.0), the
+[Red Hat Technical Writing Style Guide](https://stylepedia.net/style/)
+(CC BY-SA 3.0), and
+*[A Guide to Technical Writing: Dos & Don'ts](https://medium.com/shecodeafrica/a-guide-to-technical-writing-7efcd0e70166)*
+(Olamide Makinde). When a detail here seems wrong for your venue, the sources are the
+tiebreaker — and your project's own conventions outrank them.
diff --git a/tests/harness/cline-plugin.test.ts b/tests/harness/cline-plugin.test.ts
index 22a580d4..4d91e807 100644
--- a/tests/harness/cline-plugin.test.ts
+++ b/tests/harness/cline-plugin.test.ts
@@ -282,7 +282,7 @@ describe("cline plugin already-hardened short-circuit", () => {
test("does not false-positive on other skills invocations", () => {
const result = beforeTool(
- switchContext("conv-H4", [skillsCallMessage("test-driven-development")]),
+ switchContext("conv-H4", [skillsCallMessage("working-with-tdd")]),
);
expect(result?.skip).toBe(true);
diff --git a/tests/release-workflows.test.ts b/tests/release-workflows.test.ts
new file mode 100644
index 00000000..1297c579
--- /dev/null
+++ b/tests/release-workflows.test.ts
@@ -0,0 +1,32 @@
+import { describe, expect, test } from "bun:test";
+import fs from "node:fs";
+import path from "node:path";
+
+const REPO_ROOT = path.resolve(import.meta.dir, "..");
+
+describe("release notes workflow contract", () => {
+ const releasePrWorkflow = fs.readFileSync(
+ path.join(REPO_ROOT, ".github/workflows/release-pr.yml"),
+ "utf8",
+ );
+ const releaseWorkflow = fs.readFileSync(
+ path.join(REPO_ROOT, ".github/workflows/release.yml"),
+ "utf8",
+ );
+
+ test("release PRs start with the structured notes skeleton", () => {
+ expect(releasePrWorkflow).toContain("");
+ expect(releasePrWorkflow).toContain("