spec·loop operator's playbook
Operator's playbook · run it, don't read about it

You bring the idea.
The method builds the rest.

Uscha is spec-driven and tool-agnostic, and it starts before the spec. This is the operating manual for running it in Claude Code with the uscha-kit: each play tells you when it fires, what to do, and what verifies it.

in the kitnewproposal
The loop — inner cycle, human at the gate
idea discovery ready SPEC ADR? build verify evidence human gate
00Read the board before you movein the kit

Uscha rejects the usual assumption that you already know what to build. The expensive failure is a confident build of the wrong shape — so the first move is always to find the shape, and every later move advances on captured evidence, stopping short of merge.

trigger
Any new system or non-trivial change.
move
Walk the chain above in order. Never skip to SPEC.
gate
A human owns the merge. Always.
01Open with a grilling, not a briefingin the kit

Run /uscha-discovery when you have an idea but not a shape; run /uscha-adr-refine when the shape is known and only the edges need drawing. Neither writes code — they run on Read, Write, Glob, Grep (discovery adds WebFetch).

trigger
Vague idea, or a feature whose edges aren't pinned.
move
Let it interrogate you: contradictions, fuzzy terms, missing failure modes.
gate
Converges on a shape before emitting anything.
operating rule

Grill, don't agree. A discovery where the assistant agreed with everything failed — an agreeable interviewer just inherits your blind spots and hands them back formatted as a plan. If you're not being pushed on, restart it — friction is the deliverable.

02Crystallize the talk into repo filesin the kit

As decisions settle, write them straight into the repo — don't hoard for the end. The build reads files, not chat.

