spec·loop 1 / ?
for the team · extended edition · ~35 minutes

Everyone uses the agent
in their own way. And it works…
until you touch someone else’s code.

We all use Claude Code, and we are all productive. The problem does not appear while you are coding — it appears when you pick up someone else’s work. This is a story we have already lived through, the method that prevents it, and what using that method feels like on an ordinary day.

Uscha · spec-driven, tool-agnostic methodology · ← → to navigate

01 the current situation

Five devs, five different methods.

Each person configured Claude Code using their best judgment. And each person’s judgment is sound — but it remains their own.

The context

It lives in each session’s chat: what was decided, why, and what was left out. The chat gets compacted, deleted, lost.

The setup

A different CLAUDE.md (or none), memory prompts, different skills. The same request produces different results depending on who handles it.

The evidence

“The agent said it’s ready and the tests pass.” Which tests? Do they assert what matters? Nobody measures it — we trust the narrative.

None of this hurts today. It hurts three weeks later, when someone else inherits the work.

02 a simulated story — but one you have seen before

Friday: Vale ships coupons.

Vale builds the coupon feature with Claude Code as usual: straight into the code, iterating in the chat. It works well. Along the way, Sales sends one detail over Slack: a coupon that expired less than 24 hours ago must still be accepted (a grace period). She explains it to the agent in the chat, the agent implements it, tests go green, merge.

  claude — Vale’s session · Friday 17:40
> Sales says: if the coupon expired less than 24h ago, accept it anyway (grace period)
Understood — I’ll add the 24-hour grace window to the expiration validation…
✓ 14 tests passed · “done, implemented and tested”
— merge. The reason for the “strange bug” stayed here, in this chat. And nobody else will ever read it.

Where was the grace-period rule documented? In a Slack message that will scroll into oblivion and a Claude Code chat that gets compacted. The code contains an uncommented expiredAt + 24h. No SPEC and no test explicitly asserts the rule.

03 three weeks later

Martín inherits coupons.
And a mystery.

He needs to add category-specific expiration rules. He opens the code and finds + 24h. He asks HIS Claude Code — with a different setup, a different CLAUDE.md, and different memory — why it is there.

  claude — Martín’s session · different setup, none of Vale’s context
> why does the expiration validation add 24h? it looks like a bug
I agree — it looks like an off-by-one error in the date handling. Expiration should be
strict. I’ll fix it and normalize the timezone handling…
✗ business rule removed ✓ 14 tests passed · no test asserted the grace period

Martín’s agent is not lying: it is guessing with confidence — without Vale’s context, the grace period IS indistinguishable from a bug. The tests pass because they never pinned down that rule. The following Monday, Sales reports that “coupons are broken” at the worst possible time.

04 the honest postmortem

Nobody did anything wrong.
And everything went wrong.

failure 1

The “why” lived in the chat

The decision (a 24-hour grace period) never made it into a versioned file. A code comment? It explains, but it does not protect: nothing tests it, it cannot stop a merge, and the agent that “normalizes” the code deletes it along with the line.

failure 2

Different setups, different results

Martín’s agent does not behave like Vale’s: a different CLAUDE.md, different skills, different memory. The behavior was not reproducible.

failure 3

“Done” was a narrative

“Green tests” sounded like evidence, but nobody measured whether the tests asserted what mattered. The critical rule did not have a single test.

failure 4

Nothing stopped the second agent

What about code review? The reviewer had the same context as Martín: none. The diff looked like a legitimate off-by-one fix with green tests — and it was approved for the same reason the agent proposed it. Context was missing, not diligence.

The problem is not the tool or the developer. Knowledge lived in places that disappear — and no shared method required it to live in the repo.
05 the method

Uscha: you bring the idea,
the method builds the rest.

A spec-driven methodology that starts before the spec, advances only with captured evidence, and stops at a human-approved merge. We all run it the same way, over the same files, with the same Claude Code skills.

CONSTITUTION — invariants no station may violate idea01 discovery02 ready03 SPEC / ADR04 build05 verify06 evidence07 human gate08 inner loop · converge; do not chase zero outer loop · proposal — the merge still belongs to the human
station evidence gate human-owned inner loop
06 the working rhythm

What a day with the method feels like.

It is not a process separate from the work: it is the same day, with three new moments — let yourself be questioned before, let the loop measure during, and read the real state at the end.

