@nathapp/nax 0.36.0 → 0.36.2

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 (49) hide show
  1. package/dist/nax.js +543 -154
  2. package/package.json +1 -1
  3. package/src/agents/claude-decompose.ts +3 -3
  4. package/src/cli/constitution.ts +0 -92
  5. package/src/constitution/generator.ts +0 -33
  6. package/src/constitution/index.ts +2 -1
  7. package/src/constitution/loader.ts +1 -13
  8. package/src/context/builder.ts +1 -2
  9. package/src/context/elements.ts +1 -12
  10. package/src/context/index.ts +2 -1
  11. package/src/context/test-scanner.ts +1 -1
  12. package/src/execution/dry-run.ts +1 -1
  13. package/src/execution/escalation/escalation.ts +5 -3
  14. package/src/execution/escalation/tier-escalation.ts +41 -4
  15. package/src/execution/iteration-runner.ts +5 -0
  16. package/src/execution/parallel-executor.ts +293 -9
  17. package/src/execution/parallel.ts +40 -21
  18. package/src/execution/pipeline-result-handler.ts +3 -2
  19. package/src/execution/runner.ts +13 -3
  20. package/src/interaction/chain.ts +17 -1
  21. package/src/metrics/tracker.ts +8 -4
  22. package/src/metrics/types.ts +2 -0
  23. package/src/pipeline/event-bus.ts +1 -1
  24. package/src/pipeline/stages/completion.ts +1 -1
  25. package/src/pipeline/stages/execution.ts +23 -1
  26. package/src/pipeline/stages/verify.ts +8 -1
  27. package/src/pipeline/subscribers/reporters.ts +3 -3
  28. package/src/pipeline/types.ts +4 -0
  29. package/src/plugins/types.ts +1 -1
  30. package/src/prd/types.ts +2 -0
  31. package/src/prompts/builder.ts +13 -6
  32. package/src/prompts/sections/conventions.ts +5 -7
  33. package/src/prompts/sections/isolation.ts +7 -7
  34. package/src/prompts/sections/role-task.ts +64 -64
  35. package/src/review/orchestrator.ts +11 -1
  36. package/src/routing/strategies/llm-prompts.ts +1 -1
  37. package/src/routing/strategies/llm.ts +3 -3
  38. package/src/tdd/index.ts +2 -3
  39. package/src/tdd/isolation.ts +0 -13
  40. package/src/tdd/orchestrator.ts +5 -0
  41. package/src/tdd/prompts.ts +1 -231
  42. package/src/tdd/session-runner.ts +2 -0
  43. package/src/tdd/types.ts +2 -1
  44. package/src/tdd/verdict.ts +20 -2
  45. package/src/verification/crash-detector.ts +34 -0
  46. package/src/verification/orchestrator-types.ts +8 -1
  47. package/src/verification/parser.ts +0 -10
  48. package/src/verification/rectification-loop.ts +2 -51
  49. package/src/worktree/dispatcher.ts +0 -59
