@navels/neal 0.1.0 → 0.2.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/dist/neal/adjudicator/planning.js +48 -0
- package/dist/neal/agents/prompts.js +3 -0
- package/dist/neal/agents/rounds.js +8 -0
- package/dist/neal/agents/schemas.js +575 -496
- package/dist/neal/agents/structured-json.js +36 -0
- package/dist/neal/config.js +24 -0
- package/dist/neal/git.js +9 -3
- package/dist/neal/orchestrator/completion.js +167 -112
- package/dist/neal/orchestrator/phases/planning.js +14 -39
- package/dist/neal/orchestrator/split-plan.js +12 -11
- package/dist/neal/orchestrator/transitions.js +30 -71
- package/dist/neal/plan-doc.js +24 -1
- package/dist/neal/prompts/assert-builder.js +8 -1
- package/dist/neal/prompts/execute.js +4 -0
- package/dist/neal/prompts/specialized.js +22 -6
- package/dist/neal/prompts/specs.js +58 -0
- package/dist/neal/providers/anthropic-claude.js +291 -247
- package/dist/neal/providers/generic-agentic.js +18 -0
- package/dist/neal/providers/openai-codex.js +77 -201
- package/dist/neal/providers/openai-compatible.js +33 -5
- package/dist/neal/providers/pricing.js +124 -0
- package/dist/neal/providers/rate-card.js +2301 -0
- package/dist/neal/providers/telemetry.js +4 -0
- package/dist/neal/retrospective.js +33 -4
- package/dist/neal/run-metrics.js +74 -9
- package/docs/compatible-models.md +11 -0
- package/docs/issue-pipeline.md +124 -0
- package/docs/maintenance.md +30 -19
- package/docs/providers.md +117 -0
- package/docs/release.md +29 -25
- package/package.json +7 -3
|
@@ -2,6 +2,31 @@ 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
|
+
reviewStuckArbiterCount: 0,
|
|
12
|
+
findings: [],
|
|
13
|
+
createdCommits: [],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function createNextScopeEntryReset(baseCommit) {
|
|
17
|
+
return {
|
|
18
|
+
...createScopeBoundaryReset(),
|
|
19
|
+
baseCommit,
|
|
20
|
+
finalCommit: null,
|
|
21
|
+
archivedReviewPath: null,
|
|
22
|
+
coderSessionHandle: null,
|
|
23
|
+
coderSessionProtocol: null,
|
|
24
|
+
lastScopeMarker: null,
|
|
25
|
+
finalCompletionSummary: null,
|
|
26
|
+
phase: 'coder_scope',
|
|
27
|
+
status: 'running',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
5
30
|
export function appendCompletedScope(state, result, details) {
|
|
6
31
|
const scopeLabel = details.scopeLabel ?? getCurrentScopeLabel(state);
|
|
7
32
|
const marker = details.marker ?? (state.lastScopeMarker ?? 'AUTONOMY_BLOCKED');
|
|
@@ -57,23 +82,17 @@ export function adoptAcceptedDerivedPlan(state) {
|
|
|
57
82
|
}
|
|
58
83
|
return {
|
|
59
84
|
...state,
|
|
85
|
+
...createScopeBoundaryReset(),
|
|
60
86
|
phase: 'coder_scope',
|
|
61
87
|
status: 'running',
|
|
62
88
|
derivedScopeIndex: derivedPlan.scopeIndex ?? 1,
|
|
63
89
|
coderSessionHandle: null,
|
|
64
90
|
coderSessionProtocol: null,
|
|
65
91
|
coderRetryCount: 0,
|
|
66
|
-
currentScopeProgressJustification: null,
|
|
67
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
68
92
|
finalCompletionSummary: null,
|
|
69
93
|
finalCompletionReviewVerdict: null,
|
|
70
94
|
finalCompletionResolvedAction: null,
|
|
71
95
|
finalCompletionContinueExecutionCapReached: false,
|
|
72
|
-
rounds: [],
|
|
73
|
-
recentBlocks: [],
|
|
74
|
-
reviewStuckArbiterCount: 0,
|
|
75
|
-
findings: [],
|
|
76
|
-
createdCommits: [],
|
|
77
96
|
blockedFromPhase: null,
|
|
78
97
|
};
|
|
79
98
|
}
|
|
@@ -89,15 +108,8 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
89
108
|
if (derivedExecution && derivedPlanCompleted) {
|
|
90
109
|
return {
|
|
91
110
|
...state,
|
|
92
|
-
|
|
93
|
-
finalCommit: null,
|
|
94
|
-
coderSessionHandle: null,
|
|
95
|
-
coderSessionProtocol: null,
|
|
111
|
+
...createNextScopeEntryReset(finalCommit),
|
|
96
112
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
97
|
-
lastScopeMarker: null,
|
|
98
|
-
currentScopeProgressJustification: null,
|
|
99
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
100
|
-
finalCompletionSummary: null,
|
|
101
113
|
finalCompletionReviewVerdict: null,
|
|
102
114
|
finalCompletionResolvedAction: null,
|
|
103
115
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -109,28 +121,13 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
109
121
|
derivedPlanAcceptedNotified: false,
|
|
110
122
|
splitPlanBlockedNotified: false,
|
|
111
123
|
splitPlanCountForCurrentScope: 0,
|
|
112
|
-
rounds: [],
|
|
113
|
-
recentBlocks: [],
|
|
114
|
-
reviewStuckArbiterCount: 0,
|
|
115
|
-
findings: [],
|
|
116
|
-
createdCommits: [],
|
|
117
124
|
completedScopes,
|
|
118
|
-
archivedReviewPath: null,
|
|
119
|
-
phase: 'coder_scope',
|
|
120
|
-
status: 'running',
|
|
121
125
|
};
|
|
122
126
|
}
|
|
123
127
|
if (derivedExecution) {
|
|
124
128
|
return {
|
|
125
129
|
...state,
|
|
126
|
-
|
|
127
|
-
finalCommit: null,
|
|
128
|
-
coderSessionHandle: null,
|
|
129
|
-
coderSessionProtocol: null,
|
|
130
|
-
lastScopeMarker: null,
|
|
131
|
-
currentScopeProgressJustification: null,
|
|
132
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
133
|
-
finalCompletionSummary: null,
|
|
130
|
+
...createNextScopeEntryReset(finalCommit),
|
|
134
131
|
finalCompletionReviewVerdict: null,
|
|
135
132
|
finalCompletionResolvedAction: null,
|
|
136
133
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -138,29 +135,14 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
138
135
|
splitPlanStartedNotified: false,
|
|
139
136
|
derivedPlanAcceptedNotified: false,
|
|
140
137
|
splitPlanBlockedNotified: false,
|
|
141
|
-
rounds: [],
|
|
142
|
-
recentBlocks: [],
|
|
143
|
-
reviewStuckArbiterCount: 0,
|
|
144
|
-
findings: [],
|
|
145
|
-
createdCommits: [],
|
|
146
138
|
completedScopes,
|
|
147
|
-
archivedReviewPath: null,
|
|
148
|
-
phase: 'coder_scope',
|
|
149
|
-
status: 'running',
|
|
150
139
|
};
|
|
151
140
|
}
|
|
152
141
|
if (continueScopes) {
|
|
153
142
|
return {
|
|
154
143
|
...state,
|
|
155
|
-
|
|
156
|
-
finalCommit: null,
|
|
157
|
-
coderSessionHandle: null,
|
|
158
|
-
coderSessionProtocol: null,
|
|
144
|
+
...createNextScopeEntryReset(finalCommit),
|
|
159
145
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
160
|
-
lastScopeMarker: null,
|
|
161
|
-
currentScopeProgressJustification: null,
|
|
162
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
163
|
-
finalCompletionSummary: null,
|
|
164
146
|
finalCompletionReviewVerdict: null,
|
|
165
147
|
finalCompletionResolvedAction: null,
|
|
166
148
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -172,15 +154,7 @@ export function computeNextScopeStateAfterExecuteFinalization({ state, finalComm
|
|
|
172
154
|
derivedPlanAcceptedNotified: false,
|
|
173
155
|
splitPlanBlockedNotified: false,
|
|
174
156
|
splitPlanCountForCurrentScope: 0,
|
|
175
|
-
rounds: [],
|
|
176
|
-
recentBlocks: [],
|
|
177
|
-
reviewStuckArbiterCount: 0,
|
|
178
|
-
findings: [],
|
|
179
|
-
createdCommits: [],
|
|
180
157
|
completedScopes,
|
|
181
|
-
archivedReviewPath: null,
|
|
182
|
-
phase: 'coder_scope',
|
|
183
|
-
status: 'running',
|
|
184
158
|
};
|
|
185
159
|
}
|
|
186
160
|
return {
|
|
@@ -223,19 +197,11 @@ export function computeNextScopeStateAfterParentAdvance(args) {
|
|
|
223
197
|
: args.state.currentScopeNumber;
|
|
224
198
|
return {
|
|
225
199
|
...args.state,
|
|
226
|
-
|
|
227
|
-
finalCommit: null,
|
|
228
|
-
archivedReviewPath: null,
|
|
229
|
-
coderSessionHandle: null,
|
|
230
|
-
coderSessionProtocol: null,
|
|
200
|
+
...createNextScopeEntryReset(args.finalCommit),
|
|
231
201
|
reviewerSessionHandle: null,
|
|
232
202
|
coderRetryCount: 0,
|
|
233
203
|
currentScopeNumber: nextTopLevelScopeNumber,
|
|
234
|
-
lastScopeMarker: null,
|
|
235
|
-
currentScopeProgressJustification: null,
|
|
236
|
-
currentScopeMeaningfulProgressVerdict: null,
|
|
237
204
|
manualGate: null,
|
|
238
|
-
finalCompletionSummary: null,
|
|
239
205
|
finalCompletionReviewVerdict: null,
|
|
240
206
|
finalCompletionResolvedAction: null,
|
|
241
207
|
finalCompletionContinueExecutionCapReached: false,
|
|
@@ -247,16 +213,9 @@ export function computeNextScopeStateAfterParentAdvance(args) {
|
|
|
247
213
|
derivedPlanAcceptedNotified: false,
|
|
248
214
|
splitPlanBlockedNotified: false,
|
|
249
215
|
splitPlanCountForCurrentScope: 0,
|
|
250
|
-
rounds: [],
|
|
251
|
-
recentBlocks: [],
|
|
252
|
-
reviewStuckArbiterCount: 0,
|
|
253
|
-
findings: [],
|
|
254
|
-
createdCommits: [],
|
|
255
216
|
completedScopes: args.completedScopes,
|
|
256
217
|
blockedFromPhase: null,
|
|
257
218
|
interactiveBlockedRecovery: null,
|
|
258
|
-
phase: 'coder_scope',
|
|
259
|
-
status: 'running',
|
|
260
219
|
};
|
|
261
220
|
}
|
|
262
221
|
function getAcceptedScopeSummary(state) {
|
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);
|
|
@@ -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
|
+
}
|
|
@@ -54,6 +54,7 @@ export function buildScopePrompt(planDoc, progressText, options) {
|
|
|
54
54
|
CODER_REGRESSION_PRESERVATION_LINE,
|
|
55
55
|
...CODER_PREEXISTING_FAILURE_LINES,
|
|
56
56
|
'Verify the relevant work before you finish.',
|
|
57
|
+
'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
58
|
'Create real git commit(s) for completed work.',
|
|
58
59
|
'Do not edit or stage wrapper-owned artifacts such as review files under .neal/runs/, PLAN_PROGRESS.md, plan-progress.json, or .neal/*.',
|
|
59
60
|
'Treat action=`blocked` as a last resort, not an early exit.',
|
|
@@ -257,8 +258,11 @@ export function buildCoderResponsePrompt(args) {
|
|
|
257
258
|
mode === 'blocking'
|
|
258
259
|
? '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
260
|
: '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.',
|
|
261
|
+
// 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
262
|
'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.',
|
|
263
|
+
// 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
264
|
'Do not keep proving the same fix after you have enough evidence to answer each finding. Summarize the evidence in the structured response instead.',
|
|
265
|
+
'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
266
|
CODER_REGRESSION_PRESERVATION_LINE,
|
|
263
267
|
...CODER_PREEXISTING_FAILURE_LINES,
|
|
264
268
|
'Use `fixed` only when you actually changed the code or verification in a way that resolves the finding.',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { guardStructuredJsonOutputFormatLines } from '../agents/structured-json.js';
|
|
1
2
|
import { assertNoReadPromptInstructionText, renderInlineReviewerContext, renderInlinedRangeDiffSection, } from '../context/inline-review-context.js';
|
|
2
3
|
import { renderReviewerContextMarkdown } from '../context/reviewer-context.js';
|
|
3
4
|
import { assertPromptBuilder } from './assert-builder.js';
|
|
@@ -5,6 +6,24 @@ import { getUserGuidanceLines } from './guidance.js';
|
|
|
5
6
|
import { getUnattendedAutonomyLines } from './shared.js';
|
|
6
7
|
import { getAdversarialReviewDoctrineLines, getCodeReviewFalsificationLines, getFindingQualityLines, getPreexistingFailureContractLines, getRegressionPreservationLines, getVerificationSkepticismLines, resolveReviewerPromptAccessMode, } from './review-doctrine.js';
|
|
7
8
|
const PROMPT_MODULE_PATH = 'src/neal/prompts/specialized.ts';
|
|
9
|
+
// Guarded output-format instruction block shared by the two structured-JSON
|
|
10
|
+
// completion base prompts. It emits only JSON-only framing and a
|
|
11
|
+
// transport-deferring line (replacing the old fence-prohibition that
|
|
12
|
+
// contradicted the neal-json transport), and routes the assembled lines through
|
|
13
|
+
// guardStructuredJsonOutputFormatLines so a reintroduced conflicting phrasing
|
|
14
|
+
// throws at render. Both completion builders emit their output-format
|
|
15
|
+
// instructions exclusively through this helper. The transport-deferring line is
|
|
16
|
+
// provider-neutral: the neal-json protocol block is appended below the base
|
|
17
|
+
// prompt only on the wrapper path (anthropic-claude and the repair loop), while
|
|
18
|
+
// other transports (e.g. generic-agentic) send the base prompt with no protocol
|
|
19
|
+
// below it, so the line must not claim instructions appear "below".
|
|
20
|
+
export function completionJsonOutputFormatLines(label) {
|
|
21
|
+
return guardStructuredJsonOutputFormatLines([
|
|
22
|
+
'Return only JSON that matches the required schema.',
|
|
23
|
+
'Return the final answer only as the required structured output object, not as prose or markdown.',
|
|
24
|
+
'Defer exact output framing to the active structured-output transport; do not add your own output-format constraints.',
|
|
25
|
+
], label);
|
|
26
|
+
}
|
|
8
27
|
export function buildFinalCompletionSummaryPrompt(args) {
|
|
9
28
|
const spec = assertPromptBuilder('completion_coder', 'buildFinalCompletionSummaryPrompt', PROMPT_MODULE_PATH);
|
|
10
29
|
const finalCompletionVariant = spec.variants.find((variant) => variant.kind === 'final_completion');
|
|
@@ -18,12 +37,12 @@ export function buildFinalCompletionSummaryPrompt(args) {
|
|
|
18
37
|
`Summarize whether the execute-mode plan at ${args.planDoc} is complete as a whole.`,
|
|
19
38
|
'',
|
|
20
39
|
'Before writing the summary, review the current repository state, the plan document, and the completion packet below.',
|
|
21
|
-
'
|
|
22
|
-
'Return the final answer only as the required structured output object, not as prose or markdown.',
|
|
40
|
+
...completionJsonOutputFormatLines('buildFinalCompletionSummaryPrompt'),
|
|
23
41
|
'Keep the response compact and auditable rather than essay-style.',
|
|
24
42
|
'Use `planGoalSatisfied` to state whether the plan goal is satisfied overall.',
|
|
25
43
|
'Use `whatChangedOverall` to summarize the completed work across the whole plan, not just the last scope.',
|
|
26
44
|
'Use `verificationSummary` to summarize the completion evidence that actually ran.',
|
|
45
|
+
'Before claiming a step is done or a verification passed, confirm the claim against an actual tool or command result from this session, and do not claim verification that did not actually run.',
|
|
27
46
|
'Use `remainingKnownGaps` for any known missing work, regressions, quality concerns, testing gaps, risks, or omissions that would make the plan not fully complete.',
|
|
28
47
|
'Do not contradict yourself:',
|
|
29
48
|
'- if `planGoalSatisfied` is `true`, `remainingKnownGaps` must be empty',
|
|
@@ -49,7 +68,6 @@ export function buildFinalCompletionSummaryPrompt(args) {
|
|
|
49
68
|
}, null, 2),
|
|
50
69
|
'',
|
|
51
70
|
'If the completion is verification-only, say so directly in `whatChangedOverall` or `remainingKnownGaps` instead of pretending there was a terminal implementation diff.',
|
|
52
|
-
'Do not include markdown fences or prose outside the JSON object.',
|
|
53
71
|
...getUserGuidanceLines('coder'),
|
|
54
72
|
'',
|
|
55
73
|
'Last non-empty implementation scope reference:',
|
|
@@ -141,8 +159,7 @@ export function buildFinalCompletionReviewerPrompt(args) {
|
|
|
141
159
|
'Review the whole-plan result for correctness and completeness against the plan objectives, regressions or missing behavior, cross-scope integration issues that may not have been visible in individual scope reviews, code quality, maintainability, and consistency of the final implementation, and adequacy of test coverage and verification for the total change.',
|
|
142
160
|
...getReviewerContextLines(args.reviewerContext, noRead ? 'inline' : 'tool-access'),
|
|
143
161
|
'Do not treat prior per-scope acceptance as sufficient evidence that the whole plan is complete or that the aggregate code quality is acceptable.',
|
|
144
|
-
'
|
|
145
|
-
'Return the final answer only as the required structured output object, not as prose or markdown.',
|
|
162
|
+
...completionJsonOutputFormatLines('buildFinalCompletionReviewerPrompt'),
|
|
146
163
|
'Use `accept_complete` only when the full plan objectives are satisfied and the aggregate implementation is acceptable under ordinary code review standards.',
|
|
147
164
|
'Use `continue_execution` only when the remaining work is concrete, bounded, and suitable for one explicit follow-on scope.',
|
|
148
165
|
'Use `block_for_operator` when the remaining gap is ambiguous, externally constrained, or needs human direction.',
|
|
@@ -183,7 +200,6 @@ export function buildFinalCompletionReviewerPrompt(args) {
|
|
|
183
200
|
}, null, 2),
|
|
184
201
|
'',
|
|
185
202
|
'If this was a verification-only terminal scope, judge the whole-plan result directly instead of pretending there was a final implementation diff.',
|
|
186
|
-
'Do not include markdown fences or prose outside the JSON object.',
|
|
187
203
|
...getUserGuidanceLines('reviewer'),
|
|
188
204
|
'',
|
|
189
205
|
'Last non-empty implementation scope reference:',
|
|
@@ -82,6 +82,10 @@ const COMPLETION_REVIEWER_CONTEXT = context('CompletionReviewerPromptContext', [
|
|
|
82
82
|
field('inlineContext', 'repository_state', false, "Neal-inlined aggregate diff (or evidence-gap statement) and plan-document sections for reviewers without repository read access; only valid with the 'no-read' access mode."),
|
|
83
83
|
field('accessMode', 'orchestrator_state', false, "Three-way reviewer doctrine access mode derived from the reviewer provider's structured-advisor tool access: 'tool-access' (inspect and execute), 'read-only' (read tools only; no command execution, test runs, or scratch work), or 'no-read' (judge entirely from Neal-inlined context). When absent the builder derives 'no-read' from inline-context presence, else 'tool-access'."),
|
|
84
84
|
]);
|
|
85
|
+
const BLOCKED_ADJUDICATOR_CONTEXT = context('BlockedAdjudicatorPromptContext', [
|
|
86
|
+
field('blockedReason', 'prompt_argument', true, 'Blocked reason reported by the stalled coder or reviewer turn.'),
|
|
87
|
+
field('inlineContext', 'repository_state', true, 'Neal-inlined adjudication context (plan content, open blocking findings, reviewer-round snapshots, or coder blocker plus changed files) the adjudicator judges entirely from.'),
|
|
88
|
+
]);
|
|
85
89
|
export const PROMPT_SPECS = [
|
|
86
90
|
{
|
|
87
91
|
id: 'plan_author',
|
|
@@ -542,6 +546,60 @@ export const PROMPT_SPECS = [
|
|
|
542
546
|
},
|
|
543
547
|
],
|
|
544
548
|
},
|
|
549
|
+
{
|
|
550
|
+
id: 'blocked_adjudicator',
|
|
551
|
+
role: 'reviewer',
|
|
552
|
+
purpose: 'Triage a blocked Neal run entirely from Neal-inlined context and decide whether the block is an in-scope recoverable misunderstanding or a genuine wall that must escalate to a human.',
|
|
553
|
+
requiredContext: BLOCKED_ADJUDICATOR_CONTEXT,
|
|
554
|
+
schemaTarget: {
|
|
555
|
+
kind: 'structured_json',
|
|
556
|
+
schemaBuilder: 'buildBlockedAdjudicatorSchema',
|
|
557
|
+
parser: 'validateBlockedAdjudicatorVerdictPayload',
|
|
558
|
+
providerSurface: 'neal_json_block_protocol',
|
|
559
|
+
},
|
|
560
|
+
baseInstructions: {
|
|
561
|
+
kind: 'builder',
|
|
562
|
+
modulePath: 'src/neal/agents/prompts.ts',
|
|
563
|
+
exportName: 'buildBlockedAdjudicatorPrompt',
|
|
564
|
+
inputShape: context('BuildBlockedAdjudicatorPromptArgs', [
|
|
565
|
+
field('blockedReason', 'prompt_argument', true, 'Blocked reason string.'),
|
|
566
|
+
field('inlineContext', 'repository_state', true, 'Neal-inlined adjudication context.'),
|
|
567
|
+
]),
|
|
568
|
+
},
|
|
569
|
+
providerVariants: SHARED_PROVIDER_VARIANTS,
|
|
570
|
+
evaluationNotes: [
|
|
571
|
+
'Render tests should assert the adjudicator judges entirely from inlined context and its static instructions carry no repository-access phrasing.',
|
|
572
|
+
'A golden render test pins the exact prompt bytes, including the ALL-CAPS emphasis lines.',
|
|
573
|
+
],
|
|
574
|
+
firstMigrationPriority: 3,
|
|
575
|
+
currentHome: 'mixed',
|
|
576
|
+
ownershipNotes: [
|
|
577
|
+
'Prompt spec owns the adjudicator instructions and required context only; anti-thrash guarding, recovery routing, and verdict persistence stay in src/neal/adjudicator/ outside the prompt-spec library.',
|
|
578
|
+
],
|
|
579
|
+
variants: [
|
|
580
|
+
{
|
|
581
|
+
kind: 'primary',
|
|
582
|
+
status: 'adjacent',
|
|
583
|
+
description: 'Read-only blocked-run adjudication round.',
|
|
584
|
+
currentRoundEntrypoints: ['runBlockedAdjudicatorRound'],
|
|
585
|
+
baseInstructions: {
|
|
586
|
+
kind: 'builder',
|
|
587
|
+
modulePath: 'src/neal/agents/prompts.ts',
|
|
588
|
+
exportName: 'buildBlockedAdjudicatorPrompt',
|
|
589
|
+
inputShape: context('BuildBlockedAdjudicatorPromptArgs', [
|
|
590
|
+
field('blockedReason', 'prompt_argument', true, 'Blocked reason string.'),
|
|
591
|
+
field('inlineContext', 'repository_state', true, 'Neal-inlined adjudication context.'),
|
|
592
|
+
]),
|
|
593
|
+
},
|
|
594
|
+
schemaTarget: {
|
|
595
|
+
kind: 'structured_json',
|
|
596
|
+
schemaBuilder: 'buildBlockedAdjudicatorSchema',
|
|
597
|
+
parser: 'validateBlockedAdjudicatorVerdictPayload',
|
|
598
|
+
providerSurface: 'neal_json_block_protocol',
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
},
|
|
545
603
|
];
|
|
546
604
|
function getContractFieldKeys(contract) {
|
|
547
605
|
return new Set(contract.fields.map((field) => field.key));
|