10:00idea 10:05 · ~30-40 min/uscha-discovery grills youcontradictions · edge cases 10:45SPEC + ADR into the repocommitted, not left in chat rest of the day/uscha-devloop buildsgates inline · log-gate at closeoutPR + readinessmeasured state, not a feeling human merges
illustrative timing from the coupon story — discovery for a medium-sized feature takes half an hour, not a sprint

Friction is placed where it is cheap (half an hour of questions at 10:00) to avoid it where it is expensive (Sales reporting broken coupons on Monday).

07 the same story, with the method — act 1

Replay: Vale starts with /uscha-discovery.

Before touching code comes the interrogation. The skill does not agree with everything — it keeps asking until the shape is clear: contradictions, ambiguous terms, failure modes. One question at a time.

  claude — /uscha-discovery · Friday 10:00
discovery> What exactly happens to a coupon that expired an hour ago? A hard
rejection, or is there a business tolerance?
> good question… Sales wants a 24h grace period. I’ll confirm and get back to you
discovery> Confirmed, then: I’ll write it into the SPEC as an explicit rule with its
rationale, and as a testable acceptance criterion in ACCEPTANCE.md.

The question that nobody asked in the original story is asked here by the method — and the answer does not die in a chat.

An accommodating interviewer inherits your blind spots and hands them back formatted as a plan. Friction is the deliverable.operating rule for discovery
08 what remains when the conversation ends

The conversation crystallizes
into files in the repo.

As decisions settle, they are written down — not at the end, not in the chat. The build reads files; CI reads files; the next developer reads files.

SPEC.md

Observable behavior, I/O, errors, the grace-period rule and its rationale, rollback.

