@navels/neal 0.1.0 → 0.3.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 +206 -209
- package/SECURITY.md +16 -19
- package/dist/neal/adjudicator/{blocked-adjudicator.js → consultant.js} +21 -21
- package/dist/neal/adjudicator/execute.js +33 -33
- package/dist/neal/adjudicator/final-completion.js +2 -31
- package/dist/neal/adjudicator/planning.js +264 -24
- package/dist/neal/agents/prompts.js +7 -15
- package/dist/neal/agents/rounds.js +51 -73
- package/dist/neal/agents/schemas.js +615 -502
- package/dist/neal/agents/structured-json.js +36 -0
- package/dist/neal/blocked-guidance.js +16 -9
- package/dist/neal/cli.js +2 -2
- package/dist/neal/commands/check.js +2 -2
- package/dist/neal/commands/compat.js +100 -36
- package/dist/neal/commands/interactive-activity.js +2 -2
- package/dist/neal/commands/recovery-guidance.js +20 -8
- package/dist/neal/commands/runtime.js +4 -2
- package/dist/neal/config.js +40 -7
- package/dist/neal/context/inline-review-context.js +13 -28
- package/dist/neal/context/reviewer-context.js +19 -14
- package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
- package/dist/neal/eval/reviewer-eval.js +155 -0
- package/dist/neal/git.js +9 -3
- package/dist/neal/interactive-controls.js +5 -0
- package/dist/neal/orchestrator/completion.js +215 -113
- package/dist/neal/orchestrator/failures.js +7 -0
- package/dist/neal/orchestrator/notifications.js +9 -9
- package/dist/neal/orchestrator/phases/planning.js +124 -112
- package/dist/neal/orchestrator/phases/recovery.js +151 -115
- package/dist/neal/orchestrator/phases/review.js +28 -1
- package/dist/neal/orchestrator/split-plan.js +12 -11
- package/dist/neal/orchestrator/transitions.js +36 -71
- package/dist/neal/orchestrator.js +3 -0
- package/dist/neal/plan-doc.js +24 -1
- package/dist/neal/plan-queue.js +29 -1
- package/dist/neal/prompts/assert-builder.js +8 -1
- package/dist/neal/prompts/execute.js +11 -19
- package/dist/neal/prompts/planning.js +64 -55
- package/dist/neal/prompts/review-doctrine.js +13 -50
- package/dist/neal/prompts/specialized.js +28 -22
- package/dist/neal/prompts/specs.js +223 -17
- package/dist/neal/providers/agent-settings-isolation.js +35 -0
- package/dist/neal/providers/anthropic-claude.js +309 -248
- package/dist/neal/providers/detection.js +3 -7
- package/dist/neal/providers/git-config-isolation.js +83 -0
- package/dist/neal/providers/openai-codex.js +102 -204
- package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
- package/dist/neal/providers/openai-compatible.js +1079 -403
- package/dist/neal/providers/openrouter-routing.js +68 -0
- package/dist/neal/providers/pricing.js +124 -0
- package/dist/neal/providers/rate-card.js +2275 -0
- package/dist/neal/providers/registry.js +6 -17
- package/dist/neal/providers/telemetry.js +4 -0
- package/dist/neal/providers/types.js +12 -0
- package/dist/neal/resume-decision.js +7 -6
- package/dist/neal/resume-planner.js +9 -0
- package/dist/neal/retrospective.js +36 -7
- package/dist/neal/review-debt.js +30 -0
- package/dist/neal/review-findings/provider.js +17 -29
- package/dist/neal/review.js +17 -1
- package/dist/neal/run-metrics.js +74 -9
- package/dist/neal/state-invariants.js +11 -3
- package/dist/neal/state-views.js +27 -5
- package/dist/neal/state.js +59 -15
- package/dist/neal/status.js +28 -1
- package/dist/neal/support.js +3 -3
- package/docs/README.md +22 -18
- package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
- package/docs/architecture.md +79 -83
- package/docs/automation.md +11 -11
- package/docs/compat.md +91 -61
- package/docs/compatible-models.md +145 -110
- package/docs/demo.md +15 -5
- package/docs/maintenance.md +74 -32
- package/docs/plan-format.md +61 -33
- package/docs/prompt-evals.md +96 -0
- package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
- package/docs/providers.md +330 -283
- package/docs/release.md +43 -40
- package/docs/review-convergence.md +246 -0
- package/docs/state-machine.md +61 -59
- package/docs/storage.md +40 -31
- package/docs/troubleshooting.md +32 -29
- package/examples/compat/add-edit-verify/PLAN.md +2 -0
- package/examples/compat/add-edit-verify/broken.diff +2 -2
- package/examples/compat/add-edit-verify/good.diff +2 -2
- package/examples/compat/add-edit-verify/src/add.js +1 -1
- package/examples/compat/manifest.json +25 -29
- package/examples/compat/sum-grep-edit/PLAN.md +33 -0
- package/examples/compat/sum-grep-edit/broken.diff +12 -0
- package/examples/compat/sum-grep-edit/good.diff +12 -0
- package/examples/compat/sum-grep-edit/package.json +5 -0
- package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
- package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
- package/examples/issue-triage-js/README.md +0 -15
- package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
- package/examples/reviewer-eval/clean-doc/change.diff +9 -0
- package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
- package/examples/reviewer-eval/clean-extract/change.diff +13 -0
- package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
- package/examples/reviewer-eval/dropped-error/change.diff +15 -0
- package/examples/reviewer-eval/manifest.json +45 -0
- package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
- package/examples/reviewer-eval/off-by-one/change.diff +11 -0
- package/{config.yml → neal.yml} +1 -1
- package/package.json +20 -15
- package/dist/neal/providers/generic-agentic.js +0 -1238
- package/docs/comparison.md +0 -105
- package/examples/compat/is-even-add-test/PLAN.md +0 -30
- package/examples/compat/is-even-add-test/broken.diff +0 -11
- package/examples/compat/is-even-add-test/good.diff +0 -11
- package/examples/compat/is-even-add-test/package.json +0 -5
- package/examples/compat/is-even-add-test/src/is-even.js +0 -3
- package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
- package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
- package/examples/compat/reverse-grep-edit/broken.diff +0 -12
- package/examples/compat/reverse-grep-edit/good.diff +0 -12
- package/examples/compat/reverse-grep-edit/package.json +0 -5
- package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
- package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
|
@@ -2,6 +2,37 @@ import { aggregateChangedFilesForAcceptedDerivedScopes, getAcceptedDerivedScopes
|
|
|
2
2
|
import { toResidualReviewDebt } from '../review-debt.js';
|
|
3
3
|
import { getDerivedPlanView, getFinalCompletionView } from '../state-views.js';
|
|
4
4
|
const ALREADY_SATISFIED_ACCEPTANCE_RATIONALE_PREFIX = 'Accepted top-level already-satisfied scope';
|
|
5
|
+
export function createScopeBoundaryReset() {
|
|
6
|
+
return {
|
|
7
|
+
currentScopeProgressJustification: null,
|
|
8
|
+
currentScopeMeaningfulProgressVerdict: null,
|
|
9
|
+
rounds: [],
|
|
10
|
+
recentBlocks: [],
|
|
11
|
+
consultantAttemptCount: 0,
|
|
12
|
+
findings: [],
|
|
13
|
+
// planReviewDebt is a projection of the current findings, so clearing
|
|
14
|
+
// findings must clear it too (toPlanReviewDebt([]) === []); otherwise a new
|
|
15
|
+
// derived-plan negotiation would carry stale current-negotiation debt. The
|
|
16
|
+
// durable inheritedPlanReviewDebt is intentionally NOT reset here — it is
|
|
17
|
+
// write-once and survives scope boundaries.
|
|
18
|
+
planReviewDebt: [],
|
|
19
|
+
createdCommits: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function createNextScopeEntryReset(baseCommit) {
|
|
23
|
+
return {
|
|
24
|
+
...createScopeBoundaryReset(),
|
|
25
|
+
baseCommit,
|
|
26
|
+
finalCommit: null,
|
|
27
|
+
archivedReviewPath: null,
|
|
28
|
+
coderSessionHandle: null,
|
|
29
|
+
coderSessionProtocol: null,
|
|
30
|
+
lastScopeMarker: null,
|
|
31
|
+
finalCompletionSummary: null,
|
|
32
|
+
phase: 'coder_scope',
|
|
33
|
+
status: 'running',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
5
36
|
export function appendCompletedScope(state, result, details) {
|
|
6
37
|
const scopeLabel = details.scopeLabel ?? getCurrentScopeLabel(state);
|
|
7
38
|
const marker = details.marker ?? (state.lastScopeMarker ?? 'AUTONOMY_BLOCKED');
|
|
@@ -57,23 +88,17 @@ export function adoptAcceptedDerivedPlan(state) {
|
|
|
57
88
|
}
|
|
58
89
|
return {
|
|
59
90
|
...state,
|
|
91
|
+
...createScopeBoundaryReset(),
|
|
60
92
|
phase: 'coder_scope',
|
|
61
93
|
status: 'running',
|
|
62
94
|
derivedScopeIndex: derivedPlan.scopeIndex ?? 1,
|
|
63
95
|
coderSessionHandle: null,
|
|
64
96
|
coderSessionProtocol: null,
|
|
65
97
|
coderRetryCount: 0,
|
|
66
|
-
currentScopeProgressJustification: null,
|
|
67
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
68
98
|
finalCompletionSummary: null,
|
|
69
99
|
finalCompletionReviewVerdict: null,
|
|
70
100
|
finalCompletionResolvedAction: null,
|
|
71
101
|
finalCompletionContinueExecutionCapReached: false,
|
|
72
|
-
rounds: [],
|
|
73
|
-
recentBlocks: [],
|
|
74
|
-
reviewStuckArbiterCount: 0,
|
|
75
|
-
findings: [],
|
|
76
|
-
createdCommits: [],
|
|
77
102
|
blockedFromPhase: null,
|
|
78
103
|
};
|
|
79
104
|
}
|
|
@@ -89,15 +114,8 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
89
114
|
if (derivedExecution && derivedPlanCompleted) {
|
|
90
115
|
return {
|
|
91
116
|
...state,
|
|
92
|
-
|
|
93
|
-
finalCommit: null,
|
|
94
|
-
coderSessionHandle: null,
|
|
95
|
-
coderSessionProtocol: null,
|
|
117
|
+
...createNextScopeEntryReset(finalCommit),
|
|
96
118
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
97
|
-
lastScopeMarker: null,
|
|
98
|
-
currentScopeProgressJustification: null,
|
|
99
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
100
|
-
finalCompletionSummary: null,
|
|
101
119
|
finalCompletionReviewVerdict: null,
|
|
102
120
|
finalCompletionResolvedAction: null,
|
|
103
121
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -109,28 +127,13 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
109
127
|
derivedPlanAcceptedNotified: false,
|
|
110
128
|
splitPlanBlockedNotified: false,
|
|
111
129
|
splitPlanCountForCurrentScope: 0,
|
|
112
|
-
rounds: [],
|
|
113
|
-
recentBlocks: [],
|
|
114
|
-
reviewStuckArbiterCount: 0,
|
|
115
|
-
findings: [],
|
|
116
|
-
createdCommits: [],
|
|
117
130
|
completedScopes,
|
|
118
|
-
archivedReviewPath: null,
|
|
119
|
-
phase: 'coder_scope',
|
|
120
|
-
status: 'running',
|
|
121
131
|
};
|
|
122
132
|
}
|
|
123
133
|
if (derivedExecution) {
|
|
124
134
|
return {
|
|
125
135
|
...state,
|
|
126
|
-
|
|
127
|
-
finalCommit: null,
|
|
128
|
-
coderSessionHandle: null,
|
|
129
|
-
coderSessionProtocol: null,
|
|
130
|
-
lastScopeMarker: null,
|
|
131
|
-
currentScopeProgressJustification: null,
|
|
132
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
133
|
-
finalCompletionSummary: null,
|
|
136
|
+
...createNextScopeEntryReset(finalCommit),
|
|
134
137
|
finalCompletionReviewVerdict: null,
|
|
135
138
|
finalCompletionResolvedAction: null,
|
|
136
139
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -138,29 +141,14 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
138
141
|
splitPlanStartedNotified: false,
|
|
139
142
|
derivedPlanAcceptedNotified: false,
|
|
140
143
|
splitPlanBlockedNotified: false,
|
|
141
|
-
rounds: [],
|
|
142
|
-
recentBlocks: [],
|
|
143
|
-
reviewStuckArbiterCount: 0,
|
|
144
|
-
findings: [],
|
|
145
|
-
createdCommits: [],
|
|
146
144
|
completedScopes,
|
|
147
|
-
archivedReviewPath: null,
|
|
148
|
-
phase: 'coder_scope',
|
|
149
|
-
status: 'running',
|
|
150
145
|
};
|
|
151
146
|
}
|
|
152
147
|
if (continueScopes) {
|
|
153
148
|
return {
|
|
154
149
|
...state,
|
|
155
|
-
|
|
156
|
-
finalCommit: null,
|
|
157
|
-
coderSessionHandle: null,
|
|
158
|
-
coderSessionProtocol: null,
|
|
150
|
+
...createNextScopeEntryReset(finalCommit),
|
|
159
151
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
160
|
-
lastScopeMarker: null,
|
|
161
|
-
currentScopeProgressJustification: null,
|
|
162
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
163
|
-
finalCompletionSummary: null,
|
|
164
152
|
finalCompletionReviewVerdict: null,
|
|
165
153
|
finalCompletionResolvedAction: null,
|
|
166
154
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -172,15 +160,7 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
172
160
|
derivedPlanAcceptedNotified: false,
|
|
173
161
|
splitPlanBlockedNotified: false,
|
|
174
162
|
splitPlanCountForCurrentScope: 0,
|
|
175
|
-
rounds: [],
|
|
176
|
-
recentBlocks: [],
|
|
177
|
-
reviewStuckArbiterCount: 0,
|
|
178
|
-
findings: [],
|
|
179
|
-
createdCommits: [],
|
|
180
163
|
completedScopes,
|
|
181
|
-
archivedReviewPath: null,
|
|
182
|
-
phase: 'coder_scope',
|
|
183
|
-
status: 'running',
|
|
184
164
|
};
|
|
185
165
|
}
|
|
186
166
|
return {
|
|
@@ -223,19 +203,11 @@ export function computeNextScopeStateAfterParentAdvance(args) {
|
|
|
223
203
|
: args.state.currentScopeNumber;
|
|
224
204
|
return {
|
|
225
205
|
...args.state,
|
|
226
|
-
|
|
227
|
-
finalCommit: null,
|
|
228
|
-
archivedReviewPath: null,
|
|
229
|
-
coderSessionHandle: null,
|
|
230
|
-
coderSessionProtocol: null,
|
|
206
|
+
...createNextScopeEntryReset(args.finalCommit),
|
|
231
207
|
reviewerSessionHandle: null,
|
|
232
208
|
coderRetryCount: 0,
|
|
233
209
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
234
|
-
lastScopeMarker: null,
|
|
235
|
-
currentScopeProgressJustification: null,
|
|
236
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
237
210
|
manualGate: null,
|
|
238
|
-
finalCompletionSummary: null,
|
|
239
211
|
finalCompletionReviewVerdict: null,
|
|
240
212
|
finalCompletionResolvedAction: null,
|
|
241
213
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -247,16 +219,9 @@ export function computeNextScopeStateAfterParentAdvance(args) {
|
|
|
247
219
|
derivedPlanAcceptedNotified: false,
|
|
248
220
|
splitPlanBlockedNotified: false,
|
|
249
221
|
splitPlanCountForCurrentScope: 0,
|
|
250
|
-
rounds: [],
|
|
251
|
-
recentBlocks: [],
|
|
252
|
-
reviewStuckArbiterCount: 0,
|
|
253
|
-
findings: [],
|
|
254
|
-
createdCommits: [],
|
|
255
222
|
completedScopes: args.completedScopes,
|
|
256
223
|
blockedFromPhase: null,
|
|
257
224
|
interactiveBlockedRecovery: null,
|
|
258
|
-
phase: 'coder_scope',
|
|
259
|
-
status: 'running',
|
|
260
225
|
};
|
|
261
226
|
}
|
|
262
227
|
function getAcceptedScopeSummary(state) {
|
|
@@ -81,6 +81,9 @@ export async function initializeOrchestration(planDoc, cwd, agentConfig, topLeve
|
|
|
81
81
|
reviewMarkdownPath: join(logger.runDir, 'REVIEW.md'),
|
|
82
82
|
recoveryMarkdownPath: join(logger.runDir, 'RECOVERY.md'),
|
|
83
83
|
maxRounds: getMaxReviewRounds(cwd),
|
|
84
|
+
// Only the `neal run` queue's execution stage supplies this; every other
|
|
85
|
+
// caller omits it and the durable inherited debt seeds to an empty array.
|
|
86
|
+
inheritedPlanReviewDebt: options?.inheritedPlanReviewDebt,
|
|
84
87
|
};
|
|
85
88
|
await mkdir(stateDir, { recursive: true });
|
|
86
89
|
const baseCommit = await getHeadCommit(cwd);
|
package/dist/neal/plan-doc.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
import { realpath, stat } from 'node:fs/promises';
|
|
1
|
+
import { readFile, realpath, stat, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { getIgnoredPaths, getRepositoryRoot } from './git.js';
|
|
4
|
+
/**
|
|
5
|
+
* Run a destructive worktree action (reset --hard / clean) while preserving
|
|
6
|
+
* the plan document's byte content across it. The plan doc is a wrapper-owned
|
|
7
|
+
* overlay by contract: it may be an uncommitted modification of a TRACKED file
|
|
8
|
+
* (reset --hard would silently restore the committed content — a different
|
|
9
|
+
* plan — mid-run) or an untracked file (clean would delete it). Either way the
|
|
10
|
+
* run's plan must survive worktree hygiene. Restores nothing if the plan doc
|
|
11
|
+
* did not exist before the action.
|
|
12
|
+
*/
|
|
13
|
+
export async function withPlanDocPreserved(planDoc, action) {
|
|
14
|
+
let content = null;
|
|
15
|
+
try {
|
|
16
|
+
content = await readFile(planDoc);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
content = null;
|
|
20
|
+
}
|
|
21
|
+
const result = await action();
|
|
22
|
+
if (content !== null) {
|
|
23
|
+
await writeFile(planDoc, content);
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
4
27
|
export async function inspectPlanDocDisposition(cwd, planDoc, options = {}) {
|
|
5
28
|
const repositoryRoot = await realpath(await getRepositoryRoot(cwd));
|
|
6
29
|
const absolutePlanDoc = resolve(cwd, planDoc);
|
package/dist/neal/plan-queue.js
CHANGED
|
@@ -9,7 +9,7 @@ import { writeExecutionArtifacts } from './orchestrator/artifacts.js';
|
|
|
9
9
|
import { assertAgentConfigSupportsResume, assertAgentConfigSupportsWriterRun } from './providers/registry.js';
|
|
10
10
|
import { writeCurrentRunPointer } from './run-registry.js';
|
|
11
11
|
import { formatPublicRunStatus, getRunDisplayStatus } from './run-status.js';
|
|
12
|
-
import { getRunStatePath, loadState, saveState } from './state.js';
|
|
12
|
+
import { getRunStatePath, hydrateResidualReviewDebtItem, loadState, saveState } from './state.js';
|
|
13
13
|
import { QUEUE_LINK_FILE, QUEUE_STATE_FILE, QUEUE_SUMMARY_FILE, getCurrentPlanAndExecuteQueuePointerPath as getStorageCurrentPlanAndExecuteQueuePointerPath, getQueuesDir, } from './storage-paths.js';
|
|
14
14
|
import { executeRun, withPreparedWriterRun, } from './commands/runtime.js';
|
|
15
15
|
import { filterAllowedDirtyPathStatus, filterWrapperOwnedWorktreeStatus, parseWorktreeStatusLine, } from './worktree-status.js';
|
|
@@ -114,6 +114,7 @@ export async function createPlanAndExecuteQueue(args) {
|
|
|
114
114
|
planningRunId: null,
|
|
115
115
|
planningStatePath: null,
|
|
116
116
|
acceptedPlanPath: null,
|
|
117
|
+
planReviewDebt: [],
|
|
117
118
|
executionRunId: null,
|
|
118
119
|
executionStatePath: null,
|
|
119
120
|
activeStage: null,
|
|
@@ -496,6 +497,10 @@ async function runFreshPlanAndExecuteChild(args, onInitialized) {
|
|
|
496
497
|
// initialized, the run state is the single source of truth for
|
|
497
498
|
// this run — `neal resume` reads it back from there.
|
|
498
499
|
autoSquashOnCompletion: args.squashOnCompletion,
|
|
500
|
+
// Only the execution child inherits the accepted plan's plan-review
|
|
501
|
+
// debt (into the durable inheritedPlanReviewDebt state field). The
|
|
502
|
+
// planning child recomputes its own planReviewDebt from scratch.
|
|
503
|
+
inheritedPlanReviewDebt: args.stage === 'execution' ? args.item.planReviewDebt : undefined,
|
|
499
504
|
});
|
|
500
505
|
markInitialized();
|
|
501
506
|
assertAgentConfigSupportsWriterRun(loaded.state.agentConfig, { context: `${args.stage} queue child` });
|
|
@@ -581,6 +586,11 @@ async function completePlanningStage(state, itemIndex, finalState, deps) {
|
|
|
581
586
|
...item,
|
|
582
587
|
status: 'planned',
|
|
583
588
|
acceptedPlanPath,
|
|
589
|
+
// Carry the accepted plan's current-negotiation debt onto the queue item
|
|
590
|
+
// so the fresh execution child inherits it (both the in-process completion
|
|
591
|
+
// and the cross-process continuePlanAndExecuteQueueFromChildRun resume
|
|
592
|
+
// route through here).
|
|
593
|
+
planReviewDebt: finalState.planReviewDebt,
|
|
584
594
|
activeStage: null,
|
|
585
595
|
stopReason: null,
|
|
586
596
|
}),
|
|
@@ -916,6 +926,10 @@ function parsePlanAndExecuteQueueItem(value, expectedIndex, cwd) {
|
|
|
916
926
|
planningRunId: requireNullableString(item.planningRunId, 'Invalid queue state: malformed planningRunId'),
|
|
917
927
|
planningStatePath,
|
|
918
928
|
acceptedPlanPath,
|
|
929
|
+
// Legacy-tolerant: queue items persisted before this field existed default
|
|
930
|
+
// to an empty debt array. Present arrays are hydrated with the shared
|
|
931
|
+
// residual-debt validator so an older/newer field shape is caught.
|
|
932
|
+
planReviewDebt: parseQueueItemPlanReviewDebt(item.planReviewDebt),
|
|
919
933
|
executionRunId: requireNullableString(item.executionRunId, 'Invalid queue state: malformed executionRunId'),
|
|
920
934
|
executionStatePath,
|
|
921
935
|
activeStage: requireNullableQueueChildStage(item.activeStage, 'Invalid queue state: malformed activeStage'),
|
|
@@ -924,6 +938,20 @@ function parsePlanAndExecuteQueueItem(value, expectedIndex, cwd) {
|
|
|
924
938
|
stopReason: requireNullableString(item.stopReason, 'Invalid queue state: malformed item stopReason'),
|
|
925
939
|
};
|
|
926
940
|
}
|
|
941
|
+
function parseQueueItemPlanReviewDebt(value) {
|
|
942
|
+
// Legacy-tolerant ONLY for a genuinely absent field: queue items persisted
|
|
943
|
+
// before this field existed omit it entirely (undefined), and those default to
|
|
944
|
+
// an empty debt array. A PRESENT null (or any other non-array) is corrupted
|
|
945
|
+
// state that would silently erase accepted plan-review debt, so it is rejected
|
|
946
|
+
// as malformed rather than defaulted away.
|
|
947
|
+
if (value === undefined) {
|
|
948
|
+
return [];
|
|
949
|
+
}
|
|
950
|
+
if (!Array.isArray(value)) {
|
|
951
|
+
throw new Error('Invalid queue state: malformed planReviewDebt');
|
|
952
|
+
}
|
|
953
|
+
return value.map((item, index) => hydrateResidualReviewDebtItem(item, `queue item planReviewDebt[${index}]`));
|
|
954
|
+
}
|
|
927
955
|
function parseCurrentPlanAndExecuteQueuePointer(value, cwd) {
|
|
928
956
|
if (!value || typeof value !== 'object') {
|
|
929
957
|
throw new Error('Invalid .neal/current-queue.json: expected object');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPromptSpec } from './specs.js';
|
|
1
|
+
import { getPromptSpec, } from './specs.js';
|
|
2
2
|
export function assertPromptBuilder(id, exportName, modulePath) {
|
|
3
3
|
const spec = getPromptSpec(id);
|
|
4
4
|
const allowedBuilders = [spec.baseInstructions, ...spec.variants.map((variant) => variant.baseInstructions)];
|
|
@@ -11,3 +11,10 @@ export function assertPromptBuilder(id, exportName, modulePath) {
|
|
|
11
11
|
}
|
|
12
12
|
return spec;
|
|
13
13
|
}
|
|
14
|
+
export function resolvePrimaryVariant(spec, specLabel) {
|
|
15
|
+
const primary = spec.variants.find((variant) => variant.kind === 'primary');
|
|
16
|
+
if (!primary) {
|
|
17
|
+
throw new Error(`Prompt spec ${specLabel} is missing a primary variant`);
|
|
18
|
+
}
|
|
19
|
+
return primary;
|
|
20
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { renderReviewerContextMarkdown } from '../context/reviewer-context.js';
|
|
1
|
+
import { renderInlinedRangeDiffSection } from '../context/inline-review-context.js';
|
|
3
2
|
import { AUTONOMY_BLOCKED, AUTONOMY_DONE, AUTONOMY_SCOPE_DONE, AUTONOMY_SPLIT_PLAN, buildProgressSection, getCanonicalPlanContractLines, getDerivedPlanSectionContractLines, getExecuteScopeProgressPayloadContractLines, getProtocolMarkerArtifactProhibitionLines, getStandalonePlanPayloadSourceOfTruthLines, getTerminalMarkerArtifactBoundaryLines, getUnattendedAutonomyLines, } from './shared.js';
|
|
4
3
|
import { assertPromptBuilder } from './assert-builder.js';
|
|
5
4
|
import { getUserGuidanceLines } from './guidance.js';
|
|
6
|
-
import { getAdversarialReviewDoctrineLines, getCodeReviewFalsificationLines, getFindingQualityLines, getPreexistingFailureContractLines, getRegressionPreservationLines, getVerificationSkepticismLines,
|
|
5
|
+
import { getAdversarialReviewDoctrineLines, getCodeReviewFalsificationLines, getFindingQualityLines, getPreexistingFailureContractLines, getRegressionPreservationLines, getVerificationSkepticismLines, } from './review-doctrine.js';
|
|
7
6
|
const PROMPT_MODULE_PATH = 'src/neal/prompts/execute.ts';
|
|
8
7
|
export const CODER_REGRESSION_PRESERVATION_LINE = 'Treat existing behavior on the code paths you touch as part of the contract: identify the shared subsystems your change intersects (state, parsing, dispatch, lifecycle, storage, startup) and confirm that adjacent behavior on those paths still works — by running existing tests that exercise them when such tests exist, or by concrete reasoning through representative flows otherwise — before you finish.';
|
|
9
8
|
export const CODER_PREEXISTING_FAILURE_LINES = [
|
|
@@ -54,6 +53,7 @@ export function buildScopePrompt(planDoc, progressText, options) {
|
|
|
54
53
|
CODER_REGRESSION_PRESERVATION_LINE,
|
|
55
54
|
...CODER_PREEXISTING_FAILURE_LINES,
|
|
56
55
|
'Verify the relevant work before you finish.',
|
|
56
|
+
'Before claiming a step is done or a verification passed, confirm the claim against an actual tool or command result from this session, and state plainly when a step is not yet verified.',
|
|
57
57
|
'Create real git commit(s) for completed work.',
|
|
58
58
|
'Do not edit or stage wrapper-owned artifacts such as review files under .neal/runs/, PLAN_PROGRESS.md, plan-progress.json, or .neal/*.',
|
|
59
59
|
'Treat action=`blocked` as a last resort, not an early exit.',
|
|
@@ -114,12 +114,7 @@ export function buildReviewerPrompt(args) {
|
|
|
114
114
|
if (!primaryVariant || !meaningfulProgressVariant) {
|
|
115
115
|
throw new Error('Prompt spec scope_reviewer is missing primary or meaningful-progress coverage');
|
|
116
116
|
}
|
|
117
|
-
const accessMode =
|
|
118
|
-
builder: 'buildReviewerPrompt',
|
|
119
|
-
accessMode: args.accessMode,
|
|
120
|
-
inlineContextSupplied: Boolean(args.inlineContext),
|
|
121
|
-
});
|
|
122
|
-
const noRead = accessMode === 'no-read';
|
|
117
|
+
const accessMode = args.accessMode ?? 'tool-access';
|
|
123
118
|
// A collected diff may legitimately be the empty string (a range with no
|
|
124
119
|
// changes); distinguish "collected" (any string, including '') from "not
|
|
125
120
|
// collected" (null/undefined) so an empty diff still rides the inlined channel
|
|
@@ -157,12 +152,7 @@ export function buildReviewerPrompt(args) {
|
|
|
157
152
|
reviewTarget: 'scope diff',
|
|
158
153
|
mode: accessMode,
|
|
159
154
|
});
|
|
160
|
-
const reviewHistoryLine =
|
|
161
|
-
? 'Prior review history is inlined below if you need earlier reviewer findings or coder responses, but review the inlined diff for the current commit range directly.'
|
|
162
|
-
: `Prior review history is available at ${args.reviewMarkdownPath} if you need earlier reviewer findings or coder responses, but review the current commit range directly.`;
|
|
163
|
-
if (noRead) {
|
|
164
|
-
assertNoReadPromptInstructionText([...falsificationLines, ...skepticismLines, ...regressionLines, ...preexistingLines, reviewHistoryLine].join('\n'), 'buildReviewerPrompt');
|
|
165
|
-
}
|
|
155
|
+
const reviewHistoryLine = `Prior review history is available at ${args.reviewMarkdownPath} if you need earlier reviewer findings or coder responses, but review the current commit range directly.`;
|
|
166
156
|
return [
|
|
167
157
|
`Review the current scope for plan ${args.planDoc}.`,
|
|
168
158
|
`Review round: ${args.round}.`,
|
|
@@ -191,7 +181,7 @@ export function buildReviewerPrompt(args) {
|
|
|
191
181
|
'Changed files:',
|
|
192
182
|
changedFilesText,
|
|
193
183
|
'',
|
|
194
|
-
...getReviewerContextLines(args.reviewerContext
|
|
184
|
+
...getReviewerContextLines(args.reviewerContext),
|
|
195
185
|
`The active parent objective for meaningful-progress evaluation is scope ${args.parentScopeLabel}.`,
|
|
196
186
|
'You are the authority for meaningful-progress gating during this execute review pass, but perform that judgment after the adversarial correctness review above.',
|
|
197
187
|
'Set `meaningfulProgressAction` to `accept` only when either this scope materially advances the active parent objective, or a top-level scope has no required new diff because prior accepted work already satisfies it and you find no issues with sufficient verification evidence.',
|
|
@@ -215,7 +205,6 @@ export function buildReviewerPrompt(args) {
|
|
|
215
205
|
'If prior review history or continuity context describes a finding as fixed, rejected, or deferred, do not reopen the same claim from that history alone.',
|
|
216
206
|
'Before emitting a finding that resembles a prior fixed/rejected/deferred finding, inspect the current code or current diff and cite fresh current evidence showing the defect still exists.',
|
|
217
207
|
'If current evidence is ambiguous for an already-addressed claim, do not use a blocking finding to force another coder loop; use meaningfulProgressAction=`block_for_operator` only when operator judgment is genuinely required.',
|
|
218
|
-
...(args.inlineContext ? ['', renderInlineReviewerContext(args.inlineContext)] : []),
|
|
219
208
|
...(rangeDiffInlined
|
|
220
209
|
? [
|
|
221
210
|
'',
|
|
@@ -227,13 +216,13 @@ export function buildReviewerPrompt(args) {
|
|
|
227
216
|
: []),
|
|
228
217
|
].join('\n');
|
|
229
218
|
}
|
|
230
|
-
function getReviewerContextLines(reviewerContext
|
|
219
|
+
function getReviewerContextLines(reviewerContext) {
|
|
231
220
|
if (!reviewerContext) {
|
|
232
221
|
return [];
|
|
233
222
|
}
|
|
234
223
|
return [
|
|
235
224
|
'Bounded current-run continuity context:',
|
|
236
|
-
|
|
225
|
+
reviewerContext.promptMarkdown,
|
|
237
226
|
'',
|
|
238
227
|
];
|
|
239
228
|
}
|
|
@@ -257,8 +246,11 @@ export function buildCoderResponsePrompt(args) {
|
|
|
257
246
|
mode === 'blocking'
|
|
258
247
|
? 'Make code changes if needed, run the most relevant verification for the fixes you make, and create a real git commit if you changed code.'
|
|
259
248
|
: 'Fix local, concrete, low-expansion non-blocking findings by default. Make the smallest justified code changes, run the most relevant verification for those changes, and create a real git commit if you changed code.',
|
|
249
|
+
// model-calibrated: caps post-commit tool use once the fix is committed and verified; tuned for a model that otherwise keeps issuing redundant shell commands after it already has enough evidence.
|
|
260
250
|
'After you have made the necessary commit and run the relevant verification, stop using tools. Your next response must return the structured outcome with no additional shell commands such as git show, git status, or extra exploratory tests.',
|
|
251
|
+
// model-calibrated: stops repeated re-verification of an already-evidenced fix; tuned for a model that over-proves rather than summarizing the evidence it already gathered.
|
|
261
252
|
'Do not keep proving the same fix after you have enough evidence to answer each finding. Summarize the evidence in the structured response instead.',
|
|
253
|
+
'Before claiming a step is done or a verification passed, confirm the claim against an actual tool or command result from this session, and state plainly when a finding is not yet verified.',
|
|
262
254
|
CODER_REGRESSION_PRESERVATION_LINE,
|
|
263
255
|
...CODER_PREEXISTING_FAILURE_LINES,
|
|
264
256
|
'Use `fixed` only when you actually changed the code or verification in a way that resolves the finding.',
|