CLAUDE.md@.claude · git:20261108.7150974 · 2026-11-08 · sha256 8b00c9e65bb2222a

CLAUDE.md@.claude git:20261108.7150974A

Immutable. This exact content never changes and is served at /api/v1/blob/8b00c9e65bb2222a.

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

@../AGENTS.md

---

Above the rule: the shipped harness doc. Below: guidance for developing this repo.

## What this repo is

A Claude Code plugin (`shapeup-sdlc`). AGENTS.md is a **shipped product artifact** —
`bin/init.mjs` splices its `<!-- HARNESS_START -->…<!-- HARNESS_END -->` block into
consumer projects. Editing it is a product change: speak in skills/commands/options,
never `.mjs` paths.

## Commands

- `npm test` — Tier-0 structural suite (`tests/structural/`); zero deps, no install step.
- `claude plugin validate . --strict` (and the same for `./.claude-plugin/marketplace.json`) — CI parity.
- `claude --plugin-dir .` — load the working copy without installing.
- `npm run demo` — regenerates `docs/assets/demo-gate.svg` by running the real gate hook; never hand-edit the SVG.
- `npm run verify:cross-dir` — the cheap release-candidate check below, as a script instead of a manual exercise: fires `hooks/sandbox-guard.mjs` from a `git archive` copy at a different path, against a scratch consumer project, and confirms the decision lands. Run before a release that touches `hooks/` or `kernel/lib/paths.mjs`.

## Rules that prevent breakage

- Zero dependencies is load-bearing: executable code is plain Node ESM (`.mjs`),
  `node:` builtins only, no network calls anywhere.
- Hooks fail open, never closed, unless the bad state is positively proven
  (`hooks/sandbox-guard.mjs` is the reference implementation).
- A version bump touches both `package.json` and `.claude-plugin/plugin.json`;
  release CI fails on mismatch.
- A new worker skill takes 4 steps: `SKILL.md` → declare fields in
  `kernel/schemas/domain.schema.json` (and add it to `WorkerName`) → `agents/<name>.md` preloading it → a row in
  the `building` skill's phase table and its order in `resources/orders.md`. Step 1 alone yields a skill the loop can't dispatch.
- `tools/` is repo-only and never ships; what ships is the `files` allowlist in `package.json`.
- Commit subjects: `type(scope): lowercase declarative`.
- Public documents (`README.md`, `AGENTS.md`, `docs/`, `commands/`, `SECURITY.md`) name a gate by its plain name
  (Round Close, Ship Sign-off …), never by a code (`L2`, `H`) and never by a notation symbol (`⏸ ✦ ★ ✧`). The machine id
  appears only in the "Gate names" table of `AGENTS.md`, as the id the ledgers and answer files carry. A new gate
  needs a row there in the same commit — `harness-maintenance-audit` checks both ways (an unlisted gate, and a row nothing uses).

## Keeping the docs and the plugin honest

This repo is both a product and its own blueprint, and the two drift apart silently — a doc
saying "11 workers" over a 10-member enum reads perfectly. Two standing rules:

- **Derive facts from artifacts, never from prose.** Enums come from
  `kernel/schemas/domain.schema.json`, counts from the filesystem, and hook behavior
  from *executing the hook* against a fixture. A doc, a glossary and a screenshot can all agree
  with each other and all be wrong; reading any one of them just confirms the others.
- **Nothing in the shipped set may reference something the user did not receive.** No benchmark
  results, internal defect IDs (`HD-00x`), migration stage names, audit codenames, or paths into
  `docs/`, `tests/`, `tools/`. Keep the operative rationale, drop the evidence: a
  comment should explain *why* the code is shaped that way without citing an artifact the reader
  cannot open.

Run the **`harness-maintenance-audit`** skill (`.claude/skills/`) for both passes — before a
release, after adding or removing a skill/operation/hook/command, and after any cleanup or rename
commit, whose blast radius is routinely wider than its subject line claims. It fixes docs and
reports code defects separately, on purpose: a doc fix records reality, a code fix changes it.

Two traps it exists to catch, both of which have bitten this repo:

- A red check nobody has looked at. Run `npm test` and `npm run demo` *before* editing — a suite
  you have not run is an assumption, not a baseline. Never make a red check green by weakening it.
- A stale measured number. Trigger and acceptance rates belong to a run with a model and a date.
  If the dataset changed, say the measurement predates it — do not rescale the figure to match a
  count you just derived, which silently manufactures a measurement.

And one the audit cannot catch, because nothing run from this checkout can. Running the repo as its
own plugin puts the plugin root inside the working directory, and every experiment ends with its run
trace deleted, so two whole classes of defect are invisible here: anything gated on where the plugin
is installed, and anything that happens *after* a run ships.

**The soak is how you find those, and it is not a release gate.** Calling it one was a promise the
instrument cannot keep: on the live consumer a soak validates through BUILD and stops there, so
"soaked across two consecutive features" is evidence about the phases it reached and silence about
the rest. Run it to find defects — it has produced most of the register — and read a clean soak as
"nothing surfaced in BUILD", never as "the candidate is fit to ship".

What a release candidate *can* be held to, because it is cheap and it caught the class that worried
us: fire the changed hook from a plain `git archive` copy of the tree at some other path, with a
different project as `cwd` (this is what a marketplace clone is, and it proves a cross-directory
import resolves where the plugin actually lands), then drive one real nested session —
`claude -p --plugin-dir <repo> --permission-mode acceptEdits` — and confirm the decision reached
`decisions.jsonl`. An enforcement layer that fails to load fails OPEN and silently: exit 1, no
stdout, no ledger row, and the tool call proceeds. A missing row is the signal.