docs/adr/*.md

One durable decision per file: what was chosen, over which alternative, and why. Code links to it with // ADR: <slug>.

ACCEPTANCE.md

Criteria as testable checkboxes — “coupon expired less than 24h ago → accepted”. This feeds readiness.

CONTEXT.md

The domain glossary: it eliminates ambiguous language before it reaches the code.

CONSTITUTION.md

What is NEVER acceptable, regardless of which trade-off wins. A violation is recorded as a BLOCKER (flag-blocker) and stops everything until a human decides.

RISKS · HANDOFF

Residual risk and what to read before coding — a letter to the next developer, written while the context is fresh.

Truth lives in versioned files. The chat is where we think; the repo is where we remember.
09 the same story, with the method — act 2

Vale builds with /uscha-devloop:
evidence, not narrative.

The orchestrator takes the SPEC + ACCEPTANCE, builds, and runs a QA loop that measures instead of believing. BLOCKING results come from real artifacts parsed by qa_ledger.py — tests, coverage, linters, fact gates. What the agent reports about itself is recorded separately as narrative — and a measured red always vetoes a narrated green.

measured

Ledger

Coverage, tests, and linter findings parsed from the actual XML. Self-reported results are recorded separately: narrative, not data.

measured

Fact gates

gate-check (does the change weaken tests or thresholds?) and the other gates are persisted with log-gate: one red result blocks convergence.

human

Merge gate

The loop opens the PR, shows readiness (a 0–100 KPI) with its breakdown, and stops. A person merges it. Always.

The grace-period rule now has a criterion in ACCEPTANCE.md and a test that explicitly asserts it. It is no longer a mysterious + 24h: it is specified, pinned-down behavior.

10 what you see at the end — the KPI

Readiness: the measured state,
not the feeling that “it seems done.”

  command output (abridged) — figures from the coupon example
$ python3 qa_ledger.py readiness
READINESS: 87.0/100 — RELEASE CANDIDATE
--- dimensions (weight | raw | contribution) ---
  adr           30 | 0.90 |  27.0  ← 9/10 ACCEPTANCE.md criteria
  coverage      25 | 1.00 |  25.0  ← jacoco.xml, not the agent’s word
  static_gate   20 | 1.00 |  20.0  ← linters ran and clean (if never run: 0.0)
  convergence   15 | 1.00 |  15.0  ← all tools + fact gates clean
  integration   10 | 0.00 |   0.0  ← contract tests pending: VISIBLE, not hidden
--- churn (not readiness): max cycle 2, regressions 0
NOT READY <50 IN PROGRESS 50–79 RC 80–94 ≥95 87.0 cap 35: red tests cap 65: open BLOCKER/CRITICAL cap 75: unresolved escalation
actual engine bands and caps — a cap does not erase completed work; it limits the score until the underlying fact is resolved

The key point: the score is not negotiable. You may have completed 90 points’ worth of work — if one test is red, the cap is 35.

11 why you can trust the number

The ledger has two tiers.

MEASURED — can block snapshots (surefire/jacoco XML) · ingest-gate (linters) · log-gate (golden-diff, gate-check, pit-check, simplicity) NARRATED — advisory only log-step: what the agent says about its own pass (findings reported/fixed) — a record for the retrospective measured red VETOES
qa_ledger’s two-tier contract — narrative is retained; measured evidence decides

This answers failure 3 in the story: “done” was a narrative. In the method, narrative is retained — but it never decides. Parsed artifacts decide: the Surefire XML, the JaCoCo report, the diff structure, the golden-file bytes.

A tool that never ran is never imagined green. Absent = no evidence, never “OK.”
12 the same story, with the method — act 3

The handoff: Martín inherits
files, not mysteries.

Three weeks later, Martín picks up coupons. His agent — using the same shared setup and the same skills — reads the same files the method produced: SPEC, ADRs, ACCEPTANCE.

  claude — Martín’s session · replay
> why does the expiration validation add 24h? it looks like a bug
It is not a bug: SPEC.md §expiration documents the 24-hour grace period requested by
Sales (2026-06-12), and an acceptance criterion pins it down.
If you want to change it, that is a SPEC amendment — not a silent fix.
✓ the rule survives · and if an agent deleted its test, gate-check would mark it BLOCKER

Two layers of defense: the intent is written where the agent reads it (versioned files), and even if an agent tried to “fix” the rule, the test asserting it would turn red and gate-check would catch any attempt to delete or disable it — a BLOCKER that convergence cannot pass (weakened assertions are flagged for review; --strict gates them).

13 not everything works on the first try

In the worked example, 6 of 10 steps
involve failure or refinement.

A worked example of the method — a minimal feature, applyDiscount(monto, %), taken from start to finish. The methodology does not promise you will never deviate: it promises every deviation will have a gate that catches it.

1discovery 2⚠ vague spec→ testable 3⚠ no out-of-scope→ added 4rounding ADR 5build 6⚠ coverage liedpit-check catches it 7⚠ threshold loweredgate-check BLOCKER 8⚠ implicit case→ SPEC amendment 9⚠ OVERBUILTsimplicity: trim it 10ship advance refine the SPEC (advisory or structural) a gate stops you → fix it and continue ship

Look at step 7: the agent lowered a threshold to pass — and the gate that caught it exists because agents do exactly that. The method does not assume good behavior: it verifies it.

14 the rule that organizes everything

Facts block.
Conjecture only advises.

A gate that reads a FACT (bytes, XML, diff structure) can stop the work. A gate that guesses from prose can only whisper — because a gate with false positives gets disabled, and a dead gate is worse than none.

GateReads
gate-checkdiff structure: tests deleted/disabled, thresholds lowered or removedBLOCKS
golden-diffbytes vs .approved (migrations — the only artifact the agent cannot write: a PreToolUse hook blocks it)BLOCKS
simplicity-checkdiff budget: size, nesting, hunks (OVERBUILT = trim it)BLOCKS
structural spec-checkis there an out-of-scope section? are there acceptance criteria? (present or absent)BLOCKS
prose spec-checkheuristic: vague criteria with no metricADVISES
readiness0–100 state KPI (with hard caps: red tests ≤35, open BLOCKER/CRITICAL ≤65)REPORTS

And the rule that closes the loop on Vale and Martín’s story: measured evidence beats narrative — a red test snapshot vetoes any “green tests” claim from any agent.

15 before spending rigor on a change

Rigor scales with
what the change can break.

The opening question for every change: what can this break, and what would that failure cost? The answer determines the size of the process — we do not drag the entire machine behind a configuration tweak.

ProfileExampleMinimum gates
A · Lowminor UI / configbuild + relevant test — nothing more
B · Normallocal feature / bug fixtests + static analysis + review
C · Criticalpayments / securityunit + integration + security + rollback
D · Multi-systempublic API / eventscontract tests + versioning
E · Legacyrefactor / migrationgolden + regression protection

process heuristic — not automated

WE apply the table when classifying the change (there is no --profile flag that selects gates automatically). The coupon feature was B; if it touched payment collection, it would be C — and discovery asks that question.

16 honest adoption

What changes in your day
(and what does NOT).

changes — 4 habits
  • Before coding anything non-trivial: /uscha-discovery (new idea) or /uscha-adr-refine (known feature). Let yourself be questioned.
  • Decisions go into the repo immediately: SPEC, ADR, ACCEPTANCE. Not at the end, not in the chat.
  • The loop runs and persists QA: /uscha-devloop measures with the ledger; fact gates are recorded with log-gate.
  • The PR stops at the merge. A human reads the diff and merges — never the agent.
does NOT change
  • Trivial changes (one-liner, config): build + test, and that is it. The method does NOT trail behind a one-line fix.
  • Spikes and prototypes: no SPEC. Record what you learn; specify only what survives.
  • P0 hotfix: fix it first. The retrospective SPEC/ADR is due within 24 hours — a designed escape hatch, not an improvised one.
  • How you think. The method does not code for you: it forces you to say what you want BEFORE and prove what happened AFTER.
17 the special case that hurts us most

Legacy and migrations:
the golden is the anchor.

When you touch old code that nobody fully understands, the risk is not breaking what you know it does — it is losing behavior nobody knew was intentional (someone else’s five-year-old “24h grace period”).

1 · capture

/uscha-characterize runs the ORIGINAL code against a real corpus and emits .received fixtures — observed behavior, not inferred behavior. AND STOPS.

2 · human approval

A human reviews and establishes them as .approved. The agent cannot write them: a PreToolUse hook blocks the write. It is the only artifact beyond the agent’s reach — and that is why it exists.

3 · migrate with a safety net

Every pass runs golden-diff: byte-for-byte against the approved files. DIVERGE = stop. No fixtures = NOT-RUN, never “passed.”

If the agent authored the golden, it would encode the same partial understanding that lost the logic — and approve against its own mistake.
18 what you are thinking

The three objections
(and the honest answers).

“it adds ceremony”

Rigor scales with the blast radius. A UI fix runs build+test and nothing more. The full machine is for changes that can break expensive things — and there, ceremony costs less than the coupon incident.

“it will slow me down”

It slows you down for 30 minutes at the start — where friction is cheap — to spare you the expensive failure: a confidently wrong build. Martín lost MORE debugging on Monday than Vale spent on the entire discovery.

“my way works”

It works in isolation. It breaks at handoff — and nobody here works alone. The method does not replace your judgment: it makes that judgment legible to whoever comes next. You, three months from now, are also “whoever comes next.”

Behavior is reproduced; memory is earned. The shared setup solves the first — files in the repo solve the second.
19 a concrete start

How we start this week.

  setup — once per machine, 5 minutes
# 1. the kit skills into everyone's Claude Code (or --target codex / both)
npx --yes @andresmassello/uscha@latest install --target claude
# 2. the repo becomes methodology-ready in one step: protocol,
# invariants, config, protected golden (.gitattributes + hook) and statusline
npx --yes @andresmassello/uscha@latest init
# 3. verify
npx --yes @andresmassello/uscha@latest doctor --target claude
20 we apply the method to itself

The pilot is also measured
with facts, not feelings.

Adopting the method “because it feels better” would be exactly the flaw the method fights. Define criteria BEFORE running it, make them measurable at closeout — and allow them to say NO:

criterion 1 · the handoff test

At the end, another team member extends the feature by reading ONLY the files in the repo. If they need to ask the author, the method failed its central promise — record that as a finding; do not excuse it.

criterion 2 · gates that did work

Did any gate stop a real problem that would have slipped through without the method (a lowered threshold, a weakened test, a vague criterion)? If no gate catches anything throughout the pilot, the machinery did not earn its cost — that is data too.

criterion 3 · unproductive friction

Record every false positive and every step that got in the way without adding value. A method that receives only praise is not being tested. Use this evidence to adjust budgets and thresholds.

criterion 4 · final state

PR readiness with its breakdown (and which cap limited it, if any) + churn reported separately. Not to reward the number: to see whether the KPI tells the true story of the work’s state.

if the pilot fails the criteria, adjust the method or discard it — that is also the method working.

21 closing

It is not about coding differently.
It is about helping the team remember.

Everyone keeps using Claude Code — with all its power. What changes is where truth lives: in versioned files that any agent and any person can read, with evidence measured instead of narrated, and with a human owning every merge.

The tool executes · the method governs · evidence decides · the human approves.

Uscha · instantiated in Claude Code via uscha-kit 1.50.0 · questions → now