arbiterforge/tdd
The test-first gate. Routed to by /feature (after the spec is approved), /fix, and /refactor before any implementation code is written. Six gated phases — obligation scan, red, green, obligation verify, coverage, lint. No feature code exists before Phase 1 clears; nothing reaches commit-gate until all six are green.
npx skills add https://github.com/arbiterForge/codeArbiter --skill tdd
Test-first, or it does not ship. Routed to by /feature (after spec approval), /fix, and /refactor.
Read these, or STOP and surface the gap — never guess a command or a threshold:
{{PROJECT_DIR}}/.codearbiter/CONTEXT.md — the stage: frontmatter (the maturity value) and project context.{{PROJECT_DIR}}/.codearbiter/tech-stack.md — test, coverage, and lint invocations; file layout; mock patterns.{{PROJECT_DIR}}/.codearbiter/coding-standards.md — style, structure, naming. Required for Phase 3.{{PROJECT_DIR}}/.codearbiter/specs/<slug>.md — the approved spec, when /feature produced one. It is the primary obligation source.{{PROJECT_DIR}}/.codearbiter/security-controls.md — only when the change touches a security boundary (auth, crypto, secrets, a trust boundary). Optional; absent on most changes.{{PROJECT_DIR}}/.codearbiter/code-map.md — if present, a coarse concern→path→role map to orient before writing tests and code. Absent is fine — it is read-on-demand, populated by context-creation or commit-gate heal.An obligation is one verifiable claim about the change: (a) a unique ID, (b) a source citation,
(c) a status. Status moves OPEN → MAPPED → COVERED; an obligation Phase 4 cannot tie to a passing
test is MISSING. "We should test X" is not an obligation.
Derive every obligation before any code is written, and record each as ID · source · OPEN:
security-controls.md applies: the assertion that the security-relevant boundary holds.Gate: the obligation list is complete. Auto-pass when every obligation maps one-to-one onto the
acceptance criteria of an already-approved spec (full-lane spec or small-lane mini-spec) — the user
approved that list once; do not re-ask. User review is required only for obligations derived
BEYOND the spec (Contract and Security rows): surface just those additions, not the whole list.
Under /sprint, spec-derived obligations auto-pass the same way and beyond-spec additions are
SMARTS-decided and logged like any other auto-decision. A partial list never passes either way.
Write one or more failing tests per obligation. Bind each test ID to its obligation ID and move that
obligation OPEN → MAPPED. Run the test command from tech-stack.md.
Reject the standard traps: asserting on a mock instead of behavior; a test that can never fail; a
snapshot so broad it asserts nothing; coupling to an implementation detail instead of observable
behavior; asserting on the framework's behavior rather than your own.
Gate: the runner confirms new tests red (for the right reason) and existing tests green, with every
obligation MAPPED to a failing test. No implementation code is written until this gate clears.
Write the minimum implementation that satisfies the Phase 2 tests — no speculative logic, no
gold-plating — to the conventions in coding-standards.md. Run the full suite. A broken pre-existing
test is a regression: fix it.
Gate: full suite green, reached by satisfying the Phase 2 tests — not by weakening them. A test's
assertions MUST be unchanged between red and green; only fixtures and setup may move. A relaxed
assertion is a gate violation.
Walk the Phase 1 list item by item. Each MAPPED obligation moves to COVERED (a real passing test
that exercises the claim) or MISSING (no test truly covers it). For security-relevant or
contract-critical logic, dispatch the coverage-auditor agent
({{PLUGIN_ROOT}}/agents/coverage-auditor.md) to confirm the tests exercise the claim.
A MISSING obligation returns the workflow to Phase 2 — author a correct failing test, then re-run
Phase 3 — and loops until it is COVERED.
Stakes: when you block on a MISSING obligation, state what the untested seam leaves exposed, not
just that it is MISSING — one line naming the consequence: "this error path is untested; a 500 here
would reach users silently." The block is the rule; the stakes are why it is worth the friction.
Gate: every obligation COVERED, each backed by a passing test. Any MISSING blocks Phase 5.
Coverage scales with the maturity value (stage: in CONTEXT.md) — a rigor knob, not a promotion
gate. The threshold table is the shared {{PLUGIN_ROOT}}/includes/maturity-coverage.md (the
single source of truth, also used by refactor Phase 2).
Run the coverage command from tech-stack.md. Lines and branches must both clear the threshold
— a report satisfying one and not the other does not pass (issue #507). Below it on either → add
tests until both are met.
Name the host you measured on, and for a tree tech-stack.md marks as platform-forked, the
figure is the UNION across its supported hosts — a single-host report scores the other platform's
arm as permanently uncovered (issue #521, conditions in {{PLUGIN_ROOT}}/includes/maturity-coverage.md).
Where only one host is available, say so and name what is missing rather than quoting it as the
whole.
Where the surface has no coverage tooling at all, take the no-tooling exemption in
{{PLUGIN_ROOT}}/includes/maturity-coverage.md — which requires QUOTING the tech-stack.md
Coverage section that omits a command for this surface — and pass the phase on the Phase 4
obligation verify alone. Without that citation the phase STOPs: "I could not find the command" and
"this surface has none" look identical from here and demand opposite responses. Do NOT invent a
command, and do NOT silently skip the phase as though it had been run — a gate that cannot execute
still reads as satisfied, which is worse than an absent one.
Stakes: when coverage blocks below threshold, name the class of code left dark — the paths a later
regression could rot unnoticed — not just "below threshold." The number is the rule; the untested paths
are why it matters.
Gate: threshold met on BOTH lines and branches for the current maturity value, or the no-tooling
exemption taken WITH its citation. A test added only to move the percentage fails this gate in
spirit — it converts an honest red into a green that asserts nothing.
Run lint, and the type-check if the project is statically typed, from tech-stack.md. Resolve every
error.
Gate: clean lint and type-check, zero errors — this is what clears the path to commit-gate.
"Mostly passes" is not passing.
COVERED without a passing test that exercises the claim.CONTEXT.md.tech-stack.md or STOP.{{PLUGIN_ROOT}}/includes/harvest.md) over any [NEEDS-TRIAGE] raised this run — batch-confirm promoting work to open-tasks.md and decisions to open-questions.md so nothing languishes; nothing auto-promotes interactively.Take arbiterforge/tdd from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.