@nathapp/nax 0.27.1 → 0.28.0

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.
Files changed (41) hide show
  1. package/docs/ROADMAP.md +29 -3
  2. package/nax/features/prompt-builder/prd.json +152 -0
  3. package/nax/features/prompt-builder/progress.txt +3 -0
  4. package/nax/status.json +14 -14
  5. package/package.json +1 -1
  6. package/src/cli/config.ts +40 -1
  7. package/src/cli/prompts.ts +18 -6
  8. package/src/config/defaults.ts +1 -0
  9. package/src/config/schemas.ts +10 -0
  10. package/src/config/types.ts +7 -0
  11. package/src/pipeline/stages/execution.ts +5 -0
  12. package/src/pipeline/stages/prompt.ts +13 -4
  13. package/src/precheck/checks-warnings.ts +37 -0
  14. package/src/precheck/checks.ts +1 -0
  15. package/src/precheck/index.ts +14 -7
  16. package/src/prompts/builder.ts +178 -0
  17. package/src/prompts/index.ts +2 -0
  18. package/src/prompts/loader.ts +43 -0
  19. package/src/prompts/sections/conventions.ts +15 -0
  20. package/src/prompts/sections/index.ts +11 -0
  21. package/src/prompts/sections/isolation.ts +24 -0
  22. package/src/prompts/sections/role-task.ts +32 -0
  23. package/src/prompts/sections/story.ts +13 -0
  24. package/src/prompts/sections/verdict.ts +70 -0
  25. package/src/prompts/templates/implementer.ts +6 -0
  26. package/src/prompts/templates/single-session.ts +6 -0
  27. package/src/prompts/templates/test-writer.ts +6 -0
  28. package/src/prompts/templates/verifier.ts +6 -0
  29. package/src/prompts/types.ts +21 -0
  30. package/src/tdd/session-runner.ts +12 -12
  31. package/test/integration/cli/cli-config-prompts-explain.test.ts +74 -0
  32. package/test/integration/prompts/pb-004-migration.test.ts +523 -0
  33. package/test/unit/precheck/checks-warnings.test.ts +114 -0
  34. package/test/unit/prompts/builder.test.ts +258 -0
  35. package/test/unit/prompts/loader.test.ts +355 -0
  36. package/test/unit/prompts/sections/conventions.test.ts +30 -0
  37. package/test/unit/prompts/sections/isolation.test.ts +35 -0
  38. package/test/unit/prompts/sections/role-task.test.ts +40 -0
  39. package/test/unit/prompts/sections/sections.test.ts +238 -0
  40. package/test/unit/prompts/sections/story.test.ts +45 -0
  41. package/test/unit/prompts/sections/verdict.test.ts +58 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * PromptBuilder — unified entry point for composing agent prompts.
