@nathapp/nax 0.21.0 → 0.22.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.
- package/.mcp.json +8 -0
- package/docs/ROADMAP.md +20 -5
- package/docs/adr/ADR-005-implementation-plan.md +655 -0
- package/docs/adr/ADR-005-pipeline-re-architecture.md +464 -0
- package/package.json +1 -1
- package/src/agents/claude.ts +44 -9
- package/src/config/types.ts +11 -0
- package/src/execution/dry-run.ts +81 -0
- package/src/execution/escalation/tier-outcome.ts +29 -44
- package/src/execution/executor-types.ts +65 -0
- package/src/execution/index.ts +0 -17
- package/src/execution/iteration-runner.ts +132 -0
- package/src/execution/lifecycle/index.ts +0 -1
- package/src/execution/lifecycle/run-regression.ts +5 -5
- package/src/execution/pipeline-result-handler.ts +51 -254
- package/src/execution/sequential-executor.ts +72 -316
- package/src/execution/story-selector.ts +75 -0
- package/src/pipeline/event-bus.ts +276 -0
- package/src/pipeline/runner.ts +51 -77
- package/src/pipeline/stages/autofix.ts +133 -0
- package/src/pipeline/stages/completion.ts +22 -30
- package/src/pipeline/stages/index.ts +30 -13
- package/src/pipeline/stages/rectify.ts +93 -0
- package/src/pipeline/stages/regression.ts +88 -0
- package/src/pipeline/stages/review.ts +19 -153
- package/src/pipeline/stages/verify.ts +18 -2
- package/src/pipeline/subscribers/hooks.ts +133 -0
- package/src/pipeline/subscribers/interaction.ts +68 -0
- package/src/pipeline/subscribers/reporters.ts +174 -0
- package/src/pipeline/types.ts +10 -1
- package/src/review/orchestrator.ts +105 -0
- package/src/tdd/prompts.ts +1 -1
- package/src/verification/index.ts +1 -1
- package/src/verification/orchestrator-types.ts +145 -0
- package/src/verification/orchestrator.ts +76 -0
- package/src/{execution/post-verify-rectification.ts → verification/rectification-loop.ts} +13 -20
- package/src/verification/{gate.ts → runners.ts} +17 -105
- package/src/verification/strategies/acceptance.ts +133 -0
- package/src/verification/strategies/regression.ts +90 -0
- package/src/verification/strategies/scoped.ts +123 -0
- package/test/COVERAGE-GAPS.md +333 -0
- package/test/{acceptance → e2e}/cm-003-default-view.test.ts +1 -0
- package/test/{integration/e2e.test.ts → e2e/plan-analyze-run.test.ts} +1 -0
- package/test/integration/{agent-validation.test.ts → cli/agent-validation.test.ts} +3 -3
- package/test/integration/{cli-config-default-edge-cases.test.ts → cli/cli-config-default-edge-cases.test.ts} +6 -5
- package/test/integration/{cli-config-default-view.test.ts → cli/cli-config-default-view.test.ts} +8 -7
- package/test/integration/{cli-config-diff.test.ts → cli/cli-config-diff.test.ts} +3 -2
- package/test/integration/{cli-config.test.ts → cli/cli-config.test.ts} +3 -2
- package/test/integration/{cli-diagnose.test.ts → cli/cli-diagnose.test.ts} +5 -4
- package/test/integration/{cli-logs.test.ts → cli/cli-logs.test.ts} +12 -3
- package/test/integration/{cli-plugins.test.ts → cli/cli-plugins.test.ts} +4 -3
- package/test/integration/{cli-precheck.test.ts → cli/cli-precheck.test.ts} +4 -3
- package/test/integration/{cli-run-headless.test.ts → cli/cli-run-headless.test.ts} +3 -2
- package/test/integration/{cli.test.ts → cli/cli.test.ts} +2 -1
- package/test/integration/{precheck-integration.test.ts → cli/precheck-integration.test.ts} +10 -9
- package/test/integration/{precheck-orchestrator.test.ts → cli/precheck-orchestrator.test.ts} +4 -3
- package/test/integration/{precheck.test.ts → cli/precheck.test.ts} +5 -4
- package/test/integration/{config-loader.test.ts → config/config-loader.test.ts} +2 -1
- package/test/integration/{config.test.ts → config/config.test.ts} +2 -2
- package/test/integration/config/merger.test.ts +1 -0
- package/test/integration/config/paths.test.ts +1 -0
- package/test/integration/{security-loader.test.ts → config/security-loader.test.ts} +2 -2
- package/test/integration/{context-integration.test.ts → context/context-integration.test.ts} +7 -6
- package/test/integration/{path-security.test.ts → context/context-path-security.test.ts} +2 -2
- package/test/integration/{context-provider-injection.test.ts → context/context-provider-injection.test.ts} +7 -6
- package/test/integration/{context-verification-integration.test.ts → context/context-verification-integration.test.ts} +5 -4
- package/test/integration/{s5-greenfield-fallback.test.ts → context/s5-greenfield-fallback.test.ts} +4 -3
- package/test/integration/{isolation.test.ts → execution/execution-isolation.test.ts} +1 -1
- package/test/integration/{execution.test.ts → execution/execution.test.ts} +8 -8
- package/test/integration/{parallel.test.ts → execution/parallel.test.ts} +2 -1
- package/test/integration/{prd-pause.test.ts → execution/prd-pause.test.ts} +2 -2
- package/test/integration/{prd-resolvers.test.ts → execution/prd-resolvers.test.ts} +3 -2
- package/test/integration/{progress.test.ts → execution/progress.test.ts} +1 -1
- package/test/integration/execution/runner-batching.test.ts +682 -0
- package/test/integration/{runner-config-plugins.test.ts → execution/runner-config-plugins.test.ts} +3 -2
- package/test/integration/execution/runner-escalation.test.ts +561 -0
- package/test/integration/{runner-fixes.test.ts → execution/runner-fixes.test.ts} +4 -3
- package/test/integration/{runner-plugin-integration.test.ts → execution/runner-plugin-integration.test.ts} +6 -5
- package/test/integration/execution/runner-queue-and-attempts.test.ts +476 -0
- package/test/integration/{status-file-integration.test.ts → execution/status-file-integration.test.ts} +9 -8
- package/test/integration/{status-file.test.ts → execution/status-file.test.ts} +3 -2
- package/test/integration/{status-writer.test.ts → execution/status-writer.test.ts} +5 -4
- package/test/integration/{story-id-in-events.test.ts → execution/story-id-in-events.test.ts} +9 -8
- package/test/integration/{interaction-chain-pipeline.test.ts → interaction/interaction-chain-pipeline.test.ts} +26 -14
- package/test/integration/{hooks.test.ts → pipeline/hooks.test.ts} +4 -2
- package/test/integration/{pipeline-acceptance.test.ts → pipeline/pipeline-acceptance.test.ts} +7 -6
- package/test/integration/{pipeline-events.test.ts → pipeline/pipeline-events.test.ts} +7 -6
- package/test/integration/{pipeline.test.ts → pipeline/pipeline.test.ts} +9 -7
- package/test/integration/{reporter-lifecycle.test.ts → pipeline/reporter-lifecycle.test.ts} +9 -7
- package/test/integration/{verify-stage.test.ts → pipeline/verify-stage.test.ts} +7 -5
- package/test/integration/{analyze-integration.test.ts → plan/analyze-integration.test.ts} +3 -2
- package/test/integration/{analyze-scanner.test.ts → plan/analyze-scanner.test.ts} +8 -7
- package/test/integration/{logger.test.ts → plan/logger.test.ts} +1 -1
- package/test/integration/{plan.test.ts → plan/plan.test.ts} +3 -3
- package/test/integration/plugins/config-integration.test.ts +1 -0
- package/test/integration/plugins/config-resolution.test.ts +1 -0
- package/test/integration/plugins/loader.test.ts +1 -0
- package/test/integration/plugins/{registry.test.ts → plugins-registry.test.ts} +1 -0
- package/test/integration/plugins/validator.test.ts +1 -0
- package/test/integration/{review-config-commands.test.ts → review/review-config-commands.test.ts} +4 -3
- package/test/integration/{review-config-schema.test.ts → review/review-config-schema.test.ts} +3 -2
- package/test/integration/{review-plugin-integration.test.ts → review/review-plugin-integration.test.ts} +5 -4
- package/test/integration/{review.test.ts → review/review.test.ts} +3 -2
- package/test/integration/routing/plugin-routing-advanced.test.ts +461 -0
- package/test/integration/{plugin-routing.test.ts → routing/plugin-routing-core.test.ts} +9 -403
- package/test/integration/{routing-stage-bug-021.test.ts → routing/routing-stage-bug-021.test.ts} +8 -7
- package/test/integration/{routing-stage-greenfield.test.ts → routing/routing-stage-greenfield.test.ts} +7 -6
- package/test/integration/{tdd-cleanup.test.ts → tdd/tdd-cleanup.test.ts} +1 -1
- package/test/integration/tdd/tdd-orchestrator-core.test.ts +565 -0
- package/test/integration/tdd/tdd-orchestrator-failureCategory.test.ts +355 -0
- package/test/integration/tdd/tdd-orchestrator-fallback.test.ts +311 -0
- package/test/integration/tdd/tdd-orchestrator-lite.test.ts +289 -0
- package/test/integration/tdd/tdd-orchestrator-prompts.test.ts +260 -0
- package/test/integration/tdd/tdd-orchestrator-verdict.test.ts +536 -0
- package/test/integration/tmp/headless-test/test.jsonl +30 -0
- package/test/integration/{test-scanner.test.ts → verification/test-scanner.test.ts} +1 -1
- package/test/integration/{verification-asset-check.test.ts → verification/verification-asset-check.test.ts} +3 -2
- package/test/unit/acceptance.test.ts +1 -0
- package/test/unit/agent-stderr-capture.test.ts +1 -0
- package/test/unit/agents/claude.test.ts +1 -0
- package/test/unit/analyze-classifier.test.ts +1 -0
- package/test/unit/auto-detect.test.ts +1 -0
- package/test/unit/cli-status.test.ts +1 -0
- package/test/unit/commands/common.test.ts +1 -0
- package/test/unit/commands/logs.test.ts +1 -0
- package/test/unit/commands/unlock.test.ts +1 -0
- package/test/unit/config/defaults.test.ts +1 -0
- package/test/unit/config/regression-gate-schema.test.ts +1 -0
- package/test/unit/config/smart-runner-flag.test.ts +1 -0
- package/test/unit/constitution-generators.test.ts +1 -0
- package/test/unit/constitution.test.ts +1 -0
- package/test/unit/context/context-autodetect.test.ts +297 -0
- package/test/unit/context/context-build.test.ts +575 -0
- package/test/unit/context/context-coverage.test.ts +236 -0
- package/test/unit/context/context-error.test.ts +93 -0
- package/test/unit/context/context-estimate-tokens.test.ts +201 -0
- package/test/unit/context/context-format.test.ts +302 -0
- package/test/unit/context/context-isolation.test.ts +267 -0
- package/test/unit/context/context-sort.test.ts +93 -0
- package/test/unit/context/context-story.test.ts +108 -0
- package/test/{context → unit/context}/prior-failures.test.ts +5 -4
- package/test/unit/context.test.ts +1 -0
- package/test/unit/crash-recovery.test.ts +1 -0
- package/test/unit/escalation.test.ts +1 -0
- package/test/unit/execution/lifecycle/run-completion.test.ts +1 -0
- package/test/unit/execution/lifecycle/run-regression.test.ts +2 -0
- package/test/{execution → unit/execution}/pid-registry.test.ts +2 -1
- package/test/{execution → unit/execution}/structured-failure.test.ts +3 -2
- package/test/unit/execution-logging-stderr.test.ts +1 -0
- package/test/unit/execution-stage.test.ts +1 -0
- package/test/unit/fix-generator.test.ts +1 -0
- package/test/unit/greenfield.test.ts +1 -0
- package/test/unit/interaction/human-review-trigger.test.ts +1 -0
- package/test/unit/interaction-network-failures.test.ts +1 -0
- package/test/unit/interaction-plugins.test.ts +1 -0
- package/test/unit/logging/formatter.test.ts +1 -0
- package/test/unit/merge.test.ts +1 -0
- package/test/unit/pipeline/event-bus.test.ts +105 -0
- package/test/unit/pipeline/routing-partial-override.test.ts +1 -0
- package/test/unit/pipeline/runner-retry.test.ts +89 -0
- package/test/unit/pipeline/stages/autofix.test.ts +97 -0
- package/test/unit/pipeline/stages/rectify.test.ts +101 -0
- package/test/unit/pipeline/stages/regression-stage.test.ts +69 -0
- package/test/unit/pipeline/stages/verify.test.ts +1 -0
- package/test/unit/pipeline/subscribers/hooks.test.ts +45 -0
- package/test/unit/pipeline/subscribers/interaction.test.ts +31 -0
- package/test/unit/pipeline/subscribers/reporters.test.ts +90 -0
- package/test/unit/pipeline/verify-smart-runner.test.ts +1 -0
- package/test/unit/prd-auto-default.test.ts +1 -0
- package/test/unit/prd-failure-category.test.ts +1 -0
- package/test/unit/prd-get-next-story.test.ts +1 -0
- package/test/unit/precheck-checks.test.ts +1 -0
- package/test/unit/precheck-story-size-gate.test.ts +1 -0
- package/test/unit/precheck-types.test.ts +1 -0
- package/test/unit/prompts.test.ts +1 -0
- package/test/unit/rectification.test.ts +2 -1
- package/test/unit/registry.test.ts +1 -0
- package/test/unit/routing/routing-stability.test.ts +1 -0
- package/test/unit/routing/strategies/llm.test.ts +1 -0
- package/test/unit/routing-advanced.test.ts +313 -0
- package/test/unit/routing-core.test.ts +341 -0
- package/test/unit/routing-strategies.test.ts +442 -0
- package/test/unit/storyid-events.test.ts +1 -0
- package/test/{ui → unit/ui}/tui-controls.test.ts +8 -7
- package/test/{ui → unit/ui}/tui-cost-and-pty.test.ts +4 -3
- package/test/{ui → unit/ui}/tui-layout.test.ts +5 -4
- package/test/{ui → unit/ui}/tui-stories.test.ts +5 -4
- package/test/unit/{isolation.test.ts → unit-isolation.test.ts} +1 -0
- package/test/unit/{helpers.test.ts → utils-helpers.test.ts} +1 -0
- package/test/unit/verdict.test.ts +1 -0
- package/test/unit/verification/orchestrator-types.test.ts +54 -0
- package/test/unit/verification/orchestrator.test.ts +66 -0
- package/test/unit/verification/smart-runner-config.test.ts +1 -0
- package/test/unit/verification/smart-runner-discovery.test.ts +8 -7
- package/test/unit/verification/strategies/acceptance.test.ts +33 -0
- package/test/unit/verification/strategies/regression.test.ts +87 -0
- package/test/unit/verification/strategies/scoped.test.ts +100 -0
- package/test/unit/worktree-manager.test.ts +1 -0
- package/src/execution/lifecycle/story-hooks.ts +0 -38
- package/src/execution/post-verify.ts +0 -193
- package/src/execution/rectification.ts +0 -13
- package/src/execution/verification.ts +0 -72
- package/test/integration/rectification-flow.test.ts +0 -512
- package/test/integration/runner.test.ts +0 -1679
- package/test/integration/tdd-orchestrator.test.ts +0 -1762
- package/test/unit/execution/post-verify-regression.test.ts +0 -362
- package/test/unit/execution/post-verify.test.ts +0 -236
- package/test/unit/routing.test.ts +0 -1039
- /package/test/{integration → helpers}/helpers.test.ts +0 -0
- /package/test/integration/worktree/{merge.test.ts → worktree-merge.test.ts} +0 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import type { AgentAdapter, AgentResult } from "../../../src/agents";
|
|
6
|
+
import { DEFAULT_CONFIG } from "../../../src/config";
|
|
7
|
+
import type { UserStory } from "../../../src/prd";
|
|
8
|
+
import { runThreeSessionTdd } from "../../../src/tdd/orchestrator";
|
|
9
|
+
import { VERDICT_FILE } from "../../../src/tdd/verdict";
|
|
10
|
+
|
|
11
|
+
let originalSpawn: typeof Bun.spawn;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
originalSpawn = Bun.spawn;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
Bun.spawn = originalSpawn;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/** Create a mock agent that returns sequential results */
|
|
22
|
+
function createMockAgent(results: Partial<AgentResult>[]): AgentAdapter {
|
|
23
|
+
let callCount = 0;
|
|
24
|
+
return {
|
|
25
|
+
name: "mock",
|
|
26
|
+
displayName: "Mock Agent",
|
|
27
|
+
binary: "mock",
|
|
28
|
+
isInstalled: async () => true,
|
|
29
|
+
buildCommand: () => ["mock"],
|
|
30
|
+
run: mock(async () => {
|
|
31
|
+
const r = results[callCount] || {};
|
|
32
|
+
callCount++;
|
|
33
|
+
return {
|
|
34
|
+
success: r.success ?? true,
|
|
35
|
+
exitCode: r.exitCode ?? 0,
|
|
36
|
+
output: r.output ?? "",
|
|
37
|
+
rateLimited: r.rateLimited ?? false,
|
|
38
|
+
durationMs: r.durationMs ?? 100,
|
|
39
|
+
estimatedCost: r.estimatedCost ?? 0.01,
|
|
40
|
+
};
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Mock Bun.spawn to intercept git commands */
|
|
46
|
+
function mockGitSpawn(opts: {
|
|
47
|
+
/** Files returned by git diff for each session (indexed by git-diff call number) */
|
|
48
|
+
diffFiles: string[][];
|
|
49
|
+
/** Optional: mock test command success (default: true) */
|
|
50
|
+
testCommandSuccess?: boolean;
|
|
51
|
+
}) {
|
|
52
|
+
let revParseCount = 0;
|
|
53
|
+
let diffCount = 0;
|
|
54
|
+
const testSuccess = opts.testCommandSuccess ?? true;
|
|
55
|
+
|
|
56
|
+
// @ts-ignore — mocking global
|
|
57
|
+
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
58
|
+
// Intercept test commands (bun test, npm test, etc.)
|
|
59
|
+
if ((cmd[0] === "/bin/sh" || cmd[0] === "/bin/bash" || cmd[0] === "/bin/zsh") && cmd[1] === "-c") {
|
|
60
|
+
return {
|
|
61
|
+
pid: 9999,
|
|
62
|
+
exited: Promise.resolve(testSuccess ? 0 : 1),
|
|
63
|
+
stdout: new Response(testSuccess ? "tests pass\n" : "tests fail\n").body,
|
|
64
|
+
stderr: new Response("").body,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
68
|
+
revParseCount++;
|
|
69
|
+
return {
|
|
70
|
+
exited: Promise.resolve(0),
|
|
71
|
+
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
72
|
+
stderr: new Response("").body,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (cmd[0] === "git" && cmd[1] === "checkout") {
|
|
76
|
+
// Intercept git checkout (used in zero-file fallback) — silently succeed
|
|
77
|
+
return {
|
|
78
|
+
exited: Promise.resolve(0),
|
|
79
|
+
stdout: new Response("").body,
|
|
80
|
+
stderr: new Response("").body,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
84
|
+
const files = opts.diffFiles[diffCount] || [];
|
|
85
|
+
diffCount++;
|
|
86
|
+
return {
|
|
87
|
+
exited: Promise.resolve(0),
|
|
88
|
+
stdout: new Response(files.join("\n") + "\n").body,
|
|
89
|
+
stderr: new Response("").body,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return originalSpawn(cmd, spawnOpts);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const story: UserStory = {
|
|
97
|
+
id: "US-001",
|
|
98
|
+
title: "Add user validation",
|
|
99
|
+
description: "Add validation to user input",
|
|
100
|
+
acceptanceCriteria: ["Validation works", "Errors are clear"],
|
|
101
|
+
dependencies: [],
|
|
102
|
+
tags: [],
|
|
103
|
+
status: "pending",
|
|
104
|
+
passes: false,
|
|
105
|
+
escalations: [],
|
|
106
|
+
attempts: 0,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
describe("runThreeSessionTdd — T9: verdict integration", () => {
|
|
111
|
+
let tmpDir: string;
|
|
112
|
+
|
|
113
|
+
beforeEach(async () => {
|
|
114
|
+
tmpDir = `/tmp/nax-t9-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
115
|
+
await mkdir(tmpDir, { recursive: true });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(async () => {
|
|
119
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
120
|
+
Bun.spawn = originalSpawn;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/** Write a valid verdict file to tmpDir */
|
|
124
|
+
async function writeVerdictToDir(opts: {
|
|
125
|
+
approved: boolean;
|
|
126
|
+
failReason?: "tests-failing" | "illegitimate-mods" | "criteria-not-met" | "poor-quality";
|
|
127
|
+
}) {
|
|
128
|
+
const verdict = {
|
|
129
|
+
version: 1,
|
|
130
|
+
approved: opts.approved,
|
|
131
|
+
tests: {
|
|
132
|
+
allPassing: opts.failReason !== "tests-failing",
|
|
133
|
+
passCount: opts.failReason === "tests-failing" ? 5 : 10,
|
|
134
|
+
failCount: opts.failReason === "tests-failing" ? 3 : 0,
|
|
135
|
+
},
|
|
136
|
+
testModifications: {
|
|
137
|
+
detected: opts.failReason === "illegitimate-mods",
|
|
138
|
+
files: opts.failReason === "illegitimate-mods" ? ["test/foo.test.ts"] : [],
|
|
139
|
+
legitimate: opts.failReason !== "illegitimate-mods",
|
|
140
|
+
reasoning: opts.failReason === "illegitimate-mods" ? "Implementer cheated" : "No mods",
|
|
141
|
+
},
|
|
142
|
+
acceptanceCriteria: {
|
|
143
|
+
allMet: opts.failReason !== "criteria-not-met",
|
|
144
|
+
criteria:
|
|
145
|
+
opts.failReason === "criteria-not-met"
|
|
146
|
+
? [{ criterion: "Must work", met: false }]
|
|
147
|
+
: [{ criterion: "Works", met: true }],
|
|
148
|
+
},
|
|
149
|
+
quality: {
|
|
150
|
+
rating: opts.failReason === "poor-quality" ? "poor" : "good",
|
|
151
|
+
issues: opts.failReason === "poor-quality" ? ["Security issue"] : [],
|
|
152
|
+
},
|
|
153
|
+
fixes: [],
|
|
154
|
+
reasoning: opts.approved ? "All good." : "Implementation rejected.",
|
|
155
|
+
};
|
|
156
|
+
await writeFile(path.join(tmpDir, VERDICT_FILE), JSON.stringify(verdict, null, 2));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Mock Bun.spawn for a full 3-session T9 run.
|
|
161
|
+
* Provides 6 git diff calls (isolation + getChangedFiles per session)
|
|
162
|
+
* and optionally intercepts the post-TDD shell command (bun test).
|
|
163
|
+
*/
|
|
164
|
+
function mockGitAndTestForT9(opts: {
|
|
165
|
+
diffFiles?: string[][];
|
|
166
|
+
onTestCmd?: () => { exitCode: number; stdout: string };
|
|
167
|
+
}) {
|
|
168
|
+
const files = opts.diffFiles ?? [
|
|
169
|
+
["test/user.test.ts"], // s1 isolation
|
|
170
|
+
["test/user.test.ts"], // s1 getChangedFiles
|
|
171
|
+
["src/user.ts"], // s2 isolation
|
|
172
|
+
["src/user.ts"], // s2 getChangedFiles
|
|
173
|
+
[], // s3 isolation
|
|
174
|
+
["src/user.ts"], // s3 getChangedFiles
|
|
175
|
+
];
|
|
176
|
+
let revParseCount = 0;
|
|
177
|
+
let diffCount = 0;
|
|
178
|
+
|
|
179
|
+
// @ts-ignore — mocking global
|
|
180
|
+
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
181
|
+
if (cmd[0] === "/bin/sh" && cmd[2]?.includes("bun test")) {
|
|
182
|
+
const r = opts.onTestCmd?.() ?? { exitCode: 0, stdout: "5 pass, 0 fail\n" };
|
|
183
|
+
return {
|
|
184
|
+
pid: 9999,
|
|
185
|
+
exited: Promise.resolve(r.exitCode),
|
|
186
|
+
stdout: new Response(r.stdout).body,
|
|
187
|
+
stderr: new Response("").body,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
191
|
+
revParseCount++;
|
|
192
|
+
return {
|
|
193
|
+
exited: Promise.resolve(0),
|
|
194
|
+
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
195
|
+
stderr: new Response("").body,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
199
|
+
const f = files[diffCount] || [];
|
|
200
|
+
diffCount++;
|
|
201
|
+
return {
|
|
202
|
+
exited: Promise.resolve(0),
|
|
203
|
+
stdout: new Response(f.join("\n") + "\n").body,
|
|
204
|
+
stderr: new Response("").body,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return originalSpawn(cmd, spawnOpts);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
test("verdict approved=true: overall success even when verifier session failed", async () => {
|
|
212
|
+
await writeVerdictToDir({ approved: true });
|
|
213
|
+
mockGitAndTestForT9({});
|
|
214
|
+
|
|
215
|
+
const agent = createMockAgent([
|
|
216
|
+
{ success: true, estimatedCost: 0.01 },
|
|
217
|
+
{ success: true, estimatedCost: 0.02 },
|
|
218
|
+
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // verifier exits non-zero
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
const result = await runThreeSessionTdd({
|
|
222
|
+
agent,
|
|
223
|
+
story,
|
|
224
|
+
config: DEFAULT_CONFIG,
|
|
225
|
+
workdir: tmpDir,
|
|
226
|
+
modelTier: "balanced",
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(result.success).toBe(true);
|
|
230
|
+
expect(result.needsHumanReview).toBe(false);
|
|
231
|
+
expect(result.failureCategory).toBeUndefined();
|
|
232
|
+
expect(result.reviewReason).toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("verdict approved=true: skips the post-TDD independent test check", async () => {
|
|
236
|
+
await writeVerdictToDir({ approved: true });
|
|
237
|
+
let testCommandCalled = false;
|
|
238
|
+
mockGitAndTestForT9({
|
|
239
|
+
onTestCmd: () => {
|
|
240
|
+
testCommandCalled = true;
|
|
241
|
+
return { exitCode: 0, stdout: "" };
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const agent = createMockAgent([
|
|
246
|
+
{ success: true, estimatedCost: 0.01 },
|
|
247
|
+
{ success: true, estimatedCost: 0.02 },
|
|
248
|
+
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // verifier fails
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
// Disable rectification to avoid test command being called for full-suite gate
|
|
252
|
+
const configNoRectification = {
|
|
253
|
+
...DEFAULT_CONFIG,
|
|
254
|
+
execution: {
|
|
255
|
+
...DEFAULT_CONFIG.execution,
|
|
256
|
+
rectification: { ...DEFAULT_CONFIG.execution.rectification, enabled: false },
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
await runThreeSessionTdd({
|
|
261
|
+
agent,
|
|
262
|
+
story,
|
|
263
|
+
config: configNoRectification,
|
|
264
|
+
workdir: tmpDir,
|
|
265
|
+
modelTier: "balanced",
|
|
266
|
+
});
|
|
267
|
+
expect(testCommandCalled).toBe(false); // Test was NOT run when verdict present
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("verdict approved=false + tests-failing → failureCategory='tests-failing'", async () => {
|
|
271
|
+
await writeVerdictToDir({ approved: false, failReason: "tests-failing" });
|
|
272
|
+
mockGitAndTestForT9({});
|
|
273
|
+
|
|
274
|
+
const agent = createMockAgent([
|
|
275
|
+
{ success: true, estimatedCost: 0.01 },
|
|
276
|
+
{ success: true, estimatedCost: 0.02 },
|
|
277
|
+
{ success: true, estimatedCost: 0.01 }, // sessions succeed but verdict says rejected
|
|
278
|
+
]);
|
|
279
|
+
|
|
280
|
+
const result = await runThreeSessionTdd({
|
|
281
|
+
agent,
|
|
282
|
+
story,
|
|
283
|
+
config: DEFAULT_CONFIG,
|
|
284
|
+
workdir: tmpDir,
|
|
285
|
+
modelTier: "balanced",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(result.success).toBe(false);
|
|
289
|
+
expect(result.needsHumanReview).toBe(true);
|
|
290
|
+
expect(result.failureCategory).toBe("tests-failing");
|
|
291
|
+
expect(result.reviewReason).toContain("failure(s)");
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("verdict approved=false + illegitimate test mods → failureCategory='verifier-rejected'", async () => {
|
|
295
|
+
await writeVerdictToDir({ approved: false, failReason: "illegitimate-mods" });
|
|
296
|
+
mockGitAndTestForT9({});
|
|
297
|
+
|
|
298
|
+
const agent = createMockAgent([
|
|
299
|
+
{ success: true, estimatedCost: 0.01 },
|
|
300
|
+
{ success: true, estimatedCost: 0.02 },
|
|
301
|
+
{ success: true, estimatedCost: 0.01 },
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
const result = await runThreeSessionTdd({
|
|
305
|
+
agent,
|
|
306
|
+
story,
|
|
307
|
+
config: DEFAULT_CONFIG,
|
|
308
|
+
workdir: tmpDir,
|
|
309
|
+
modelTier: "balanced",
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
expect(result.success).toBe(false);
|
|
313
|
+
expect(result.failureCategory).toBe("verifier-rejected");
|
|
314
|
+
expect(result.reviewReason).toContain("illegitimate test modifications");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("verdict approved=false + criteria not met → failureCategory='verifier-rejected'", async () => {
|
|
318
|
+
await writeVerdictToDir({ approved: false, failReason: "criteria-not-met" });
|
|
319
|
+
mockGitAndTestForT9({});
|
|
320
|
+
|
|
321
|
+
const agent = createMockAgent([
|
|
322
|
+
{ success: true, estimatedCost: 0.01 },
|
|
323
|
+
{ success: true, estimatedCost: 0.02 },
|
|
324
|
+
{ success: true, estimatedCost: 0.01 },
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
const result = await runThreeSessionTdd({
|
|
328
|
+
agent,
|
|
329
|
+
story,
|
|
330
|
+
config: DEFAULT_CONFIG,
|
|
331
|
+
workdir: tmpDir,
|
|
332
|
+
modelTier: "balanced",
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
expect(result.success).toBe(false);
|
|
336
|
+
expect(result.failureCategory).toBe("verifier-rejected");
|
|
337
|
+
expect(result.reviewReason).toContain("Must work");
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("no verdict file → fallback: post-TDD test check is run on session failures", async () => {
|
|
341
|
+
// No verdict file — when verifier fails, falls back to running tests independently
|
|
342
|
+
let testCommandCalled = false;
|
|
343
|
+
mockGitAndTestForT9({
|
|
344
|
+
onTestCmd: () => {
|
|
345
|
+
testCommandCalled = true;
|
|
346
|
+
return { exitCode: 0, stdout: "5 pass, 0 fail\n" }; // Tests pass in fallback
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const agent = createMockAgent([
|
|
351
|
+
{ success: true, estimatedCost: 0.01 },
|
|
352
|
+
{ success: true, estimatedCost: 0.02 },
|
|
353
|
+
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // verifier fails
|
|
354
|
+
]);
|
|
355
|
+
|
|
356
|
+
const result = await runThreeSessionTdd({
|
|
357
|
+
agent,
|
|
358
|
+
story,
|
|
359
|
+
config: DEFAULT_CONFIG,
|
|
360
|
+
workdir: tmpDir,
|
|
361
|
+
modelTier: "balanced",
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(testCommandCalled).toBe(true); // Fallback test run was executed
|
|
365
|
+
expect(result.success).toBe(true); // Tests pass in fallback → success
|
|
366
|
+
expect(result.verdict).toBeNull(); // No verdict available
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("malformed verdict → fallback: post-TDD test check is run", async () => {
|
|
370
|
+
// Write invalid JSON — should trigger fallback
|
|
371
|
+
await writeFile(path.join(tmpDir, VERDICT_FILE), "{ this is not valid json }");
|
|
372
|
+
let testCommandCalled = false;
|
|
373
|
+
mockGitAndTestForT9({
|
|
374
|
+
onTestCmd: () => {
|
|
375
|
+
testCommandCalled = true;
|
|
376
|
+
return { exitCode: 0, stdout: "5 pass\n" };
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const agent = createMockAgent([
|
|
381
|
+
{ success: true, estimatedCost: 0.01 },
|
|
382
|
+
{ success: true, estimatedCost: 0.02 },
|
|
383
|
+
{ success: false, exitCode: 1, estimatedCost: 0.01 },
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
const result = await runThreeSessionTdd({
|
|
387
|
+
agent,
|
|
388
|
+
story,
|
|
389
|
+
config: DEFAULT_CONFIG,
|
|
390
|
+
workdir: tmpDir,
|
|
391
|
+
modelTier: "balanced",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
expect(testCommandCalled).toBe(true); // Fallback used when verdict is malformed
|
|
395
|
+
expect(result.verdict).toBeNull(); // Malformed = null
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("verdict stored in result.verdict for logging/debugging (approved=true)", async () => {
|
|
399
|
+
await writeVerdictToDir({ approved: true });
|
|
400
|
+
mockGitAndTestForT9({});
|
|
401
|
+
|
|
402
|
+
const agent = createMockAgent([
|
|
403
|
+
{ success: true, estimatedCost: 0.01 },
|
|
404
|
+
{ success: true, estimatedCost: 0.02 },
|
|
405
|
+
{ success: true, estimatedCost: 0.01 },
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
const result = await runThreeSessionTdd({
|
|
409
|
+
agent,
|
|
410
|
+
story,
|
|
411
|
+
config: DEFAULT_CONFIG,
|
|
412
|
+
workdir: tmpDir,
|
|
413
|
+
modelTier: "balanced",
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
expect(result.verdict).toBeDefined();
|
|
417
|
+
expect(result.verdict).not.toBeNull();
|
|
418
|
+
expect(result.verdict!.version).toBe(1);
|
|
419
|
+
expect(result.verdict!.approved).toBe(true);
|
|
420
|
+
expect(result.verdict!.tests.allPassing).toBe(true);
|
|
421
|
+
expect(result.verdict!.tests.passCount).toBe(10);
|
|
422
|
+
expect(result.verdict!.reasoning).toBe("All good.");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test("verdict stored in result.verdict for logging/debugging (approved=false)", async () => {
|
|
426
|
+
await writeVerdictToDir({ approved: false, failReason: "tests-failing" });
|
|
427
|
+
mockGitAndTestForT9({});
|
|
428
|
+
|
|
429
|
+
const agent = createMockAgent([
|
|
430
|
+
{ success: true, estimatedCost: 0.01 },
|
|
431
|
+
{ success: true, estimatedCost: 0.02 },
|
|
432
|
+
{ success: true, estimatedCost: 0.01 },
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
const result = await runThreeSessionTdd({
|
|
436
|
+
agent,
|
|
437
|
+
story,
|
|
438
|
+
config: DEFAULT_CONFIG,
|
|
439
|
+
workdir: tmpDir,
|
|
440
|
+
modelTier: "balanced",
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
expect(result.verdict).not.toBeNull();
|
|
444
|
+
expect(result.verdict!.approved).toBe(false);
|
|
445
|
+
expect(result.verdict!.tests.failCount).toBe(3);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test("verdict file is deleted after reading (cleanup enforced)", async () => {
|
|
449
|
+
await writeVerdictToDir({ approved: true });
|
|
450
|
+
mockGitAndTestForT9({});
|
|
451
|
+
|
|
452
|
+
const verdictPath = path.join(tmpDir, VERDICT_FILE);
|
|
453
|
+
expect(existsSync(verdictPath)).toBe(true); // File exists before run
|
|
454
|
+
|
|
455
|
+
const agent = createMockAgent([
|
|
456
|
+
{ success: true, estimatedCost: 0.01 },
|
|
457
|
+
{ success: true, estimatedCost: 0.02 },
|
|
458
|
+
{ success: true, estimatedCost: 0.01 },
|
|
459
|
+
]);
|
|
460
|
+
await runThreeSessionTdd({
|
|
461
|
+
agent,
|
|
462
|
+
story,
|
|
463
|
+
config: DEFAULT_CONFIG,
|
|
464
|
+
workdir: tmpDir,
|
|
465
|
+
modelTier: "balanced",
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
expect(existsSync(verdictPath)).toBe(false); // File cleaned up after run
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("no verdict + all sessions succeed → success without running test check", async () => {
|
|
472
|
+
// All sessions succeed, no verdict → should succeed and NOT run the test command
|
|
473
|
+
let testCommandCalled = false;
|
|
474
|
+
mockGitAndTestForT9({
|
|
475
|
+
onTestCmd: () => {
|
|
476
|
+
testCommandCalled = true;
|
|
477
|
+
return { exitCode: 0, stdout: "" };
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const agent = createMockAgent([
|
|
482
|
+
{ success: true, estimatedCost: 0.01 },
|
|
483
|
+
{ success: true, estimatedCost: 0.02 },
|
|
484
|
+
{ success: true, estimatedCost: 0.01 },
|
|
485
|
+
]);
|
|
486
|
+
|
|
487
|
+
// Disable rectification to avoid test command being called for full-suite gate
|
|
488
|
+
const configNoRectification = {
|
|
489
|
+
...DEFAULT_CONFIG,
|
|
490
|
+
execution: {
|
|
491
|
+
...DEFAULT_CONFIG.execution,
|
|
492
|
+
rectification: { ...DEFAULT_CONFIG.execution.rectification, enabled: false },
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
const result = await runThreeSessionTdd({
|
|
497
|
+
agent,
|
|
498
|
+
story,
|
|
499
|
+
config: configNoRectification,
|
|
500
|
+
workdir: tmpDir,
|
|
501
|
+
modelTier: "balanced",
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(result.success).toBe(true);
|
|
505
|
+
expect(testCommandCalled).toBe(false); // Not needed when sessions all succeed
|
|
506
|
+
expect(result.verdict).toBeNull(); // No verdict
|
|
507
|
+
expect(result.failureCategory).toBeUndefined();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("early-exit before session 3 (session 1 fails) → verdict is undefined (not attempted)", async () => {
|
|
511
|
+
// If we exit before session 3, verdict reading is never attempted
|
|
512
|
+
mockGitAndTestForT9({
|
|
513
|
+
diffFiles: [
|
|
514
|
+
["test/user.test.ts"], // s1 isolation
|
|
515
|
+
["test/user.test.ts"], // s1 getChangedFiles
|
|
516
|
+
],
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const agent = createMockAgent([
|
|
520
|
+
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // session 1 fails
|
|
521
|
+
]);
|
|
522
|
+
|
|
523
|
+
const result = await runThreeSessionTdd({
|
|
524
|
+
agent,
|
|
525
|
+
story,
|
|
526
|
+
config: DEFAULT_CONFIG,
|
|
527
|
+
workdir: tmpDir,
|
|
528
|
+
modelTier: "balanced",
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
expect(result.success).toBe(false);
|
|
532
|
+
expect(result.sessions).toHaveLength(1);
|
|
533
|
+
// verdict is undefined (field not set) because we never got to session 3
|
|
534
|
+
expect(result.verdict).toBeUndefined();
|
|
535
|
+
});
|
|
536
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{"timestamp":"2026-03-06T08:58:27.466Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
2
|
+
{"timestamp":"2026-03-06T08:58:27.482Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
3
|
+
{"timestamp":"2026-03-06T08:58:27.482Z","level":"debug","stage":"test.stage","message":"Debug message"}
|
|
4
|
+
{"timestamp":"2026-03-06T08:58:27.482Z","level":"info","stage":"test.stage","message":"Info message"}
|
|
5
|
+
{"timestamp":"2026-03-06T08:58:27.482Z","level":"error","stage":"test.stage","message":"Error message"}
|
|
6
|
+
{"timestamp":"2026-03-06T08:58:27.483Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
7
|
+
{"timestamp":"2026-03-06T09:00:48.487Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
8
|
+
{"timestamp":"2026-03-06T09:00:48.520Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
9
|
+
{"timestamp":"2026-03-06T09:00:48.521Z","level":"debug","stage":"test.stage","message":"Debug message"}
|
|
10
|
+
{"timestamp":"2026-03-06T09:00:48.521Z","level":"info","stage":"test.stage","message":"Info message"}
|
|
11
|
+
{"timestamp":"2026-03-06T09:00:48.521Z","level":"error","stage":"test.stage","message":"Error message"}
|
|
12
|
+
{"timestamp":"2026-03-06T09:00:48.521Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
13
|
+
{"timestamp":"2026-03-06T09:03:16.966Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
14
|
+
{"timestamp":"2026-03-06T09:03:16.981Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
15
|
+
{"timestamp":"2026-03-06T09:03:16.981Z","level":"debug","stage":"test.stage","message":"Debug message"}
|
|
16
|
+
{"timestamp":"2026-03-06T09:03:16.981Z","level":"info","stage":"test.stage","message":"Info message"}
|
|
17
|
+
{"timestamp":"2026-03-06T09:03:16.981Z","level":"error","stage":"test.stage","message":"Error message"}
|
|
18
|
+
{"timestamp":"2026-03-06T09:03:16.982Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
19
|
+
{"timestamp":"2026-03-06T09:08:50.110Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
20
|
+
{"timestamp":"2026-03-06T09:08:50.143Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
21
|
+
{"timestamp":"2026-03-06T09:08:50.144Z","level":"debug","stage":"test.stage","message":"Debug message"}
|
|
22
|
+
{"timestamp":"2026-03-06T09:08:50.144Z","level":"info","stage":"test.stage","message":"Info message"}
|
|
23
|
+
{"timestamp":"2026-03-06T09:08:50.144Z","level":"error","stage":"test.stage","message":"Error message"}
|
|
24
|
+
{"timestamp":"2026-03-06T09:08:50.144Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
25
|
+
{"timestamp":"2026-03-06T10:13:12.262Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
26
|
+
{"timestamp":"2026-03-06T10:13:12.263Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
27
|
+
{"timestamp":"2026-03-06T10:13:12.263Z","level":"debug","stage":"test.stage","message":"Debug message"}
|
|
28
|
+
{"timestamp":"2026-03-06T10:13:12.263Z","level":"info","stage":"test.stage","message":"Info message"}
|
|
29
|
+
{"timestamp":"2026-03-06T10:13:12.263Z","level":"error","stage":"test.stage","message":"Error message"}
|
|
30
|
+
{"timestamp":"2026-03-06T10:13:12.263Z","level":"info","stage":"test.stage","message":"Test message","data":{"foo":"bar"}}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
generateTestCoverageSummary,
|
|
11
11
|
scanTestFiles,
|
|
12
12
|
truncateToTokenBudget,
|
|
13
|
-
} from "
|
|
13
|
+
} from "../../../src/context/test-scanner";
|
|
14
14
|
|
|
15
15
|
describe("extractTestStructure", () => {
|
|
16
16
|
test("extracts describe and test blocks", () => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
// RE-ARCH: keep
|
|
1
2
|
/**
|
|
2
3
|
* Tests for asset verification with contextFiles/expectedFiles split
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import { getExpectedFiles } from "
|
|
7
|
-
import type { UserStory } from "
|
|
7
|
+
import { getExpectedFiles } from "../../../src/prd";
|
|
8
|
+
import type { UserStory } from "../../../src/prd";
|
|
8
9
|
|
|
9
10
|
const createStory = (partial: Partial<UserStory>): UserStory => ({
|
|
10
11
|
id: "US-001",
|