@nathapp/nax 0.36.1 → 0.37.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/README.md +19 -0
- package/bin/nax.ts +38 -4
- package/dist/nax.js +412 -71
- package/package.json +1 -1
- package/src/cli/index.ts +2 -0
- package/src/cli/init.ts +7 -1
- package/src/cli/prompts.ts +62 -8
- package/src/config/schemas.ts +6 -2
- package/src/execution/dry-run.ts +1 -1
- package/src/execution/escalation/escalation.ts +5 -3
- package/src/execution/escalation/tier-escalation.ts +41 -4
- package/src/execution/iteration-runner.ts +5 -0
- package/src/execution/parallel-executor.ts +293 -9
- package/src/execution/parallel.ts +40 -21
- package/src/execution/pipeline-result-handler.ts +3 -2
- package/src/execution/runner.ts +13 -3
- package/src/metrics/tracker.ts +8 -4
- package/src/metrics/types.ts +2 -0
- package/src/pipeline/event-bus.ts +1 -1
- package/src/pipeline/stages/completion.ts +1 -1
- package/src/pipeline/stages/verify.ts +8 -1
- package/src/pipeline/subscribers/reporters.ts +3 -3
- package/src/pipeline/types.ts +4 -0
- package/src/plugins/types.ts +1 -1
- package/src/prd/types.ts +2 -0
- package/src/review/orchestrator.ts +24 -9
- package/src/tdd/types.ts +2 -1
- package/src/verification/crash-detector.ts +34 -0
- package/src/verification/orchestrator-types.ts +8 -1
|
@@ -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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
+
runElapsedMs: Date.now() - startTime,
|
|
130
130
|
cost: 0,
|
|
131
131
|
tier: "balanced",
|
|
132
132
|
testStrategy: "test-after",
|
package/src/pipeline/types.ts
CHANGED
|
@@ -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
|
/**
|
package/src/plugins/types.ts
CHANGED
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
|
}
|
|
@@ -15,16 +15,28 @@ import type { PluginRegistry } from "../plugins";
|
|
|
15
15
|
import { runReview } from "./runner";
|
|
16
16
|
import type { ReviewConfig, ReviewResult } from "./types";
|
|
17
17
|
|
|
18
|
-
async function getChangedFiles(workdir: string): Promise<string[]> {
|
|
18
|
+
async function getChangedFiles(workdir: string, baseRef?: string): Promise<string[]> {
|
|
19
19
|
try {
|
|
20
|
-
const [
|
|
21
|
-
|
|
22
|
-
spawn({ cmd: ["git",
|
|
20
|
+
const diffArgs = ["diff", "--name-only"];
|
|
21
|
+
const [stagedProc, unstagedProc, baseProc] = [
|
|
22
|
+
spawn({ cmd: ["git", ...diffArgs, "--cached"], cwd: workdir, stdout: "pipe", stderr: "pipe" }),
|
|
23
|
+
spawn({ cmd: ["git", ...diffArgs], cwd: workdir, stdout: "pipe", stderr: "pipe" }),
|
|
24
|
+
baseRef
|
|
25
|
+
? spawn({ cmd: ["git", ...diffArgs, `${baseRef}...HEAD`], cwd: workdir, stdout: "pipe", stderr: "pipe" })
|
|
26
|
+
: null,
|
|
23
27
|
];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
await Promise.all([stagedProc.exited, unstagedProc.exited, baseProc?.exited]);
|
|
30
|
+
|
|
31
|
+
const [staged, unstaged, based] = await Promise.all([
|
|
32
|
+
new Response(stagedProc.stdout).text().then((t) => t.trim().split("\n").filter(Boolean)),
|
|
33
|
+
new Response(unstagedProc.stdout).text().then((t) => t.trim().split("\n").filter(Boolean)),
|
|
34
|
+
baseProc
|
|
35
|
+
? new Response(baseProc.stdout).text().then((t) => t.trim().split("\n").filter(Boolean))
|
|
36
|
+
: Promise.resolve([]),
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
return Array.from(new Set([...staged, ...unstaged, ...based]));
|
|
28
40
|
} catch {
|
|
29
41
|
return [];
|
|
30
42
|
}
|
|
@@ -60,7 +72,10 @@ export class ReviewOrchestrator {
|
|
|
60
72
|
if (plugins) {
|
|
61
73
|
const reviewers = plugins.getReviewers();
|
|
62
74
|
if (reviewers.length > 0) {
|
|
63
|
-
|
|
75
|
+
// Use the story's start ref if available to capture auto-committed changes
|
|
76
|
+
// biome-ignore lint/suspicious/noExplicitAny: baseRef injected into config for pipeline use
|
|
77
|
+
const baseRef = (executionConfig as any)?.storyGitRef;
|
|
78
|
+
const changedFiles = await getChangedFiles(workdir, baseRef);
|
|
64
79
|
const pluginResults: ReviewResult["pluginReviewers"] = [];
|
|
65
80
|
|
|
66
81
|
for (const reviewer of reviewers) {
|
package/src/tdd/types.ts
CHANGED
|
@@ -12,7 +12,8 @@ export type FailureCategory =
|
|
|
12
12
|
/** Verifier explicitly rejected the implementation */
|
|
13
13
|
| "verifier-rejected"
|
|
14
14
|
/** Greenfield project with no test files — TDD not applicable (BUG-010) */
|
|
15
|
-
| "greenfield-no-tests"
|
|
15
|
+
| "greenfield-no-tests"
|
|
16
|
+
| "runtime-crash";
|
|
16
17
|
|
|
17
18
|
/** Isolation verification result */
|
|
18
19
|
export interface IsolationCheck {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Crash Detector — BUG-070
|
|
3
|
+
*
|
|
4
|
+
* Detects Bun runtime crashes in test output so they can be classified as
|
|
5
|
+
* RUNTIME_CRASH rather than TEST_FAILURE, preventing spurious tier escalation.
|
|
6
|
+
*
|
|
7
|
+
* STUB — implementation is intentionally absent. Tests are RED until
|
|
8
|
+
* the real logic is written.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Known patterns emitted by the Bun runtime before any test results
|
|
13
|
+
* when a crash occurs (segfault, panic, etc.).
|
|
14
|
+
*/
|
|
15
|
+
export const CRASH_PATTERNS = [
|
|
16
|
+
"panic(main thread)",
|
|
17
|
+
"Segmentation fault",
|
|
18
|
+
"Bun has crashed",
|
|
19
|
+
"oh no: Bun has crashed",
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Detect whether the given test runner output contains a Bun runtime crash.
|
|
24
|
+
*
|
|
25
|
+
* Returns true if any known crash pattern is found in the output.
|
|
26
|
+
* These patterns are emitted by Bun itself before any test result lines.
|
|
27
|
+
*
|
|
28
|
+
* @param output - Raw stdout/stderr from the test runner
|
|
29
|
+
*/
|
|
30
|
+
export function detectRuntimeCrash(output: string | undefined | null): boolean {
|
|
31
|
+
// STUB: not implemented yet — always returns false
|
|
32
|
+
if (!output) return false;
|
|
33
|
+
return CRASH_PATTERNS.some((pattern) => output.includes(pattern));
|
|
34
|
+
}
|
|
@@ -50,7 +50,14 @@ export interface StructuredTestFailure {
|
|
|
50
50
|
// Result
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
52
|
|
|
53
|
-
export type VerifyStatus =
|
|
53
|
+
export type VerifyStatus =
|
|
54
|
+
| "PASS"
|
|
55
|
+
| "TEST_FAILURE"
|
|
56
|
+
| "TIMEOUT"
|
|
57
|
+
| "BUILD_ERROR"
|
|
58
|
+
| "SKIPPED"
|
|
59
|
+
| "ASSET_CHECK_FAILED"
|
|
60
|
+
| "RUNTIME_CRASH";
|
|
54
61
|
|
|
55
62
|
export interface VerifyResult {
|
|
56
63
|
success: boolean;
|