Imported from aganesy/QFAI (
packages/qfai/assets/init/.qfai/assistant/skills/qfai-atdd/SKILL.md). Install upstream withnpx skills add aganesy/QFAI --skill qfai-atdd. Copyright stays with the author.
/qfai-atdd — Implement Automated Acceptance Tests (ATDD)
[DRIFT-PROTOCOL:MANDATORY]
User Questions (AskUserQuestion Protocol)
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#user-questions-askuserquestion-protocol.
Skill-specific examples:
- test scope decisions
- runtime environment confirmation
FORMAT SSOT (Mandatory)
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#format-ssot-mandatory.
Inputs Priority (Preflight)
When unsure, read inputs in this order:
- P1:
.qfai/assistant/constitution/* - P2:
.qfai/assistant/manifest/*+.qfai/assistant/catalog/* - P3:
.qfai/specs/<spec-id>/01_Spec.md(Primary SSOT / Consumer View). Read its lifecycle before anything else and stop on a retired spec. A spec is retired by a complete declaration in that header block: a top-levelStatus: supersededwhoseSuperseded-by:names a spec that exists and itself declaresStatus: active, orStatus: deprecated/Status: removedwith aDeprecated-at:that is a real calendar date — the same resolutionvalidateperforms. Itstest-list.mdrows below are history, not obligations:npx qfai validateandnpx qfai reporthave already dropped them, and/qfai-implementrefuses the handoff for a retired spec, so writing acceptance tests from them produces work nobody owes and a handoff nobody will take. Report the declaredStatus:(and, forsuperseded, its successor) and ask for a row in the inheritor's ledger instead. An incomplete declaration is not a retirement and does not stop this run: the ledger still gates, so proceed and report the incomplete declaration - P4: specs/contracts obligations
.qfai/specs/<spec-id>/02_User-stories.md(US).qfai/specs/<spec-id>/03_Acceptance-Criteria.md(AC).qfai/specs/<spec-id>/05_Examples.md(EX).qfai/specs/<spec-id>/06_Test-Cases.md(TC).qfai/specs/<spec-id>/tdd/test-list.md(the execution ledger — enumerate theLayer = E2E/Layer = API/Layer = Integrationrows this run owes evidence for, with theirTDD-ID, obligation column andSelector).qfai/contracts/api/**(CON-API) and.qfai/contracts/db/**(CON-DB).qfai/contracts/ui/**and.qfai/contracts/design/**when the target spec is UI-bearingqfai.config.yaml— resolvepaths.specsDir/paths.contractsDirfirst (.qfai/specs/.qfai/contractsare only the defaults; the resolver scans the configured trees, so an override moves every path in this bullet), thenprototyping.primarySpecId, every<contractsDir>/ui/**(.qfai/contracts/ui/**by default) file as its path relative to<contractsDir>/ui/, not its basename (a surface declared only asspec-<spec-id>/screens/home.yamlis matched on itsspec-<spec-id>/ancestor directory; the basename carries no spec id), and every sibling<specsDir>/*/01_Spec.mdfrontmatter and body — the legacy# … prototyping …heading counts as the same opt-in, so a heading-only sibling flips it too — always, even for a spec with no surface of its own: theUS-*narrowing is the project-wide opt-in above, so a sibling's declaration alone decides whether this spec owes E2E references
- P5:
.qfai/specs/<spec-id>/07_Decisions.md+.qfai/specs/_policies/08_Decisions.md(Decision Records,DR-*; if no spec yet, state "not applicable") - P6: legacy artifacts (optional only)
.qfai/specs/<spec-id>/scenario.feature- coverage ledger files
Do not read discussion-pack UI/UX sidecars. UI-bearing acceptance tests consume only specs and contracts normalized by /qfai-sdd.
Read Set Contract (Mandatory)
- Default Mode:
.qfai/specs/<spec-id>/01_Spec.md.qfai/specs/<spec-id>/03_Acceptance-Criteria.md.qfai/specs/<spec-id>/05_Examples.md.qfai/specs/<spec-id>/06_Test-Cases.mdqfai.config.yaml(paths.specsDir/paths.contractsDirfirst —.qfai/specs/.qfai/contractsare defaults an override replaces, and the two scans below follow the configured trees), then<contractsDir>/ui/**(.qfai/contracts/ui/**by default), keeping each hit's path relative to<contractsDir>/ui/, not just the basename — aspec-<spec-id>/ancestor directory is itself a match signal, so a basename-only reading drops it — and every sibling<specsDir>/*/01_Spec.mdfrontmatter and body, the legacy# … prototyping …heading being an equal opt-in signal — the project-wide surface opt-in that decides this spec'sUS-*obligation. Resolve it before the Test Volume Estimate; leaving it to the closingnpx qfai validateturns a known row count into a lateQFAI-ATDD-111..qfai/specs/<spec-id>/tdd/test-list.md— read, never written. A seeded row'sTest fileandSelectorare still-there, because Phase 2b seeds them before any test exists: record the path and selector of the test this run authored as the handoff entry's row identity rather than copying that placeholder, since this stage is where they first exist and/qfai-implementPhase Red step 3b is the step that writes them into the ledger. A run that does not enumerate itsLayer = E2E/Layer = API/Layer = Integrationrows produces no## Ledger rows advancedentry for them, and/qfai-implementPhase Red step 3b then stops on a missing handoff..qfai/contracts/api/**(CON-API) and.qfai/contracts/db/**(CON-DB) — whatQFAI-ATDD-113/QFAI-ATDD-115grade this stage on. Both are attributed to.qfai/contracts/**and survive--spec, so a run that never opens them cannot know which contracts it owes, and cannot reach the-- x-qfai-status: planneddeferral either.
- Escalation Mode:
- allowed only when
01_Spec.mdEscalation Hook signals ambiguity / conflict / missing constraint / trade-off - read only
.qfai/specs/_policies/01_Objective.mdand.qfai/specs/_policies/08_Decisions.md
- allowed only when
- Default Mode is a floor, not a closed set — Inputs Priority P4 governs what else this stage may open — but do not read
_policies/**by default. - Do not read
_policies/**by default. One narrow exception, and only when the scoped gate exits 1 on a residualQFAI-ATDD-113/-115— whether it is a sibling's, this spec's own or an orphan's is what this read decides, so the exception fires on the unresolved finding, not on an ownership you cannot yet have: theOwning specfield cannot be filled from the finding, so read the generated Contract → Spec map (npx qfai report --inthe scoped gate'svalidate.spec-<id>.json, whose path is derived fromoutput.validateJsonPathand not frompaths.outDir; never--run-validate, which re-runs the full profile unscoped and advances every spec's scaffold-placeholder counters) and merge into it theContract-Refscolumn of.qfai/specs/*/04_Business-Rules.md— always, not only when the map answers(none), since the map misses specs that bind a contract in the rule table alone, and misses the ones that write the shortAPI-NNNN/DB-NNNNform its keys never match — that column only, nothing written back (references/cross-spec-obligations.md#resolving-the-owning-spec).
Sub-agent Delegation (MANDATORY)
Follow .qfai/assistant/constitution/shared-skill-delegation-baseline.md.
Orchestrator Protocol (MUST)
- Follow the shared baseline.
- Orchestrator MUST NOT self-approve.
- Orchestrator MUST NOT generate the primary artifact first draft.
Capability Probe (MUST)
- No additional overrides.
Delegation Failure (Hard Stop)
- No additional overrides.
- Do not simulate roles. Classify the failure per the baseline taxonomy first:
unavailablestops the stage with a remediation report;saturateduses the bounded retry branch and keeps the stage open.
Work Orders Summary
Use the shared schema.
Stage Minimum Roles (MUST)
- Delegate:
test-design-analystdefines coverage and layer ownership. - Delegate:
acceptance-test-engineerimplements E2E, API, and integration acceptance tests. - Delegate:
devops-ci-engineercaptures execution evidence when CI/runtime proof is needed. - Integrate:
orchestratorconsolidates delegated outputs and presents results. - Gate:
completion-revieweris delegated independently and returns onlyPASSorREVISE. - Orchestrator must not draft the primary artifact body and must not self-approve.
Reviewer Gate (MUST)
- Follow
.qfai/assistant/constitution/shared-skill-delegation-baseline.md#reviewer-gate-baseline. - Final completion gate MUST be delegated to an independent
completion-reviewer. - ATDD-specific reviewer checks:
- coverage obligations met: E2E covers
US, API coversCON-API, Integration covers every declaredCON-DB(QFAI-ATDD-115) — a contract this spec owns but outside the current slice deferred with-- x-qfai-status: planned, never silently uncovered — and everyTCthat declaresL3/L4/L5or noLevelis covered from the directory thatLevelroutes to. A sibling spec's uncoveredCON-DBis not that case, and the reviewer must not ask for that edit:QFAI-ATDD-115is filed against.qfai/contracts/**and survives--spec, so it reaches this gate without becoming this run's work — record it as a cross-spec obligation and leave the contract file alone (CRITICAL CONSTRAINTS), because marking itplanneddefers the owning spec's DB test and hides a real gap.L1/UnitandL2/Componentowe nothing here (CRITICAL CONSTRAINTS): the ledger covers them. An existing L1/L2 annotation intests/integration/**is not a violation — the validator declines to count it and declines to flag it — so do not require one to be added, and do not require an existing one to be removed; - Coverage Depth Matrix and its business rule coverage table are reviewed and no unjustified
❌cells remain in either; that table is reconciled against the spec's04_Business-Rules.md, which the reviewer work order MUST carry as an input — every activeBR-IDit declares owns a row, whether the declaration is a Rule Table row or aBR-*heading carrying no retiringStatus:, and a table of only✅rows that drops a declared rule is a REVISE, not a PASS (a spec declaring no activeBR-*states the omission instead of carrying the table); - validation evidence exists and
npx qfai validate --profile atdd --fail-on error --spec <spec-id>reached one of its two passing states — exit 0, orPASS with cross-spec obligations: every finding this spec owns is clean, and each residualQFAI-ATDD-113/-115is recorded one row per contract under## Cross-spec obligationswith a named sibling owner. Exit 1 alone is notREVISEhere; residue that is unrecorded, unattributable, or attributed to this spec is (references/cross-spec-obligations.md); - Drift Protocol is enforced;
- test-layer policy is checked against
.qfai/assistant/catalog/test-layers.md; - coverage floors and ratios are signals, not gates;
scenario.featureand coverage ledgers remain optional legacy inputs, not completion gates.
- coverage obligations met: E2E covers
- Route specialist reviewers from
.qfai/assistant/manifest/agent-routing.yml. - Default ATDD review set:
completion-reviewerqa-gatekeeper
- Add
implementation-revieweronly when helper/runtime support code changed. - Do not declare DONE until all routed blocking reviewers return
PASS.
Work order template (copy/paste)
Use the shared template.
Reviewer response template
Use the shared template.
- Required field:
Status (PASS/REVISE/PENDING).PENDINGmarks a gate that could not be run (see the baseline's reviewer-budget branch); it never counts asPASS.
Stage 0 — Steering completion refresh (mandatory)
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#stage-0---steering-completion-refresh-mandatory.
Delta Rejected Guard (Mandatory)
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#delta-rejected-guard-mandatory.
CRITICAL CONSTRAINTS (Read First)
-
Do NOT declare completion based on unit/component tests.
-
10_Plan.mdis the primary How SSOT for execution phases. -
If
10_Plan.mdis missing, stop and run owner planning flow before proceeding. -
Completion gate is validation with zero errors for this spec:
npx qfai validate --profile atdd --fail-on error --spec <spec-id>. The scope flag is not optional bookkeeping. This skill runs one spec per invocation, and unscoped it reports every other spec'sQFAI-ATDD-111/-112obligations — findings this run cannot act on and must not be blocked by. A--specrun also writes<report>/validate.spec-<id>.jsonrather than the sharedvalidate.json, so the JSON gate artifact is per spec, and an unknown or unparseable value fails the run (QFAI-SCOPE-001/QFAI-SCOPE-002) instead of silently widening back to the whole repository. That is not the same as being parallel-safe.<report>/validate.logand the run-log pointer are shared by every run, scoped or not, and nothing serializes them — so two stages running at once can leave that pointer naming the other one's run. Cite the per-run<report>/run-*/directory, or this spec'svalidate.spec-<id>.json, as the Validate Hard Gate evidence; do not citevalidate.logfrom a run you shared with another stage. -
--specscopes the spec-owned rules only, and the gate still fails on the rest. Every rule whose finding names a spec is scoped:QFAI-ATDD-111(US) andQFAI-ATDD-112(TC) by the specs they name,QFAI-ATDD-101/-102by the spec in the unknown token,QFAI-ATDD-121/-122/-123by the specs whose TCs are misplaced, andD-SCAFFOLD-PLACEHOLDERby the spec its skeleton belongs to. A scoped run reports all of those for the requested spec and drops a sibling's. What cannot be scoped does fail a scoped gate —QFAI-ATDD-113(CON-API) andQFAI-ATDD-115(CON-DB), attributed to.qfai/contracts/**, which has no spec owner in the model, and the repo-level cases beside them:references/cross-spec-obligations.md#what-the-scope-flag-cannot-narrowenumerates all of them.So a sibling spec's uncovered contract exits 1 on this spec's gate. That is a real limit, not a formality. When it happens: record the finding, its owning spec and why it is not this stage's work as a cross-spec obligation in this stage's evidence, under
## Cross-spec obligations, and say so in the completion report — do not claim the gate passed, weaken the profile, lower--fail-on, or waive it. Closing them is the owning spec's next/qfai-atddrun. The repo-wide run belongs to/qfai-verify, at the end of the stage. That record is a terminal state, not a deferral of one: a run whose every residual finding is attributed to a named sibling spec completes asPASS with cross-spec obligations(#success-criteria-definition-of-done), and the repo-wide/qfai-verifyrun settles the residue. Unnamed, that state is unreachable — the owning spec's run hits this same block from the other side, so every spec waits for every other one and the four moves just forbidden are the only exits left. It is not free: a finding you cannot attribute to a named sibling spec is this spec's, and it fails. -
Coverage obligations are mandatory : , and
requirednarrows on a different mechanism for each ID kind —US-*by surface type,TC-*by its declaredLevel,CON-API-*by active-vs-deferred. They share a word, not a rule; never carry one kind's over to another:tests/e2e/**must cover all requiredUS-*. A story outside the current slice is deferred in02_User-stories.mdwith a- x-qfai-status: plannedmeta line in its ownUS-XXXXblock (a##-or-deeper heading, or its catalog list entry) — the same token both contract kinds use — and is named atinfobyQFAI-ATDD-118. It is not left uncovered, and it is not covered by a test that asserts nothing.exceptionis not the alternative here: that branch belongs to a ledger row, and aUS-*owns none (references/red-provenance.md#a-spec-with-no-atdd-owned-rows). Required here = every declaredUS-*of a user-facing spec, "user-facing" being the same surface union/qfai-prototypingresolves — frontmattersurface_type: ui-bearingin01_Spec.md, a matching UI contract in.qfai/contracts/ui/(the resolver accepts these names and no others:<spec-id>.yaml,spec-<spec-id>.yaml,ui-<spec-id>.yaml,ui-<spec-id>-<slug>.yaml, or any*.yamlat any depth under aspec-<spec-id>/subdirectory — a basename that merely contains the id, such as0002-orders.yaml, is not one, and a.ymlextension never is), a legacy# … prototyping …heading, or the spec pinned byqfai.config.yaml#prototyping.primarySpecId; any one signal is enough. That narrowing is a project-wide, all-or-nothing opt-in: it turns on the moment any one spec in the repository declares a user-facing surface, and until then it is off repo-wide. So a spec with no user-facing surface owes no E2E reference once the project has opted in, and before that owes one for every declaredUS-*— the obligation on the spec in front of you can change because a different spec added a surface declaration, with nothing in this stage's inputs to show it. On a resolution failureqfainames the reason on stderr and keeps the obligation project-wide, so read stderr before treating an unexpectedly wideQFAI-ATDD-111as a spec error (catalog/test-layers.md#atdd-annotation-hard-gate).- Every
TC-*must be covered from the directory its declaredLevelroutes to:L3/Integration->tests/integration/**,L4/API->tests/api/**,L5/E2E->tests/e2e/**. A TC with no declaredLevelroutes totests/integration/**. L1/UnitandL2/Componentowe nothing here — out of this skill's scope, excluded fromQFAI-ATDD-112, gated bytdd/test-list.mdunder/qfai-implement, and named on every run byQFAI-ATDD-117(info). Do not duplicate an L1/L2 annotation intotests/integration/**to quiet a gate: that is the all-integration collapsecatalog/test-layers.mdlists as an anti-pattern.tests/api/**must cover all requiredCON-API-*. An ID carried by a file that declares no test is not coverage: the scan reads markdown too, and a.test.tsholding only the annotation is the same ledger renamed.QFAI-ATDD-119(info) names them, and coverage ismissingandcoveredByCarrierOnlyinsummary.json, nevermissingalone. This skill runs one spec, so gate on the narrowedQFAI-ATDD-119in<report>/validate.spec-<id>.json:summary.jsonstays repo-wide under every scope, and a sibling spec's placeholder would keep its arrays non-empty forever. Required here = every active declaredCON-API-*id — the unit is theQFAI-CONTRACT-IDa contract file declares (one per file; a second isQFAI-CONTRACT-011), never the OpenAPI operation: one annotation covers that id however many operations the document describes, so counting per operation overstates both the estimate and the DoD. An id deferred withx-qfai-status: plannedis declared but not owed. That marker is read as a top-level key of the contract document — or, when the document does not parse or declares no such top-level key, as a column-0 comment (# x-qfai-status: planned, unindented; the comment form is accepted precisely because column 0 cannot be an operation-level key) — and either form defers the whole file, i.e. the id that file declares. Written under an OpenAPI operation, commented or not, it is ignored: the contract stays active andQFAI-ATDD-113fires for its uncovered id, so slice a partially-planned contract into its own file (with its own id) rather than marking the operation. Surface typing does not touch this obligation.
-
Forbidden references (a TC annotation outside its declared home):
tests/api/**andtests/e2e/**must not containQFAI:SPEC-XXXX:TC-YYYYunless that TC declaresLevelL4/APIorL5/E2Erespectively. -
Unknown references (
US/TC/CON-APInot declared) must be treated as errors. -
The E2E/API ledger rows this stage feeds are bound by
/qfai-implement's lifecycle. See "Execution Ledger" below: a row advanced on none of the three RED-provenance forms is a lifecycle violation. -
Floors/ratios are planning signals only, not gates.
-
Legacy
scenario.featureor coverage ledgers may exist but are not mandatory inputs for completion. -
The per-item evidence file
.qfai/evidence/atdd-<spec-id>.mdis required and committed. LedgerEvidencecells point to its anchors, and validation must resolve them on a fresh clone. The managed.gitignoreblock re-includes it, alongside.qfai/evidence/implement-<spec-id>.md. Governance records also stay in version control: Change Requests (.qfai/decisions/CR-*.md), durable decision records (.qfai/evidence/decisions/*.json), the Coverage Depth Matrix (.qfai/evidence/coverage-depth-<spec-id>.md) and this stage's own.qfai/evidence/atdd-<spec-id>.md, whose RED provenance is taken before the passing code exists and so cannot be regenerated. A negation does not stage a file — commit it (.qfai/assistant/constitution/drift-protocol.md#which-evidence-is-committed). -
The matrix is a governance record, not a log, so it is committed:
.qfai/evidence/coverage-depth-<spec-id>.md, one justification per❌(references/test-case-depth-checklist.md#where-the-matrix-lives).
Completion Contract (Shared)
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#completion-contract-shared. Smallest applicable smoke check (this skill's override): the acceptance tests this run created or changed — every tests/e2e/**, tests/api/** and tests/integration/** file you wrote or edited, filled-in scaffolds among them — executed with the runner from catalog/tech.md#standard-commands-copy-paste, reaching a red/green verdict rather than a collection or import error. Not "the tests you just scaffolded": scaffold emits the Integration-owned TCs only, so an E2E/API-only run has no scaffold output at all and would have had nothing to execute. A run that wrote no test file still has a target — a spec whose obligations were already satisfied and only re-verified — and it is the spec's existing acceptance suite: the files carrying this spec's US / TC / CON-API annotations, in the directories those obligations route to, run the same way. A run that never reached the assertions is UNRUN, not a pass.
Follow .qfai/assistant/constitution/shared-skill-operating-baseline.md#gate-failure-autorepair-protocol for validate, doctor, and quality-gate failures.
Goal
Turn specs/contracts obligations (US / TC / CON-API / CON-DB) into runnable acceptance tests in this repository.
Execution Ledger: the rows this skill feeds
.qfai/specs/<spec-id>/tdd/test-list.md is /qfai-implement's execution
ledger, and qfai-implement/SKILL.md states the split: Layer = E2E,
Layer = API and Layer = Integration rows are tracked there, but their tests
are authored here. Integration is there because this skill's scope puts it
there: QFAI-ATDD-112 covers every L3 TC, and every TC with no declared
Level, from tests/integration/**, and P4 writes those tests. Self-owned,
they had /qfai-implement demand a fresh RED for a test already green here.
- This skill does not write the ledger.
/qfai-implementowns theStatus/DR-ID/Evidencecells of every row — one writer, as.qfai/assistant/constitution/drift-protocol.mdgrants. This stage owes the evidence those cells point at, in.qfai/evidence/atdd-<spec-id>.md. - The lifecycle is
../qfai-implement/references/execution-ledger.md#allowed-transitions: forward-only fromtodo, andtodo -> redrequires an admissible RED observed before the code that makes it pass exists. /qfai-sddPhase 2b seeds oneLayer = E2Erow per activeUS-*and oneLayer = APIrow per activeCON-API-*the spec owns — the lowest-numbered spec naming that contract; this stage still cannot create them. A spec with an active obligation of its own therefore normally arrives with rows here — enumerate them and build the handoff from them. Zero is legitimate when every obligation is exempt and every activeCON-API-*the spec references is owned by another spec, and even then is not "nothing to do"; a row missing for an active obligation this spec owns is an incomplete Phase 2b — report it, never write it. Do not demand an API row for a contract another spec owns: that row must not exist twice.references/red-provenance.md#a-spec-with-no-atdd-owned-rows.- The stage order makes that a real question: Work Orders build the surfaces a journey needs (P3, P4), so a journey written after them passes first run — an anomaly bound for
exception, which then becomes the only reachable terminal state.
RED provenance for an ATDD-owned row (MUST)
Read references/red-provenance.md before advancing any row. It defines
three branches, in order — observed RED (stage gate P1b, before P2-P4 build
any surface), falsifiability when the surface is already there, and exception
with a DR-* when neither is available. Take the first that applies, record
which one, and put the evidence in .qfai/evidence/atdd-<spec-id>.md under
## Ledger rows advanced — /qfai-implement consumes that entry rather than
re-observing a RED. Branch 3 is the last resort: a stage that routes every row
to exception has recorded that it did not try branches 1 and 2.
Scope (ATDD only)
In scope: E2E, API, Integration. Out of scope: Unit and Component
(/qfai-implement). Tests that must sign in: references/credential-reuse.md.
Non-goals
- Unit/Component test implementation.
- Product feature changes beyond what is needed for ATDD test execution.
Mandatory Outputs
- Test Volume Estimate (signal table with evidence)
- Coverage Depth Matrix, written to
.qfai/evidence/coverage-depth-<spec-id>.md(per spec; template and scoring inreferences/test-case-depth-checklist.md). Committed — see CRITICAL CONSTRAINTS. - Coverage obligations checklist (
US/TC/CON-API), and the implemented tests per layer (E2E/API/Integration) - Reviewer notes (
PASSor concrete rework list) - Evidence file:
.qfai/evidence/atdd-<spec-id>.md
Volume Signals (mandatory, not gates)
Every row counts this spec's own obligations: E2E = required US-*, API = required CON-API-*, meaning the CON-API-* this spec references that are not deferred, Integration = required TC-* routing to tests/integration/** (L3 or no Level) plus the CON-DB-* this spec references. L1/L2 owe nothing here; an L4/L5 TC counts in the row its Level routes it to. Read required per ID kind from Coverage obligations — the US-* row is surface-scoped and the CON-API-* row drops every contract deferred with x-qfai-status: planned, so filling either in from "every declared" overstates the Raw count before a single test is written.
Contract references come from the SSOT the spec carries — Contract-Refs in 04_Business-Rules.md, plus a QFAI-CONTRACT-REF line in 01_Spec.md when there is one — never the ledger; a contract deferred with x-qfai-status: planned owes no test, so exclude it from the count and name it in Notes.
E2E_s / API_s / INT_s, their bands, and what a low or high one obliges: references/volume-signals.md. A Signal cell is never a copy of its Raw count; never fail on a signal value alone.
Estimator output table (required)
| Layer | Raw count | Signal | Evidence | Notes |
|---|---|---|---|---|
| E2E | #US | E2E_s | user stories + L5 TCs |
|
| API | #CON | API_s | active CON-API-* + L4 TCs |
|
| Integration | #TC | INT_s | L3/no-Level TCs + active CON-DB-* |
Scaffolding
npx qfai atdd scaffold --spec <spec-id> bulk-emits one placeholder test per TC-* this skill owns, each carrying its QFAI:SPEC-XXXX:TC-YYYY annotation, into tests/integration/<spec-id>/ — the directory QFAI-ATDD-112 scans. It is idempotent. L1/L2 and L4/L5 TCs are skipped and named on stderr, and a skeleton left in placeholder shape escalates: references/scaffolding.md.
Annotation obligations (mandatory)
Every generated ATDD test MUST include QFAI annotations by layer:
tests/e2e/**:QFAI:SPEC-XXXX:US-YYYY(plusQFAI:SPEC-XXXX:TC-YYYYfor a TC that declaresLevelL5/E2E)tests/integration/**:QFAI:SPEC-XXXX:TC-YYYY(TCs declaringL3/Integration, and TCs with no declaredLevel)tests/api/**:QFAI:CON-API-XXXX(plusQFAI:SPEC-XXXX:TC-YYYYfor a TC that declaresLevelL4/API)tests/integration/**also carriesQFAI:CON-DB-XXXXfor every declared DB contract the slice exercises
Notes:
- A TC's annotation belongs in exactly one directory — the one its declared
Levelroutes to. Elsewhere it is both uncovered (QFAI-ATDD-112) and forbidden, symmetrically: one left behind intests/integration/**after the TC moved toL4/L5is rejected byQFAI-ATDD-123just as an early one intests/api/**is rejected byQFAI-ATDD-121. - AC annotations are optional in code.
QFAI:CON-API-*in E2E is not forbidden, but contract guarantee belongs to API tests.
Success Criteria (Definition of Done)
- All required
USare covered by E2E tests (QFAI-ATDD-111); a story outside the current slice is deferred with- x-qfai-status: plannedin its02_User-stories.mdblock and reported byQFAI-ATDD-118(info), not left uncovered. —requiredas defined under Coverage obligations (every declaredUS-*, narrowed to user-facing specs once any one spec in the project declares a surface, project-wide before that). The DoD and the obligation read from that one definition; do not re-derive it here. - All required
TCare covered from the directory their declaredLevelroutes to (L3/Integration->tests/integration/**,L4/API->tests/api/**,L5/E2E->tests/e2e/**, no declaredLevel->tests/integration/**).L1/UnitandL2/Componentare outside this obligation — the ledger covers them — so a spec whose TCs are all L1/L2 is done here with no ATDD annotation at all. Duplicating a TC into a second layer is a not-done condition, not extra credit. - All required
CON-APIthis spec owns are covered by API tests. Ownership is the merge inreferences/cross-spec-obligations.md#resolving-the-owning-spec, not membership in the finding: a contract a named sibling spec declares, recorded one row per ID under## Cross-spec obligations, is that spec's to cover and does not hold this bullet open. Residue that is unrecorded, attributable to no named sibling, or attributed to a spec this one co-owns the contract with does hold it open.requiredas defined under Coverage obligations (active contract ids, one per contract file, never per OpenAPI operation;x-qfai-status: planneddefers the whole file). - All required
CON-DBthis spec owns are covered by integration tests (QFAI-ATDD-115); a contract outside the current slice is deferred with-- x-qfai-status: planned, not left uncovered. Sibling-owned residue is read exactly as in theCON-APIbullet above — recorded and attributed, it is that spec's; otherwise it is this run's. - Validation passes for this spec in the two parts the scope model implies — run
npx qfai validate --profile atdd --fail-on error --spec <spec-id>, then: (1) no finding this spec owns remains, every rule--specnarrows reporting clean for<spec-id>; and (2) every residual finding is attributed and recorded, sinceQFAI-ATDD-113/-115are filed against.qfai/contracts/**, which no spec owns, so a sibling's uncovered contract holds the command at exit 1 — each such finding names its owning sibling spec under## Cross-spec obligationsin this stage's evidence, and the completion report says so. Both parts met isPASS with cross-spec obligations, the terminal state of a run that discharged everything its spec owns; requiring exit 0 outright left that run not-done with no other state to be in, which is the reading that ends in one of the four moves CRITICAL CONSTRAINTS forbids. A residual finding attributable to no named sibling spec is this spec's own and still FAILs (references/cross-spec-obligations.md). - Repository quality gates (format/lint/type/tests, and pack/verify if distributed) pass with evidence.
- Evidence file exists and includes work orders + reviewer notes.
- Every ledger row this cycle advanced carries one of the three RED-provenance forms — an observed RED pair with its
Oracle proof, theSatisfied-by+ falsifiability trio, or aDR-*recording why neither was available — andqa-gatekeeperhas accepted it. The third form is a valid branch, and it is not a completion:exceptionis a blocking output and needs a user-approvedTDDLIST-001waiver, or the row is parked and the spec stays open (references/red-provenance.md#branch-3-does-not-close-a-spec-on-its-own). - Completion is approved by a reviewer who did not implement tests.
- The P8 reviewer's
Audited evidence hashis recomputed before completion is declared, from the current stage evidence file and Coverage Depth Matrix, by the stage-review procedure that produced it (.qfai/assistant/constitution/shared-skill-delegation-baseline.md#reviewer-response-template); a mismatch means the evidence moved after the verdict. Seal the P8 pack too: when the last reviewer response lands, and before this stage writes its verdict, hash the pack this stage opened —.qfai/review/review-<timestamp>/, whole — by the same procedure, and record it outside the pack in the stage evidence file's## Final statusasReview pack:(that path) andReview pack seal:(that hash). That section is the one part excluded from the P8 audit subject, so writing it there does not stale the verdict, and it exists even on a spec with no ATDD-owned rows — where there is no item evidence entry to hold the seal at all. At completion, recompute the seal over the recorded path and compare it with the recorded value —../qfai-implement/references/evidence-revision.mdstates that rule once and it applies here:## Final statusis outside every audit subject and outside the working-tree revision, so an expected value read from the working tree could be rewritten in the same pass that edited the pack, and every recomputation would still agree, and check that## Final statussays what that pack says. The recording and the recomputation must be two moments: a value computed from the pack at completion always matches itself whatever was edited in between, and the stage hash covers the evidence but not the verdict, so aREVISEedited toPASSin the response, the summary and the status together left every recomputation unchanged. On a spec with no ATDD-owned rows/qfai-implement's gate item 10 never runs, so without this the stored hash was written by P8 and read by nobody — and the evidence tree is out of the working-tree revision, so a later edit moved nothing else either.
Not-done criteria
- Any required
US/TCremains uncovered, or any requiredCON-API/CON-DBthis spec owns does. A residual contract attributed to a named sibling spec and recorded under## Cross-spec obligationsis not this criterion — that is the terminal state, and reading it back as "required and uncovered" restores from this line the deadlock the two-part DoD removes. Unrecorded, unattributable, or self-attributed residue is still this criterion. Readingrequiredper ID kind from Coverage obligations (USby surface type,TCby declaredLevel,CON-APIby active-vs-deferred). - Forbidden references remain.
- Tests exist but were never executed.
- Validation evidence is missing, or failing on a finding this spec owns. A residual
QFAI-ATDD-113/-115attributed to a named sibling spec and recorded under## Cross-spec obligationsis not this criterion — it blocks that spec's completion, not this one, and/qfai-verifysettles the repo-wide residue at the end of the stage. Unrecorded residue is, and so is an entry that names no owning spec, names this spec as the owner, or omits the contract ID the finding cites. - Coverage Depth Matrix is missing, omits the business rule coverage table on a spec that declares an active
BR-*, or contains unjustified ❌ cells in either table, or that table drops an activeBR-IDdeclared in04_Business-Rules.md(normal-path-only coverage is incomplete). - A ledger row was advanced past
todowith none of the three forms — no observed RED, no falsifiability evidence, and noDR-*. - A row was sent to
exceptionwithout aDR-*recording why both branches were unavailable. "The surface was built earlier in this cycle" is not such a reason.
Failure handling (mandatory)
- If blocked/unknown, stop and raise a Decision Record.
- Do not declare completion when any gate is FAIL; iterate until PASS. A scoped validate gate that exits 1 only on residue attributed and recorded per
references/cross-spec-obligations.mdis not a FAIL gate — it isPASS with cross-spec obligations, and iterating on it is waiting for a sibling spec that is waiting for this one.
Evidence (MANDATORY)
Create and update: .qfai/evidence/atdd-<spec-id>.md
Required sections: the template below is the list. Three of them carry a contract the heading cannot:
- Ledger rows advanced — an index table plus one
### TDD-NNNNsection per row (references/red-provenance.md#evidence-shape). Exactly one form per row, never both and never neither; the cell is an anchor and the payload goes in the section. A rework round is a#### Round Nblock nested inside that row's section, not a section of its own: the list is closed, and nesting attributes it to a row (references/review-fix-rounds.md). - Coverage Depth Matrix — a link to
.qfai/evidence/coverage-depth-<spec-id>.mdand the✅/⚠️/❌totals. The matrix and its per-❌justifications live in that committed file; restating them here would lose them. - Cross-spec obligations — one row per uncovered contract ID the scoped gate still exits 1 on, never one per finding:
QFAI-ATDD-113/-115aggregate every uncovered contract into one finding'srefs, so split them into a row each.Nonewhen the run exited 0. It is what a completion reviewer reads to tellPASS with cross-spec obligationsfrom an ordinary FAIL. Fields, worked example and the FAIL cases:references/cross-spec-obligations.md#the-evidence-entry.
Template:
# ATDD Evidence: <spec-id>
## Objective
## Inputs reviewed (files/paths)
## Decisions made (with rationale)
## Work performed (what changed, where)
## Commands executed + key outputs
## Test volume estimate
## Coverage obligations checklist
## Ledger rows advanced
<!-- Index table + one `### TDD-NNNN` section per row:
`references/red-provenance.md#evidence-shape`. Rework rounds nest inside a
row's section as `#### Round N`: `references/review-fix-rounds.md`. -->
## Coverage Depth Matrix
See `.qfai/evidence/coverage-depth-<spec-id>.md` (committed). Totals: ✅ N / ⚠️ N / ❌ N.
## Work Orders Summary
## Cross-spec obligations
## Execution logs
## Gaps / Open risks
## Final status (PASS / PASS with cross-spec obligations / FAIL) + who confirmed
ATDD Work Orders (mandatory)
- Test Case Depth Analyst:
test-design-analystevaluates test cases usingreferences/test-case-depth-checklist.md, produces Coverage Depth Matrix, flags gaps in boundary/error/edge coverage. - Test Volume Estimator: compute US/TC/CON signals with evidence.
- ATDD Implementers, one per layer: required
UScoverage in E2E,CON-APIin API,TCandCON-DBin Integration. - Reviewer: validate coverage obligations + gate results + Coverage Depth Matrix (non-edit).
- Runtime Gatekeeper: run suites and capture logs.
Completion Separation (mandatory)
- Implementation and completion approval must be separate.
- Reviewer must be non-edit (
PASSorREVISEonly).
Stage Gates (Do not skip)
- P0: Plan and obligations checklist prepared. A project whose routing has no
redphase predates it:references/red-provenance.md#a-project-without-the-red-phase. - P1: Layer assignment validated against
.qfai/assistant/catalog/test-layers.md#layer-derivation-procedure-normative. - P1b: A branch is chosen for every row, provisional until its handoff.
- P1c: A branch 1 row is discharged in that loop — write the test, take the
RED,
qa-gatekeeperPASS, hand it to/qfai-implement, GREEN, checkpoint — before the next branch-1 row's failing test is written, and before P2-P4 build any surface. One loop perTDD-ID; the nested run is an item cycle, not a completion gate (references/red-provenance.md#what-the-nested-run-owes). - P1d: Branch 3 rows are judged here, then handed over. Route
qa-gatekeeperon theDR-*— the claim is that the obligation genuinely cannot be observed — and hand the row over with that PASS recorded. P1b's gatekeeper judges branch 1 only and/qfai-implement's exception path writestodo -> exceptionand stops, so without this a correct branch-3 row reached a terminal status judged by nobody./qfai-implementis the only writer ofStatus/DR-ID/Evidence, so every branch needs a handoff; which goes when:references/red-provenance.md#which-stage-hands-a-row-over. - P2: E2E implementation completed.
- P3: API implementation completed.
- P4: Integration implementation completed.
- P4b: Branch 2 rows are handed over, after P4 and before P6 — their mutation
needs the surface P2-P4 build, the trio is the row's RED payload, and that nested
run is an item cycle like P1c's (
references/red-provenance.md#what-the-nested-run-owes). - P5: Validation gate passed.
- P6: Runtime evidence captured.
- P7: Repo quality gates passed.
- P8: Reviewer confirms completion.
Completion Criteria (Final Gate)
Before declaring completion:
-
Confirm required
US/TC/CON-APIcoverage is complete for the obligations this spec owns; a sibling-ownedCON-API/CON-DBrecorded under## Cross-spec obligationsis complete here and open there. -
Run:
npx qfai validate --profile atdd --fail-on error --spec <spec-id>--specscopes the gate to the spec this invocation owns. Omitting it makes the gate report obligations belonging to specs this run never touched, which is how a spec with every obligation discharged still fails to close. -
Run repository standard gates:
- format check
- lint
- typecheck
- tests
- pack/verify (if distributed)
-
Record exact commands and outcomes.
If commands cannot be run due to environment limits, request user execution and do not assume PASS.
Output
- Acceptance test implementation files (with required annotations)
- Runbook snippet (copy-paste command)
- Verification evidence summary
- Gate results (
PASS/PASS with cross-spec obligations/FAIL) — the middle one names its recorded obligations in the completion report
DONE Declaration (Mandatory Output)
Include the referenced inputs (instructions/steering and spec delta), the DR-IDs referenced (or "none"), and confirmation that no rejected options were reintroduced (or the RE-OPEN DR-IDs).
FINAL CHECKLIST (Check Last)
- CRITICAL CONSTRAINTS were followed.
- Evidence file exists and is complete.
- Mandatory checks were executed and recorded.
- No untracked gaps remain (or they are explicitly documented).
- Completion approved by an independent reviewer.
Completion Checklist (MUST)
- This skill's Definition of Done is satisfied.
- Required artifacts were produced or updated.
- Open questions that place a new obligation on the product were routed to the owner phase (
/qfai-sdd) as an advisory / Change Request proposal per.qfai/assistant/constitution/drift-protocol.md#reviewer-originated-obligations; questions about this skill's own inputs or settings stay in its own output for the user to answer. This skill does not write08_Open-questions.md. - The completion message was presented to the user.
- Next actions were enumerated for all available options.
Completion Message & Next Actions (MUST)
When this skill is complete, provide a final user-facing completion message and enumerate all actionable next steps.
- Proceed (recommended):
/qfai-implement. Action: run unified TDD micro-cycle (Red/Green/Refactor) one test at a time from test-list.md. - Acceptance tests need fixes: rerun
/qfai-atdd. Action: close uncoveredUS/TC/CON-APIobligations and rerun validation.
Default Autopilot Policy
The skill collapses avoidable per-session prompts to 0-1 by classifying every decision into one of three named buckets:
- auto-decide:
- output formatting
- ID / sequence numbering
- append-vs-create on subject overlap
- equivalent-option pick
- ask-user:
- CREATE / DELETE / SPLIT / MERGE / SUPERSEDE / UPDATE:REMOVE triage operations (each with a prompt template that names the target and rationale)
- destructive operations (rm / overwrite / force-push)
- version-pin changes (
package.json#version, branch pin) - scope expansions outside the active envelope
- hard-required:
companyName- brand intent
primarySpecId(when absent from inputs)
A skill MAY narrow any of the three buckets (drop an entry the skill cannot reach), and MAY instantiate a category entry — approval-required governance operations — with the operations its own run cannot authorize for itself. It MUST NOT introduce an entry outside the prototype's categories. Widening triggers a Reviewer-Gate finding.
project_memory:
- Coverage obligations stay layer-pinned for US and CON-API: tests/e2e/** must cover all required US; tests/api/** all required CON-API. "Required" narrows differently per ID kind — US by surface type (a project-wide opt-in: active only once some spec declares a user-facing surface, project-wide before that), TC by declared Level, CON-API by active-vs-deferred, counted per declared QFAI-CONTRACT-ID (one per file) and never per OpenAPI operation (x-qfai-status: planned defers that whole contract file, as a top-level key or as a column-0 comment; under an operation it is ignored). Resolving the US opt-in needs qfai.config.yaml (paths.specsDir / paths.contractsDir first, then prototyping.primarySpecId), every ui contract path relative to /ui/ — not just the basename, since a spec-/ ancestor directory is itself the match — and every sibling 01_Spec.md frontmatter or legacy "# … prototyping …" heading, read before the Volume Estimate. Each TC declaring L3/L4/L5, or no Level, is covered from the directory that Level routes to (L3/Integration -> tests/integration/**, L4/API -> tests/api/**, L5/E2E -> tests/e2e/**; no declared Level -> tests/integration/**). L1/Unit and L2/Component owe no ATDD annotation — tdd/test-list.md covers them. An existing one in tests/integration/** is neither counted nor flagged, so do not require adding or removing it.
- Forbidden references guard the test-layer policy: a TC annotation outside its declared home is rejected — tests/api/** must not carry QFAI:SPEC-XXXX:TC-YYYY unless that TC declares L4/API, and tests/e2e/** likewise unless it declares L5/E2E.
- Floor / ratio signals are planning hints, never gates; legacy scenario.feature / coverage ledger files remain optional inputs.