@@ -41,7 +41,7 @@ export interface StoryCompletedEvent {
41
41
  storyId: string;
42
42
  story: UserStory;
43
43
  passed: boolean;
44
- durationMs: number;
44
+ runElapsedMs: number;
45
45
  /** Optional: passed by executor/stage for hook/reporter subscribers */
46
46
  cost?: number;
47
47
  modelTier?: string;
@@ -67,7 +67,7 @@ export const completionStage: PipelineStage = {
67
67
  storyId: completedStory.id,
68
68
  story: completedStory,
69
69
  passed: true,
70
- durationMs: storyMetric?.durationMs ?? 0,
70
+ runElapsedMs: storyMetric?.durationMs ?? 0,
71
71
  // Extra fields picked up by subscribers via `as any`
72
72
  cost: costPerStory,
73
73
  modelTier: ctx.routing?.modelTier,
@@ -134,6 +134,7 @@ export const executionStage: PipelineStage = {
134
134
  workdir: ctx.workdir,
135
135
  modelTier: ctx.routing.modelTier,
136
136
  contextMarkdown: ctx.contextMarkdown,
137
+ constitution: ctx.constitution?.content,
137
138
  dryRun: false,
138
139
  lite: isLiteMode,
139
140
  });
@@ -156,7 +157,7 @@ export const executionStage: PipelineStage = {
156
157
  // Store failure category in context for runner to use at max-attempts decision
157
158
  ctx.tddFailureCategory = tddResult.failureCategory;
158
159
 
159
- // Log needsHumanReview context when present
160
+ // Log and notify when human review is needed
160
161
  if (tddResult.needsHumanReview) {
161
162
  logger.warn("execution", "Human review needed", {
162
163
  storyId: ctx.story.id,
@@ -164,6 +165,27 @@ export const executionStage: PipelineStage = {
164
165
  lite: tddResult.lite,
165
166
  failureCategory: tddResult.failureCategory,
166
167
  });
168
+ // Send notification via interaction chain (Telegram in headless mode)
169
+ if (ctx.interaction) {
170
+ try {
171
+ await ctx.interaction.send({
172
+ id: `human-review-${ctx.story.id}-${Date.now()}`,
173
+ type: "notify",
174
+ featureName: ctx.featureDir ? (ctx.featureDir.split("/").pop() ?? "unknown") : "unknown",
175
+ storyId: ctx.story.id,
176
+ stage: "execution",
177
+ summary: `⚠️ Human review needed: ${ctx.story.id}`,
178
+ detail: `Story: ${ctx.story.title}\nReason: ${tddResult.reviewReason ?? "No reason provided"}\nCategory: ${tddResult.failureCategory ?? "unknown"}`,
179
+ fallback: "continue",
180
+ createdAt: Date.now(),
181
+ });
182
+ } catch (notifyErr) {
183
+ logger.warn("execution", "Failed to send human review notification", {
184
+ storyId: ctx.story.id,
185
+ error: String(notifyErr),
186
+ });
187
+ }
188
+ }
167
189
  }
168
190
 
169
191
  return routeTddFailure(tddResult.failureCategory, isLiteMode, ctx, tddResult.reviewReason);
@@ -11,6 +11,7 @@
11
11
 
12
12
  import type { SmartTestRunnerConfig } from "../../config/types";
13
13
  import { getLogger } from "../../logger";
14
+ import { detectRuntimeCrash } from "../../verification/crash-detector";
14
15
  import type { VerifyStatus } from "../../verification/orchestrator-types";
15
16
  import { regression } from "../../verification/runners";
16
17
  import { _smartRunnerDeps } from "../../verification/smart-runner";
@@ -133,7 +134,13 @@ export const verifyStage: PipelineStage = {
133
134
  // Store result on context for rectify stage
134
135
  ctx.verifyResult = {
135
136
  success: result.success,
136
- status: (result.status === "TIMEOUT" ? "TIMEOUT" : result.success ? "PASS" : "TEST_FAILURE") as VerifyStatus,
137
+ status: (result.status === "TIMEOUT"
138
+ ? "TIMEOUT"
139
+ : result.success
140
+ ? "PASS"
141
+ : detectRuntimeCrash(result.output)
142
+ ? "RUNTIME_CRASH"
143
+ : "TEST_FAILURE") as VerifyStatus,
137
144
  storyId: ctx.story.id,
138
145
  strategy: "scoped",
139
146
  passCount: result.passCount ?? 0,
@@ -74,7 +74,7 @@ export function wireReporters(
74
74
  runId,
75
75
  storyId: ev.storyId,
76
76
  status: "completed",
77
- durationMs: ev.durationMs,
77
+ runElapsedMs: ev.runElapsedMs,
78
78
  cost: ev.cost ?? 0,
79
79
  tier: ev.modelTier ?? "balanced",
80
80
  testStrategy: ev.testStrategy ?? "test-after",
@@ -100,7 +100,7 @@ export function wireReporters(
100
100
  runId,
101
101
  storyId: ev.storyId,
102
102
  status: "failed",
103
- durationMs: Date.now() - startTime,
103
+ runElapsedMs: Date.now() - startTime,
104
104
  cost: 0,
105
105
  tier: "balanced",
106
106
  testStrategy: "test-after",
@@ -126,7 +126,7 @@ export function wireReporters(
126
126
  runId,
127
127
  storyId: ev.storyId,
128
128
  status: "paused",
129
- durationMs: Date.now() - startTime,
129
+ runElapsedMs: Date.now() - startTime,
130
130
  cost: 0,
131
131
  tier: "balanced",
132
132
  testStrategy: "test-after",
@@ -110,8 +110,12 @@ export interface PipelineContext {
110
110
  tddFailureCategory?: FailureCategory;
111
111
  /** Set to true when TDD full-suite gate already passed — verify stage skips to avoid redundant run (BUG-054) */
112
112
  fullSuiteGatePassed?: boolean;
113
+ /** Number of runtime crashes (RUNTIME_CRASH verify status) encountered for this story (BUG-070) */
114
+ storyRuntimeCrashes?: number;
113
115
  /** Structured review findings from plugin reviewers — passed to escalation for retry context */
114
116
  reviewFindings?: import("../plugins/types").ReviewFinding[];
117
+ /** Accumulated cost across all prior escalation attempts (BUG-067) */
118
+ accumulatedAttemptCost?: number;
115
119
  }
116
120
 
117
121
  /**
@@ -274,7 +274,7 @@ export interface StoryCompleteEvent {
274
274
  runId: string;
275
275
  storyId: string;
276
276
  status: "completed" | "failed" | "skipped" | "paused";
277
- durationMs: number;
277
+ runElapsedMs: number;
278
278
  cost: number;
279
279
  tier: string;
280
280
  testStrategy: string;
package/src/prd/types.ts CHANGED
@@ -49,6 +49,8 @@ export interface StructuredFailure {
49
49
  testFailures?: TestFailureContext[];
50
50
  /** Structured review findings from plugin reviewers (e.g., semgrep, eslint) */
51
51
  reviewFindings?: import("../plugins/types").ReviewFinding[];
52
+ /** Estimated cost of this attempt (BUG-067: accumulated across escalations) */
53
+ cost?: number;
52
54
  /** ISO timestamp when failure was recorded */
53
55
  timestamp: string;
54
56
  }
@@ -5,9 +5,10 @@
5
5
  * (1) Constitution
6
6
  * (2) Role task body (user override OR default template)
7
7
  * (3) Story context [non-overridable]
8
- * (4) Isolation rules [non-overridable]
9
- * (5) Context markdown
10
- * (6) Conventions footer [non-overridable, always last]
8
+ * (4) Verdict section [verifier only, non-overridable]
9
+ * (5) Isolation rules [non-overridable]
10
+ * (6) Context markdown
11
+ * (7) Conventions footer [non-overridable, always last]
11
12
  */
12
13
 
13
14
  import type { NaxConfig } from "../config/types";
@@ -16,6 +17,7 @@ import { buildConventionsSection } from "./sections/conventions";
16
17
  import { buildIsolationSection } from "./sections/isolation";
17
18
  import { buildRoleTaskSection } from "./sections/role-task";
18
19
  import { buildStorySection } from "./sections/story";
20
+ import { buildVerdictSection } from "./sections/verdict";
19
21
  import type { PromptOptions, PromptRole } from "./types";
20
22
 
21
23
  const SECTION_SEP = "\n\n---\n\n";
@@ -81,16 +83,21 @@ export class PromptBuilder {
81
83
  sections.push(buildStorySection(this._story));
82
84
  }
83
85
 
84
- // (4) Isolation rules — non-overridable
86
+ // (4) Verdict sectionverifier only, non-overridable
87
+ if (this._role === "verifier" && this._story) {
88
+ sections.push(buildVerdictSection(this._story));
89
+ }
90
+
91
+ // (5) Isolation rules — non-overridable
85
92
  const isolation = this._options.isolation as string | undefined;
86
93
  sections.push(buildIsolationSection(this._role, isolation as "strict" | "lite" | undefined));
87
94
 
88
- // (5) Context markdown
95
+ // (6) Context markdown
89
96
  if (this._contextMd) {
90
97
  sections.push(this._contextMd);
91
98
  }
92
99
 
93
- // (6) Conventions footer — non-overridable, always last
100
+ // (7) Conventions footer — non-overridable, always last
94
101
  sections.push(buildConventionsSection());
95
102
 
96
103
  return sections.join(SECTION_SEP);
@@ -5,11 +5,9 @@
5
5
  */
6
6
 
7
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
- );
8
+ return `# Conventions
9
+
10
+ Follow existing code patterns and conventions. Write idiomatic, maintainable code.
11
+
12
+ Commit your changes when done using conventional commit format (e.g. \`feat:\`, \`fix:\`, \`test:\`).`;
15
13
  }
@@ -29,31 +29,31 @@ export function buildIsolationSection(
29
29
 
30
30
  const role = roleOrMode as "implementer" | "test-writer" | "verifier" | "single-session" | "tdd-simple";
31
31
 
32
- const header = "# Isolation Rules\n\n";
32
+ const header = "# Isolation Rules";
33
33
  const footer = `\n\n${TEST_FILTER_RULE}`;
34
34
 
35
35
  if (role === "test-writer") {
36
36
  const m = mode ?? "strict";
37
37
  if (m === "strict") {
38
- 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}`;
38
+ return `${header}\n\nisolation 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}`;
39
39
  }
40
40
 
41
41
  // lite mode for test-writer
42
- 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}`;
42
+ return `${header}\n\nisolation 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}`;
43
43
  }
44
44
 
45
45
  if (role === "implementer") {
46
- return `${header}isolation scope: Implement source code in src/ to make tests pass. Do not modify test files. Run tests frequently to track progress.${footer}`;
46
+ return `${header}\n\nisolation scope: Implement source code in src/ to make tests pass. Do not modify test files. Run tests frequently to track progress.${footer}`;
47
47
  }
48
48
 
49
49
  if (role === "verifier") {
50
- return `${header}isolation scope: Read-only inspection. Review all test results, implementation code, and acceptance criteria compliance. You MAY write a verdict file (.nax-verifier-verdict.json) and apply legitimate fixes if needed.${footer}`;
50
+ return `${header}\n\nisolation scope: Read-only inspection. Review all test results, implementation code, and acceptance criteria compliance. You MAY write a verdict file (.nax-verifier-verdict.json) and apply legitimate fixes if needed.${footer}`;
51
51
  }
52
52
 
53
53
  if (role === "single-session") {
54
- return `${header}isolation scope: Create test files in test/ directory, then implement source code in src/ to make tests pass. Both directories are in scope for this session.${footer}`;
54
+ return `${header}\n\nisolation scope: Create test files in test/ directory, then implement source code in src/ to make tests pass. Both directories are in scope for this session.${footer}`;
55
55
  }
56
56
 
57
57
  // tdd-simple role — no isolation restrictions (no footer needed)
58
- return `${header}isolation scope: You may modify both src/ and test/ files. Write failing tests FIRST, then implement to make them pass.`;
58
+ return `${header}\n\nisolation scope: You may modify both src/ and test/ files. Write failing tests FIRST, then implement to make them pass.`;
59
59
  }
@@ -27,83 +27,83 @@ export function buildRoleTaskSection(
27
27
  if (role === "implementer") {
28
28
  const v = variant ?? "standard";
29
29
  if (v === "standard") {
30
- return (
31
- "# Role: Implementer\n\n" +
32
- "Your task: make failing tests pass.\n\n" +
33
- "Instructions:\n" +
34
- "- Implement source code in src/ to make tests pass\n" +
35
- "- Do NOT modify test files\n" +
36
- "- Run tests frequently to track progress\n" +
37
- "- When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'\n" +
38
- "- Goal: all tests green, all changes committed"
39
- );
30
+ return `# Role: Implementer
31
+
32
+ Your task: make failing tests pass.
33
+
34
+ Instructions:
35
+ - Implement source code in src/ to make tests pass
36
+ - Do NOT modify test files
37
+ - Run tests frequently to track progress
38
+ - When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'
39
+ - Goal: all tests green, all changes committed`;
40
40
  }
41
41
 
42
42
  // lite variant
43
- return (
44
- "# Role: Implementer (Lite)\n\n" +
45
- "Your task: Write tests AND implement the feature in a single session.\n\n" +
46
- "Instructions:\n" +
47
- "- Write tests first (test/ directory), then implement (src/ directory)\n" +
48
- "- All tests must pass by the end\n" +
49
- "- Use Bun test (describe/test/expect)\n" +
50
- "- When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'\n" +
51
- "- Goal: all tests green, all criteria met, all changes committed"
52
- );
43
+ return `# Role: Implementer (Lite)
44
+
45
+ Your task: Write tests AND implement the feature in a single session.
46
+
47
+ Instructions:
48
+ - Write tests first (test/ directory), then implement (src/ directory)
49
+ - All tests must pass by the end
50
+ - Use Bun test (describe/test/expect)
51
+ - When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'
52
+ - Goal: all tests green, all criteria met, all changes committed`;
53
53
  }
54
54
 
55
55
  if (role === "test-writer") {
56
- return (
57
- "# Role: Test-Writer\n\n" +
58
- "Your task: Write comprehensive failing tests for the feature.\n\n" +
59
- "Instructions:\n" +
60
- "- Create test files in test/ directory that cover acceptance criteria\n" +
61
- "- Tests must fail initially (RED phase) the feature is not yet implemented\n" +
62
- "- Use Bun test (describe/test/expect)\n" +
63
- "- Write clear test names that document expected behavior\n" +
64
- "- Focus on behavior, not implementation details\n" +
65
- "- Goal: comprehensive test suite ready for implementation"
66
- );
56
+ return `# Role: Test-Writer
57
+
58
+ Your task: Write comprehensive failing tests for the feature.
59
+
60
+ Instructions:
61
+ - Create test files in test/ directory that cover acceptance criteria
62
+ - Tests must fail initially (RED phase) — the feature is not yet implemented
63
+ - Use Bun test (describe/test/expect)
64
+ - Write clear test names that document expected behavior
65
+ - Focus on behavior, not implementation details
66
+ - Goal: comprehensive test suite ready for implementation`;
67
67
  }
68
68
 
69
69
  if (role === "verifier") {
70
- return (
71
- "# Role: Verifier\n\n" +
72
- "Your task: Review and verify the implementation against acceptance criteria.\n\n" +
73
- "Instructions:\n" +
74
- "- Review all test results — verify tests pass\n" +
75
- "- Check that implementation meets all acceptance criteria\n" +
76
- "- Inspect code quality, error handling, and edge cases\n" +
77
- "- Verify test modifications (if any) are legitimate fixes\n" +
78
- "- Write a detailed verdict with reasoning\n" +
79
- "- Goal: provide comprehensive verification and quality assurance"
80
- );
70
+ return `# Role: Verifier
71
+
72
+ Your task: Review and verify the implementation against acceptance criteria.
73
+
74
+ Instructions:
75
+ - Review all test results verify tests pass
76
+ - Check that implementation meets all acceptance criteria
77
+ - Inspect code quality, error handling, and edge cases
78
+ - Verify test modifications (if any) are legitimate fixes
79
+ - Write a detailed verdict with reasoning
80
+ - Goal: provide comprehensive verification and quality assurance`;
81
81
  }
82
82
 
83
83
  if (role === "single-session") {
84
- return (
85
- "# Role: Single-Session\n\n" +
86
- "Your task: Write tests AND implement the feature in a single focused session.\n\n" +
87
- "Instructions:\n" +
88
- "- Phase 1: Write comprehensive tests (test/ directory)\n" +
89
- "- Phase 2: Implement to make all tests pass (src/ directory)\n" +
90
- "- Use Bun test (describe/test/expect)\n" +
91
- "- Run tests frequently throughout implementation\n" +
92
- "- When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'\n" +
93
- "- Goal: all tests passing, all changes committed, full story complete"
94
- );
84
+ return `# Role: Single-Session
85
+
86
+ Your task: Write tests AND implement the feature in a single focused session.
87
+
88
+ Instructions:
89
+ - Phase 1: Write comprehensive tests (test/ directory)
90
+ - Phase 2: Implement to make all tests pass (src/ directory)
91
+ - Use Bun test (describe/test/expect)
92
+ - Run tests frequently throughout implementation
93
+ - When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'
94
+ - Goal: all tests passing, all changes committed, full story complete`;
95
95
  }
96
96
 
97
97
  // tdd-simple role — test-driven development in one session
98
- return (
99
- "# Role: TDD-Simple\n\n" +
100
- "Your task: Write failing tests FIRST, then implement to make them pass.\n\n" +
101
- "Instructions:\n" +
102
- "- RED phase: Write failing tests FIRST for the acceptance criteria\n" +
103
- "- RED phase: Run the tests to confirm they fail\n" +
104
- "- GREEN phase: Implement the minimum code to make tests pass\n" +
105
- "- REFACTOR phase: Refactor while keeping tests green\n" +
106
- "- When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'\n" +
107
- "- Goal: all tests passing, feature complete, all changes committed"
108
- );
98
+ return `# Role: TDD-Simple
99
+
100
+ Your task: Write failing tests FIRST, then implement to make them pass.
101
+
102
+ Instructions:
103
+ - RED phase: Write failing tests FIRST for the acceptance criteria
104
+ - RED phase: Run the tests to confirm they fail
105
+ - GREEN phase: Implement the minimum code to make tests pass
106
+ - REFACTOR phase: Refactor while keeping tests green
107
+ - When all tests are green, stage and commit ALL changed files with: git commit -m 'feat: <description>'
108
+ - Goal: all tests passing, feature complete, all changes committed`;
109
109
  }
@@ -64,9 +64,18 @@ export class ReviewOrchestrator {
64
64
  const pluginResults: ReviewResult["pluginReviewers"] = [];
65
65
 
66
66
  for (const reviewer of reviewers) {
67
- logger?.info("review", `Running plugin reviewer: ${reviewer.name}`);
67
+ logger?.info("review", `Running plugin reviewer: ${reviewer.name}`, {
68
+ changedFiles: changedFiles.length,
69
+ });
68
70
  try {
69
71
  const result = await reviewer.check(workdir, changedFiles);
72
+ // Always log the result so skips/passes are visible in the log
73
+ logger?.info("review", `Plugin reviewer result: ${reviewer.name}`, {
74
+ passed: result.passed,
75
+ exitCode: result.exitCode,
76
+ output: result.output?.slice(0, 500),
77
+ findings: result.findings?.length ?? 0,
78
+ });
70
79
  pluginResults.push({
71
80
  name: reviewer.name,
72
81
  passed: result.passed,
@@ -85,6 +94,7 @@ export class ReviewOrchestrator {
85
94
  }
86
95
  } catch (error) {
87
96
  const errorMsg = error instanceof Error ? error.message : String(error);
97
+ logger?.warn("review", `Plugin reviewer threw error: ${reviewer.name}`, { error: errorMsg });
88
98
  pluginResults.push({ name: reviewer.name, passed: false, output: "", error: errorMsg });
89
99
  builtIn.pluginReviewers = pluginResults;
90
100
  return {
@@ -59,7 +59,7 @@ Respond with ONLY this JSON (no markdown, no explanation):
59
59
  * @param config - nax configuration
60
60
  * @returns Formatted batch prompt string
61
61
  */
62
- export function buildBatchPrompt(stories: UserStory[], config: NaxConfig): string {
62
+ export function buildBatchRoutingPrompt(stories: UserStory[], config: NaxConfig): string {
63
63
  const storyBlocks = stories
64
64
  .map((story, idx) => {
65
65
  const criteria = story.acceptanceCriteria.map((c, i) => ` ${i + 1}. ${c}`).join("\n");
@@ -13,12 +13,12 @@ import type { UserStory } from "../../prd/types";
13
13
  import { determineTestStrategy } from "../router";
14
14
  import type { RoutingContext, RoutingDecision, RoutingStrategy } from "../strategy";
15
15
  import { keywordStrategy } from "./keyword";
16
- import { buildBatchPrompt, buildRoutingPrompt, parseBatchResponse, parseRoutingResponse } from "./llm-prompts";
16
+ import { buildBatchRoutingPrompt, buildRoutingPrompt, parseBatchResponse, parseRoutingResponse } from "./llm-prompts";
17
17
 
18
18
  // Re-export for backward compatibility
19
19
  export {
20
20
  buildRoutingPrompt,
21
- buildBatchPrompt,
21
+ buildBatchRoutingPrompt as buildBatchPrompt,
22
22
  validateRoutingDecision,
23
23
  stripCodeFences,
24
24
  parseRoutingResponse,
@@ -190,7 +190,7 @@ export async function routeBatch(stories: UserStory[], context: RoutingContext):
190
190
  }
191
191
 
192
192
  const modelTier = llmConfig.model ?? "fast";
193
- const prompt = buildBatchPrompt(stories, config);
193
+ const prompt = buildBatchRoutingPrompt(stories, config);
194
194
 
195
195
  try {
196
196
  const output = await callLlm(adapter, modelTier, prompt, config);
package/src/tdd/index.ts CHANGED
@@ -15,9 +15,8 @@ export {
15
15
  export { runThreeSessionTdd } from "./orchestrator";
16
16
  export { cleanupProcessTree, getPgid } from "./cleanup";
17
17
  export {
18
- buildTestWriterPrompt,
19
- buildImplementerPrompt,
20
- buildVerifierPrompt,
18
+ buildImplementerRectificationPrompt,
19
+ buildRectificationPrompt,
21
20
  } from "./prompts";
22
21
  export type { VerifierVerdict, VerdictCategorization } from "./verdict";
23
22
  export { VERDICT_FILE, readVerdict, cleanupVerdict, categorizeVerdict } from "./verdict";
@@ -37,19 +37,6 @@ export async function getChangedFiles(workdir: string, fromRef = "HEAD"): Promis
37
37
  return output.trim().split("\n").filter(Boolean);
38
38
  }
39
39
 
40
- /** Get staged files */
41
- export async function getStagedFiles(workdir: string): Promise<string[]> {
42
- const proc = Bun.spawn(["git", "diff", "--name-only", "--cached"], {
43
- cwd: workdir,
44
- stdout: "pipe",
45
- stderr: "pipe",
46
- });
47
-
48
- await proc.exited;
49
- const output = await new Response(proc.stdout).text();
50
- return output.trim().split("\n").filter(Boolean);
51
- }
52
-
53
40
  /** Check if a file path matches any of the allowed patterns (glob-like) */
54
41
  function matchesAllowedPath(filePath: string, allowedPaths: string[]): boolean {
55
42
  return allowedPaths.some((pattern) => {
@@ -28,6 +28,7 @@ export interface ThreeSessionTddOptions {
28
28
  workdir: string;
29
29
  modelTier: ModelTier;
30
30
  contextMarkdown?: string;
31
+ constitution?: string;
31
32
  dryRun?: boolean;
32
33
  lite?: boolean;
33
34
  _recursionDepth?: number;
@@ -44,6 +45,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
44
45
  workdir,
45
46
  modelTier,
46
47
  contextMarkdown,
48
+ constitution,
47
49
  dryRun = false,
48
50
  lite = false,
49
51
  _recursionDepth = 0,
@@ -131,6 +133,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
131
133
  contextMarkdown,
132
134
  lite,
133
135
  lite,
136
+ constitution,
134
137
  );
135
138
  sessions.push(session1);
136
139
  }
@@ -235,6 +238,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
235
238
  contextMarkdown,
236
239
  lite,
237
240
  lite,
241
+ constitution,
238
242
  );
239
243
  sessions.push(session2);
240
244
 
@@ -280,6 +284,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
280
284
  undefined,
281
285
  false,
282
286
  false,
287
+ constitution,
283
288
  );
284
289
  sessions.push(session3);
285
290