CONTEXT.mdDomain glossary — kills fuzzy language.
DOMAIN-MODEL.mdThe approved shape: entities + relations.
SPEC.mdBehavior, I/O, errors, acceptance, rollback.
docs/adr/*.mdOne per durable decision; executable.
ACCEPTANCE.mdDoD as - [ ] — what readiness measures.
RISKS · HANDOFFResidual risk; what to read before coding.
why

Context resets (new session, /clear, compaction) lose the chat. Sub-agents and fresh sessions read files. The truth lives in versioned files.

03Classify the change before you spend rigor on itprocess — not mechanized

Ask: what can this break, and what does the break cost? The answer sizes the apparatus. Don't drag the full machine behind a config tweak. Profiles are a process heuristic — no profile concept exists in the kit's code or config, and no profile selects gates automatically; you pick them.

ProfileExampleMinimum gates
A · Lowminor UI / configbuild + relevant test
B · Normallocal feature / bugfixtests + static + review
C · Criticalpayments / securityunit+integration+security+rollback
D · Multi-systempublic API / eventscontract tests + versioning
E · Legacyrefactor / migrationcharacterization + no-regression
flag

Profile E → golden testing is mandatory (see play 08). Baseline the old behavior before you touch it.

04Sort each fact into SPEC, ADR, or CONSTITUTIONin the kit

Three documents, three levels of force.

  • SPEC — observable behavior. What must happen.
  • ADR — a choice between alternatives. Why this shape.
  • CONSTITUTION — what's never acceptable, whatever the trade-off.

Write an ADR only if all three are true

  • Hard to reverse.
  • Surprising without context.
  • The result of a real trade-off.

Miss one → it's a SPEC line. The Implementation Plan makes an ADR executable; code links back with // ADR: <slug>.

hard stop

An ADR chooses; the CONSTITUTION forbids. An ADR may pick Postgres vs Redis. An ADR may not pick "store the secret in plaintext" — the CONSTITUTION vetoes it first. When the agent detects a violation it must log it via flag-blocker --kind constitution; once logged, it caps readiness ≤65 and blocks convergence until a human runs --resolve. The engine does not read CONSTITUTION.md itself — detection is the agent's (and your) duty; enforcement-after-logging is the engine's. Invariants are CWE-mapped.

05Run /uscha-devloop and let it convergein the kit

The orchestrator takes the ADRs + ACCEPTANCE.md, builds, and runs a severity-gated review loop that stops. Three laws: converge, don't chase zero · tests are a guardrail, not the finale · generating tests ≠ running them.

  1. Setup + Phase 0. qa_ledger.py init; ADRs + acceptance in. No acceptance → run /uscha-adr-refine first.
  2. Phase 1 — coverage gate. Above threshold, existing suite guards. Below → write characterization tests at the edge; human reviews them.
  3. Phase 2 — build. One commit per logical step. Stop and propose an ADR on a new dep/pattern/contradiction.
  4. Phase 3 — QA loop. code-review → judgment-day → improve. Apply fixes ≥ gate; defer the rest. Log, ingest, persist each fact-gate verdict via log-gate, check converged/oscillation.
  5. Phase 4–5 — integration + verify. Cross-repo contract tests; then the deferred fine-grained coverage.
  6. Phase 6 — PR, then STOP. Converged + gates clean, open PR, stop at the merge gate — human reviews, then ship.
  7. Phase 7–8 — smoke + docs. Manual smoke list; retrospective from the ledger. /uscha-sysdoc is optional reporting on request, not a mandatory phase.
watch for

The Ralph Wiggum loop — the agent fires the "done" token too early. Only the objective gates plus the human at the merge gate close it; never the agent's own opinion — the engine doesn't close the loop by itself.

06Trust the artifacts, never the narration

If the maker writes its own report card, it can log exit_code: 0 having run nothing. So the ledger parses real files — it doesn't transcribe claims.

# qa_ledger.py reads files, not the agent
coverage  ← coverage report (e.g. jacoco.xml / lcov)
tests     ← test result XML (e.g. JUnit / surefire)
static    ← linters · type-checker · security scanner
LOC       ← its own count (stdlib)
hard rule

Absent = no evidence, never "OK". An empty-but-present report credits the fix; an absent report means the gate didn't run. A tool that didn't run is never invented as green — a lint-capable repo whose static gate never ran scores UNMEASURED (0.0) on that dimension, never a perfect 1.0.

principle

Maker ≠ checker. The one who makes doesn't graduate the work — a fresh-context checker does. (Anthropic's evaluator-optimizer, Dec 2024.)

07Let facts block; let guesses only advise

A gate that reads a fact may block the work. A gate that guesses over prose may only whisper. Break this and false positives kill the gate — the dev turns it off, and a dead gate is worse than none.

GateReads
golden-diffbytes vs .approvedBLOCKS
pit-checkmutation XML (scheduled/incremental tier, not inner-loop)BLOCKS
gate-checkdiff structure (deleted/disabled tests · lowered thresholds · added secrets — 1.12.0)BLOCKS
static gate4 linters, normalizedBLOCKS
spec-check · structureout-of-scope section + acceptance criteria present + traceable AC-n IDsBLOCKS
phase --require pr-readystate DERIVED from the ledger; a spike/* branch never passes (1.18.0–1.19.0)BLOCKS
regression-checkfindings closed with no new test = NARRATED (1.16.0); --strict gates itADVISES
waste-checkType-1/2 clones of the diff vs the repo (reuse-first, 1.26.0) — the duplication simplicity-check can't see; flags name the file:line to reuse; gates only with --gate or defaults.waste.gateADVISES
rubric-ingestrubric grade (versioned qualitative criteria, vendor-neutral JSON contract, 1.23.0); gates only when the human declares itADVISES
spec-check · prosevague terms, EARS, stack naming (--strict gates)ADVISES
field note

One uncorrelated checker caught 93.4% of what four caught (Osmani). Stack different opinions, not four similar linters. Fact gates run inline in /uscha-devloop (exit 0/1) and each verdict is persisted via qa_ledger.py log-gate — a persisted fail blocks convergence and caps readiness ≤65; a not-run is recorded but never green.

08Anchor every migration with a goldennew

Everything else is agent-authored. The golden is the one thing it can't author — and that's the point. Capture what the old code actually does by running it, freeze it as human-approved .approved, then diff the migrated code against it.

trigger
Any Profile-E migration / modernization.
move
Capture → human approves → migrate → diff.
gate
golden-diff clean, or no merge.
  • Never let the agent generate or edit .approved — a PreToolUse hook blocks the write.
  • Never capture by reasoning about output; capture by executing against real corpus.
  • Pin the target locale, freeze clocks, normalize GUIDs/map-order before serializing.
  • Add *.approved.* binary to .gitattributes — line endings will lie otherwise.
why it's sacred

If the agent authored the golden, it would encode the same partial understanding that lost the logic. The golden is a check on the agent, not one it grants itself — which is precisely why it exists.

09Decide before you automate the loopproposal

The outer loop is a heartbeat that fires the inner one on a cadence. Don't build it until all four are true.

  • Repeats (weekly+) — else one prompt is cheaper.
  • Automated verification — a test/build/linter that fails the work without you.
  • Budget absorbs waste — it re-reads and retries; it burns tokens either way.
  • Senior tools — logs, repro env, runs what it writes.
# heartbeat wrapping /uscha-devloop
> /loop 0 3 * * *              # nightly cadence
  /goal readiness ≥ 80 AND tests green
        AND 0 CRITICAL         # fresh checker: other agent family, or at least
                               # another profile — process, not enforced by code
  > run /uscha-devloop over <machine-checkable scope>
    stop    = /goal met (fresh checker) OR budget out
    on-done = draft PR · escalate CONSTITUTION touches
    human   = merge/deploy ALWAYS with approval
security tax

Unattended loop = unattended attack surface. SAST + secret-scan + dep-audit before any PR; vet every skill's origin before installing; non-verbose logs; re-audit perms every 30d, start read-only; rotate the session before it bloats. Metric: cost per accepted change — below 50% accept, the loop is losing.

your call

Regulated/fiscal, payments, and safety-critical retail all sit on the "do NOT autonomize" side. Gated-by-design isn't a gap — it's the decision.

10Know when to skip the method entirely
  • Spike / exploration. No SPEC. Keep a note of what you learned; specify only what survives.
  • Throwaway prototype. Mark it, isolate it, give it a death date.
p0 hotfix

Fix first. But minimum evidence (what changed, how to roll back) + a retroactive SPEC/ADR are due within 24h — a process rule the team commits to, not a shipped feature. A designed escape valve keeps the method honest under pressure; an improvised one destroys it.

Set up the workbench & clone it to the teamin the kit

Claude Code + Python + git/gh + skills run the method. Stack-specific bits (build tool, database driver, linters) are the per-repo adapter in each CLAUDE.md — not the workbench.

# native installer — no Node, self-updating
curl -fsSL https://claude.ai/install.sh | bash   # mac/linux/wsl
irm https://claude.ai/install.ps1 | iex          # windows
cp -r uscha-kit/.claude/skills/* ~/.claude/skills/
scoop bucket add gentleman https://github.com/Gentleman-Programming/scoop-bucket
scoop install gentle-ai                          # author's toolchain — optional, not required by the kit
claude --version && claude doctor

Cloning to a teammate isn't one file — it's 7 layers: config, plugins, skills, agents+commands, hooks, the external CLI, and MCP servers (author's setup — not shipped in the kit; the kit itself needs only Claude Code, Python 3.8+, git, optional gh, and the skills copied to ~/.claude/skills).

  • Gotcha: settings.json hardcodes C:\Users\…. The author's bootstrap tooling tokenizes the path on export and rewrites it on import (not shipped in the kit).
  • Memory is NOT shared. engram + MEMORY.md are per-machine and build themselves. Behavior is replicated; memory is earned.
§Command reference
/uscha-discoveryidea→shape grill; proposes entities/endpoints.
/uscha-adr-refinepolish a decision's edges.
/uscha-devloopplan → coverage → build → QA loop → integration.
/uscha-sysdoctwo-view deck from the ledger — optional reporting, on request.
doctorinstallation diagnosis (flutter-doctor spirit): python/git, 9 skills, INV-GOLDEN-01 hook, config/ACCEPTANCE/ledger, per-type toolchains — exit 1 only on errors (1.22.0).
qa_ledger initbuild ledger from uscha.config.json.
snapshotcoverage/tests/LOC (--phase pre|post).
check-coverageexit 0 if ≥ threshold (phase-1 gate).
ingest-gateparse linters, normalize severity, diff IDs.
convergedlatest step of every QA tool clean + every linter clean + every persisted fact gate clean; a red measured snapshot vetoes a narrated green.
oscillationperiod-2 fingerprint repeat.
readiness0–100 KPI that reports (always exit 0); hard caps: tests red ≤35, BLOCKER/CRITICAL ≤65, unresolved escalation ≤75. Single-verdict (anti-ceremony, 1.25.0): the default is ONE screen — verdict + a collapsed --- gates: line; --verbose expands the dimensions and by-repo detail.
rebuildspec completeness: COVERS/PARTIAL/DIVERGE — exits 1 unless COVERS.
golden-diffbytes vs .approved — field truth, human-approved. Exit 0 CLEAN · 1 DIVERGE · 2 NOT-RUN (zero fixtures = NOT-RUN, never CLEAN).
log-gatepersist a fact-gate verdict (golden-diff · gate-check · pit-check · simplicity · regression); a fail blocks convergence and caps readiness ≤65.
flag-blockerlog a CONSTITUTION breach; caps readiness ≤65 and blocks convergence until --resolve (which requires --escape-analysis: which gate/test should have caught it — 1.16.0).
resolve-escalationhuman clears an escalation so readiness can rise past 75.
regression-checkFind Bugs Once (1.16.0): findings closed with no new test line = NARRATED (advises; --strict gates).
waste-checkreuse-first (1.26.0): deterministic Type-1/2 clones of the diff vs the repo — the duplication simplicity-check can't see; flags name the file:line to reuse; advisory unless --gate or defaults.waste.gate.
summaryretrospective metrics (--json is consumed by /uscha-sysdoc); includes first-time yield (FTY, 1.27.0): % of repos that cleared QA on the 1st cycle — informational, never gates.
phaseworkflow state DERIVED from the ledger (plan/build/qa/escalated/pr-ready), never self-declared; --require pr-ready gates the PR and vetoes spike/* branches (1.18.0–1.19.0).
rubric-ingestingests the rubric grader JSON (evidence-or-nothing, weighted score vs threshold); advisory by default, --gate or defaults.rubric.gate makes it blocking (1.23.0).
initledger command.
log-steprecords one QA-tool pass.
escalaterecords a human escalation.
production-findingrecords or resolves production feedback.
spec-doubtrecords or resolves a SPEC doubt.
spec-change-requestrecords or resolves a human SPEC/ADR change request.
execution-policyprints model/effort routing by phase.
dashboardemits Mirador data contract.
simplicity-checkscores diff minimality and complexity.
pit-checkscores test effectiveness from PIT.
gate-checkdetects weakened gates or secrets.
spec-checkvalidates SPEC/ACCEPTANCE structure and traceability.
tracked-md protocol

Before editing any tracked .md (CLAUDE.md, plan/delta docs, docs/adr), pull the repo's current version first. Those files carry live progress — never regenerate from scratch.

the tool executes · the methodology governs · the evidence decides · the human approves
Uscha · operator's playbook · instantiated in Claude Code via uscha-kit