git:20260701.2525c6d to git:20261012.213de19

87 added, 64 removed. Audit A to A.

---
name: unit-test-writer
- description: Plans and implements comprehensive unit tests for the PABX monorepo — Angular frontend (Jest + jest-preset-angular, .spec.ts) and Node.js backend (Jest, apps/backend/__tests__/unit/). Runs interactively (3 phases with an approval checkpoint) when called by a user, or autonomously (no pause) when dispatched by implement-feature/evaluator. Also runs in correction mode to fix a specific failing test flagged by the evaluator. Enforces PABX rules (real component imports, manual service instantiation, localStorage/jQuery mock order, Promise.all() mock correctness, cleanup, English-only). Never modifies production code.
+ 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 (PABX)
+ # Unit Test Writer
- Plan and implement fast, isolated, high-quality **unit tests** for the PABX monorepo. Validates
- logic without hitting real databases, HTTP, or filesystems. **Never modifies production code** —
- if a test fails, only the test is adjusted.
+ 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.
- > **Project scope:** this skill assumes the **PABX monorepo** structure. All the hard,
- > project-specific rules (jQuery/localStorage mock order, `Promise.all()` mock pattern, file
- > locations, execution commands) are in **`references/pabx-rules.md`** — read it before writing.
- > Generic SDD flow docs: `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/GUIA_DO_WORKFLOW.md`.
+ > **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:**
- - **Frontend:** `apps/frontend/` — Jest + jest-preset-angular, `*.spec.ts` co-located with the source.
- - **Backend:** `apps/backend/__tests__/unit/` — Jest, `*.test.js` mirroring `src/` (1:1).
+ **Scope:** the unit suites the project profile names.
## INPUT
- `target_file` (required) — the source file to test.
- - `mode` (optional) — `interactive` (default when a user calls directly) or `autonomous` (set by implement-feature/evaluator).
- - `test_file_path` (optional) — existing test file to expand; otherwise created automatically.
- - `focus_areas` / `batch_size` (optional) — prioritized methods; tests per batch (default 8, 5–10).
- - `test_catalog_path` (optional) — a Test Catalog entry to accelerate Phase 1.
- - `evaluation_report` (optional) — when present, run in **correction mode** (fix only the failing test named in the report).
+ - `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/expanded `.spec.ts` / `.test.js` unit tests following the PABX rules, in the correct location.
- - A `.unit-test.md` checklist (planning may be Portuguese; **all test code is English**).
- - Per-batch execution confirmation (tests run in milliseconds) and a final coverage summary.
- - No production code is ever modified.
+ - 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:
+ - **"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 (user-invoked, default):** run the full 3-phase process and **stop at the Phase 2
- checkpoint** to get explicit user approval before implementing.
+ **Interactive (default):** full 3-phase process, **stop at the Phase 2 checkpoint** for explicit approval.
- **Autonomous (dispatched by implement-feature / evaluator):** run the same 3 phases but **skip
- the approval checkpoint** — auto-accept the checklist and proceed to batch implementation. Never
- pause for a human. This mirrors spec-writer's Batch Mode.
+ **Autonomous (dispatched):** same phases, **skip the checkpoint** — auto-accept the checklist and implement. Never pause.
- **Correction mode (dispatched by evaluator on a `kind:test` failure):** an `evaluation_report`
- is provided. Read the failing test's `testFile`/`location`/`message`, and **fix only that test**
- (smallest footprint) so it conforms to the PABX rules and passes. Do not rewrite unrelated tests
- and do not touch production code. Return control to the evaluator.
+ **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. If `test_catalog_path` given, read the catalog entry (priority, gaps, methods, mocks, pattern) and validate it against the actual source.
- 2. **Backend:** study existing `apps/backend/__tests__/unit/` for patterns. **Frontend:** IGNORE legacy Karma/Jasmine; write fresh Jest tests.
- 3. Read the target file fully; map functionalities, dependencies, code paths.
- 4. Identify every external dependency to mock (HttpClient, DB, services, localStorage, jQuery, Router, ActivatedRoute).
- 5. Map scenarios: success, error handling, edge cases, boundary conditions.
+ 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
- Create a `.unit-test.md` checklist organized by method/function, using the minimum categories
- for the target type (component / service / backend util / controller / model — see
- `references/pabx-rules.md`).
-
- - **Interactive:** present the checklist and ask: does it guarantee max coverage of success+failure? missing edge cases? all deps identified? → **WAIT for explicit approval.**
- - **Autonomous / correction:** skip the checkpoint; treat the checklist as approved and proceed.
+ 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 (only after approval / autonomously)
- 1. GROUP similar tests into batches of 5–10 (same method/scenario/mocks).
- 2. IMPLEMENT the batch in one operation, applying the PABX rules (`references/pabx-rules.md`).
- 3. EXECUTE the batch — confirm milliseconds; a test > 1s means a broken mock.
- 4. UPDATE the checklist; REPEAT until complete.
+ ### 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.
- In **correction mode**, Phases 1–2 collapse to reading the report + the failing test; implement
- only the corrected test(s) and run them.
+ ### Phase C — Correction mode
+ 1. Build the profile and resolve the language as in Phase 1 (steps 1, 2 and 4). Read the report's `testFile`, `targetFile`, `message`, `evidence`, and the target; 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 the profile's command offers that holds the flagged test — the test alone where the runner selects by name, its file or 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 where the test file already has a checklist — its row, and one Execution line with the runs. Create none for a file that has none: the runs then go in the result. Return *done*.
---
## RULES
**Always:**
- - Follow `references/pabx-rules.md` exactly (component/service/model/controller patterns, mock order, cleanup, performance).
- - Write ALL test content in English; keep tests independent, deterministic, fast.
- - Import the REAL component; instantiate services manually; mock localStorage/jQuery before imports.
- - Use the correct `Promise.all()` mock pattern (new builder per call, `.then` via `Promise.resolve().then.bind`).
- - Place files per the location standard (frontend co-located; backend 1:1 mirror).
- - In correction mode, fix only the flagged test (smallest footprint) and return to the evaluator.
+ - 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 (components, services, utils, controllers, models). Fix only tests.
- - Use real DB/HTTP/filesystem; `TestBed.inject()`/`HttpClientTestingModule` for services; inline test components.
- - Write anything in Portuguese in test files.
- - Pause for approval in autonomous/correction mode.
- - Leave shared mutable state between tests or create slow tests.
+ - 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
- - **Test file missing:** create it in the correct location.
- - **Component uses jQuery:** global `$` mock BEFORE all imports (else CRITICAL failure).
- - **Model uses `Promise.all()`:** apply the dedicated mock pattern or tests hang for minutes.
- - **Stack not PABX (no `apps/` layout):** this skill is PABX-specific; report that the caller should use a generic approach instead.
- - **Called autonomously but the checklist reveals a blocking ambiguity:** apply a best-practice default, note it, and proceed (do not pause).
+ - **`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.