| name | cd-test-architecture |
| description | Evaluate an existing application's tests and recommend a CD-pipeline-aligned test architecture — fast, deterministic tests with minimal tooling that fully validate behavior (including cross-service interaction) and run in CI without configuring the rest of the system. Use when the user says "evaluate how this app is tested", "design a test architecture", "align our tests for CD", "make our CI tests deterministic", "our tests need the whole system configured", "our tests live in another repo / Postman / manual scripts", or asks for UI/service/batch test patterns. |
| role | worker |
| user-invocable | true |
| argument-hint | [--component <name>] [--ci <path>] [--external-tests <path>] [--stack <id>] [--pdf] [--yes] |
CD Test Architecture
Role: worker. This command assesses and reports — it does not write tests or
refactor code; it hands the migration steps to /plan or /build; Step 4b's
build-vs-document ask is this skill's one interactive branch point, and even
there it proposes a Story rather than invoking /build itself.
Overview
An advisory, application-level skill: it assesses how an existing application is tested, classifies that against a CD-aligned test taxonomy, finds the tests that can't run in a clean CI gate, and recommends a target architecture plus a migration path. It does not write tests or refactor code.
Where test-design-advisor works at the unit/module level and test-smell-review finds smells in test files, this skill works at the whole-application level: test types, pipeline stages, and per-component patterns.
Grounded in these knowledge references — read the first two before assessing:
knowledge/cd-test-architecture.md — the six test types, the determinism→pre-merge-gate rule, the adapter rule, double validation, pipeline stages, and MinimumCD-vs-Fowler terminology.
knowledge/component-test-patterns.md — per-component patterns (UI / Services / Batch) with isolation strategy and pipeline placement.
knowledge/database-test-patterns.md — load when a component is database-backed: Database Sandbox isolation, Transaction Rollback / Table Truncation Teardown, and the rule that pushes most data-logic tests onto in-memory Fakes so they stay pre-merge-gate eligible.
knowledge/test-doubles.md — the Stub/Fake/Mock/Spy taxonomy; load when Step 4b's database or downstream-service branch proposes a hand-rolled Fake double, so it's named correctly (a Fake, never a "mock").
knowledge/virtual-service-libraries.md — the per-stack record-and-replay virtual-service library catalog, its preferred-vs-backup framing, and its Resolution order (existing-tool detected → catalog default → operator override/decline); load when Step 4b's Downstream-service branch resolves a Build (Fake) row's library choice.
Constraints
- Advisory only. Assess and recommend; do not edit production or test code. Hand the migration steps to
/plan or /build — including Step 4b's build-vs-document ask: it proposes a downstream Story, it never invokes /build or edits code directly.
- Use MinimumCD vocabulary (unit / component / contract / integration / E2E / static analysis) consistently; when the codebase uses other names, map them explicitly.
- The pre-merge gate may contain only deterministic tests (static, unit, component, contract). Any test that needs a database, broker, downstream service, or environment secrets configured to run is, by definition, not a pre-merge test — flag it.
- Recommend isolation via in-memory doubles (or, for downstream-service adapters, a record-and-replay virtual-service library per
virtual-service-libraries.md) + owned adapters, validated by the double-validation loop. Do not recommend standing up the whole system (docker-compose of dependencies) for the gate — that is the configured-dependency problem this architecture removes.
- Do not assume provider cooperation. For dependencies the team doesn't control, the defense against contract breakage is consumer-owned, scheduled verification against the provider's test environment (out-of-band) plus consumer resilience — not provider-side CDC verification. Recommend accordingly.
- Baseline before refactor (don't lead with refactoring). For under-tested or legacy components, first recommend the best outside-in test achievable at existing seams without changing the code — a characterization baseline — then the refactor that improves testability under that green baseline. Never change behavior and structure in the same step. Defer the procedure to the
legacy-code skill and use the DDD skills (domain-driven-design, domain-analysis) to suggest the target structure for the refactor.
- Minimal tooling: prefer a record-and-replay virtual-service library (
virtual-service-libraries.md) or in-memory doubles for the pre-merge gate, one real browser for UI, testcontainers only for off-gate adapter integration. Don't recommend a sprawl of frameworks.
- Be concise: tables and ordered steps, not prose. Cite the knowledge file instead of restating it.
Parse Arguments
Arguments: a target application/repo path or description. Optional --component <name> to scope to one component, --ci <path> to point at the existing pipeline config, --external-tests <path-or-repo-or-description> to point at tests that live outside this repo (another repo's suite, a third-party runner, Postman/Insomnia collections, manual test scripts, recorded UI flows, spreadsheets of test cases), and --stack <id> to override stack detection (default: detect from manifests; resolved profile key like dotnet, node, spring-boot, go, django, react, vue, ssr-htmx). --yes runs unattended: Step 4b's build-vs-document prompt is skipped and every flagged component defaults to Document-only, per human-oversight-protocol's non-interactive convention. If little or no in-repo testing is found and no external location is given, ask where the application is actually tested before concluding it is untested. If no target is given, ask for one.
Steps
0. Detect stack
Resolve the project's stack so the assessment can resolve concrete tools from knowledge/test-stack-profiles/<stack>.md. Mirrors skills/test-design-advisor/SKILL.md:31, 62: when --stack <id> is provided, it takes precedence and detection is skipped. Otherwise, read manifests at the target — package.json (refined to react/vue via dependency, or to ssr-htmx when an htmx dep is present alongside templates/*.html), *.csproj / *.sln, pom.xml / build.gradle*, go.mod, pyproject.toml / requirements.txt — and resolve the matching profile key. Load knowledge/test-stack-profiles/<stack>.md (and any references it points at) so the Target architecture table can name concrete tools per layer. When no profile matches, proceed with stack-agnostic guidance and name the missing profile in the report — never block on it.
1. Inventory the application's components
Map the deployable/testable surfaces and assign each its pattern from component-test-patterns.md (User Interface; API Provider / API Consumer / Event Consumer / Event Producer / Stateful Service / CLI-Library; Scheduled Job). A real system is usually several — list each surface.
Exhaustive surface-type discovery is the mandatory default — always on, never operator-gated (issue #1464). Every invocation of this step actively searches for each pattern type in component-test-patterns.md's table (Step 1's own list above already names all eight — not a separate list to invent here), not just the first or most obvious one found. Stopping after the first obvious surface type — e.g. finding a couple of HTTP routes and concluding the inventory is complete — is a spec violation of this step, not an acceptable shallow default: a real system frequently has surfaces in more than one pattern group at once (Services and Batch and UI, say), and each group must be actively searched for, not merely recorded when stumbled upon. Record the search itself, not only what it found: in the Output's ### Components & patterns table (Step 6), include a row for every pattern type actively searched for — even one with no matching surface (state None found rather than omitting the row) — mirroring /gherkin-derive Step 2's ## Analysis Coverage section (issue #1450), so a thin run is detectable from the report rather than merely asserted here.
Existing-tool detection (feeds Step 4b's Downstream-service branch). While inventorying, also check the target's dependency manifest and test files for an existing virtual-service/record-replay library (e.g. Nock, WireMock, WireMock.Net, VCR.py, go-vcr). When found, note it: per knowledge/virtual-service-libraries.md's Resolution order, it becomes the default recommended tool for that component's Build (Fake) row in Step 4b instead of the catalog's per-stack default, with no switch suggested. This is inventory-phase data gathering, not a Step 4 recommendation.
Graph-assisted inventory. Prefer CodeGraph/Repowise over raw Grep for locating components, their deployable surfaces, and existing test suites — see knowledge/codegraph-vs-graphify.md for tool selection and the fallback contract.
2. Inventory the existing tests and classify them
Find the test suites in the repo and classify each against the six types in cd-test-architecture.md. For each, record: type, what it actually exercises, whether it is deterministic, and what it requires to run (DB URL, broker, downstream service, secrets, sleep, real clock).
If in-repo tests are sparse or absent, the application is not necessarily untested — it may be tested out-of-repo (see Step 2b — locate and harvest out-of-repo tests). Do not conclude "no tests" without checking.
2b. Locate and harvest out-of-repo tests
When --external-tests is given (or in-repo tests are sparse and the user points you to external coverage), treat the external location as the current specification of intended behavior and harvest it:
- Other-repo automated suites — read the suites; classify them by type just like in-repo tests; note that they live outside the component's repo and pipeline.
- Postman / Insomnia /
.http collections — each request + its assertions describes an API contract and a scenario. Extract: endpoint, request shape, expected response, and which success/failure scenario it covers.
- Manual test scripts / spreadsheets / recorded UI flows — extract each step as a behavior the team cares about (a candidate component/E2E scenario), and note it is currently human-executed and non-repeatable.
Produce a behavior inventory from these sources, mapped to the component patterns from Step 1 (inventory the application's components). This becomes the basis for improvement — the behaviors to re-express as deterministic, in-repo, gated tests.
3. Diagnose CD-fitness gaps
Flag, with evidence:
- Out-of-repo / third-party-runner testing — the component's tests live in another repo, a separate QA runner, Postman collections, or manual scripts rather than alongside the code. This is an anti-pattern, even when that external coverage is extensive: the tests cannot gate the component's own merges, are not versioned with the code they verify, are usually non-deterministic and environment-coupled, and silently drift from the code. The goal state is deterministic tests co-located with the code and run in its pipeline. Flag this explicitly and treat the external suite as the source material (Step 2b — locate and harvest out-of-repo tests), not the destination.
- Manual / non-repeatable testing — behavior verified only by humans following scripts. Non-repeatable, unsuitable for any gate; each such script is a behavior to automate.
- Mis-typed gate tests — "unit/component" tests that require a real dependency or are non-deterministic (real clock/RNG/network/sleep). These cannot be a pre-merge gate.
- Configured-dependency tests — tests that need the rest of the system stood up to run.
- Coverage gaps — behavior (success + failure modes per the component pattern) not covered at any deterministic layer.
- Drift risk — doubles with no validation loop. In particular, assume no provider cooperation: a contract that nobody runs against the real provider on a schedule is undefended. Flag the absence of consumer-owned, scheduled provider-contract verification in a test environment — relying on provider-side CDC verification is not sufficient for providers you don't control.
- No resilience to a broken contract — the consumer assumes the provider holds; no tests that it survives a provider break (timeout, retry/backoff, circuit breaker, drifted/malformed response). Assume the provider will break without versioning.
- Inverted shape — reliance on integration/E2E where component/contract tests would gate deterministically.
3b. Find testable seams and the achievable outside-in baseline
For each under-tested or untested component, identify the testable seams — places where behavior can be observed or substituted without editing the code (HTTP handler, CLI entrypoint, message handler, exported function, existing injection points; object seams via interfaces/polymorphism, link seams via DI/module substitution). Then state the best outside-in test achievable right now without refactoring — a characterization test at the outermost reachable seam that locks in current behavior. This is the immediate, zero-risk baseline, distinct from the (later) clean CD gate. See cd-test-architecture.md → Outside-In First, and the legacy-code skill.
4. Recommend the target architecture
Per component, using its pattern: which test types cover which layers, what to double to run pre-merge without configuration, the success scenarios and failure modes to cover, the double-validation loop, and the pipeline stage for each (pre-merge gate vs Stage 1/2 vs out-of-band vs post-deploy). Show the resulting pre-merge gate is deterministic and config-free. For under-tested components, separate the recommendation into (a) the outside-in characterization baseline writable today without refactoring and (b) the post-baseline refactor toward this target (use the DDD skills to suggest where boundaries/seams should land).
E2E justification gate — canonical in knowledge/cd-test-architecture.md#the-e2e-justification-gate. For each E2E recommendation, document that all four conditions hold, and record one sentence per behavior explaining why E2E was not chosen when the first three cover it.
The pyramid is a cost heuristic, not a target shape — canonical in knowledge/cd-test-architecture.md#the-pyramid-is-a-cost-heuristic-not-a-target-shape. Per-component / per-behavior placement is the valid output; if the suite shape is pathological, name the pathology and the behaviors that suffer from it — never propose a numeric redistribution.
4b. Build-vs-document decision (off-gate adapter test doubles)
When Step 4 identifies one or more components needing an off-gate adapter test double (today: a testcontainers-based real-DB test, or a downstream-service adapter test), ask the operator once per run, batched across every such component regardless of adapter kind — this is the shared prompt every adapter-kind slice appends to, not a new prompt each run; the Downstream-service branch below, including its record-and-replay-library sub-question, is one such append, already shipped, not future work. The one prompt lists every such component and asks the operator, for each one, to choose exactly one of three options — there is no separate follow-up sub-question (a Build (Fake) row's construction-method sub-question is carried in this same prompt and reply — see the Downstream-service branch below):
- Build (testcontainers) — propose a downstream Story for a real-dependency test using testcontainers (exact Story shape per the branch subsection below).
- Build (Fake) — propose a downstream Story for a hand-rolled Fake double (exact Story shape per the branch subsection below).
- Document-only (default) — the recommendation lands in the report as today, with no further action.
The operator answers with one of these three choices for each listed component in a single reply — one prompt is surfaced per run, carrying a per-component three-way answer, not one verdict applied to every component in the batch.
Non-interactive runs (per human-oversight-protocol's --yes / DEV_TEAM_AUTO_APPROVE=1 / no-TTY convention) skip the prompt entirely — no prompt is surfaced, and every such component's recommendation lands in the report only, document-only, exactly as today.
An ambiguous or absent answer for any component in the batched three-way question — for example "maybe", "I'm not sure", a bare "yes" or "no", an unqualified "build" that does not name which of the two Build options (none of these map to one of the three labeled options), or silence/empty input — defaults to Document-only for that component, same as any other ambiguous answer: never guessed, never blocked on.
Repos with no such gap see no behavior change: no prompt is asked, and the report is unchanged.
Database-specific branch
For the database-IS-the-SUT band, the three-way answer directly determines the outcome per component:
- Build (testcontainers) — propose a Story titled
[<component>] Add testcontainers-based real-DB test. Its description names Database Sandbox isolation and both teardown options — Transaction Rollback and Table Truncation — and cites database-test-patterns.md.
- Build (Fake) — propose a Story titled
[<component>] Add hand-rolled Fake database double. Its description names the Fake as an in-memory repository implementing the same interface, per test-doubles.md's Fake taxonomy, cites both database-test-patterns.md and test-doubles.md, and always carries this caveat verbatim: "Caveat: this hand-rolled Fake cannot verify actual SQL, mapping, or schema correctness the way a real-engine test can — a deliberate coverage trade-off, not a silent downgrade."
- Document-only — the recommendation lands in the report only, as today; no further action.
Downstream-service branch
Applies to API Consumer, Event Consumer, and Event Producer components (component-test-patterns.md) that Step 4 flagged for the same off-gate adapter-test-double decision as the database branch above — added to the same batched per-run prompt, not a second prompt: the shared prompt above already states later adapter-kind slices append here, and this is that append.
Every row offers all three options identically, with no ownership-based distinction: Build (testcontainers), Build (Fake), Document-only.
All off-gate validation of a downstream-service test double runs out-of-band, on a schedule — never pre-merge/in-band — regardless of whether the dependency is team-controlled or third-party. This applies uniformly to the testcontainers-based adapter-integration test, the hand-rolled Fake's companion provider-contract verification, and the library-backed double's re-record/diff companion action alike, per component-test-patterns.md's and cd-test-architecture.md's Integration-test placement rule: a real dependency is non-deterministic regardless of who owns it, so it never earns an in-band exception.
An ambiguous or absent answer for a downstream-service row defaults to Document-only, the same ambiguous-answer rule as any other ambiguous answer above — not a new rule.
When the top-level answer for a row is Build (Fake), a second, narrower sub-question resolves which construction method to use — asked in the same single batched prompt and reply as the top-level three-way choice above, never a follow-up round-trip. The sub-question applies only to rows where the operator chose (or defaulted to) Build (Fake); a Build (testcontainers) or Document-only row carries no such sub-question, and any install/decline preference volunteered for either of those rows is ignored, not an error. The answer grammar is explicit and three-valued, not binary: (a) accept the recommended virtual-service library (the existing-tool-detected or catalog default, per virtual-service-libraries.md's Resolution order), (b) name a specific different library, or (c) decline (hand-rolled Fake, the backup).
An ambiguous or absent sub-answer defaults to (a), installing the recommended library — the preferred solution — not (c) decline. This is a distinct, narrower default rule from the top-level three-way choice's own ambiguous-answer default stated above, which remains Document-only, unchanged: the library is preferred and hand-rolled is backup, so the safer default when this narrower sub-question is unspecified is the preferred option, not the fallback. An operator's explicit-accept answer (e.g. "Build (Fake), accept") installs the recommended library identically to this ambiguous/absent-answer case.
For an Event Consumer/Producer row whose broker protocol has no protocol-appropriate virtual-service library (per virtual-service-libraries.md's broker-tooling note), this sub-question is not presented at all — the row states plainly that no such library exists for its protocol, and defaults to the hand-rolled Fake Story below, with no tool name substituted.
Each selected option proposes a Story, following the database branch's shape:
- Build (testcontainers) — propose a Story titled
[<component>] Add testcontainers-based adapter integration test. Its description exercises the real outbound client against a testcontainers-hosted, protocol-compatible instance, states the test runs out-of-band on a schedule (never pre-merge/in-band), and cites the relevant component-test-patterns.md pattern (API Consumer / Event Consumer / Event Producer — name whichever applies). (Maintainer note, not emitted Story content: #1435 deliberately scoped the record-and-replay library-preference sub-question below to the Build (Fake) row only — this bullet carries no tool-selection language, enforced by test_sub_question_only_applies_to_build_fake_row in tests/skills/test_cd_test_architecture_virtual_service_libraries.py.)
- Build (Fake) — propose a Story titled
[<component>] Add hand-rolled Fake downstream-service double. Its description names a hand-rolled Fake behind a team-owned thin adapter, per test-doubles.md's Fake taxonomy, and cites component-test-patterns.md. When the sub-answer above resolves to (c) decline, the Story always carries this caveat verbatim: "Caveat: this hand-rolled Fake cannot verify that the adapter actually satisfies the real service's wire contract — pair it with scheduled provider-contract verification against the provider's real environment, per the API Consumer / Event Consumer / Event Producer patterns." The Story always — no ownership-based exemption — additionally proposes scheduled, out-of-band provider-contract verification against the dependency's real environment as a companion action, not a fourth option. When the sub-answer above resolves to installing a library instead (default, named override, or explicit accept), the Story title instead becomes [<component>] Add <tool-name>-backed downstream-service double (e.g. [<component>] Add Nock-backed downstream-service double), naming the concrete tool chosen, and its description states the library-backed double runs in-band (pre-merge); this library-backed outcome carries its own one-line caveat in place of the hand-rolled-Fake wire-contract caveat above — no wire-contract caveat is needed for a library-backed double, because the library's own off-gate re-record/diff validation (below) is what keeps it honest, not a hand-rolled assumption about the wire contract; the same scheduled provider-contract-verification companion action described above is unchanged in count — still one, not a second, additional off-gate test artifact — only its mechanism changes: for a library-backed double, that action is the library's own re-record/diff capability against the real dependency (when the chosen tool has one), in place of a hand-rolled contract test. When the chosen library has no re-record/diff capability, the companion action reverts to the hand-rolled provider-contract verification described earlier in this bullet — still one artifact, not a second one.
5. Produce a migration path
Order the moves from current → target, lowest-risk first. The spine is baseline before refactor: get behavior under test at existing seams without changing code, then refactor under that green baseline (never behavior + structure in one step). When tests are out-of-repo (Step 2b — locate and harvest out-of-repo tests), the harvested behaviors feed that baseline. Typical full sequence:
- Characterization baseline (no refactoring) — at the outermost reachable seam, write outside-in tests that lock in current behavior; harvest any out-of-repo/manual behaviors into this inventory and reproduce them here. Get green.
- Introduce owned adapters and seams — under the baseline (Adapter Rule;
testability-patterns.md; DDD skills suggest where boundaries/seams belong). Refactor only with the baseline green.
- Add in-memory doubles + deterministic component tests reproducing the baselined behaviors.
- Add contract tests pinning request/response boundaries.
- Add consumer resilience tests (survive a provider break).
- Add scheduled provider-contract verification against a test environment.
- Move real-dependency tests off the gate to adapter-integration / out-of-band.
- Add post-deploy checks.
- Decommission the out-of-repo/manual suites and the coarse characterization tests as their behaviors land in the deterministic gate.
Each step is behavior-preserving and independently shippable.
6. Report
Write the assessment (see Output). Keep every recommendation tied to a concrete next action.
Report the surface-coverage gate (issue #1464). Step 1's "Exhaustive
surface-type discovery is the mandatory default" mandate is backed by the
same coverage-gate script /gherkin-derive uses for its own ## Analysis Coverage section (issue #1450), run here against this skill's own
### Components & patterns section in its cd-test-architecture
configuration:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/gherkin_analysis_coverage_gate.py" --file <report-path> --config cd-test-architecture
Print the gate's result as its own report section: OK: all 8 component-pattern categories recorded when it exits 0, or N component-pattern categor(y/ies) missing from the coverage record, listing
each missing category, when it exits 1. A third outcome exists — exit 2
means the gate did not run (no ### Components & patterns section was
found in the report, or the report file itself is missing); report this as
"gate did not run — no Components & patterns section found in <path>,
re-check the assessment report," never as an OK/all-clear (a scan that
never found the section is not the same as a section with nothing
missing).
Output
Write to .dev-team-reports/cd-test-architecture-<app>.md (or chat for a single component).
When --pdf was passed and a report file was written, render it to a
sibling PDF per knowledge/report-pdf-integration.md:
sh "$CLAUDE_PLUGIN_ROOT/hooks/py.sh" "$CLAUDE_PLUGIN_ROOT/hooks/lib/report_pdf.py" .dev-team-reports/cd-test-architecture-<app>.md
In the single-component chat-only case no file was written, so --pdf is a
no-op: state --pdf: no report file was written this run, nothing to render.
and do nothing else. Additive; non-fatal if no engine is available.
--pdf renders the main assessment report only; the setup guide (below) is
not rendered to PDF.
For the header block and closing Provenance section, follow
knowledge/report-template.md; the sections below are this skill's own
body.
# CD Test Architecture
**Date**: <ISO 8601>
**Target**: <app>
**Tool versions**: _Not applicable — no tool versions apply to this assessment._
**Scope**: <full app | single component>
## CD Test Architecture — <app>
### Components & patterns
| Component | Pattern | Surfaces |
Include a row for every pattern type actively searched for in Step 1 (issue
#1464) — even when no surface of that type was found (`None found` rather
than an omitted row) — so the report shows search coverage, not just found
surfaces.
### Current tests (in-repo)
| Suite | MinimumCD type | Deterministic? | Requires to run | Pre-merge-safe? |
### Out-of-repo / external test sources (if any)
| Source (repo / Postman / manual / …) | Location | Behaviors it covers | Why it's an anti-pattern here |
### CD-fitness gaps
| Gap | Type | Evidence (file) | Impact |
### Testable seams & achievable baseline (under-tested components)
| Component | Outermost seam | Best outside-in test writable today (no refactor) |
### Target architecture (per component)
| Component | Layer | Test type | Double (to run config-free) | Pipeline stage | Build/Document status |
`Build/Document status` is one of `Build (testcontainers)`, `Build (Fake)`, or `Document-only` (set per Step 4b) for a row Step 4 flagged as needing an off-gate adapter test double. A row Step 4 did not flag for that decision (e.g. a component/layer already pre-merge-gate-eligible with no real dependency to double) carries **no value in this column at all** — left blank, never one of the three enum values and never guessed. This blank-vs-set distinction is the literal, table-level signal for which rows are off-gate-eligible; cites this sentence rather than re-deriving its own eligibility rule. A row whose status is also carries that row's branch-specific caveat, verbatim, in the same cell — see the Database-specific branch or Downstream-service branch above for the exact wording.
When Step 0 loaded a profile, (and any reference it points at) in the or column so the concrete tool choice is traceable to the stack-specific reference.
When a Downstream-service row resolves to a virtual-service library (default, named override, or explicit accept), cite the chosen tool (e.g. Nock, WireMock.Net) in this same column — no new column or enum value is added for it.
… → 2. … (lowest-risk first, each independently shippable)
Refactor/seams → /plan or /build
Per-file smells → /test-design
Repository:
Branch / SHA: /
Run parameters:
plugin version:
Companion: test-double setup guide
Whenever Step 4's Target architecture table recommends a testcontainers-based
test, a virtual-service-library-backed double, or a hand-rolled fallback for
any component, write a companion test-double setup guide — independent of
that component's Step 4b Build/Document-only outcome: this applies even when
the operator chose Document-only for that component, or the run was
non-interactive, and the setup guide still includes that component's section
exactly as it would if the operator had chosen to build it.
Path. Write the guide to
.dev-team-reports/cd-test-architecture-<app>-test-double-setup.md — the
main report's own filename (cd-test-architecture-<app>.md) with its .md
extension replaced by -test-double-setup.md, same directory and naming
convention as the main report, never a legacy path outside
.dev-team-reports/. In the single-component chat-only case (the
main report goes to chat, not a file), the setup guide's content is appended
to that same chat output instead of written as a second file. When no
component in the run falls into this off-gate adapter-double decision space,
write no setup-guide file or chat section at all — never an empty one.
Header and Provenance. For the header block and closing Provenance
section, follow knowledge/report-template.md; the per-component sections
below are this guide's own body. **Scope** reads the components covered by
this guide — the components with a per-component section below, not the
full application inventory from Step 1.
Per-component sections. One section per component — even when two
components resolve to the same tool, each still gets its own section with
its own component-specific example prompt. A non-off-gate component (no
adapter-double recommendation at all) gets no section. Each section has:
- Tool name + one-line purpose.
- Doc link to the tool's official documentation — omitted only for the
hand-rolled-fallback case (there is nothing to link to for hand-rolled
code); the other three elements still apply to that case.
- Configuration steps that cite — never restate — the same
test-stack-profiles/<stack>.md entry (and, for the virtual-service-
library case, the virtual-service-libraries.md entry) Step 4 already
resolved for that component's Double column. When Step 0 resolved no
stack profile for the component, state that plainly (e.g. "No matching
stack profile for this component") rather than citing a profile that was
never resolved.
- One concrete, ready-to-run example prompt naming the actual component
and tool — never a generic template placeholder.
- The closing command
/apply-test-doubles <path>, where <path> is
always the main assessment report's own resolved path
(.dev-team-reports/cd-test-architecture-<app>.md), never the setup
guide's own path — per ../apply-test-doubles/SKILL.md's own Parse
Arguments section, not restated here. In the
single-component chat-only case, there is no saved main-report file to
substitute for <path> — the assessment output is in this chat session,
not a saved file — so the closing command is emitted with no path
argument (plain /apply-test-doubles), never falling back to the setup
guide's own path.
Classification — cited from Step 4b's branches above, not invented here:
- Testcontainers (Database-specific or Downstream-service branch,
Build (testcontainers)) — name the testcontainers tool.
- Virtual-service library (Downstream-service branch,
Build (Fake)
resolving to a library) — name whichever tool Step 4b's Downstream-service
branch's construction-method sub-question resolved for this component
(see that branch above — not restated here). Caveat: this case's
configuration steps must also cite virtual-service-libraries.md's
"Recorded artifacts contain real traffic — scrub before commit" section
(in addition to the per-tool catalog entry) and name that tool's
credential-filtering hook as a step ordered before the first recording,
never after.
- Hand-rolled fallback (
Build (Fake) resolving to hand-rolled, or Step
4b's Downstream-service branch's no-matching-library fallback applying to
this component's adapter kind — see that branch above, not restated here)
— no doc link; the other three elements (name+purpose, configuration
steps, and the example prompt) still describe the specific
fake/contract-test to write for that component.
Downstream-service components with no posed sub-question. For a
Downstream-service component whose construction-method sub-question was
never posed or answered (the operator chose Document-only, the run was
non-interactive, or the top-level answer was ambiguous), classify it using
the same tool-resolution rule Step 4b's sub-question applies when it does
fire — see the Downstream-service branch above for what determines the
tool, not restated here — applied to rows that never got a sub-question in
the first place, because this guide's trigger is independent of the
Build/Document-only outcome, never a guess and never omitted. It falls back
to the Hand-rolled fallback classification above only when Step 4b's own
no-matching-library condition applies for this component's adapter kind —
again, see that branch, not restated here. This three-case list is
illustrative of "no sub-question ever reached this row"; it excludes the
separate case where a Build (Fake) row's sub-question WAS posed but got an
ambiguous or absent answer, which Step 4b's own stated default for that case
already resolves (see that branch above), not this fallback. A
volunteered-but-ignored install/decline preference on a Document-only row is
likewise not honored by this fallback either — per Step 4b's own "ignored,
not an error" framing, it defers to Step 4b's own resolution rule above (not
restated here), never the operator's stated-but-disregarded preference.
Integration
- Pairs with
test-design-advisor (unit/module design) and the test-smell-review / test-review agents (per-file findings). This skill sets the application-level target those operate within.
- For under-tested/legacy components, the characterization-baseline-then-refactor procedure is the
legacy-code skill (Feathers' algorithm: change points → test points/seams → break dependencies → characterization tests → refactor under green). Defer the mechanics to it.
- Use the
domain-driven-design and domain-analysis skills to suggest the target structure for the post-baseline refactor — where bounded contexts, ports, and seams should land — so refactoring improves the domain model, not just testability.
- Hand the migration path to
/plan or /build for TDD implementation. This skill stops at the architecture and plan, except that Step 4b may propose a downstream Story as part of that hand-off — it still never invokes /build itself.