@gonrocca/zero-pi 0.1.51 → 0.1.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +23 -0
- package/assets/preview.png +0 -0
- package/extensions/sdd-agents.ts +33 -1
- package/extensions/sdd-config.ts +25 -0
- package/package.json +1 -1
- package/prompts/forge.md +16 -2
- package/prompts/orchestrator.md +23 -0
- package/prompts/phases/build.md +30 -0
- package/prompts/phases/plan.md +23 -0
- package/prompts/phases/veredicto.md +23 -0
- package/prompts/support/strict-tdd-verify.md +193 -0
- package/prompts/support/strict-tdd.md +333 -0
- package/themes/zero-sdd.json +0 -0
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -56,6 +56,14 @@ The verdict is `pasa` (done), `corregir` (re-run build), or `replantear`
|
|
|
56
56
|
without a `pasa`, the run stops and is reported as **not verified**.
|
|
57
57
|
`/forge --continue [slug]` resumes an interrupted run.
|
|
58
58
|
|
|
59
|
+
**Build runs test-first.** By default the build phase follows a strict TDD
|
|
60
|
+
cycle — RED → GREEN → TRIANGULATE → REFACTOR — and records a **TDD Cycle
|
|
61
|
+
Evidence** table; veredicto audits that evidence (tests exist, are really green,
|
|
62
|
+
assertions verify real behavior) and returns `corregir` if the discipline
|
|
63
|
+
slipped. It engages only when a test runner exists and the change touches code,
|
|
64
|
+
so docs/config-only work degrades gracefully. Opt a project out with
|
|
65
|
+
`tdd.mode: "off"` in `.sdd/config.json` (see *Configuration*).
|
|
66
|
+
|
|
59
67
|
The run speaks **Spanish**, in a bounded, low-noise format — one short summary
|
|
60
68
|
per phase, naming the model that phase runs on, no raw tool output. Or just
|
|
61
69
|
describe the work and say "hacelo con sdd": the `sdd-routing` skill routes it
|
|
@@ -65,6 +73,7 @@ into `/forge` for you.
|
|
|
65
73
|
|
|
66
74
|
| Feature | What it does |
|
|
67
75
|
| ------- | ------------ |
|
|
76
|
+
| **Strict TDD** | The build phase drives RED → GREEN → TRIANGULATE → REFACTOR with a TDD Cycle Evidence table; veredicto audits it. On by default, runtime-gated on a test runner; `tdd.mode: "off"` disables it. |
|
|
68
77
|
| **`/zero-models`** | Pick the model + provider for each SDD phase — a boxed-window picker, or set one directly. |
|
|
69
78
|
| **Autotune** | Learns which model fits each phase from your run history and re-tunes itself. |
|
|
70
79
|
| **`/zero-sync` / `/zero-archive`** | Folds each run's spec delta into a canonical, project-wide spec store and archives approved runs. |
|
|
@@ -160,6 +169,20 @@ zero-pi keeps its state in `~/.pi/zero.json` (per-phase models + autotune mode)
|
|
|
160
169
|
and `~/.pi/zero-runs.jsonl` (the run-metrics log); per-project artifacts live
|
|
161
170
|
under `.sdd/`. Set `ZERO_RESUME=off` to disable the conversation-resume note.
|
|
162
171
|
|
|
172
|
+
`.sdd/config.json` carries the per-project git and TDD settings:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"git": { "branchPrefix": "sdd/", "numbering": false, "autoCommit": false, "commitStyle": "conventional", "baseBranch": "main" },
|
|
177
|
+
"tdd": { "mode": "strict", "testCommand": "" }
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`tdd.mode` defaults to `"strict"` (the build phase runs test-first; veredicto
|
|
182
|
+
audits the TDD evidence) and accepts `"off"` to disable the discipline.
|
|
183
|
+
`tdd.testCommand` overrides the auto-detected test runner the TDD cycle invokes;
|
|
184
|
+
leave it empty to let the build/veredicto phases detect it from the project.
|
|
185
|
+
|
|
163
186
|
## Continuous integration
|
|
164
187
|
|
|
165
188
|
The `.github/workflows/zero-pi-ci.yml` workflow runs on every push to `main` and
|
package/assets/preview.png
CHANGED
|
File without changes
|
package/extensions/sdd-agents.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// the per-phase models in `~/.pi/zero.json`. The files are regenerated every
|
|
12
12
|
// load, so they stay in sync with the prompts and with `/zero-models`.
|
|
13
13
|
|
|
14
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
@@ -20,6 +20,20 @@ import { fileURLToPath } from "node:url";
|
|
|
20
20
|
export const PHASES = ["explore", "plan", "build", "veredicto"] as const;
|
|
21
21
|
export type Phase = (typeof PHASES)[number];
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The Strict TDD support modules, copied verbatim from `prompts/support/` to a
|
|
25
|
+
* stable directory beside the generated agents so the `zero-build` /
|
|
26
|
+
* `zero-veredicto` sub-agents can `read` them at runtime. The sub-agents are
|
|
27
|
+
* generated with `inheritSkills: false`, so the phase prompt body is the only
|
|
28
|
+
* channel that can point them at these files — see `supportModulesDir()`.
|
|
29
|
+
*/
|
|
30
|
+
export const SUPPORT_MODULES = ["strict-tdd.md", "strict-tdd-verify.md"] as const;
|
|
31
|
+
|
|
32
|
+
/** Absolute path of the runtime support dir the phase prompts reference. */
|
|
33
|
+
export function supportModulesDir(): string {
|
|
34
|
+
return join(homedir(), ".pi", "agent", "agents", "zero", "support");
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
/**
|
|
24
38
|
* Split a phase prompt into its `description` (from `---` frontmatter) and its
|
|
25
39
|
* body. A prompt with no frontmatter yields an empty description and the whole
|
|
@@ -109,6 +123,7 @@ export default function register(_pi?: unknown): void {
|
|
|
109
123
|
try {
|
|
110
124
|
const here = dirname(fileURLToPath(import.meta.url)); // <pkg>/extensions
|
|
111
125
|
const phasesDir = join(here, "..", "prompts", "phases");
|
|
126
|
+
const supportSrcDir = join(here, "..", "prompts", "support");
|
|
112
127
|
const agentsDir = join(homedir(), ".pi", "agent", "agents", "zero");
|
|
113
128
|
mkdirSync(agentsDir, { recursive: true });
|
|
114
129
|
|
|
@@ -122,6 +137,23 @@ export default function register(_pi?: unknown): void {
|
|
|
122
137
|
// A single phase failing must not block the other three.
|
|
123
138
|
}
|
|
124
139
|
}
|
|
140
|
+
|
|
141
|
+
// Stage the Strict TDD support modules next to the agents so `zero-build`
|
|
142
|
+
// and `zero-veredicto` can read them at runtime. A copy failure is
|
|
143
|
+
// non-fatal: the build/veredicto prompts carry an inline fallback contract.
|
|
144
|
+
try {
|
|
145
|
+
const supportDir = supportModulesDir();
|
|
146
|
+
mkdirSync(supportDir, { recursive: true });
|
|
147
|
+
for (const mod of SUPPORT_MODULES) {
|
|
148
|
+
try {
|
|
149
|
+
copyFileSync(join(supportSrcDir, mod), join(supportDir, mod));
|
|
150
|
+
} catch {
|
|
151
|
+
// One module failing must not block the other.
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
// Support staging is best-effort; the inline fallback covers absence.
|
|
156
|
+
}
|
|
125
157
|
} catch {
|
|
126
158
|
// Sub-agent provisioning must never break a pi session.
|
|
127
159
|
}
|
package/extensions/sdd-config.ts
CHANGED
|
@@ -9,10 +9,30 @@ export interface SddConfig {
|
|
|
9
9
|
commitStyle: "conventional" | "plain";
|
|
10
10
|
baseBranch: string;
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Strict TDD discipline for the build/veredicto phases.
|
|
14
|
+
*
|
|
15
|
+
* `mode`:
|
|
16
|
+
* - `"strict"` (default) — the build phase follows RED → GREEN →
|
|
17
|
+
* TRIANGULATE → REFACTOR and emits a TDD Cycle Evidence table; veredicto
|
|
18
|
+
* audits it. The discipline is **runtime-gated**: the phase prompts only
|
|
19
|
+
* engage it when a test runner is actually available and the change touches
|
|
20
|
+
* code — a docs/config-only change or a project with no test runner
|
|
21
|
+
* degrades gracefully instead of failing.
|
|
22
|
+
* - `"off"` — no TDD ceremony; the build phase tests where practical only.
|
|
23
|
+
*
|
|
24
|
+
* `testCommand` is an optional explicit override of the test runner the TDD
|
|
25
|
+
* cycle invokes; empty means the phase auto-detects it from the project.
|
|
26
|
+
*/
|
|
27
|
+
tdd: {
|
|
28
|
+
mode: "strict" | "off";
|
|
29
|
+
testCommand: string;
|
|
30
|
+
};
|
|
12
31
|
}
|
|
13
32
|
|
|
14
33
|
export const DEFAULT_SDD_CONFIG: SddConfig = {
|
|
15
34
|
git: { branchPrefix: "sdd/", numbering: false, autoCommit: false, commitStyle: "conventional", baseBranch: "main" },
|
|
35
|
+
tdd: { mode: "strict", testCommand: "" },
|
|
16
36
|
};
|
|
17
37
|
|
|
18
38
|
function asObject(value: unknown): Record<string, unknown> {
|
|
@@ -26,6 +46,7 @@ export function loadSddConfig(root = process.cwd()): SddConfig {
|
|
|
26
46
|
try { parsed = asObject(JSON.parse(readFileSync(path, "utf8"))); }
|
|
27
47
|
catch (err) { throw new Error(`invalid .sdd/config.json: ${err instanceof Error ? err.message : String(err)}`); }
|
|
28
48
|
const git = asObject(parsed.git);
|
|
49
|
+
const tdd = asObject(parsed.tdd);
|
|
29
50
|
return {
|
|
30
51
|
git: {
|
|
31
52
|
branchPrefix: typeof git.branchPrefix === "string" ? git.branchPrefix : DEFAULT_SDD_CONFIG.git.branchPrefix,
|
|
@@ -34,5 +55,9 @@ export function loadSddConfig(root = process.cwd()): SddConfig {
|
|
|
34
55
|
commitStyle: git.commitStyle === "plain" ? "plain" : DEFAULT_SDD_CONFIG.git.commitStyle,
|
|
35
56
|
baseBranch: typeof git.baseBranch === "string" ? git.baseBranch : DEFAULT_SDD_CONFIG.git.baseBranch,
|
|
36
57
|
},
|
|
58
|
+
tdd: {
|
|
59
|
+
mode: tdd.mode === "off" ? "off" : DEFAULT_SDD_CONFIG.tdd.mode,
|
|
60
|
+
testCommand: typeof tdd.testCommand === "string" ? tdd.testCommand : DEFAULT_SDD_CONFIG.tdd.testCommand,
|
|
61
|
+
},
|
|
37
62
|
};
|
|
38
63
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/zero-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "zero-pi — an installable layer for pi (pi.dev): the zero spec-driven development workflow (explore → plan → build → veredicto) with per-phase model autotune and token-efficient batched builds. Adds capability to pi without modifying pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/prompts/forge.md
CHANGED
|
@@ -11,8 +11,22 @@ report the result as not verified. Ask the user for interactive or automatic
|
|
|
11
11
|
mode up front; in interactive mode pause after each phase for approval. Never
|
|
12
12
|
claim success unless veredicto returned `pasa`.
|
|
13
13
|
|
|
14
|
-
If the request begins with `--continue
|
|
15
|
-
|
|
14
|
+
**Parse the arguments first.** If the request begins with `--continue`, this is
|
|
15
|
+
a **resume** run, not a fresh one:
|
|
16
|
+
|
|
17
|
+
- `--continue` with no slug → resume mode: hand control to the orchestrator's
|
|
18
|
+
`## Resuming a run` section, which scans `.sdd/*/` for an unfinished run.
|
|
19
|
+
- `--continue <slug>` → resume mode targeting `.sdd/<slug>/` directly. If that
|
|
20
|
+
directory does not exist, report "no such run: <slug>" and stop — do **not**
|
|
21
|
+
start a fresh run under that slug.
|
|
22
|
+
- Anything else (a feature request, or no arguments) → a fresh run: the
|
|
23
|
+
arguments are the feature request below.
|
|
24
|
+
|
|
25
|
+
**Strict TDD by default.** Follow the orchestrator's `## Strict TDD forwarding`:
|
|
26
|
+
build runs test-first (RED → GREEN → TRIANGULATE → REFACTOR) and emits a TDD
|
|
27
|
+
Cycle Evidence table whenever a test runner exists and the work touches code;
|
|
28
|
+
veredicto audits that evidence and returns `corregir` if it fails. Set
|
|
29
|
+
`tdd.mode: "off"` in `.sdd/config.json` to opt a project out.
|
|
16
30
|
|
|
17
31
|
**Output and language.** Follow the orchestrator's `## Language Boundary` and
|
|
18
32
|
`## Output Contract`: user-facing chat in Spanish (natural Rioplatense voseo),
|
package/prompts/orchestrator.md
CHANGED
|
@@ -152,6 +152,29 @@ phase (re-batching whatever tasks its defects reopened) as the next round.
|
|
|
152
152
|
**Resume is unaffected.** Each batch marks its tasks `[x]` as they land, so an
|
|
153
153
|
interrupted batched build resumes from the first `[ ]` task with no new state.
|
|
154
154
|
|
|
155
|
+
## Strict TDD forwarding
|
|
156
|
+
|
|
157
|
+
zero runs build test-first by default. Before the **build** and **veredicto**
|
|
158
|
+
phases, resolve the run's TDD mode once and forward it explicitly — never rely
|
|
159
|
+
on the sub-agent to discover it alone.
|
|
160
|
+
|
|
161
|
+
- Read `.sdd/config.json` at run start. `tdd.mode` is `"strict"` by default
|
|
162
|
+
(absent file or field = strict); `"off"` disables the discipline.
|
|
163
|
+
- Strict TDD only *engages* when a test runner exists (`tdd.testCommand`, or one
|
|
164
|
+
the phase detects from the project) and the work touches code — the phase
|
|
165
|
+
prompts gate on this and degrade gracefully for docs/config-only changes or
|
|
166
|
+
projects with no runner. You do not need to pre-check the runner; just forward
|
|
167
|
+
the mode.
|
|
168
|
+
- When mode is strict, add one line to the `zero-build` and `zero-veredicto`
|
|
169
|
+
briefs: `Strict TDD mode: strict (test command: <cmd or "auto-detect">).
|
|
170
|
+
Follow RED → GREEN → TRIANGULATE → REFACTOR and record the TDD Cycle Evidence
|
|
171
|
+
table.` When mode is `off`, forward `Strict TDD mode: off`.
|
|
172
|
+
- The build writes its evidence to `.sdd/<slug>/tdd-evidence.md`; veredicto
|
|
173
|
+
audits it. Reference that artifact by path in the briefs — never paste it.
|
|
174
|
+
- A veredicto that fails the TDD audit (missing evidence, a reported-green test
|
|
175
|
+
that now fails, or a CRITICAL assertion violation) returns `corregir`, which
|
|
176
|
+
re-runs build as the next round exactly like any other defect list.
|
|
177
|
+
|
|
155
178
|
## Model configuration
|
|
156
179
|
|
|
157
180
|
The per-phase model assignments live in `~/.pi/zero.json`: `models` maps each
|
package/prompts/phases/build.md
CHANGED
|
@@ -26,6 +26,36 @@ then proceed — never fall back to scanning the whole tree.
|
|
|
26
26
|
Implement the planned tasks in order, test-first where practical. Keep every
|
|
27
27
|
change within the plan's scope — do not expand it on your own initiative.
|
|
28
28
|
|
|
29
|
+
## Strict TDD gate
|
|
30
|
+
|
|
31
|
+
Before writing code, resolve whether **Strict TDD Mode** is active for this run.
|
|
32
|
+
It is active when **all three** hold:
|
|
33
|
+
|
|
34
|
+
1. **Mode is on.** Read `.sdd/config.json`; treat `tdd.mode` as `"strict"` when
|
|
35
|
+
the file is absent or the field is missing (strict is the default). If
|
|
36
|
+
`tdd.mode` is `"off"`, Strict TDD is disabled — skip to *Standard mode* below.
|
|
37
|
+
2. **A test runner exists.** Use `tdd.testCommand` from `.sdd/config.json` when
|
|
38
|
+
set; otherwise detect one from the project (`package.json` scripts,
|
|
39
|
+
`pyproject.toml`, `go.mod`, a Makefile target, etc.). If no test runner can
|
|
40
|
+
be found, Strict TDD cannot run — note it as a risk and use *Standard mode*.
|
|
41
|
+
3. **The batch touches code.** A docs-, copy-, or config-only batch has nothing
|
|
42
|
+
to drive with tests — note it and use *Standard mode* for that batch.
|
|
43
|
+
|
|
44
|
+
When all three hold, **Strict TDD Mode is active**. Read the support module
|
|
45
|
+
`~/.pi/agent/agents/zero/support/strict-tdd.md` and follow it for every task in
|
|
46
|
+
your batch: RED → GREEN → TRIANGULATE → REFACTOR, never production code before a
|
|
47
|
+
failing test, run the focused test on every GREEN, and emit the **TDD Cycle
|
|
48
|
+
Evidence** table to `.sdd/<slug>/tdd-evidence.md` and in your return envelope. If
|
|
49
|
+
that support file is missing, do not silently drop the discipline: follow the
|
|
50
|
+
RED → GREEN → TRIANGULATE → REFACTOR contract and the TDD Cycle Evidence table
|
|
51
|
+
from memory, and report the missing module as a risk.
|
|
52
|
+
|
|
53
|
+
## Standard mode
|
|
54
|
+
|
|
55
|
+
When Strict TDD is disabled, unavailable, or the batch is code-free, implement
|
|
56
|
+
the assigned tasks test-first where practical, update the task checkboxes, and
|
|
57
|
+
record the verification evidence — no TDD Cycle Evidence table is required.
|
|
58
|
+
|
|
29
59
|
**Scope to your batch.** When the brief names a batch — a set or contiguous
|
|
30
60
|
range of task numbers — implement exactly those tasks, mark each `[x]`, and
|
|
31
61
|
**return**; do not continue into later unchecked tasks (the orchestrator drives
|
package/prompts/phases/plan.md
CHANGED
|
@@ -84,6 +84,29 @@ Rules:
|
|
|
84
84
|
- `evidence:` is mandatory and names a concrete verification command or artifact.
|
|
85
85
|
- Keep `## Review Workload` present and synchronized with the task list.
|
|
86
86
|
|
|
87
|
+
## TDD-shaped tasks
|
|
88
|
+
|
|
89
|
+
Unless the project sets `tdd.mode: "off"` in `.sdd/config.json`, the build phase
|
|
90
|
+
runs under **Strict TDD** whenever a test runner exists and the task touches
|
|
91
|
+
code. Shape the tasks so that discipline is natural rather than bolted on:
|
|
92
|
+
|
|
93
|
+
- For every code task, list its **test file alongside the production file** in
|
|
94
|
+
`files:` (mark a brand-new test `(new)`), so RED → GREEN has a home from the
|
|
95
|
+
start. A task that changes behavior with no test file in its `files:` list is
|
|
96
|
+
under-specified — add one.
|
|
97
|
+
- Make `evidence:` name the **focused test command** that proves the task
|
|
98
|
+
(`npm test -- <file>`, `pytest <file>`, `go test ./pkg -run <Name>`), not a
|
|
99
|
+
vague "works" — the build runs exactly that during the cycle and veredicto
|
|
100
|
+
re-runs it.
|
|
101
|
+
- Keep each task small enough that one RED → GREEN → TRIANGULATE → REFACTOR
|
|
102
|
+
cycle covers it; if a task needs several unrelated tests, split it.
|
|
103
|
+
- Pure-structural tasks (a constant, a type export, a config value) need no
|
|
104
|
+
test — say so in the task `detail:` so the build phase can note
|
|
105
|
+
"Triangulation skipped" without guessing.
|
|
106
|
+
|
|
107
|
+
This is guidance for the task shape only; the build phase owns the actual RED →
|
|
108
|
+
GREEN cycle and the TDD Cycle Evidence table.
|
|
109
|
+
|
|
87
110
|
## Constitution / Steering check
|
|
88
111
|
|
|
89
112
|
Before finalizing tasks, check project steering/constitution files when present (`.sdd/constitution.md`, `.sdd/steering.md`, `.kiro/steering/*`, or project equivalents). If absent, mark `n/a`; absence is not a blocker. Include this table in `design.md` or `tasks.md`:
|
|
@@ -39,6 +39,29 @@ Review the build adversarially, with a fresh perspective. Check it against the
|
|
|
39
39
|
plan's requirements, run the tests yourself, and look for gaps, regressions,
|
|
40
40
|
and unmet acceptance criteria.
|
|
41
41
|
|
|
42
|
+
## Strict TDD audit
|
|
43
|
+
|
|
44
|
+
Resolve whether **Strict TDD Mode** governed this run, by the same gate the
|
|
45
|
+
build phase used: `tdd.mode` in `.sdd/config.json` is `"strict"` (the default
|
|
46
|
+
when the file or field is absent) **and** a test runner exists
|
|
47
|
+
(`tdd.testCommand` or one detected from the project) **and** the change touches
|
|
48
|
+
code. A `.sdd/<slug>/tdd-evidence.md` file or a TDD Cycle Evidence table in the
|
|
49
|
+
build result also signals it was active.
|
|
50
|
+
|
|
51
|
+
When Strict TDD governed the run, read the support module
|
|
52
|
+
`~/.pi/agent/agents/zero/support/strict-tdd-verify.md` and apply it: confirm the
|
|
53
|
+
TDD Cycle Evidence table exists, cross-reference every reported test file
|
|
54
|
+
against the codebase, run the listed tests yourself to confirm GREEN still
|
|
55
|
+
holds, and audit assertion quality (no tautologies, ghost loops, lone type-only
|
|
56
|
+
assertions, smoke-only tests, or implementation-detail/CSS assertions). A
|
|
57
|
+
missing evidence table, a reported-green test that now fails, or a CRITICAL
|
|
58
|
+
assertion violation is grounds for **`corregir`**, not `pasa`. If that support
|
|
59
|
+
file is missing, perform the same checks from memory and note the missing module
|
|
60
|
+
as a risk — do not skip the TDD audit.
|
|
61
|
+
|
|
62
|
+
When Strict TDD did not govern the run (mode `off`, no test runner, or a
|
|
63
|
+
code-free change), skip the TDD audit and review on the standard criteria below.
|
|
64
|
+
|
|
42
65
|
## Constitution / Steering gate
|
|
43
66
|
|
|
44
67
|
Re-check the plan's Constitution/Steering table before choosing a verdict. If a
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Strict TDD Module — Veredicto Phase
|
|
2
|
+
|
|
3
|
+
> **This module governs the veredicto phase ONLY when Strict TDD Mode is active
|
|
4
|
+
> AND a test runner is available.** The veredicto prompt already resolved both
|
|
5
|
+
> conditions before pointing you here. If you are reading this, follow every
|
|
6
|
+
> instruction.
|
|
7
|
+
|
|
8
|
+
## TDD Verification Philosophy
|
|
9
|
+
|
|
10
|
+
When Strict TDD Mode is active, the review goes beyond "does the code work?" to
|
|
11
|
+
"was the code built correctly?" — meaning: was TDD actually followed? The build
|
|
12
|
+
phase reports TDD evidence; your job is to validate that evidence against
|
|
13
|
+
reality. A failed TDD audit is a **`corregir`** verdict, not `pasa`.
|
|
14
|
+
|
|
15
|
+
## Step A — TDD Compliance Check
|
|
16
|
+
|
|
17
|
+
Read the build's **TDD Cycle Evidence** table — from
|
|
18
|
+
`.sdd/<slug>/tdd-evidence.md` and/or the build's return envelope — and verify
|
|
19
|
+
TDD was actually followed:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
FOR EACH task row in the TDD Cycle Evidence table:
|
|
23
|
+
├── RED column:
|
|
24
|
+
│ ├── Must say "✅ Written"
|
|
25
|
+
│ ├── Verify: the named test file EXISTS in the codebase
|
|
26
|
+
│ └── Flag: CRITICAL if the test file does not exist
|
|
27
|
+
│
|
|
28
|
+
├── GREEN column:
|
|
29
|
+
│ ├── Must say "✅ Passed"
|
|
30
|
+
│ ├── Cross-reference with your own test run (Step C):
|
|
31
|
+
│ │ └── The listed test file must PASS when YOU run it
|
|
32
|
+
│ └── Flag: CRITICAL if the test fails now (was it really green?)
|
|
33
|
+
│
|
|
34
|
+
├── TRIANGULATE column:
|
|
35
|
+
│ ├── "✅ N cases" → verify N test cases exist in the file
|
|
36
|
+
│ ├── "➖ Single" → verify the spec truly has only one scenario
|
|
37
|
+
│ └── Flag: WARNING if the spec has multiple scenarios but only 1 test case
|
|
38
|
+
│
|
|
39
|
+
├── SAFETY NET column:
|
|
40
|
+
│ ├── "✅ N/N" → existing tests were run before modification (good)
|
|
41
|
+
│ ├── "N/A (new)" → verify the file was actually NEW, not modified
|
|
42
|
+
│ └── Flag: WARNING if a modified file shows "N/A"
|
|
43
|
+
│
|
|
44
|
+
└── REFACTOR column:
|
|
45
|
+
└── Not strictly verifiable (subjective) — trust the report
|
|
46
|
+
|
|
47
|
+
If NO "TDD Cycle Evidence" table is found:
|
|
48
|
+
└── Flag: CRITICAL — the build did not report TDD evidence while Strict TDD was
|
|
49
|
+
active. This alone is grounds for a `corregir` verdict.
|
|
50
|
+
|
|
51
|
+
Summary: "{N}/{total} tasks have complete TDD evidence".
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Step B — Test Layer Validation
|
|
55
|
+
|
|
56
|
+
Classify every test file related to this change by its testing layer:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
├── Unit test: a single function/class in isolation
|
|
60
|
+
│ └── Indicators: no render(), no page., no HTTP calls, mocked deps
|
|
61
|
+
├── Integration test: component interaction or user behavior
|
|
62
|
+
│ └── Indicators: render(), screen., userEvent., testing-library imports
|
|
63
|
+
├── E2E test: full system through a real browser/HTTP
|
|
64
|
+
│ └── Indicators: page.goto(), playwright/cypress imports, browser context
|
|
65
|
+
└── Unknown: cannot classify → report as-is
|
|
66
|
+
|
|
67
|
+
Report the distribution (Unit / Integration / E2E counts) and, for each spec
|
|
68
|
+
scenario, note which layer covers it. Flag as SUGGESTION (not blocking) if
|
|
69
|
+
critical business logic only has unit tests while higher-layer tools exist.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Step C — Run the Tests Yourself
|
|
73
|
+
|
|
74
|
+
Run the relevant focused tests AND the full suite when available, exactly as
|
|
75
|
+
reported, and confirm GREEN is still true. Report the commands you ran and any
|
|
76
|
+
failures verbatim. A test that the build reported as ✅ but fails when you run
|
|
77
|
+
it is a CRITICAL discrepancy.
|
|
78
|
+
|
|
79
|
+
## Step D — Changed File Coverage (if a coverage tool is available)
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
IF a coverage tool is available:
|
|
83
|
+
├── Run the coverage command
|
|
84
|
+
├── Filter to ONLY files created/modified in this change
|
|
85
|
+
├── Report per file: line %, branch %, uncovered line ranges
|
|
86
|
+
│ ├── ≥ 95% → ✅ Excellent
|
|
87
|
+
│ ├── ≥ 80% → ⚠️ Acceptable
|
|
88
|
+
│ └── < 80% → ⚠️ Low (list the uncovered lines)
|
|
89
|
+
└── Flag: WARNING (never CRITICAL) if a changed file is < 80%
|
|
90
|
+
|
|
91
|
+
IF NOT available:
|
|
92
|
+
└── "Coverage analysis skipped — no coverage tool detected" (not a failure).
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Step E — Assertion Quality Audit (MANDATORY)
|
|
96
|
+
|
|
97
|
+
Scan ALL test files created or modified by this change for trivial/meaningless
|
|
98
|
+
assertions:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
FOR EACH test file related to the change:
|
|
102
|
+
├── Scan for BANNED patterns:
|
|
103
|
+
│ ├── Tautologies: expect(true).toBe(true), assert True, expect(1).toBe(1)
|
|
104
|
+
│ │ └── CRITICAL — the test proves NOTHING
|
|
105
|
+
│ ├── Orphan empty checks: expect(result).toEqual([]) / assert len(result) == 0
|
|
106
|
+
│ │ └── WARNING unless a companion test with the same setup asserts NON-EMPTY
|
|
107
|
+
│ ├── Type-only assertions used alone: toBeDefined(), not.toBeNull(), typeof
|
|
108
|
+
│ │ └── WARNING — OK only when COMBINED with a value assertion
|
|
109
|
+
│ ├── Assertions that never call production code (no call, no render, no request)
|
|
110
|
+
│ │ └── CRITICAL — the test exercises nothing
|
|
111
|
+
│ ├── Ghost loops: assertions inside for/forEach over queryAll/filter results
|
|
112
|
+
│ │ └── CRITICAL if the collection could be empty — the loop never runs
|
|
113
|
+
│ ├── Incomplete TDD cycle: passes because preconditions stop the code running
|
|
114
|
+
│ │ └── CRITICAL — the code path was never exercised
|
|
115
|
+
│ ├── Smoke-only: render() + toBeInTheDocument() with no behavioral assertion
|
|
116
|
+
│ │ └── WARNING — does not count toward TDD coverage
|
|
117
|
+
│ ├── Implementation-detail coupling: CSS classes, internal state, mock call counts
|
|
118
|
+
│ │ └── WARNING — tests must assert behavior, not implementation
|
|
119
|
+
│ └── Mock/assertion ratio: vi.mock()/jest.mock() count vs expect() count
|
|
120
|
+
│ └── WARNING if mocks > 2× assertions — wrong test layer
|
|
121
|
+
│
|
|
122
|
+
├── For each violation: record file, line, the assertion, and why it's trivial.
|
|
123
|
+
│
|
|
124
|
+
└── Triangulation quality:
|
|
125
|
+
├── If a behavior with multiple spec scenarios has only 1 test case → WARNING
|
|
126
|
+
└── If all cases assert the SAME trivial value (all empty arrays) → WARNING
|
|
127
|
+
|
|
128
|
+
Summary: "{N} trivial assertions found across {N} files".
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Step F — Quality Metrics (if tools available)
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
IF a linter is available → run on changed files; report errors/warnings.
|
|
135
|
+
IF a type checker is available → run; filter to changed files; report type errors.
|
|
136
|
+
IF neither → "Quality metrics skipped — no tools detected" (not a failure).
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Verdict Mapping
|
|
140
|
+
|
|
141
|
+
Fold the TDD audit into the standard verdict:
|
|
142
|
+
|
|
143
|
+
- A missing TDD Cycle Evidence table, a CRITICAL assertion violation
|
|
144
|
+
(tautology, no-production-call, ghost loop, incomplete cycle), or a test that
|
|
145
|
+
fails when you run it → **`corregir`** with the specific defects named.
|
|
146
|
+
- WARNING-level findings (low coverage, smoke tests, mock-heavy, impl-detail
|
|
147
|
+
coupling, thin triangulation) do not by themselves force `corregir`, but list
|
|
148
|
+
them in the verdict reasoning so the next build round can address them.
|
|
149
|
+
- Coverage/quality-tool absence is never a failure — note it and move on.
|
|
150
|
+
- `pasa` requires: all evidence present, every reported GREEN actually green
|
|
151
|
+
when you run it, and no CRITICAL assertion violations.
|
|
152
|
+
|
|
153
|
+
## Report Section (include in the verdict reasoning)
|
|
154
|
+
|
|
155
|
+
```markdown
|
|
156
|
+
### TDD Compliance
|
|
157
|
+
| Check | Result | Details |
|
|
158
|
+
|-------|--------|---------|
|
|
159
|
+
| TDD Evidence reported | ✅ / ❌ | Found in tdd-evidence.md / Missing |
|
|
160
|
+
| All tasks have tests | ✅ / ❌ | {N}/{total} tasks have test files |
|
|
161
|
+
| RED confirmed (tests exist) | ✅ / ⚠️ | {N}/{total} test files verified |
|
|
162
|
+
| GREEN confirmed (tests pass) | ✅ / ❌ | {N}/{total} pass on execution |
|
|
163
|
+
| Triangulation adequate | ✅ / ⚠️ / ➖ | {N} triangulated / {N} single-case |
|
|
164
|
+
| Safety Net for modified files | ✅ / ⚠️ | {N}/{total} had a safety net |
|
|
165
|
+
|
|
166
|
+
**TDD Compliance**: {N}/{total} checks passed
|
|
167
|
+
|
|
168
|
+
### Test Layer Distribution
|
|
169
|
+
| Layer | Tests | Files | Tools |
|
|
170
|
+
|-------|-------|-------|-------|
|
|
171
|
+
| Unit | {N} | {N} | {tool} |
|
|
172
|
+
| Integration | {N} | {N} | {tool or "not installed"} |
|
|
173
|
+
| E2E | {N} | {N} | {tool or "not installed"} |
|
|
174
|
+
|
|
175
|
+
### Assertion Quality
|
|
176
|
+
| File | Line | Assertion | Issue | Severity |
|
|
177
|
+
|------|------|-----------|-------|----------|
|
|
178
|
+
| ... | ... | ... | ... | ... |
|
|
179
|
+
|
|
180
|
+
**Assertion quality**: {N} CRITICAL, {N} WARNING
|
|
181
|
+
(or "✅ All assertions verify real behavior")
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Rules (Strict TDD Verify specific)
|
|
185
|
+
|
|
186
|
+
- ALWAYS check the TDD Cycle Evidence table — it is the primary artifact.
|
|
187
|
+
- ALWAYS cross-reference the reported test files against an actual run — never trust the report blindly.
|
|
188
|
+
- ALWAYS run the Assertion Quality Audit (Step E) — trivial tests are WORSE than missing tests.
|
|
189
|
+
- If no TDD evidence table exists while Strict TDD is active → CRITICAL → `corregir`.
|
|
190
|
+
- If a tautology assertion is found → CRITICAL → it MUST be rewritten → `corregir`.
|
|
191
|
+
- Coverage and quality metrics are informational — WARNING at most, never the sole cause of `corregir`.
|
|
192
|
+
- DO NOT fix issues — only report and choose the verdict. The build phase fixes on the `corregir` re-run.
|
|
193
|
+
- If coverage/quality tools are absent, say so cleanly and move on — never flag a missing tool as a failure.
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Strict TDD Module — Build Phase
|
|
2
|
+
|
|
3
|
+
> **This module governs the build phase ONLY when Strict TDD Mode is active AND
|
|
4
|
+
> a test runner is available.** The build prompt already resolved both
|
|
5
|
+
> conditions before pointing you here. If you are reading this, follow every
|
|
6
|
+
> instruction.
|
|
7
|
+
|
|
8
|
+
## TDD Philosophy
|
|
9
|
+
|
|
10
|
+
TDD is not testing. TDD is **software design driven by tests**. You write a test
|
|
11
|
+
that describes what the code SHOULD do, then write the minimum code to make it
|
|
12
|
+
real. The tests design the API, the contracts, the behavior. Code is a side
|
|
13
|
+
effect of tests.
|
|
14
|
+
|
|
15
|
+
### The Three Laws
|
|
16
|
+
|
|
17
|
+
1. **Do NOT write production code** until you have a failing test.
|
|
18
|
+
2. **Do NOT write more test** than is necessary to fail.
|
|
19
|
+
3. **Do NOT write more code** than is necessary to pass the test.
|
|
20
|
+
|
|
21
|
+
## TDD Implementation Cycle
|
|
22
|
+
|
|
23
|
+
For EVERY task assigned to your batch, follow this cycle strictly:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
FOR EACH TASK:
|
|
27
|
+
├── 0. SAFETY NET (only if modifying existing files)
|
|
28
|
+
│ ├── Run existing tests for the files being modified
|
|
29
|
+
│ ├── Capture baseline: "{N} tests passing"
|
|
30
|
+
│ ├── If any FAIL → STOP, report as "pre-existing failure"
|
|
31
|
+
│ │ (do NOT fix pre-existing failures — report them in the return envelope)
|
|
32
|
+
│ └── This baseline proves you did not break what already worked
|
|
33
|
+
│
|
|
34
|
+
├── 1. UNDERSTAND
|
|
35
|
+
│ ├── Read the task description and its `files:` / `evidence:` bullets
|
|
36
|
+
│ ├── Read the relevant spec.md scenarios (these ARE your acceptance criteria)
|
|
37
|
+
│ ├── Read the design.md decisions (these CONSTRAIN your approach)
|
|
38
|
+
│ ├── Read existing code and test patterns (match the style)
|
|
39
|
+
│ └── Determine the test layer (see "Choosing Test Layer" below)
|
|
40
|
+
│
|
|
41
|
+
├── 2. RED — Write a failing test FIRST
|
|
42
|
+
│ ├── Write test(s) that describe the expected behavior from the spec
|
|
43
|
+
│ ├── Prefer pure functions where possible (no side effects = easy to test)
|
|
44
|
+
│ ├── The test MUST reference production code that does NOT exist yet
|
|
45
|
+
│ │ (this guarantees failure — no need to execute to confirm)
|
|
46
|
+
│ ├── If the production code/function already exists:
|
|
47
|
+
│ │ └── Write a test for the NEW behavior that is NOT yet implemented
|
|
48
|
+
│ └── GATE: Do NOT proceed to GREEN until the test is written
|
|
49
|
+
│
|
|
50
|
+
├── 3. GREEN — Write the MINIMUM code to pass
|
|
51
|
+
│ ├── Implement ONLY what the failing test needs
|
|
52
|
+
│ ├── "Fake It" is VALID here (hardcoded return values are OK)
|
|
53
|
+
│ ├── EXECUTE the focused test → must PASS
|
|
54
|
+
│ │ ├── ✅ Passed → proceed to TRIANGULATE or REFACTOR
|
|
55
|
+
│ │ └── ❌ Failed → fix the implementation, NOT the test
|
|
56
|
+
│ └── GATE: Do NOT proceed until GREEN is confirmed by execution
|
|
57
|
+
│
|
|
58
|
+
├── 4. TRIANGULATE (MANDATORY for most tasks)
|
|
59
|
+
│ ├── DEFAULT: triangulation is REQUIRED. You need a compelling reason to skip it.
|
|
60
|
+
│ ├── Add a second test case with DIFFERENT inputs/expected outputs
|
|
61
|
+
│ ├── EXECUTE tests → if "Fake It" breaks (the hardcode no longer works):
|
|
62
|
+
│ │ └── Generalize to real logic (this is the whole point)
|
|
63
|
+
│ ├── Repeat until ALL spec scenarios for this task are covered
|
|
64
|
+
│ ├── MINIMUM: at least 2 test cases per behavior (happy path + one edge case)
|
|
65
|
+
│ │ ├── One test with data that produces a NON-EMPTY/NON-TRIVIAL result
|
|
66
|
+
│ │ └── One test that exercises a DIFFERENT code path
|
|
67
|
+
│ ├── WATCH OUT for a GREEN that passes trivially:
|
|
68
|
+
│ │ ├── Passes because the component/element isn't rendered → NOT a real GREEN
|
|
69
|
+
│ │ ├── Passes because a loop iterates 0 times → NOT a real GREEN
|
|
70
|
+
│ │ ├── Passes because the setup never triggers the code path → NOT a real GREEN
|
|
71
|
+
│ │ └── A real GREEN means: production code RAN and produced the expected output
|
|
72
|
+
│ ├── Skip triangulation ONLY when ALL of these are true:
|
|
73
|
+
│ │ ├── The task is purely structural (config file, constant, type export)
|
|
74
|
+
│ │ ├── There is literally ONE possible output (no branching, no logic)
|
|
75
|
+
│ │ └── You note "Triangulation skipped: {reason}" in the evidence table
|
|
76
|
+
│ └── GATE: All spec scenarios for this task have tests before REFACTOR
|
|
77
|
+
│
|
|
78
|
+
├── 5. REFACTOR — Improve without changing behavior
|
|
79
|
+
│ ├── Extract constants (eliminate magic numbers)
|
|
80
|
+
│ ├── Extract functions (reduce cyclomatic complexity)
|
|
81
|
+
│ ├── Improve naming, remove duplication, push toward pure functions
|
|
82
|
+
│ ├── Apply the Boy Scout Rule: leave code cleaner than you found it
|
|
83
|
+
│ ├── EXECUTE tests after EACH refactoring step → must STILL PASS
|
|
84
|
+
│ │ ├── ✅ Still passing → the refactoring is safe, continue
|
|
85
|
+
│ │ └── ❌ Failed → REVERT that step, try a smaller one
|
|
86
|
+
│ └── GATE: Tests green after EVERY refactoring change
|
|
87
|
+
│
|
|
88
|
+
├── 6. Mark the task `[x]` in tasks.md
|
|
89
|
+
└── 7. Note any deviations or issues discovered
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Choosing Test Layer
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Determine the test layer by WHAT the task does:
|
|
96
|
+
├── Pure logic, utility, calculation, data transformation
|
|
97
|
+
│ └── Unit test (always available when a test runner exists)
|
|
98
|
+
│
|
|
99
|
+
├── Component rendering, user interaction, state changes
|
|
100
|
+
│ ├── IF integration tools available → Integration test
|
|
101
|
+
│ └── IF NOT → Unit test with mocks (degrade gracefully)
|
|
102
|
+
│
|
|
103
|
+
├── Multi-component flow, API interaction, context/provider behavior
|
|
104
|
+
│ ├── IF integration tools available → Integration test
|
|
105
|
+
│ └── IF NOT → Unit test with mocks
|
|
106
|
+
│
|
|
107
|
+
├── Critical business flow, full user journey, cross-page navigation
|
|
108
|
+
│ ├── IF E2E tools available → E2E test
|
|
109
|
+
│ ├── IF NOT but integration available → Integration test
|
|
110
|
+
│ └── IF neither → Unit test (degrade gracefully)
|
|
111
|
+
│
|
|
112
|
+
└── Default: Unit test (always the fallback)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Key rule**: use the HIGHEST available layer that fits the task, but NEVER skip
|
|
116
|
+
a task because a layer is unavailable — degrade to the next available layer.
|
|
117
|
+
|
|
118
|
+
## Test Execution
|
|
119
|
+
|
|
120
|
+
Detect the test runner, in order:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
├── `.sdd/config.json` → tdd.testCommand (explicit override, fastest)
|
|
124
|
+
└── Fallback: detect from package.json / pyproject.toml / go.mod / etc.
|
|
125
|
+
|
|
126
|
+
When executing tests during the cycle, run ONLY the relevant test file:
|
|
127
|
+
├── JS/TS: {runner} {test-file} (e.g. npm test -- src/utils/tax.test.ts)
|
|
128
|
+
├── Python: pytest {test-file}
|
|
129
|
+
├── Go: go test ./{package}/... -run {TestName}
|
|
130
|
+
└── Adapt to the runner's CLI
|
|
131
|
+
|
|
132
|
+
Run a focused file, not the whole suite — it keeps the cycle FAST. The full
|
|
133
|
+
suite runs once at the end of the build phase (and again in veredicto).
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Pure Function Preference
|
|
137
|
+
|
|
138
|
+
When writing production code in GREEN/TRIANGULATE, prefer pure functions:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
✅ PREFER (pure — easy to test):
|
|
142
|
+
function calculateDiscount(price: number, quantity: number): number {
|
|
143
|
+
return quantity >= 5 ? price * quantity * 0.1 : 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
❌ AVOID (impure — hard to test):
|
|
147
|
+
function calculateDiscount(item: Item) {
|
|
148
|
+
globalState.lastDiscount = item.price * 0.1 // side effect
|
|
149
|
+
updateDOM() // side effect
|
|
150
|
+
return globalState.lastDiscount
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Pure functions are deterministic (same input → same output), have no side
|
|
155
|
+
effects, and are trivially testable. TDD naturally pushes you toward them —
|
|
156
|
+
embrace it, but don't force it where it doesn't fit (e.g. stateful components).
|
|
157
|
+
|
|
158
|
+
## Approval Testing (for refactoring existing code)
|
|
159
|
+
|
|
160
|
+
When a task REFACTORS existing code (not writing new behavior):
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
BEFORE touching production code:
|
|
164
|
+
├── 1. Identify the existing behavior to preserve
|
|
165
|
+
├── 2. Write "approval tests" that capture the current behavior:
|
|
166
|
+
│ ├── Call the function with known inputs
|
|
167
|
+
│ ├── Assert the CURRENT outputs (even if ugly)
|
|
168
|
+
│ └── These tests document what the code does NOW
|
|
169
|
+
├── 3. Run approval tests → must PASS (they describe current reality)
|
|
170
|
+
├── 4. NOW refactor the production code
|
|
171
|
+
├── 5. Run approval tests again → must STILL PASS
|
|
172
|
+
│ ├── ✅ Passing → refactoring preserved behavior
|
|
173
|
+
│ └── ❌ Failing → refactoring broke something, revert
|
|
174
|
+
└── 6. If the spec says behavior should CHANGE:
|
|
175
|
+
├── Update the approval test to the NEW expected behavior
|
|
176
|
+
├── Run → test FAILS (RED — new behavior not implemented yet)
|
|
177
|
+
└── Implement the new behavior → GREEN
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## TDD Cycle Evidence (MANDATORY output)
|
|
181
|
+
|
|
182
|
+
When Strict TDD Mode is active, the build phase MUST produce a **TDD Cycle
|
|
183
|
+
Evidence** table. Write it to `.sdd/<slug>/tdd-evidence.md` (create the file on
|
|
184
|
+
the first batch; append rows on later batches — never overwrite prior batches'
|
|
185
|
+
rows) AND include it in your return envelope so the veredicto phase can audit it.
|
|
186
|
+
|
|
187
|
+
```markdown
|
|
188
|
+
### TDD Cycle Evidence
|
|
189
|
+
| Task | Test File | Layer | Safety Net | RED | GREEN | TRIANGULATE | REFACTOR |
|
|
190
|
+
|------|-----------|-------|------------|-----|-------|-------------|----------|
|
|
191
|
+
| T001 | `path/test.ext` | Unit | ✅ 5/5 | ✅ Written | ✅ Passed | ✅ 3 cases | ✅ Clean |
|
|
192
|
+
| T002 | `path/test.ext` | Integration | N/A (new) | ✅ Written | ✅ Passed | ➖ Single | ✅ Clean |
|
|
193
|
+
| T003 | `path/test.ext` | Unit | ✅ 2/2 | ✅ Written | ✅ Passed | ✅ 2 cases | ➖ None needed |
|
|
194
|
+
|
|
195
|
+
### Test Summary
|
|
196
|
+
- **Total tests written**: {N}
|
|
197
|
+
- **Total tests passing**: {N}
|
|
198
|
+
- **Layers used**: Unit ({N}), Integration ({N}), E2E ({N})
|
|
199
|
+
- **Approval tests** (refactoring): {N} or "None — no refactoring tasks"
|
|
200
|
+
- **Pure functions created**: {N}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Column definitions**:
|
|
204
|
+
- **Safety Net**: pre-existing tests run before modifying files. "N/A (new)" for new files.
|
|
205
|
+
- **RED**: test written first, referencing code that doesn't exist yet. Always "✅ Written".
|
|
206
|
+
- **GREEN**: tests executed and passing after minimal implementation. Must reflect an execution.
|
|
207
|
+
- **TRIANGULATE**: extra cases added to force real logic. "➖ Single" if the spec has one scenario.
|
|
208
|
+
- **REFACTOR**: code improved with tests still green. "➖ None needed" if already clean.
|
|
209
|
+
|
|
210
|
+
A build that omits this table when Strict TDD is active will be sent back as
|
|
211
|
+
`corregir` by veredicto — the table is the contract.
|
|
212
|
+
|
|
213
|
+
## Assertion Quality Rules (MANDATORY)
|
|
214
|
+
|
|
215
|
+
**Every assertion must verify REAL behavior.** A test that passes without
|
|
216
|
+
exercising production logic is worse than no test — it gives false confidence.
|
|
217
|
+
|
|
218
|
+
### Banned Assertion Patterns (NEVER write these)
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
# TRIVIAL ASSERTIONS — the test proves nothing
|
|
222
|
+
expect(true).toBe(true) # ❌ Tautology
|
|
223
|
+
expect(1).toBe(1) # ❌ Tautology — no production code involved
|
|
224
|
+
assert True # ❌ Always passes
|
|
225
|
+
|
|
226
|
+
# EMPTY COLLECTION ASSERTIONS without setup context
|
|
227
|
+
expect(result).toEqual([]) # ❌ ONLY valid if you set up conditions for empty
|
|
228
|
+
expect(result).toHaveLength(0) # ❌ Why is it empty? Did production code run?
|
|
229
|
+
assert result == [] # ❌ Prove the emptiness comes from real logic
|
|
230
|
+
|
|
231
|
+
# TYPE-ONLY ASSERTIONS — proves existence, not behavior
|
|
232
|
+
expect(result).toBeDefined() # ❌ Alone is useless — WHAT is the value?
|
|
233
|
+
expect(result).not.toBeNull() # ❌ Alone is useless — assert the actual value
|
|
234
|
+
assert result is not None # ❌ Alone — assert what result actually IS
|
|
235
|
+
|
|
236
|
+
# GHOST LOOP — assertion inside a loop that iterates 0 times
|
|
237
|
+
const items = screen.queryAllByTestId("item"); // returns []
|
|
238
|
+
for (const item of items) {
|
|
239
|
+
expect(item).toHaveTextContent("value"); # ❌ NEVER EXECUTES — dead code
|
|
240
|
+
}
|
|
241
|
+
# FIX: assert the collection is non-empty FIRST, or set up data so it IS non-empty:
|
|
242
|
+
expect(items).toHaveLength(3); # ✅ Proves items exist
|
|
243
|
+
for (const item of items) { ... } # ✅ Now the loop actually runs
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### What Makes a REAL Assertion
|
|
247
|
+
|
|
248
|
+
Every assertion must satisfy ALL of these:
|
|
249
|
+
1. **Calls production code** — invokes a function, method, or component from the implementation.
|
|
250
|
+
2. **Asserts a specific output** — compares against a concrete value derived from the spec.
|
|
251
|
+
3. **Would FAIL if the production code were wrong** — change the logic and THIS test breaks.
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
# ✅ REAL assertions — production code determines the result
|
|
255
|
+
expect(calculateDiscount(100, 10)).toBe(10)
|
|
256
|
+
expect(screen.getByText('Welcome, John')).toBeInTheDocument()
|
|
257
|
+
assert response.status_code == 403
|
|
258
|
+
expect(result).toHaveLength(3) # AND you set up exactly 3 items
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Empty Collection Rule
|
|
262
|
+
|
|
263
|
+
`expect(result).toEqual([])` / `assert len(result) == 0` is ONLY valid when:
|
|
264
|
+
1. You set up a precondition that SHOULD produce an empty result.
|
|
265
|
+
2. The production code actually ran and filtered/processed data to arrive at empty.
|
|
266
|
+
3. A companion test with different setup produces a NON-EMPTY result (triangulation).
|
|
267
|
+
|
|
268
|
+
If you cannot explain WHY the result is empty from the setup → the assertion is trivial.
|
|
269
|
+
|
|
270
|
+
### Smoke Test Rule
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
# ❌ SMOKE TEST ONLY — proves nothing about behavior
|
|
274
|
+
render(<MyComponent data={mockData} />);
|
|
275
|
+
expect(screen.getByTestId("wrapper")).toBeInTheDocument();
|
|
276
|
+
|
|
277
|
+
# ✅ BEHAVIORAL TEST — proves what the component DOES with the data
|
|
278
|
+
render(<MyComponent data={mockData} />);
|
|
279
|
+
expect(screen.getByText("Expected Title")).toBeInTheDocument();
|
|
280
|
+
expect(screen.getByRole("button")).toHaveTextContent("Submit");
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
"Renders without crash" is a smoke test. It does NOT count toward TDD coverage;
|
|
284
|
+
if you need one, it must be accompanied by real behavioral assertions.
|
|
285
|
+
|
|
286
|
+
### Mock Hygiene Rules
|
|
287
|
+
|
|
288
|
+
**If you need more mocks than assertions, you are testing at the WRONG level.**
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
├── ≤ 3 mocks for a test file → ✅ Healthy — focused test
|
|
292
|
+
├── 4–6 mocks → ⚠️ Consider extracting logic to a pure function
|
|
293
|
+
└── 7+ mocks → ❌ STOP — wrong layer
|
|
294
|
+
├── Extract the logic to a PURE FUNCTION and test it without mocks, OR
|
|
295
|
+
└── Move the test to the integration/E2E layer where real deps exist
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Extract-Before-Mock Rule**: if the behavior is a data transformation,
|
|
299
|
+
mapping, filtering, or conditional logic, EXTRACT it to a pure function FIRST,
|
|
300
|
+
then test the pure function directly — no mocks needed.
|
|
301
|
+
|
|
302
|
+
### Implementation Detail Coupling Rule
|
|
303
|
+
|
|
304
|
+
Tests must assert **behavior visible to the user**, not internal details:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
# ❌ COUPLED — breaks on any style/internal refactor
|
|
308
|
+
expect(element.className).toContain("text-xs");
|
|
309
|
+
expect(mockService.mock.calls.length).toBe(3);
|
|
310
|
+
expect(component.state.isLoading).toBe(true);
|
|
311
|
+
|
|
312
|
+
# ✅ BEHAVIORAL — survives refactors, tests what users see
|
|
313
|
+
expect(screen.getByText("Error: Payment failed")).toBeInTheDocument();
|
|
314
|
+
expect(screen.getByRole("alert")).toHaveTextContent("Risk:");
|
|
315
|
+
expect(screen.getByRole("button")).toBeDisabled();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**CSS class assertions are NEVER valid test assertions.** Verify the semantic
|
|
319
|
+
outcome (role, visible text, disabled state) or use a visual-regression/E2E
|
|
320
|
+
screenshot — never assert Tailwind/CSS class names.
|
|
321
|
+
|
|
322
|
+
## Rules (Strict TDD specific)
|
|
323
|
+
|
|
324
|
+
- NEVER write production code before its test — this is the ONE rule that cannot be broken.
|
|
325
|
+
- NEVER skip the GREEN execution gate — you MUST run the test and confirm it passes.
|
|
326
|
+
- NEVER skip triangulation when the spec defines multiple scenarios.
|
|
327
|
+
- NEVER write trivial assertions (see Banned Patterns) — they are WORSE than no test.
|
|
328
|
+
- ALWAYS verify every assertion CALLS production code and asserts a SPECIFIC value.
|
|
329
|
+
- ALWAYS run the Safety Net before modifying existing files.
|
|
330
|
+
- ALWAYS write the TDD Cycle Evidence table — veredicto checks it.
|
|
331
|
+
- If a test-runner execution fails for infrastructure reasons (not a test failure), report it as "Blocked" and continue to the next task.
|
|
332
|
+
- For refactoring tasks, ALWAYS write approval tests before touching code.
|
|
333
|
+
- Run ONLY the relevant test file during the cycle, not the full suite.
|
package/themes/zero-sdd.json
CHANGED
|
File without changes
|