3
+ *
4
+ * Composes prompts from ordered sections:
5
+ * (1) Constitution
6
+ * (2) Role task body (user override OR default template)
7
+ * (3) Story context [non-overridable]
8
+ * (4) Isolation rules [non-overridable]
9
+ * (5) Context markdown
10
+ * (6) Conventions footer [non-overridable, always last]
11
+ */
12
+
13
+ import type { NaxConfig } from "../config/types";
14
+ import type { UserStory } from "../prd";
15
+ import type { PromptOptions, PromptRole } from "./types";
16
+
17
+ const SECTION_SEP = "\n\n---\n\n";
18
+
19
+ export class PromptBuilder {
20
+ private _role: PromptRole;
21
+ private _options: PromptOptions;
22
+ private _story: UserStory | undefined;
23
+ private _contextMd: string | undefined;
24
+ private _constitution: string | undefined;
25
+ private _overridePath: string | undefined;
26
+ private _workdir: string | undefined;
27
+ private _loaderConfig: NaxConfig | undefined;
28
+
29
+ private constructor(role: PromptRole, options: PromptOptions = {}) {
30
+ this._role = role;
31
+ this._options = options;
32
+ }
33
+
34
+ static for(role: PromptRole, options?: PromptOptions): PromptBuilder {
35
+ return new PromptBuilder(role, options ?? {});
36
+ }
37
+
38
+ story(story: UserStory): PromptBuilder {
39
+ this._story = story;
40
+ return this;
41
+ }
42
+
43
+ context(md: string | undefined): PromptBuilder {
44
+ if (md) this._contextMd = md;
45
+ return this;
46
+ }
47
+
48
+ constitution(c: string | undefined): PromptBuilder {
49
+ if (c) this._constitution = c;
50
+ return this;
51
+ }
52
+
53
+ override(path: string): PromptBuilder {
54
+ this._overridePath = path;
55
+ return this;
56
+ }
57
+
58
+ withLoader(workdir: string, config: NaxConfig): PromptBuilder {
59
+ this._workdir = workdir;
60
+ this._loaderConfig = config;
61
+ return this;
62
+ }
63
+
64
+ async build(): Promise<string> {
65
+ const sections: string[] = [];
66
+
67
+ // (1) Constitution
68
+ if (this._constitution) {
69
+ sections.push(`# CONSTITUTION (follow these rules strictly)\n\n${this._constitution}`);
70
+ }
71
+
72
+ // (2) Role task body — user override or default template
73
+ sections.push(await this._resolveRoleBody());
74
+
75
+ // (3) Story context — non-overridable
76
+ if (this._story) {
77
+ sections.push(buildStoryContext(this._story));
78
+ }
79
+
80
+ // (4) Isolation rules — non-overridable
81
+ sections.push(buildIsolationRules(this._role, this._options));
82
+
83
+ // (5) Context markdown
84
+ if (this._contextMd) {
85
+ sections.push(this._contextMd);
86
+ }
87
+
88
+ // (6) Conventions footer — non-overridable, always last
89
+ sections.push(CONVENTIONS_FOOTER);
90
+
91
+ return sections.join(SECTION_SEP);
92
+ }
93
+
94
+ private async _resolveRoleBody(): Promise<string> {
95
+ // withLoader takes priority over explicit override path
96
+ if (this._workdir && this._loaderConfig) {
97
+ const { loadOverride } = await import("./loader");
98
+ const content = await loadOverride(this._role, this._workdir, this._loaderConfig);
99
+ if (content !== null) {
100
+ return content;
101
+ }
102
+ }
103
+ if (this._overridePath) {
104
+ try {
105
+ const file = Bun.file(this._overridePath);
106
+ if (await file.exists()) {
107
+ return await file.text();
108
+ }
109
+ } catch {
110
+ // fall through to default template
111
+ }
112
+ }
113
+ return buildDefaultRoleBody(this._role, this._story?.title, this._options);
114
+ }
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Section builders (module-private)
119
+ // ---------------------------------------------------------------------------
120
+
121
+ function buildDefaultRoleBody(role: PromptRole, title = "", options: PromptOptions = {}): string {
122
+ const variant = options.variant as string | undefined;
123
+ switch (role) {
124
+ case "test-writer":
125
+ return `# Test Writer — "${title}"\n\nYour role: Write failing tests ONLY. Do NOT implement any source code.`;
126
+ case "implementer":
127
+ if (variant === "lite") {
128
+ return `# Implementer (Lite) — "${title}"\n\nYour role: Write tests AND implement the feature in a single session.`;
129
+ }
130
+ return `# Implementer — "${title}"\n\nYour role: Make all failing tests pass.`;
131
+ case "verifier":
132
+ return `# Verifier — "${title}"\n\nYour role: Verify the implementation and tests.`;
133
+ case "single-session":
134
+ return `# Task — "${title}"\n\nYour role: Write tests AND implement the feature in a single session.`;
135
+ }
136
+ }
137
+
138
+ function buildStoryContext(story: UserStory): string {
139
+ return `# Story Context
140
+
141
+ **Story:** ${story.title}
142
+
143
+ **Description:**
144
+ ${story.description}
145
+
146
+ **Acceptance Criteria:**
147
+ ${story.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join("\n")}`;
148
+ }
149
+
150
+ const TEST_FILTER_RULE =
151
+ "When running tests, run ONLY test files related to your changes" +
152
+ " (e.g. `bun test ./test/specific.test.ts`). NEVER run `bun test` without a file filter" +
153
+ " — full suite output will flood your context window and cause failures.";
154
+
155
+ function buildIsolationRules(role: PromptRole, options: PromptOptions = {}): string {
156
+ const header = "# Isolation Rules\n\n";
157
+ const footer = `\n\n${TEST_FILTER_RULE}`;
158
+ const isolation = options.isolation as string | undefined;
159
+
160
+ switch (role) {
161
+ case "test-writer":
162
+ if (isolation === "lite") {
163
+ return `${header}isolation scope: Primarily create test files in the test/ directory. You MAY read source files and MAY import from source files to ensure correct types/interfaces. Stub-only src/ files are allowed (empty exports, no logic). Tests must fail for the right reasons (feature not implemented).${footer}`;
164
+ }
165
+ return `${header}isolation scope: Only create or modify files in the test/ directory. Tests must fail because the feature is not yet implemented. Do NOT modify any source files in src/.${footer}`;
166
+ case "implementer":
167
+ return `${header}isolation scope: Implement source code in src/ to make the tests pass. Do NOT modify test files. Run tests frequently to track progress.${footer}`;
168
+ case "verifier":
169
+ return `${header}isolation scope: Verify and fix only — do not change behaviour unless it violates acceptance criteria. Ensure all tests pass and all criteria are met.${footer}`;
170
+ case "single-session":
171
+ return `${header}isolation scope: Write tests first (test/ directory), then implement (src/ directory). All tests must pass by the end.${footer}`;
172
+ }
173
+ }
174
+
175
+ const CONVENTIONS_FOOTER =
176
+ "# Conventions\n\n" +
177
+ "Follow existing code patterns and conventions. Write idiomatic, maintainable code." +
178
+ " Commit your changes when done.";
@@ -0,0 +1,2 @@
1
+ export { PromptBuilder } from "./builder";
2
+ export type { PromptRole, PromptSection, PromptOptions } from "./types";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Prompt Override Loader
3
+ *
4
+ * Resolves and reads user-supplied override files relative to workdir.
5
+ */
6
+
7
+ import { join } from "node:path";
8
+ import type { NaxConfig } from "../config/types";
9
+ import type { PromptRole } from "./types";
10
+
11
+ /**
12
+ * Load a user override for the given role from the path specified in config.
13
+ *
14
+ * @param role - The prompt role
15
+ * @param workdir - The project working directory
16
+ * @param config - The merged NaxConfig
17
+ * @returns The override file contents, or null if absent/missing
18
+ * @throws Error when file path is set but file is unreadable (e.g. permissions error)
19
+ */
20
+ export async function loadOverride(role: PromptRole, workdir: string, config: NaxConfig): Promise<string | null> {
21
+ const overridePath = config.prompts?.overrides?.[role];
22
+
23
+ if (!overridePath) {
24
+ return null;
25
+ }
26
+
27
+ const absolutePath = join(workdir, overridePath);
28
+ const file = Bun.file(absolutePath);
29
+
30
+ if (!(await file.exists())) {
31
+ return null;
32
+ }
33
+
34
+ try {
35
+ return await file.text();
36
+ } catch (err) {
37
+ throw new Error(
38
+ `Cannot read prompt override for role "${role}" at "${absolutePath}": ${
39
+ err instanceof Error ? err.message : String(err)
40
+ }`,
41
+ );
42
+ }
43
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Conventions Section
3
+ *
4
+ * Includes bun test scoping warning and commit message instructions (non-overridable).
5
+ */
6
+
7
+ export function buildConventionsSection(): string {
8
+ return (
9
+ "# Conventions\n\n" +
10
+ "Follow existing code patterns and conventions. Write idiomatic, maintainable code.\n\n" +
11
+ "When running tests, run ONLY test files related to your changes (e.g. `bun test ./test/specific.test.ts`). " +
12
+ "NEVER run `bun test` without a file filter — full suite output will flood your context window and cause failures.\n\n" +
13
+ "Commit your changes when done using conventional commit format (e.g. `feat:`, `fix:`, `test:`)."
14
+ );
15
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Prompt Sections
3
+ *
4
+ * Non-overridable section builders for the PromptBuilder.
5
+ */
6
+
7
+ export { buildIsolationSection } from "./isolation";
8
+ export { buildRoleTaskSection } from "./role-task";
9
+ export { buildStorySection } from "./story";
10
+ export { buildVerdictSection } from "./verdict";
11
+ export { buildConventionsSection } from "./conventions";
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Isolation Rules Section
3
+ *
4
+ * Generates isolation rules based on mode:
5
+ * - strict: No access to src/ files
6
+ * - lite: May read src/ and create minimal stubs
7
+ */
8
+
9
+ const TEST_FILTER_RULE =
10
+ "When running tests, run ONLY test files related to your changes " +
11
+ "(e.g. `bun test ./test/specific.test.ts`). NEVER run `bun test` without a file filter " +
12
+ "— full suite output will flood your context window and cause failures.";
13
+
14
+ export function buildIsolationSection(mode: "strict" | "lite"): string {
15
+ const header = "# Isolation Rules\n\n";
16
+ const footer = `\n\n${TEST_FILTER_RULE}`;
17
+
18
+ if (mode === "strict") {
19
+ return `${header}isolation scope: Isolation scope: Only create or modify files in the test/ directory. Tests must fail because the feature is not yet implemented. Do NOT modify any source files in src/.${footer}`;
20
+ }
21
+
22
+ // lite mode
23
+ return `${header}isolation scope: Create test files in test/. MAY read src/ files and MAY import from src/ to ensure correct types/interfaces. May create minimal stubs in src/ if needed to make imports work, but do NOT implement real logic.${footer}`;
24
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Role-Task Section
3
+ *
4
+ * Generates role definition for:
5
+ * - standard: Make failing tests pass (implementer role)
6
+ * - lite: Write tests first then implement (combined role)
7
+ */
8
+
9
+ export function buildRoleTaskSection(variant: "standard" | "lite"): string {
10
+ if (variant === "standard") {
11
+ return (
12
+ "# Role: Implementer\n\n" +
13
+ "Your task: make failing tests pass.\n\n" +
14
+ "Instructions:\n" +
15
+ "- Implement source code in src/ to make tests pass\n" +
16
+ "- Do NOT modify test files\n" +
17
+ "- Run tests frequently to track progress\n" +
18
+ "- Goal: all tests green"
19
+ );
20
+ }
21
+
22
+ // lite variant
23
+ return (
24
+ "# Role: Implementer (Lite)\n\n" +
25
+ "Your task: Write tests AND implement the feature in a single session.\n\n" +
26
+ "Instructions:\n" +
27
+ "- Write tests first (test/ directory), then implement (src/ directory)\n" +
28
+ "- All tests must pass by the end\n" +
29
+ "- Use Bun test (describe/test/expect)\n" +
30
+ "- Goal: all tests green, all criteria met"
31
+ );
32
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Story Section
3
+ *
4
+ * Formats story title, description, and numbered acceptance criteria.
5
+ */
6
+
7
+ import type { UserStory } from "../../prd/types";
8
+
9
+ export function buildStorySection(story: UserStory): string {
10
+ const criteria = story.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join("\n");
11
+
12
+ return `# Story Context\n\n**Story:** ${story.title}\n\n**Description:**\n${story.description}\n\n**Acceptance Criteria:**\n${criteria}`;
13
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Verdict Section
3
+ *
4
+ * Verifier verdict JSON schema instructions (non-overridable).
5
+ * Provides instructions for writing the .nax-verifier-verdict.json file.
6
+ */
7
+
8
+ import type { UserStory } from "../../prd/types";
9
+
10
+ export function buildVerdictSection(story: UserStory): string {
11
+ return `# Verdict Instructions
12
+
13
+ ## Write Verdict File
14
+
15
+ After completing your verification, you **MUST** write a verdict file at the **project root**:
16
+
17
+ **File:** \`.nax-verifier-verdict.json\`
18
+
19
+ Set \`approved: true\` when ALL of these conditions are met:
20
+ - All tests pass
21
+ - Implementation is clean and follows conventions
22
+ - All acceptance criteria met
23
+ - Any test modifications by implementer are legitimate fixes
24
+
25
+ Set \`approved: false\` when ANY of these conditions are true:
26
+ - Tests are failing and you cannot fix them
27
+ - The implementer loosened test assertions to mask bugs
28
+ - Critical acceptance criteria are not met
29
+ - Code quality is poor (security issues, severe bugs, etc.)
30
+
31
+ **Full JSON schema example** (fill in all fields with real values):
32
+
33
+ \`\`\`json
34
+ {
35
+ "version": 1,
36
+ "approved": true,
37
+ "tests": {
38
+ "allPassing": true,
39
+ "passCount": 42,
40
+ "failCount": 0
41
+ },
42
+ "testModifications": {
43
+ "detected": false,
44
+ "files": [],
45
+ "legitimate": true,
46
+ "reasoning": "No test files were modified by the implementer"
47
+ },
48
+ "acceptanceCriteria": {
49
+ "allMet": true,
50
+ "criteria": [
51
+ { "criterion": "Example criterion", "met": true }
52
+ ]
53
+ },
54
+ "quality": {
55
+ "rating": "good",
56
+ "issues": []
57
+ },
58
+ "fixes": [],
59
+ "reasoning": "All tests pass, implementation is clean, all acceptance criteria are met."
60
+ }
61
+ \`\`\`
62
+
63
+ **Field notes:**
64
+ - \`quality.rating\` must be one of: \`"good"\`, \`"acceptable"\`, \`"poor"\`
65
+ - \`testModifications.files\` — list any test files the implementer changed
66
+ - \`fixes\` — list any fixes you applied yourself during this verification session
67
+ - \`reasoning\` — brief summary of your overall assessment
68
+
69
+ When done, commit any fixes with message: "fix: verify and adjust ${story.title}"`;
70
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default template body for the implementer role.
3
+ * Stub — logic not yet implemented.
4
+ */
5
+
6
+ export const defaultTemplate = "";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default template body for the single-session role.
3
+ * Stub — logic not yet implemented.
4
+ */
5
+
6
+ export const defaultTemplate = "";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default template body for the test-writer role.
3
+ * Stub — logic not yet implemented.
4
+ */
5
+
6
+ export const defaultTemplate = "";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default template body for the verifier role.
3
+ * Stub — logic not yet implemented.
4
+ */
5
+
6
+ export const defaultTemplate = "";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * PromptBuilder Types
3
+ *
4
+ * Shared types for the unified prompt building system.
5
+ */
6
+
7
+ /** Role determining which default template body to use */
8
+ export type PromptRole = "test-writer" | "implementer" | "verifier" | "single-session";
9
+
10
+ /** A single section of a composed prompt */
11
+ export interface PromptSection {
12
+ /** Unique section identifier */
13
+ id: string;
14
+ /** Section content */
15
+ content: string;
16
+ /** Whether this section can be removed by user override */
17
+ overridable: boolean;
18
+ }
19
+
20
+ /** Options passed to PromptBuilder.for() */
21
+ export type PromptOptions = Record<string, unknown>;
@@ -9,15 +9,9 @@ import type { ModelTier, NaxConfig } from "../config";
9
9
  import { resolveModel } from "../config";
10
10
  import { getLogger } from "../logger";
11
11
  import type { UserStory } from "../prd";
12
+ import { PromptBuilder } from "../prompts";
12
13
  import { cleanupProcessTree } from "./cleanup";
13
14
  import { getChangedFiles, verifyImplementerIsolation, verifyTestWriterIsolation } from "./isolation";
14
- import {
15
- buildImplementerLitePrompt,
16
- buildImplementerPrompt,
17
- buildTestWriterLitePrompt,
18
- buildTestWriterPrompt,
19
- buildVerifierPrompt,
20
- } from "./prompts";
21
15
  import type { IsolationCheck } from "./types";
22
16
  import type { TddSessionResult, TddSessionRole } from "./types";
23
17
 
@@ -95,15 +89,21 @@ export async function runTddSession(
95
89
  let prompt: string;
96
90
  switch (role) {
97
91
  case "test-writer":
98
- prompt = lite ? buildTestWriterLitePrompt(story, contextMarkdown) : buildTestWriterPrompt(story, contextMarkdown);
92
+ prompt = await PromptBuilder.for("test-writer", { isolation: lite ? "lite" : "strict" })
93
+ .withLoader(workdir, config)
94
+ .story(story)
95
+ .context(contextMarkdown)
96
+ .build();
99
97
  break;
100
98
  case "implementer":
101
- prompt = lite
102
- ? buildImplementerLitePrompt(story, contextMarkdown)
103
- : buildImplementerPrompt(story, contextMarkdown);
99
+ prompt = await PromptBuilder.for("implementer", { variant: lite ? "lite" : "standard" })
100
+ .withLoader(workdir, config)
101
+ .story(story)
102
+ .context(contextMarkdown)
103
+ .build();
104
104
  break;
105
105
  case "verifier":
106
- prompt = buildVerifierPrompt(story);
106
+ prompt = await PromptBuilder.for("verifier").withLoader(workdir, config).story(story).build();
107
107
  break;
108
108
  }
109
109
 
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Integration tests for `nax config --explain` prompts section (PB-005)
3
+ *
4
+ * Verifies that the prompts.overrides config block is documented in the
5
+ * --explain output with example paths.
6
+ */
7
+
8
+ import { mkdtempSync, rmSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
+ import { configCommand } from "../../../src/cli/config";
13
+ import { loadConfig } from "../../../src/config/loader";
14
+
15
+ describe("config --explain: prompts section", () => {
16
+ let tempDir: string;
17
+ let originalCwd: string;
18
+ let consoleOutput: string[];
19
+ let originalConsoleLog: typeof console.log;
20
+
21
+ beforeEach(() => {
22
+ tempDir = mkdtempSync(join(tmpdir(), "nax-config-prompts-test-"));
23
+ originalCwd = process.cwd();
24
+
25
+ consoleOutput = [];
26
+ originalConsoleLog = console.log;
27
+ console.log = (...args: unknown[]) => {
28
+ consoleOutput.push(args.map((a) => String(a)).join(" "));
29
+ };
30
+ });
31
+
32
+ afterEach(() => {
33
+ console.log = originalConsoleLog;
34
+ process.chdir(originalCwd);
35
+ rmSync(tempDir, { recursive: true, force: true });
36
+ });
37
+
38
+ test("explain output includes a prompts section", async () => {
39
+ const config = await loadConfig(tempDir);
40
+ await configCommand(config, { explain: true });
41
+
42
+ const output = consoleOutput.join("\n");
43
+ expect(output).toContain("prompts");
44
+ });
45
+
46
+ test("explain output documents prompts.overrides", async () => {
47
+ const config = await loadConfig(tempDir);
48
+ await configCommand(config, { explain: true });
49
+
50
+ const output = consoleOutput.join("\n");
51
+ expect(output).toContain("prompts.overrides");
52
+ });
53
+
54
+ test("explain output includes example path .nax/prompts/test-writer.md", async () => {
55
+ const config = await loadConfig(tempDir);
56
+ await configCommand(config, { explain: true });
57
+
58
+ const output = consoleOutput.join("\n");
59
+ expect(output).toContain(".nax/prompts/test-writer.md");
60
+ });
61
+
62
+ test("explain output mentions override roles (test-writer, implementer, verifier)", async () => {
63
+ const config = await loadConfig(tempDir);
64
+ await configCommand(config, { explain: true });
65
+
66
+ const output = consoleOutput.join("\n");
67
+ // At least one of the roles should appear in the prompts documentation
68
+ const mentionsRole =
69
+ output.includes("test-writer") ||
70
+ output.includes("implementer") ||
71
+ output.includes("verifier");
72
+ expect(mentionsRole).toBe(true);
73
+ });
74
+ });