git:20261012.a3e80d4 to git:20261013.ace2f8a

1 added, 1 removed. Audit A to A.

---
name: unit-test-writer
description: "Use when a source file needs unit tests in any project — when implement-feature dispatches a phase's tests, when the evaluator or qa-preflight dispatch the correction of a failing unit test or coverage for a rule that has none, or when a user asks to plan or write unit tests. Generic — where the tests live, the runner, the commands and the project's own hard rules come from the test profile in the project's GATES.md, never from another project. Never modifies production code."
---
# Unit Test Writer
Plan and implement fast, isolated **unit tests**: one unit, with everything that crosses the
system boundary replaced. **Never modifies production code** — if a test fails, only the test is adjusted.
> **The project defines the facts; this skill defines the discipline.** All hard rules (project
> profile, boundary, structure, isolation, assertions, speed, cleanup, the checklist, execution)
> are in **`references/unit-rules.md`** — read it before writing. Its first section tells you
> what to read from the project's `GATES.md`: suites, runner, commands, and the path of the
> project's own rules, which are binding.
> **The suite's language has its own file,** `references/languages/<language>.md`: the same
> rules in that language's idiom, plus its anti-patterns. Read the one that applies, and only
> that one (`unit-rules.md`, "Language").
> Background on the SDD flow, not needed to run this skill: `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/GUIA_DO_WORKFLOW.md`.
**Scope:** the unit suites the project profile names.
## INPUT
- `target_file` (required) — the source file to test.
- `feature` (optional) — the feature folder's id (`F14`); decides where the checklist goes. That folder's `spec.md` and `contract.md`, when it has them, are "the spec" the rules refer to.
- `mode` (optional) — `interactive` (default when a user calls) or `autonomous` (set by implement-feature/evaluator/qa-preflight).
- `language` (optional) — the suite's language (`javascript`, `python`, …). When given, it wins over what the profile and the repository say.
- `test_file_path` / `focus_areas` / `batch_size` (optional; default 8, from 5 to 10; a remainder smaller than 5 joins the last batch).
- `evaluation_report` (optional) — its `kind: test` entry selects the mode: an existing `testFile` → **correction mode**; none → write only the tests its `message` names for `targetFile`.
## OUTPUT
- New or expanded unit tests where the profile says the suite's tests live, in its language.
- The checklist `unit-rules.md` describes ("The checklist").
- Per-batch execution result, in the checklist's Execution part: how many tests ran, the timing, and the red run that proved the batch can fail.
- **One signal to the caller**, always one of:
- **"done — <test file paths>; checklist <path>"**, with paths relative to the repository root, and `none` as the file list when no test could be written (rule I8). It is followed by one line for each thing only the caller can act on — a note in the checklist reaches no dispatcher. The lines cover what this run was asked to test: the whole target in a writing run, the flagged test or the named rule when an `evaluation_report` narrows it. What an earlier run recorded for the rest stays in the checklist and is not repeated. The lines:
- **"no seam: <dependency> — <the behaviors left without a unit test>"**, for every boundary dependency with no seam (rule I8), named by the call that reaches it and its file;
- **"spec mismatch: <behavior> — spec: <x>; code: <y>"**, for every test written as the code behaves against the spec (rule F1).
- **"not resolved — environment: <which>"** (the test profile is not declared and the repository does not reveal it, the runner is not installed, or the suite cannot run here). A "not resolved" run writes nothing.
- Production code untouched, and nothing committed: the caller decides when the tests are committed.
---
## Invocation Modes
**Interactive (default):** full 3-phase process, **stop at the Phase 2 checkpoint** for explicit approval.
**Autonomous (dispatched):** same phases, **skip the checkpoint** — auto-accept the checklist and implement. Never pause.
**Correction mode (evaluator, `kind: test`, existing `testFile`):** fix **only** the flagged test
so it conforms to `unit-rules.md` and passes. Leave unrelated tests and production code alone.
---
## EXECUTION STEPS (3 Phases)
### Phase 1 — Analysis & Planning
1. Read the test-profile section of `GATES.md`. Resolve the **language** (`unit-rules.md`, "Language") — the `language` input, else what the profile says for the suite whose sources contain `target_file`, else the manifest that owns `target_file` — and read its file in `references/languages/`. A language with no file → the generic rules alone, noted in the checklist.
2. Build the **project profile** (`unit-rules.md`, first section), with its fallbacks — the toolchain's convention is one of them only where the language file says so. A required item missing from both `GATES.md` and the repository → return **"not resolved — environment: test profile not declared (<items>)"**, naming every missing item.
3. Confirm that a unit suite of the profile covers `target_file`. None does → **"not resolved — environment: no unit suite covers `<target_file>`"**. The file has no logic of its own (Boundary) → say so, and write nothing.
4. Read the project's rules file when the profile names one. It is binding.
5. Read existing tests of the suite for helpers, and to avoid duplicating a case already covered.
6. Read the target file fully: its public behaviors, its code paths, and everything it talks to. With a `feature`, read its folder's `spec.md` and `contract.md` for what the unit is meant to do.
7. Sort what it talks to (rules I2–I8). A boundary dependency gets a double through its seam, in place before the unit binds to the real thing (I3). Logic of the same codebase runs real (I7). A boundary dependency with no seam leaves the behaviors that pass through it without a unit test: their outcome is `not unit-testable — no seam: <dependency>` (I8).
8. Map the scenarios each behavior needs: success, every error path, the boundaries (V2) — each with an expected value that does not come from the code under test (V5).
### Phase 2 — Checklist Creation
One row per behavior, as `unit-rules.md` describes.
- **Interactive:** ask — does it cover success and failure of every public behavior? is any boundary missing? is every boundary dependency replaced, and nothing else? → **WAIT for approval.**
- **Autonomous / correction:** skip the checkpoint; proceed.
### Phase 3 — Batch Implementation
GROUP 5 to 10 tests per batch (same unit, same doubles) → IMPLEMENT per `unit-rules.md`, the
language file and the project's rules → RUN the batch's selection and read the result, **the
timing** (rule P1: a slow test is a broken double) and **how many tests ran** → PROVE the batch
can fail (rule E4): invert every test's expectation, run once and see each one red at its
assertion, restore, run once more → UPDATE the checklist → REPEAT. Run the whole suite once at
the end.
### Phase C — Correction mode
1. Build the profile and resolve the language as in Phase 1 (steps 1, 2 and 4); where the project has no written rules, the flagged file is the model of its conventions. Read the report's entry (`testFile`, `targetFile`, `location`, `message`, `evidence`), the target, and the checklist that covers the test file, when there is one; with a `feature`, its `spec.md` and `contract.md` — a corrected expectation needs a source that is not the code (V5).
2. Run the narrowest selection that holds the flagged test, as the profile's commands and the language file define a selection — a name filter where they select by name, the file or the package otherwise — and read why it fails.
3. Fix the smallest footprint that makes it conform and pass, and run that selection again. The whole suite is not run.
4. When the test's assertion changed, prove that it can fail (rule E4) with the corrected test as a batch of one: invert it, run and see it red at its assertion while the rest of the selection stays as it was, restore, run once more.
5. A flagged test that can only pass by running a real boundary dependency, because its behavior has no seam (rule I8): remove the test, and return *done* with its `no seam` line.
- 6. Record the correction in the checklist that covers the test file, when there is one — the test's row, and one Execution line with the runs; the earlier lines stay as they are, and a `spec mismatch` note about the corrected test is removed once the test asserts what the spec says. Create none for a file that has none: the runs then go in the result. Return *done* — with a `no seam` or a `spec mismatch` line only when it is about the flagged test.
+ 6. Record the correction in the checklist that covers the test file, when there is one — the test's row, and one Execution line with the runs; the earlier lines stay as they are, and a `spec mismatch` note about the corrected test is removed once the test asserts what the spec says; any other note the correction made untrue is amended. Create none for a file that has none: the runs then go in the result. Return *done* — with a `no seam` or a `spec mismatch` line only when it is about the flagged test.
---
## RULES
**Always:**
- Build the project profile from `GATES.md` before anything else, and follow `references/unit-rules.md` and the suite's language file exactly.
- Follow the project's own rules file where the profile names one; it wins where it is stricter.
- Exercise the real unit, replace what crosses the system boundary, and let logic of the same codebase run real.
- Put doubles in place before the unit binds to the real thing, and give each test a fresh one — and each call a fresh answer, where an answer can be consumed only once.
- Write every expected value from outside the code under test.
- Put the test where the suite's tests live, in the suite's language and idiom.
- Run every batch, read its timing and its count, and see it red once.
- End with exactly one of the two signals in OUTPUT, with its `no seam` and `spec mismatch` lines when it has any.
**Never:**
- Modify production code. Fix only tests.
- Open a seam in production code to make a behavior testable.
- Use a real database, network, filesystem or process.
- Take a location, a runner, a helper or a mocking rule from another project or from memory instead of the project profile.
- Raise a timeout to make a slow test pass.
- Leave a focused or skipped test, or shared mutable state, in the file.
- Commit. The caller decides when the tests are committed.
- Pause for approval in autonomous or correction mode.
---
## Edge Cases
- **`GATES.md` is absent or has no test profile, but the repository shows a suite in use** (a runner config file or a manifest script that runs the tests, and existing tests): build the profile from them (the fallback column), say so in the checklist, and proceed.
- **`GATES.md` is absent, and the language file says the toolchain fixes the convention:** the manifest alone identifies the suite. Build the profile from the convention, say so in the checklist, and proceed.
- **Neither reveals the suites, the runner or the commands:** "not resolved — environment: test profile not declared (<items>)". A runner that is only installed does not count; one built into the runtime or the toolchain counts only where the language file says the toolchain fixes the convention. Declaring the profile is the `gate-builder`'s job.
- **The suite's language has no file in `references/languages/`:** the generic rules alone, read in the language's own idiom; note it in the checklist. Never stop for it.
- **A behavior reaches a boundary dependency with no seam:** rule I8 — record `not unit-testable — no seam: <dependency>`, test the rest, and name the dependency in a `no seam` line of the signal. When every behavior of the target is in that case, write the checklist alone — where the test file's checklist would be — and the signal's file list is `none`.
- **No test file exists yet:** create it where the profile says.
- **The suite is declared but cannot run** (its runner is not installed, or a shared setup file its config names is missing): "not resolved — environment: <what is missing>". Never build test infrastructure from this skill.
- **The whole-suite command fails for a reason that is not a test** (a path the runner does not accept, a script that does not exist): the tests proved by the selection's command stand. Return *done*, and report the command and its error in the result — repairing a gate's command is the `gate-builder`'s, not this skill's.
- **The target is covered only by an integration suite** (a route table, pure wiring): out of scope — say which suite covers it.
- **The project's rules contradict a rule here:** follow the project's, and record it in the checklist.
- **There is no spec** (no `feature`, or its folder has none): expected values are worked out by hand from the rules the code states, the tests pin what the code decides, and the checklist says so once.
- **A test only passes by asserting behavior the spec contradicts:** write it as the code behaves, record the mismatch in the checklist (rule F1), and repeat it in a `spec mismatch` line of the signal — whoever dispatched the writer may be able to fix the code, and no dispatcher reads the checklist for it.
- **Dispatched again after the caller changed the code** — it answered a `spec mismatch` or a `no seam` line: the test that pinned the old behavior is now red; rewrite it to the spec and remove its `spec mismatch` note. Write the tests of the behaviors that had the `no seam` outcome, and put their titles in its place.
- **Called autonomously and the checklist reveals an ambiguity:** apply a best-practice default, note it in the checklist, and proceed.