@nathapp/nax 0.18.6 → 0.20.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/docs/ROADMAP.md +2 -0
- package/nax/config.json +2 -2
- package/nax/features/nax-compliance/prd.json +52 -0
- package/nax/features/nax-compliance/progress.txt +1 -0
- package/nax/features/v0.19.0-hardening/plan.md +7 -0
- package/nax/features/v0.19.0-hardening/prd.json +84 -0
- package/nax/features/v0.19.0-hardening/progress.txt +7 -0
- package/nax/features/v0.19.0-hardening/spec.md +18 -0
- package/nax/features/v0.19.0-hardening/tasks.md +8 -0
- package/nax/features/verify-v2/prd.json +79 -0
- package/nax/features/verify-v2/progress.txt +3 -0
- package/nax/status.json +27 -0
- package/package.json +2 -2
- package/src/acceptance/fix-generator.ts +6 -2
- package/src/acceptance/generator.ts +3 -1
- package/src/acceptance/types.ts +3 -1
- package/src/agents/claude-plan.ts +6 -5
- package/src/cli/analyze.ts +1 -0
- package/src/cli/init.ts +7 -6
- package/src/config/defaults.ts +3 -1
- package/src/config/schemas.ts +2 -0
- package/src/config/types.ts +6 -0
- package/src/context/injector.ts +18 -18
- package/src/execution/crash-recovery.ts +7 -10
- package/src/execution/lifecycle/acceptance-loop.ts +1 -0
- package/src/execution/lifecycle/index.ts +1 -1
- package/src/execution/lifecycle/precheck-runner.ts +1 -1
- package/src/execution/lifecycle/run-completion.ts +29 -0
- package/src/execution/lifecycle/run-regression.ts +301 -0
- package/src/execution/lifecycle/run-setup.ts +14 -14
- package/src/execution/parallel.ts +1 -1
- package/src/execution/pipeline-result-handler.ts +0 -1
- package/src/execution/post-verify.ts +31 -194
- package/src/execution/runner.ts +2 -19
- package/src/execution/sequential-executor.ts +1 -1
- package/src/hooks/runner.ts +2 -2
- package/src/interaction/plugins/auto.ts +2 -2
- package/src/logger/logger.ts +3 -5
- package/src/pipeline/stages/verify.ts +26 -22
- package/src/plugins/loader.ts +36 -9
- package/src/routing/batch-route.ts +32 -0
- package/src/routing/index.ts +1 -0
- package/src/routing/loader.ts +7 -0
- package/src/utils/path-security.ts +56 -0
- package/src/verification/executor.ts +6 -13
- package/src/verification/smart-runner.ts +52 -0
- package/test/integration/plugins/config-resolution.test.ts +3 -3
- package/test/integration/plugins/loader.test.ts +3 -1
- package/test/integration/precheck-integration.test.ts +18 -11
- package/test/integration/rectification-flow.test.ts +3 -3
- package/test/integration/review-config-commands.test.ts +1 -1
- package/test/integration/security-loader.test.ts +83 -0
- package/test/integration/verify-stage.test.ts +9 -0
- package/test/unit/config/defaults.test.ts +69 -0
- package/test/unit/config/regression-gate-schema.test.ts +159 -0
- package/test/unit/execution/lifecycle/run-completion.test.ts +239 -0
- package/test/unit/execution/lifecycle/run-regression.test.ts +418 -0
- package/test/unit/execution/post-verify-regression.test.ts +31 -84
- package/test/unit/execution/post-verify.test.ts +28 -48
- package/test/unit/formatters.test.ts +2 -3
- package/test/unit/hooks/shell-security.test.ts +40 -0
- package/test/unit/pipeline/stages/verify.test.ts +266 -0
- package/test/unit/pipeline/verify-smart-runner.test.ts +1 -0
- package/test/unit/utils/path-security.test.ts +47 -0
- package/src/execution/lifecycle/run-lifecycle.ts +0 -312
- package/test/unit/run-lifecycle.test.ts +0 -140
package/docs/ROADMAP.md
CHANGED
|
@@ -224,6 +224,8 @@
|
|
|
224
224
|
- [x] **BUG-032:** Routing stage overrides escalated `modelTier` with complexity-derived tier. `src/pipeline/stages/routing.ts:43` always runs `complexityToModelTier(routing.complexity, config)` even when `story.routing.modelTier` was explicitly set by `handleTierEscalation()`. BUG-026 was escalated to `balanced` (logged in iteration header), but `Task classified` shows `modelTier=fast` because `complexityToModelTier("simple", config)` → `"fast"`. Related to BUG-013 (escalation routing not applied) which was marked fixed, but the fix in `applyCachedRouting()` in `pipeline-result-handler.ts:295-310` runs **after** the routing stage — too late. **Location:** `src/pipeline/stages/routing.ts:43`. **Fix:** When `story.routing.modelTier` is explicitly set (by escalation), skip `complexityToModelTier()` and use the cached tier directly. Only derive from complexity when `story.routing.modelTier` is absent.
|
|
225
225
|
- [x] **BUG-033:** LLM routing has no retry on timeout — single attempt with hardcoded 15s default. All 5 LLM routing attempts in the v0.18.3 run timed out at 15s, forcing keyword fallback every time. `src/routing/strategies/llm.ts:63` reads `llmConfig?.timeoutMs ?? 15000` but there's no retry logic — one timeout = immediate fallback. **Location:** `src/routing/strategies/llm.ts:callLlm()`. **Fix:** Add `routing.llm.retries` config (default: 1) with backoff. Also surface `routing.llm.timeoutMs` in `nax config --explain` and consider raising default to 30s for batch routing which processes multiple stories.
|
|
226
226
|
|
|
227
|
+
- [ ] **BUG-037:** Test output summary (verify stage) captures precheck boilerplate instead of actual `bun test` failure. **Symptom:** Logs show successful prechecks (Head) instead of failed tests (Tail). **Fix:** Change `Test output preview` log to tail the last 20 lines of output instead of heading the first 10.
|
|
228
|
+
- [ ] **BUG-038:** `smart-runner` over-matching when global defaults change. **Symptom:** Changing `DEFAULT_CONFIG` matches broad integration tests that fail due to environment/precheck side effects, obscuring targeted results. **Fix:** Refine path mapping to prioritize direct unit tests and exclude known heavy integration tests from default smart-runner matches unless explicitly relevant.
|
|
227
229
|
### Features
|
|
228
230
|
- [x] ~~`nax unlock` command~~
|
|
229
231
|
- [x] ~~Constitution file support~~
|
package/nax/config.json
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"maxIterations": 6,
|
|
60
60
|
"iterationDelayMs": 2000,
|
|
61
61
|
"costLimit": 8.0,
|
|
62
|
-
"sessionTimeoutSeconds":
|
|
62
|
+
"sessionTimeoutSeconds": 7200,
|
|
63
63
|
"verificationTimeoutSeconds": 300,
|
|
64
64
|
"maxStoriesPerFeature": 15,
|
|
65
65
|
"rectification": {
|
|
@@ -147,4 +147,4 @@
|
|
|
147
147
|
"scopeToStory": true
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project": "@nathapp/nax",
|
|
3
|
+
"feature": "nax-compliance",
|
|
4
|
+
"branchName": "feat/v0.19.0-sec",
|
|
5
|
+
"createdAt": "2026-03-05T02:37:00Z",
|
|
6
|
+
"updatedAt": "2026-03-05T02:38:46.450Z",
|
|
7
|
+
"userStories": [
|
|
8
|
+
{
|
|
9
|
+
"id": "US-001",
|
|
10
|
+
"title": "Node.js API Removal",
|
|
11
|
+
"description": "Replace all forbidden Node.js APIs (readFileSync, appendFileSync, existsSync, setTimeout) with Bun-native equivalents (Bun.file().text(), Bun.write, Bun.file().exists(), Bun.sleep) across the codebase as per the v0.19.0 roadmap. Refer to .claude/rules/04-forbidden-patterns.md.",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
"No occurrences of readFileSync or appendFileSync remain in src/",
|
|
14
|
+
"No occurrences of existsSync remain in src/ (unless performance critical)",
|
|
15
|
+
"No occurrences of setTimeout remain in src/",
|
|
16
|
+
"Codebase passes typecheck and lint after changes"
|
|
17
|
+
],
|
|
18
|
+
"status": "failed",
|
|
19
|
+
"passes": false,
|
|
20
|
+
"attempts": 1,
|
|
21
|
+
"routing": {
|
|
22
|
+
"complexity": "simple",
|
|
23
|
+
"modelTier": "powerful",
|
|
24
|
+
"testStrategy": "test-after"
|
|
25
|
+
},
|
|
26
|
+
"priorErrors": [
|
|
27
|
+
"Attempt 1 failed with model tier: fast: Stage requested escalation to higher tier",
|
|
28
|
+
"Attempt 1 failed with model tier: balanced: Stage requested escalation to higher tier"
|
|
29
|
+
],
|
|
30
|
+
"priorFailures": [
|
|
31
|
+
{
|
|
32
|
+
"attempt": 1,
|
|
33
|
+
"modelTier": "fast",
|
|
34
|
+
"stage": "escalation",
|
|
35
|
+
"summary": "Failed with tier fast, escalating to next tier",
|
|
36
|
+
"timestamp": "2026-03-05T02:37:41.586Z"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"attempt": 1,
|
|
40
|
+
"modelTier": "balanced",
|
|
41
|
+
"stage": "escalation",
|
|
42
|
+
"summary": "Failed with tier balanced, escalating to next tier",
|
|
43
|
+
"timestamp": "2026-03-05T02:38:14.713Z"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"escalations": [],
|
|
47
|
+
"dependencies": [],
|
|
48
|
+
"tags": [],
|
|
49
|
+
"storyPoints": 1
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[2026-03-05T02:38:46.450Z] US-001 — FAILED — Node.js API Removal — Execution failed
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project": "@nathapp/nax",
|
|
3
|
+
"feature": "v0.19.0-hardening",
|
|
4
|
+
"branchName": "feat/v0.19.0-sec",
|
|
5
|
+
"createdAt": "2026-03-05T02:58:00Z",
|
|
6
|
+
"updatedAt": "2026-03-05T03:13:29.408Z",
|
|
7
|
+
"userStories": [
|
|
8
|
+
{
|
|
9
|
+
"id": "US-001",
|
|
10
|
+
"title": "Security P0 Hardening",
|
|
11
|
+
"description": "Implement path validation for loaders (SEC-1, SEC-2), fix shell injection vectors (SEC-3, SEC-4), and respect dangerouslySkipPermissions config (SEC-5).",
|
|
12
|
+
"status": "passed",
|
|
13
|
+
"passes": true,
|
|
14
|
+
"attempts": 1,
|
|
15
|
+
"priorErrors": [],
|
|
16
|
+
"priorFailures": [],
|
|
17
|
+
"escalations": [],
|
|
18
|
+
"dependencies": [],
|
|
19
|
+
"tags": [],
|
|
20
|
+
"acceptanceCriteria": [],
|
|
21
|
+
"storyPoints": 1
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "US-002",
|
|
25
|
+
"title": "BUG-1 Parallel Race Condition",
|
|
26
|
+
"description": "Replace Promise.race loop with proper concurrency control in parallel executor.",
|
|
27
|
+
"status": "passed",
|
|
28
|
+
"passes": true,
|
|
29
|
+
"attempts": 1,
|
|
30
|
+
"priorErrors": [],
|
|
31
|
+
"priorFailures": [],
|
|
32
|
+
"escalations": [],
|
|
33
|
+
"dependencies": [],
|
|
34
|
+
"tags": [],
|
|
35
|
+
"acceptanceCriteria": [],
|
|
36
|
+
"storyPoints": 1
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "US-003",
|
|
40
|
+
"title": "Node.js API Removal",
|
|
41
|
+
"description": "Replace readFileSync, appendFileSync, existsSync, and setTimeout with Bun-native equivalents.",
|
|
42
|
+
"status": "passed",
|
|
43
|
+
"passes": true,
|
|
44
|
+
"attempts": 1,
|
|
45
|
+
"priorErrors": [],
|
|
46
|
+
"priorFailures": [],
|
|
47
|
+
"escalations": [],
|
|
48
|
+
"dependencies": [],
|
|
49
|
+
"tags": [],
|
|
50
|
+
"acceptanceCriteria": [],
|
|
51
|
+
"storyPoints": 1
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "US-004",
|
|
55
|
+
"title": "Type Fixes (v0.19.0)",
|
|
56
|
+
"description": "Fix the 18 type errors introduced during the Node.js API removal and security hardening in the src/ directory. Ensure all Bun-native APIs (Bun.file, Bun.write) are used correctly with valid types. Fix variable scopes and missing imports.",
|
|
57
|
+
"status": "failed",
|
|
58
|
+
"passes": false,
|
|
59
|
+
"attempts": 1,
|
|
60
|
+
"routing": {
|
|
61
|
+
"complexity": "medium",
|
|
62
|
+
"modelTier": "powerful",
|
|
63
|
+
"testStrategy": "test-after"
|
|
64
|
+
},
|
|
65
|
+
"priorErrors": [
|
|
66
|
+
"Attempt 1 failed with model tier: balanced: Stage requested escalation to higher tier"
|
|
67
|
+
],
|
|
68
|
+
"priorFailures": [
|
|
69
|
+
{
|
|
70
|
+
"attempt": 1,
|
|
71
|
+
"modelTier": "balanced",
|
|
72
|
+
"stage": "escalation",
|
|
73
|
+
"summary": "Failed with tier balanced, escalating to next tier",
|
|
74
|
+
"timestamp": "2026-03-05T03:12:59.254Z"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"escalations": [],
|
|
78
|
+
"dependencies": [],
|
|
79
|
+
"tags": [],
|
|
80
|
+
"acceptanceCriteria": [],
|
|
81
|
+
"storyPoints": 1
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# v0.19.0 Hardening & Compliance
|
|
2
|
+
|
|
3
|
+
Address Security, Reliability, and Technical Debt findings from the 2026-03-04 audit.
|
|
4
|
+
|
|
5
|
+
## Goals
|
|
6
|
+
- SEC-1 to SEC-5: Complete security hardening (RCE, Shell, Permissions).
|
|
7
|
+
- BUG-1: Fix parallel concurrency race condition.
|
|
8
|
+
- BUG-3, BUG-5, MEM-2: Reliability fixes (metrics, mutation, memory leak).
|
|
9
|
+
- Technical Debt: Replace forbidden Node.js APIs (readFileSync, etc.) with Bun-native equivalents.
|
|
10
|
+
- Architecture: Split 400-line files and cleanup dead code.
|
|
11
|
+
|
|
12
|
+
## Acceptance Criteria
|
|
13
|
+
- SEC-1/2: Dynamic imports restricted to allowed roots.
|
|
14
|
+
- SEC-3/4: Shell injection via backticks/dollar-signs blocked.
|
|
15
|
+
- SEC-5: --dangerously-skip-permissions respects config.
|
|
16
|
+
- BUG-1: Parallel execution respects maxConcurrency exactly.
|
|
17
|
+
- Node.js APIs: All readFileSync/appendFileSync/setTimeout replaced with Bun equivalents.
|
|
18
|
+
- Files: cli/config.ts split below 400 lines.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project": "nax",
|
|
3
|
+
"branchName": "feat/v0.20.0-verify-v2",
|
|
4
|
+
"feature": "verify-v2",
|
|
5
|
+
"userStories": [
|
|
6
|
+
{
|
|
7
|
+
"id": "US-001",
|
|
8
|
+
"title": "Remove test from review defaults",
|
|
9
|
+
"description": "Change review.checks default from ['typecheck', 'lint', 'test'] to ['typecheck', 'lint'] in src/config/defaults.ts. The test check in review duplicates the pipeline verify stage. Keep 'test' as a valid enum value in the schema for backwards compatibility but remove it from the default config. Update any tests that assert on the default review checks array.",
|
|
10
|
+
"complexity": "simple",
|
|
11
|
+
"status": "passed",
|
|
12
|
+
"attempts": 0,
|
|
13
|
+
"priorErrors": [
|
|
14
|
+
"Attempt 1 failed with model tier: balanced: Stage requested escalation to higher tier"
|
|
15
|
+
],
|
|
16
|
+
"priorFailures": [
|
|
17
|
+
{
|
|
18
|
+
"attempt": 1,
|
|
19
|
+
"modelTier": "balanced",
|
|
20
|
+
"stage": "escalation",
|
|
21
|
+
"summary": "Failed with tier balanced, escalating to next tier",
|
|
22
|
+
"timestamp": "2026-03-05T11:14:42.773Z"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"escalations": [],
|
|
26
|
+
"dependencies": [],
|
|
27
|
+
"tags": [],
|
|
28
|
+
"acceptanceCriteria": [],
|
|
29
|
+
"storyPoints": 1,
|
|
30
|
+
"routing": {
|
|
31
|
+
"complexity": "simple",
|
|
32
|
+
"modelTier": "powerful",
|
|
33
|
+
"testStrategy": "test-after",
|
|
34
|
+
"reasoning": "override: simple config default change, tests already exist"
|
|
35
|
+
},
|
|
36
|
+
"passes": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "US-002",
|
|
40
|
+
"title": "Remove post-verify scoped duplicate",
|
|
41
|
+
"description": "In src/execution/post-verify.ts, remove the scoped verification logic (getChangedTestFiles + runVerification for scoped tests) from runPostAgentVerification(). The pipeline verify stage already runs Smart Test Runner scoped tests. post-verify should ONLY run the regression gate (full suite) and handle failure revert with StructuredFailure. Remove getChangedTestFiles() and scopeTestCommand() helper functions. Remove the scoped rectification loop call. Update the function signature to no longer need storyGitRef. Update all tests for post-verify accordingly.",
|
|
42
|
+
"complexity": "medium",
|
|
43
|
+
"status": "passed",
|
|
44
|
+
"attempts": 0,
|
|
45
|
+
"priorErrors": [],
|
|
46
|
+
"priorFailures": [],
|
|
47
|
+
"escalations": [],
|
|
48
|
+
"dependencies": [],
|
|
49
|
+
"tags": [],
|
|
50
|
+
"acceptanceCriteria": [],
|
|
51
|
+
"storyPoints": 1,
|
|
52
|
+
"passes": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "US-003",
|
|
56
|
+
"title": "Deferred regression gate",
|
|
57
|
+
"description": "Create new src/execution/lifecycle/run-regression.ts that implements a deferred regression gate. Instead of running the full test suite after every story, run it once after all stories complete. Steps: (1) Add 'mode' field to RegressionGateConfigSchema with values 'deferred' | 'per-story' | 'disabled' (default: 'deferred'). (2) Add 'maxRectificationAttempts' field (default: 2). (3) In run-regression.ts: run full suite once, parse failures, use reverse Smart Test Runner mapping (test file -> source file -> responsible story via git log), attempt targeted rectification per responsible story, re-run full suite to confirm. (4) Call deferred regression from run-completion.ts before final metrics, only when mode is 'deferred'. (5) When mode is 'deferred', skip the per-story regression gate in post-verify.ts. (6) Add reverseMapTestToSource() to smart-runner.ts. (7) Handle edge cases: partial completion (only check passed stories), overlapping file changes (try last story first), unmapped tests (warn and mark all passed stories for re-verification).",
|
|
58
|
+
"complexity": "complex",
|
|
59
|
+
"status": "passed",
|
|
60
|
+
"attempts": 0,
|
|
61
|
+
"priorErrors": [],
|
|
62
|
+
"priorFailures": [],
|
|
63
|
+
"escalations": [],
|
|
64
|
+
"dependencies": [],
|
|
65
|
+
"tags": [],
|
|
66
|
+
"acceptanceCriteria": [],
|
|
67
|
+
"storyPoints": 1,
|
|
68
|
+
"routing": {
|
|
69
|
+
"complexity": "complex",
|
|
70
|
+
"modelTier": "balanced",
|
|
71
|
+
"testStrategy": "three-session-tdd-lite",
|
|
72
|
+
"reasoning": "override: complex new file + schema changes, needs powerful model"
|
|
73
|
+
},
|
|
74
|
+
"failureCategory": "session-failure",
|
|
75
|
+
"passes": true
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"updatedAt": "2026-03-05T11:58:46.858Z"
|
|
79
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
[2026-03-05T07:05:07.935Z] US-002 — PASSED — Remove post-verify scoped duplicate — Cost: $0.1170
|
|
2
|
+
[2026-03-05T08:08:59.656Z] US-003 — FAILED — Deferred regression gate — Execution failed
|
|
3
|
+
[2026-03-05T08:13:50.586Z] US-001 — FAILED — Remove test from review defaults — Execution failed
|
package/nax/status.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"run": {
|
|
4
|
+
"id": "run-2026-03-05T02-37-04-540Z",
|
|
5
|
+
"feature": "nax-compliance",
|
|
6
|
+
"startedAt": "2026-03-05T02:37:04.540Z",
|
|
7
|
+
"status": "stalled",
|
|
8
|
+
"dryRun": false,
|
|
9
|
+
"pid": 814245
|
|
10
|
+
},
|
|
11
|
+
"progress": {
|
|
12
|
+
"total": 1,
|
|
13
|
+
"passed": 0,
|
|
14
|
+
"failed": 1,
|
|
15
|
+
"paused": 0,
|
|
16
|
+
"blocked": 0,
|
|
17
|
+
"pending": 0
|
|
18
|
+
},
|
|
19
|
+
"cost": {
|
|
20
|
+
"spent": 0,
|
|
21
|
+
"limit": 8
|
|
22
|
+
},
|
|
23
|
+
"current": null,
|
|
24
|
+
"iterations": 3,
|
|
25
|
+
"updatedAt": "2026-03-05T02:38:46.469Z",
|
|
26
|
+
"durationMs": 101929
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { AgentAdapter } from "../agents/types";
|
|
9
|
-
import type { ModelDef } from "../config/schema";
|
|
9
|
+
import type { ModelDef, NaxConfig } from "../config/schema";
|
|
10
10
|
import { getLogger } from "../logger";
|
|
11
11
|
import type { PRD, UserStory } from "../prd/types";
|
|
12
12
|
|
|
@@ -70,6 +70,8 @@ export interface GenerateFixStoriesOptions {
|
|
|
70
70
|
workdir: string;
|
|
71
71
|
/** Model definition for LLM call */
|
|
72
72
|
modelDef: ModelDef;
|
|
73
|
+
/** Global config */
|
|
74
|
+
config: NaxConfig;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
/**
|
|
@@ -224,7 +226,9 @@ export async function generateFixStories(
|
|
|
224
226
|
|
|
225
227
|
try {
|
|
226
228
|
// Call agent to generate fix description
|
|
227
|
-
const
|
|
229
|
+
const skipPerms = options.config.quality?.dangerouslySkipPermissions ?? true;
|
|
230
|
+
const permArgs = skipPerms ? ["--dangerously-skip-permissions"] : [];
|
|
231
|
+
const cmd = [adapter.binary, "--model", modelDef.model, ...permArgs, "-p", prompt];
|
|
228
232
|
|
|
229
233
|
const proc = Bun.spawn(cmd, {
|
|
230
234
|
cwd: workdir,
|
|
@@ -178,7 +178,9 @@ export async function generateAcceptanceTests(
|
|
|
178
178
|
|
|
179
179
|
try {
|
|
180
180
|
// Call agent to generate tests (using decompose as pattern)
|
|
181
|
-
const
|
|
181
|
+
const skipPerms = options.config.quality?.dangerouslySkipPermissions ?? true;
|
|
182
|
+
const permArgs = skipPerms ? ["--dangerously-skip-permissions"] : [];
|
|
183
|
+
const cmd = [adapter.binary, "--model", options.modelDef.model, ...permArgs, "-p", prompt];
|
|
182
184
|
|
|
183
185
|
const proc = Bun.spawn(cmd, {
|
|
184
186
|
cwd: options.workdir,
|
package/src/acceptance/types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Types for generating acceptance tests from spec.md acceptance criteria.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ModelDef, ModelTier } from "../config/schema";
|
|
7
|
+
import type { ModelDef, ModelTier, NaxConfig } from "../config/schema";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A single acceptance criterion extracted from spec.md.
|
|
@@ -55,6 +55,8 @@ export interface GenerateAcceptanceTestsOptions {
|
|
|
55
55
|
modelTier: ModelTier;
|
|
56
56
|
/** Resolved model definition */
|
|
57
57
|
modelDef: ModelDef;
|
|
58
|
+
/** Global config for quality settings */
|
|
59
|
+
config: NaxConfig;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
/**
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
1
4
|
/**
|
|
2
5
|
* Claude Code Plan Logic
|
|
3
6
|
*
|
|
@@ -96,9 +99,7 @@ export async function runPlan(
|
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
// Non-interactive: redirect stdout to temp file via Bun.file()
|
|
99
|
-
|
|
100
|
-
const { mkdtempSync, readFileSync, rmSync } = require("node:fs");
|
|
101
|
-
const { tmpdir } = require("node:os");
|
|
102
|
+
|
|
102
103
|
const tempDir = mkdtempSync(join(tmpdir(), "nax-plan-"));
|
|
103
104
|
const outFile = join(tempDir, "stdout.txt");
|
|
104
105
|
const errFile = join(tempDir, "stderr.txt");
|
|
@@ -120,8 +121,8 @@ export async function runPlan(
|
|
|
120
121
|
// Unregister PID after exit
|
|
121
122
|
await pidRegistry.unregister(proc.pid);
|
|
122
123
|
|
|
123
|
-
const specContent =
|
|
124
|
-
const conversationLog =
|
|
124
|
+
const specContent = await Bun.file(outFile).text();
|
|
125
|
+
const conversationLog = await Bun.file(errFile).text();
|
|
125
126
|
|
|
126
127
|
if (exitCode !== 0) {
|
|
127
128
|
throw new Error(`Plan mode failed with exit code ${exitCode}: ${conversationLog || "unknown error"}`);
|
package/src/cli/analyze.ts
CHANGED
package/src/cli/init.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Initializes nax configuration directories and files.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { mkdir } from "node:fs/promises";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { globalConfigDir, projectConfigDir } from "../config/paths";
|
|
10
11
|
import { DEFAULT_CONFIG } from "../config/schema";
|
|
@@ -34,7 +35,7 @@ async function updateGitignore(projectRoot: string): Promise<void> {
|
|
|
34
35
|
|
|
35
36
|
let existing = "";
|
|
36
37
|
if (existsSync(gitignorePath)) {
|
|
37
|
-
existing =
|
|
38
|
+
existing = await Bun.file(gitignorePath).text();
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const missingEntries = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -95,7 +96,7 @@ async function initGlobal(): Promise<void> {
|
|
|
95
96
|
|
|
96
97
|
// Create ~/.nax if it doesn't exist
|
|
97
98
|
if (!existsSync(globalDir)) {
|
|
98
|
-
|
|
99
|
+
await mkdir(globalDir, { recursive: true });
|
|
99
100
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
100
101
|
}
|
|
101
102
|
|
|
@@ -120,7 +121,7 @@ async function initGlobal(): Promise<void> {
|
|
|
120
121
|
// Create ~/.nax/hooks/ directory if it doesn't exist
|
|
121
122
|
const hooksDir = join(globalDir, "hooks");
|
|
122
123
|
if (!existsSync(hooksDir)) {
|
|
123
|
-
|
|
124
|
+
await mkdir(hooksDir, { recursive: true });
|
|
124
125
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
125
126
|
} else {
|
|
126
127
|
logger.info("init", "Global hooks directory already exists", { path: hooksDir });
|
|
@@ -138,7 +139,7 @@ async function initProject(projectRoot: string): Promise<void> {
|
|
|
138
139
|
|
|
139
140
|
// Create nax/ directory if it doesn't exist
|
|
140
141
|
if (!existsSync(projectDir)) {
|
|
141
|
-
|
|
142
|
+
await mkdir(projectDir, { recursive: true });
|
|
142
143
|
logger.info("init", "Created project config directory", { path: projectDir });
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -163,7 +164,7 @@ async function initProject(projectRoot: string): Promise<void> {
|
|
|
163
164
|
// Create nax/hooks/ directory if it doesn't exist
|
|
164
165
|
const hooksDir = join(projectDir, "hooks");
|
|
165
166
|
if (!existsSync(hooksDir)) {
|
|
166
|
-
|
|
167
|
+
await mkdir(hooksDir, { recursive: true });
|
|
167
168
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
168
169
|
} else {
|
|
169
170
|
logger.info("init", "Project hooks directory already exists", { path: hooksDir });
|
package/src/config/defaults.ts
CHANGED
|
@@ -67,6 +67,7 @@ export const DEFAULT_CONFIG: NaxConfig = {
|
|
|
67
67
|
enabled: true,
|
|
68
68
|
timeoutSeconds: 120,
|
|
69
69
|
acceptOnTimeout: true,
|
|
70
|
+
maxRectificationAttempts: 2,
|
|
70
71
|
},
|
|
71
72
|
contextProviderTokenBudget: 2000,
|
|
72
73
|
smartTestRunner: true,
|
|
@@ -80,6 +81,7 @@ export const DEFAULT_CONFIG: NaxConfig = {
|
|
|
80
81
|
detectOpenHandles: true,
|
|
81
82
|
detectOpenHandlesRetries: 1,
|
|
82
83
|
gracePeriodMs: 5000,
|
|
84
|
+
dangerouslySkipPermissions: true,
|
|
83
85
|
drainTimeoutMs: 2000,
|
|
84
86
|
shell: "/bin/sh",
|
|
85
87
|
stripEnvVars: ["CLAUDECODE", "REPL_ID", "AGENT"],
|
|
@@ -112,7 +114,7 @@ export const DEFAULT_CONFIG: NaxConfig = {
|
|
|
112
114
|
},
|
|
113
115
|
review: {
|
|
114
116
|
enabled: true,
|
|
115
|
-
checks: ["typecheck", "lint"
|
|
117
|
+
checks: ["typecheck", "lint"],
|
|
116
118
|
commands: {},
|
|
117
119
|
},
|
|
118
120
|
plan: {
|
package/src/config/schemas.ts
CHANGED
|
@@ -63,6 +63,8 @@ const RegressionGateConfigSchema = z.object({
|
|
|
63
63
|
enabled: z.boolean().default(true),
|
|
64
64
|
timeoutSeconds: z.number().int().min(10).max(600).default(120),
|
|
65
65
|
acceptOnTimeout: z.boolean().default(true),
|
|
66
|
+
mode: z.enum(["deferred", "per-story", "disabled"]).default("deferred"),
|
|
67
|
+
maxRectificationAttempts: z.number().int().min(1).default(2),
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
const SmartTestRunnerConfigSchema = z.object({
|
package/src/config/types.ts
CHANGED
|
@@ -78,6 +78,10 @@ export interface RegressionGateConfig {
|
|
|
78
78
|
timeoutSeconds: number;
|
|
79
79
|
/** Accept timeout as pass instead of failing (BUG-026, default: true) */
|
|
80
80
|
acceptOnTimeout?: boolean;
|
|
81
|
+
/** Mode of regression gate: 'deferred' (run once after all stories), 'per-story' (run after each story), 'disabled' (default: 'deferred') */
|
|
82
|
+
mode?: "deferred" | "per-story" | "disabled";
|
|
83
|
+
/** Max rectification attempts for deferred regression gate (default: 2) */
|
|
84
|
+
maxRectificationAttempts?: number;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/** Smart test runner configuration (STR-007) */
|
|
@@ -145,6 +149,8 @@ export interface QualityConfig {
|
|
|
145
149
|
detectOpenHandlesRetries: number;
|
|
146
150
|
/** Grace period in ms after SIGTERM before sending SIGKILL (default: 5000) */
|
|
147
151
|
gracePeriodMs: number;
|
|
152
|
+
/** Use --dangerously-skip-permissions for agent sessions (default: false) */
|
|
153
|
+
dangerouslySkipPermissions: boolean;
|
|
148
154
|
/** Deadline in ms to drain stdout/stderr after killing process (Bun stream workaround, default: 2000) */
|
|
149
155
|
drainTimeoutMs: number;
|
|
150
156
|
/** Shell to use for running verification commands (default: /bin/sh) */
|