@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.
Files changed (120) hide show
  1. package/README.md +206 -209
  2. package/SECURITY.md +16 -19
  3. package/dist/neal/adjudicator/{blocked-adjudicator.js → consultant.js} +21 -21
  4. package/dist/neal/adjudicator/execute.js +33 -33
  5. package/dist/neal/adjudicator/final-completion.js +2 -31
  6. package/dist/neal/adjudicator/planning.js +264 -24
  7. package/dist/neal/agents/prompts.js +7 -15
  8. package/dist/neal/agents/rounds.js +51 -73
  9. package/dist/neal/agents/schemas.js +615 -502
  10. package/dist/neal/agents/structured-json.js +36 -0
  11. package/dist/neal/blocked-guidance.js +16 -9
  12. package/dist/neal/cli.js +2 -2
  13. package/dist/neal/commands/check.js +2 -2
  14. package/dist/neal/commands/compat.js +100 -36
  15. package/dist/neal/commands/interactive-activity.js +2 -2
  16. package/dist/neal/commands/recovery-guidance.js +20 -8
  17. package/dist/neal/commands/runtime.js +4 -2
  18. package/dist/neal/config.js +40 -7
  19. package/dist/neal/context/inline-review-context.js +13 -28
  20. package/dist/neal/context/reviewer-context.js +19 -14
  21. package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
  22. package/dist/neal/eval/reviewer-eval.js +155 -0
  23. package/dist/neal/git.js +9 -3
  24. package/dist/neal/interactive-controls.js +5 -0
  25. package/dist/neal/orchestrator/completion.js +215 -113
  26. package/dist/neal/orchestrator/failures.js +7 -0
  27. package/dist/neal/orchestrator/notifications.js +9 -9
  28. package/dist/neal/orchestrator/phases/planning.js +124 -112
  29. package/dist/neal/orchestrator/phases/recovery.js +151 -115
  30. package/dist/neal/orchestrator/phases/review.js +28 -1
  31. package/dist/neal/orchestrator/split-plan.js +12 -11
  32. package/dist/neal/orchestrator/transitions.js +36 -71
  33. package/dist/neal/orchestrator.js +3 -0
  34. package/dist/neal/plan-doc.js +24 -1
  35. package/dist/neal/plan-queue.js +29 -1
  36. package/dist/neal/prompts/assert-builder.js +8 -1
  37. package/dist/neal/prompts/execute.js +11 -19
  38. package/dist/neal/prompts/planning.js +64 -55
  39. package/dist/neal/prompts/review-doctrine.js +13 -50
  40. package/dist/neal/prompts/specialized.js +28 -22
  41. package/dist/neal/prompts/specs.js +223 -17
  42. package/dist/neal/providers/agent-settings-isolation.js +35 -0
  43. package/dist/neal/providers/anthropic-claude.js +309 -248
  44. package/dist/neal/providers/detection.js +3 -7
  45. package/dist/neal/providers/git-config-isolation.js +83 -0
  46. package/dist/neal/providers/openai-codex.js +102 -204
  47. package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
  48. package/dist/neal/providers/openai-compatible.js +1079 -403
  49. package/dist/neal/providers/openrouter-routing.js +68 -0
  50. package/dist/neal/providers/pricing.js +124 -0
  51. package/dist/neal/providers/rate-card.js +2275 -0
  52. package/dist/neal/providers/registry.js +6 -17
  53. package/dist/neal/providers/telemetry.js +4 -0
  54. package/dist/neal/providers/types.js +12 -0
  55. package/dist/neal/resume-decision.js +7 -6
  56. package/dist/neal/resume-planner.js +9 -0
  57. package/dist/neal/retrospective.js +36 -7
  58. package/dist/neal/review-debt.js +30 -0
  59. package/dist/neal/review-findings/provider.js +17 -29
  60. package/dist/neal/review.js +17 -1
  61. package/dist/neal/run-metrics.js +74 -9
  62. package/dist/neal/state-invariants.js +11 -3
  63. package/dist/neal/state-views.js +27 -5
  64. package/dist/neal/state.js +59 -15
  65. package/dist/neal/status.js +28 -1
  66. package/dist/neal/support.js +3 -3
  67. package/docs/README.md +22 -18
  68. package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
  69. package/docs/architecture.md +79 -83
  70. package/docs/automation.md +11 -11
  71. package/docs/compat.md +91 -61
  72. package/docs/compatible-models.md +145 -110
  73. package/docs/demo.md +15 -5
  74. package/docs/maintenance.md +74 -32
  75. package/docs/plan-format.md +61 -33
  76. package/docs/prompt-evals.md +96 -0
  77. package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
  78. package/docs/providers.md +330 -283
  79. package/docs/release.md +43 -40
  80. package/docs/review-convergence.md +246 -0
  81. package/docs/state-machine.md +61 -59
  82. package/docs/storage.md +40 -31
  83. package/docs/troubleshooting.md +32 -29
  84. package/examples/compat/add-edit-verify/PLAN.md +2 -0
  85. package/examples/compat/add-edit-verify/broken.diff +2 -2
  86. package/examples/compat/add-edit-verify/good.diff +2 -2
  87. package/examples/compat/add-edit-verify/src/add.js +1 -1
  88. package/examples/compat/manifest.json +25 -29
  89. package/examples/compat/sum-grep-edit/PLAN.md +33 -0
  90. package/examples/compat/sum-grep-edit/broken.diff +12 -0
  91. package/examples/compat/sum-grep-edit/good.diff +12 -0
  92. package/examples/compat/sum-grep-edit/package.json +5 -0
  93. package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
  94. package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
  95. package/examples/issue-triage-js/README.md +0 -15
  96. package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
  97. package/examples/reviewer-eval/clean-doc/change.diff +9 -0
  98. package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
  99. package/examples/reviewer-eval/clean-extract/change.diff +13 -0
  100. package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
  101. package/examples/reviewer-eval/dropped-error/change.diff +15 -0
  102. package/examples/reviewer-eval/manifest.json +45 -0
  103. package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
  104. package/examples/reviewer-eval/off-by-one/change.diff +11 -0
  105. package/{config.yml → neal.yml} +1 -1
  106. package/package.json +20 -15
  107. package/dist/neal/providers/generic-agentic.js +0 -1238
  108. package/docs/comparison.md +0 -105
  109. package/examples/compat/is-even-add-test/PLAN.md +0 -30
  110. package/examples/compat/is-even-add-test/broken.diff +0 -11
  111. package/examples/compat/is-even-add-test/good.diff +0 -11
  112. package/examples/compat/is-even-add-test/package.json +0 -5
  113. package/examples/compat/is-even-add-test/src/is-even.js +0 -3
  114. package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
  115. package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
  116. package/examples/compat/reverse-grep-edit/broken.diff +0 -12
  117. package/examples/compat/reverse-grep-edit/good.diff +0 -12
  118. package/examples/compat/reverse-grep-edit/package.json +0 -5
  119. package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
  120. package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
@@ -1,10 +1,45 @@
1
- import { assertNoReadPromptInstructionText, renderInlineReviewerContext, } from '../context/inline-review-context.js';
2
- import { renderReviewerContextMarkdown } from '../context/reviewer-context.js';
3
1
  import { AUTONOMY_BLOCKED, AUTONOMY_DONE, getCanonicalPlanContractLines, getDerivedPlanSectionContractLines, getProtocolMarkerArtifactProhibitionLines, getTerminalMarkerArtifactBoundaryLines, getUnattendedAutonomyLines, } from './shared.js';
4
2
  import { assertPromptBuilder } from './assert-builder.js';
5
3
  import { getUserGuidanceLines } from './guidance.js';
6
- import { getAdversarialReviewDoctrineLines, getPlanReviewFalsificationLines, getVerificationSkepticismLines, resolveReviewerPromptAccessMode, } from './review-doctrine.js';
7
4
  const PROMPT_MODULE_PATH = 'src/neal/prompts/planning.ts';
5
+ const PLAN_VERIFICATION_NECESSITY_RULE = 'A repository-wide invariant or global regression guarantee belongs in the plan only when it is necessary for the requested change to be correct.';
6
+ function getPlanVerificationScopeLines(role) {
7
+ return [
8
+ PLAN_VERIFICATION_NECESSITY_RULE,
9
+ role === 'planner'
10
+ ? 'Otherwise, do not introduce it. If the plan already contains an unnecessary broader guarantee, narrow or remove it instead of expanding the implementation or verification scope to satisfy it.'
11
+ : 'Otherwise, do not require additional implementation or verification to satisfy it. If an unnecessary broader guarantee makes the plan misleading, require the planner to narrow or remove the guarantee rather than expand its coverage.',
12
+ ];
13
+ }
14
+ function getPlanDetailLevelLines() {
15
+ return [
16
+ 'Write for both neal and the operator. Prefer the smallest human-reviewable plan that communicates the approach, scope boundaries, and important decisions.',
17
+ 'Use moderate-to-high-level implementation detail. Name major subsystems, important files, or key interfaces when they clarify the approach, but leave routine implementation discovery to the coder and reviewer working against the implementation.',
18
+ 'Inspect the repository enough to confirm that the approach fits the existing architecture and to identify major dependencies, constraints, and affected subsystems. Do not try to complete the implementation or its full blast-radius analysis in the plan.',
19
+ 'Leave routine file, symbol, caller, and test discovery to execution. Avoid line-by-line change lists, exhaustive inventories, exact assertions, pinned values, and fixture recipes unless they preserve a fixed decision or prevent a known failure.',
20
+ 'Use allowed-path lists, forbidden-path lists, and detailed blocker handling only when the task has a real boundary or known hazard that needs to survive into execution.',
21
+ ...getPlanVerificationScopeLines('planner'),
22
+ ];
23
+ }
24
+ function getPlanResponseDetailLevelLines() {
25
+ return [
26
+ 'Keep the revised plan concise, human-reviewable, and at moderate-to-high-level implementation detail.',
27
+ 'Resolve material findings without expanding the plan into an exhaustive implementation inventory; leave routine discovery to execution.',
28
+ ...getPlanVerificationScopeLines('planner'),
29
+ ];
30
+ }
31
+ function getPlanReviewerReadinessLines() {
32
+ return [
33
+ 'Review the plan independently and look for material problems before accepting it.',
34
+ 'A ready plan preserves the requested objective, proposes a sound approach, accounts for major architectural constraints and dependencies, uses a valid execution shape with coherent scopes, and gives each scope meaningful verification and a reviewable success condition.',
35
+ 'The plan is written for both neal and the operator. It should be concise enough for a person to review, specific enough to guide implementation, and open enough for the coder to make local choices after inspecting the code.',
36
+ 'Raise a blocking finding only when an omission is likely to produce the wrong implementation, make a scope unsafe or impractical, hide a major dependency, leave sequencing materially unclear, or allow incorrect work to appear complete.',
37
+ 'Missing routine implementation detail is not a finding. Do not require exhaustive file, symbol, caller, test, command, assertion, line-number, pinned-value, or fixture inventories.',
38
+ 'More detail can make a plan worse by turning tentative implementation choices into acceptance requirements. Ask for low-level detail only when it records a fixed decision, preserves compatibility, or prevents a known failure.',
39
+ ...getPlanVerificationScopeLines('reviewer'),
40
+ 'Inspect enough repository context to support material findings. Do not audit every implementation path before code has been written.',
41
+ ];
42
+ }
8
43
  function getPlanningPromptBaseLines(planDoc) {
9
44
  return [
10
45
  `Refine the existing plan document at ${planDoc} into a stronger future execution plan for neal.`,
@@ -15,15 +50,17 @@ function getPlanningPromptBaseLines(planDoc) {
15
50
  '3. Reset your instructions for this turn from the current contents of the plan and referenced context.',
16
51
  '',
17
52
  'Then identify weaknesses in the current plan and improve it. Look specifically for:',
18
- '- Underdeveloped scopes that need more implementation detail.',
53
+ '- Underdeveloped scopes that need enough implementation detail to guide the coder.',
54
+ '- Missing major architectural constraints or dependencies.',
19
55
  '- Vague or missing acceptance criteria / completion criteria.',
20
56
  '- Ambiguous scope boundaries or hidden assumptions about the repository.',
21
57
  '- Poor or unclear sequencing between scopes.',
22
- '- Verification that is not executable or not concrete.',
58
+ '- Verification that would not meaningfully establish the scope\'s success.',
23
59
  '- Planning-task scaffolding left over from an earlier draft.',
24
60
  'Produce a substantively improved revision in the same file.',
25
61
  'If the current plan is already strong, do not invent new weaknesses; revise only what actually improves it.',
26
62
  "Refine for executability without expanding the author's scope. Include refactoring only when the plan calls for it, the objective genuinely requires it, or implementing the change would otherwise increase complexity such that a refactor is a natural outgrowth — not opportunistically.",
63
+ ...getPlanDetailLevelLines(),
27
64
  '',
28
65
  'Revise only plan-related artifacts.',
29
66
  'Do not edit runtime source code outside the plan itself and adjacent planning notes.',
@@ -31,10 +68,8 @@ function getPlanningPromptBaseLines(planDoc) {
31
68
  'Your output must be a pure future execution plan, not a planning-task checklist.',
32
69
  'Replace the plan in place so the resulting file is meant to be run later with `neal execute`, not `neal plan`.',
33
70
  'Do not leave planning-only scaffolding in the final file. Remove or replace sections such as planning mode instructions, Required Inputs for the planner, Verification For This Planning Task, and Completion Criteria For This Planning Task.',
34
- 'Ground the plan in the actual current repository state. Inspect the real target files and write steps against the symbols, exports, and file structure that actually exist.',
35
- 'Do not leave avoidable ambiguity in the plan when the repository already answers the question. Name concrete target functions, files, and exports when they are knowable from the repo.',
36
- 'Do not ask the future executor to perform redundant edits. If an export already propagates through an existing barrel file, say to verify that behavior instead of adding a fake extra edit step.',
37
- 'Make the final plan explicit about scope boundaries, allowed scope, forbidden paths, implementation steps, verification, completion criteria, blocker handling, and any repeated-scope selection rules.',
71
+ 'Your refined plan will be reviewed for execution readiness on three dimensions; satisfy each before finalizing: scope granularity (the work is divided into coherent, bounded scopes without exhaustive path inventories), verification concreteness (each scope names meaningful checks without specifying every possible oracle), and resume safety (scopes have understandable ordering and clean stopping points).',
72
+ 'Make the final plan explicit about its execution shape, scope goals and approach, sequencing, meaningful verification, and success conditions. Add constraints, non-goals, or blocker handling only where the task needs them.',
38
73
  'Choose `multi_scope` when the work changes orchestration or state-machine behavior, resume semantics, persistence or schema shape, multiple independent subsystems, or otherwise naturally falls into staged rollout checkpoints.',
39
74
  'Choose `multi_scope_unknown` when the work repeats one bounded recurring slice at a time and the total number of scopes is intentionally unknown until an explicit completion condition is satisfied.',
40
75
  'Choose `one_shot` only when the work can realistically be executed, reviewed, and verified as one bounded scope without hidden staging assumptions.',
@@ -75,10 +110,10 @@ function getPlanReviewerModeLines(args) {
75
110
  : 'Examples of blocking leftover scaffolding include planning-mode execution headers, planner-only required-input sections, "Verification For This Planning Task", and "Completion Criteria For This Planning Task".',
76
111
  focusRule: mode === 'derived-plan'
77
112
  ? 'Focus on whether the derived plan actually addresses the failure mode, is concrete enough to execute, reduces blast radius, and is truly not a blocker.'
78
- : 'Focus on plan quality for refinement: scopes that need more detail, acceptance criteria that are vague or missing, ambiguous boundaries, hidden assumptions about the repository, weak sequencing, and non-executable verification. Call out plan steps that are avoidably ambiguous or redundant when the current repository already provides a more specific answer, such as existing function names, current exports, or barrel re-export behavior.',
113
+ : 'Focus on plan quality for refinement: a sound implementation approach, material architectural constraints, coherent scopes, clear sequencing, meaningful verification, and reviewable success conditions. Do not turn routine implementation discovery into plan findings.',
79
114
  contractRule: mode === 'derived-plan'
80
115
  ? derivedPlanContractRule
81
- : 'Focus on whether the plan is now a clean future execution plan, explicit about single-scope vs repeated-scope behavior, and clear about verification and completion.',
116
+ : 'Focus on whether the plan is now a clean, human-reviewable future execution plan, explicit about single-scope vs repeated-scope behavior, and clear about verification and completion.',
82
117
  };
83
118
  }
84
119
  function getAuthoredOneShotPlanningLines(authoredOneShot) {
@@ -142,49 +177,22 @@ export function buildPlanReviewerPrompt(args) {
142
177
  if (!spec.variants.some((variant) => variant.kind === expectedVariant)) {
143
178
  throw new Error(`Prompt spec ${spec.id} is missing variant ${expectedVariant}`);
144
179
  }
145
- const accessMode = resolveReviewerPromptAccessMode({
146
- builder: 'buildPlanReviewerPrompt',
147
- accessMode: args.accessMode,
148
- inlineContextSupplied: Boolean(args.inlineContext),
149
- });
150
- const noRead = accessMode === 'no-read';
180
+ const accessMode = args.accessMode ?? 'tool-access';
151
181
  const modeLines = getPlanReviewerModeLines({
152
182
  mode,
153
183
  planDoc: args.planDoc,
154
184
  parentPlanDoc: args.parentPlanDoc,
155
185
  derivedFromScopeNumber: args.derivedFromScopeNumber,
156
186
  });
157
- const skepticismLines = getVerificationSkepticismLines({
158
- reviewTarget: 'planned behavior',
159
- coverageTarget: 'planned behavior',
160
- coverageGapDescription: 'the plan lacks, weakens, or fails to require meaningful coverage for the riskiest planned behavior',
161
- insufficientCoverageTarget: 'planned behavior',
162
- mode: accessMode,
163
- });
164
- const reviewHistoryLine = noRead
165
- ? 'Prior review history and coder responses are inlined below; consider them before finalizing findings.'
166
- : `Read ${args.reviewMarkdownPath} before finalizing findings so you can inspect prior review history and coder responses.`;
167
- const planInspectionLine = noRead
168
- ? 'The reviewed plan content (and any parent plan content) is inlined below; finalize findings entirely from the inlined context in this prompt.'
169
- : accessMode === 'read-only'
170
- ? 'The reviewed plan content is inlined below; use your read-only file tools to inspect directly referenced companion docs and repository source files before finalizing findings.'
171
- : 'The reviewed plan content is inlined below; use repository tools to inspect directly referenced companion docs and repository source files before finalizing findings.';
172
- if (noRead) {
173
- assertNoReadPromptInstructionText([...skepticismLines, reviewHistoryLine, planInspectionLine].join('\n'), 'buildPlanReviewerPrompt');
174
- }
187
+ const reviewHistoryLine = `Read ${args.reviewMarkdownPath} before finalizing findings so you can inspect prior review history and coder responses.`;
188
+ const planInspectionLine = accessMode === 'read-only'
189
+ ? 'The reviewed plan content is inlined below; use your read-only file tools to inspect directly referenced companion docs and repository source files before finalizing findings.'
190
+ : 'The reviewed plan content is inlined below; use repository tools to inspect directly referenced companion docs and repository source files before finalizing findings.';
175
191
  return [
176
192
  modeLines.heading,
177
193
  `Review round: ${args.round}.`,
178
194
  '',
179
- ...getAdversarialReviewDoctrineLines({
180
- reviewSubject: 'the plan-readiness claim',
181
- falsificationTarget: 'whether the plan is actually Neal-executable',
182
- creditPhrase: 'accept it as ready',
183
- claimSources: 'the plan narrative, prior review history, or operator framing',
184
- judgmentTarget: 'plan readiness',
185
- proofTarget: 'the plan will guide safe execution',
186
- }),
187
- ...skepticismLines,
195
+ ...getPlanReviewerReadinessLines(),
188
196
  '',
189
197
  'Produce only structured review findings.',
190
198
  'This is a read-only plan review. Do not edit files, create scratch artifacts, stage changes, commit, or otherwise mutate the repository.',
@@ -192,41 +200,41 @@ export function buildPlanReviewerPrompt(args) {
192
200
  'You must confirm the declared execution shape and echo it in the required `executionShape` field of your structured output.',
193
201
  'Raise a blocking finding when the declared shape is missing, internally inconsistent, or not safe for neal execution.',
194
202
  'Assess execution readiness explicitly across these dimensions: scope granularity, verification concreteness, and resume safety.',
195
- ...getPlanReviewFalsificationLines(),
196
203
  'When you raise a blocking finding about execution readiness, name the failing dimension directly in the claim or required action.',
197
204
  'Every finding must rest on concrete evidence, not assumption. Any claim about the repository’s current state — whether a file, symbol, export, or test node id exists, or whether a verification command collects or passes — must be verified by direct inspection (read the file, search the tree, collect the tests) and cite what you found; any claim about the plan must quote the exact plan text it rests on. If you cannot verify a claim, state the uncertainty instead of raising it as blocking.',
198
205
  'Never infer the repository’s current state from what the plan proposes to change. A plan proposing to add a file, class, or test is not evidence that it already exists; a plan introducing a new abstraction is not evidence that existing code or tests are already organized around it. Confirm presence or absence against the actual tree before asserting it.',
199
- 'Scope granularity means boundaries stay narrow, auditable, and avoid accidental widening.',
200
- 'Verification concreteness means the plan uses executable verification commands or deterministic repo-derived checks rather than vague instructions.',
201
- 'Resume safety means scopes have clean stopping points, understandable ordering, and no hidden staging assumptions.',
206
+ 'Scope granularity means the work is divided into coherent, bounded scopes. It does not require an exhaustive allowed-path inventory.',
207
+ 'Verification concreteness means each scope names checks that can meaningfully establish success. It does not require every possible test, oracle, assertion, or shell command to be designed in advance.',
208
+ 'Resume safety means scopes have understandable ordering and clean stopping points. It does not require the plan to prescribe routine Git mechanics.',
202
209
  'A plan should generally be forced to `multi_scope` when it changes orchestration behavior, resume semantics, persistence/schema shape, multiple independent subsystems, or naturally staged rollout checkpoints.',
203
210
  'A plan should generally be forced to `multi_scope_unknown` when the work is an intentionally open-ended recurring loop that still executes one bounded scope per cycle and ends only when a stated completion condition becomes true.',
204
211
  modeLines.blockingRule,
205
212
  modeLines.scaffoldingRule,
206
213
  modeLines.wideningRule,
207
214
  'Use non_blocking severity for clarity improvements that do not block execution.',
215
+ 'Classify every finding with a `findingClass` of either `plan_correctness` or `verification_hardening`.',
216
+ 'A `verification_hardening` finding asks for a material improvement to how already-specified behavior will be verified. It may block only when the planned checks could allow the requested change itself to be wrong while still appearing complete. Incomplete enforcement of an unnecessary broader guarantee introduced by the plan is non-blocking.',
217
+ 'A `plan_correctness` finding means the plan would build the wrong thing, build an unverifiable thing, or contains an impossible or self-contradictory instruction. When you are unsure which class applies, use `plan_correctness`.',
208
218
  modeLines.focusRule,
209
219
  modeLines.contractRule,
210
220
  'If the plan is already Neal-executable, confirm that quickly and return no manufactured findings.',
211
221
  reviewHistoryLine,
212
222
  ...getAuthoredOneShotReviewerLines(args.authoredOneShot),
213
- ...getReviewerContextLines(args.reviewerContext, noRead ? 'inline' : 'tool-access'),
223
+ ...getReviewerContextLines(args.reviewerContext),
214
224
  ...getUnattendedAutonomyLines(args.unattended),
215
225
  ...getUserGuidanceLines('reviewer'),
216
226
  '',
217
227
  planInspectionLine,
218
228
  ...getReviewedPlanContentLines({
219
- noRead,
220
229
  planDoc: args.planDoc,
221
230
  reviewedPlanContent: args.reviewedPlanContent,
222
231
  parentPlanDoc: args.parentPlanDoc,
223
232
  parentPlanContent: args.parentPlanContent,
224
233
  }),
225
- ...(args.inlineContext ? ['', renderInlineReviewerContext(args.inlineContext)] : []),
226
234
  ].join('\n');
227
235
  }
228
236
  function getReviewedPlanContentLines(args) {
229
- if (args.noRead || !args.reviewedPlanContent) {
237
+ if (!args.reviewedPlanContent) {
230
238
  return [];
231
239
  }
232
240
  return [
@@ -247,14 +255,14 @@ function getReviewedPlanContentLines(args) {
247
255
  'Use the inlined plan content above as the authoritative plan text for plan-readiness review. Repository inspection should focus on validating referenced files, symbols, assumptions, and companion docs.',
248
256
  ];
249
257
  }
250
- function getReviewerContextLines(reviewerContext, mode = 'tool-access') {
258
+ function getReviewerContextLines(reviewerContext) {
251
259
  if (!reviewerContext) {
252
260
  return [];
253
261
  }
254
262
  return [
255
263
  '',
256
264
  'Bounded current-run continuity context:',
257
- mode === 'inline' ? renderReviewerContextMarkdown(reviewerContext, { mode: 'inline' }) : reviewerContext.promptMarkdown,
265
+ reviewerContext.promptMarkdown,
258
266
  ];
259
267
  }
260
268
  export function buildCoderPlanResponsePrompt(args) {
@@ -277,6 +285,7 @@ export function buildCoderPlanResponsePrompt(args) {
277
285
  : 'Edit only the plan document and directly related planning artifacts.',
278
286
  'Do not edit runtime source code.',
279
287
  'Do not make git commits.',
288
+ ...getPlanResponseDetailLevelLines(),
280
289
  reviewMode === 'derived-plan'
281
290
  ? 'Keep the same target, but make the derived plan concrete enough to replace the abandoned scope safely.'
282
291
  : 'The final file must be a pure future execution plan for `neal execute`.',
@@ -285,7 +294,7 @@ export function buildCoderPlanResponsePrompt(args) {
285
294
  : 'Do not leave planning-task scaffolding behind after you respond to the findings.',
286
295
  reviewMode === 'derived-plan'
287
296
  ? 'Revise the derived plan so it uses the same Neal-executable contract as a top-level plan. Any derived-plan-specific rationale sections are optional additive context only; they must not replace the canonical machine-consumed sections.'
288
- : 'Where the current repository already answers an implementation detail, revise the plan to use the concrete existing symbol names and exports instead of leaving generic or redundant instructions.',
297
+ : 'Resolve the material finding without adding planning-task scaffolding.',
289
298
  ...(reviewMode === 'derived-plan' ? getDerivedPlanSectionContractLines() : []),
290
299
  ...getProtocolMarkerArtifactProhibitionLines(),
291
300
  ...(reviewMode === 'derived-plan' ? getCanonicalPlanContractLines() : []),
@@ -1,20 +1,3 @@
1
- // Resolve the effective doctrine access mode for a reviewer prompt builder.
2
- // Precedence: an explicit accessMode wins; when absent, derive exactly the
3
- // legacy two-way behavior (inline context present -> 'no-read', else
4
- // 'tool-access') so existing callers render byte-identically. Inline context
5
- // combined with an explicit mode other than 'no-read' is mismatched wiring
6
- // (the call site inlined context for a reviewer the capability registry says
7
- // can read), which is a bug rather than a render choice.
8
- export function resolveReviewerPromptAccessMode(args) {
9
- if (args.inlineContextSupplied && args.accessMode !== undefined && args.accessMode !== 'no-read') {
10
- throw new Error(`${args.builder} received Neal-inlined reviewer context with explicit access mode ${JSON.stringify(args.accessMode)}; ` +
11
- "inline context is only valid with the 'no-read' access mode");
12
- }
13
- if (args.accessMode !== undefined) {
14
- return args.accessMode;
15
- }
16
- return args.inlineContextSupplied ? 'no-read' : 'tool-access';
17
- }
18
1
  export function getAdversarialReviewDoctrineLines(context) {
19
2
  const falsificationTarget = context.falsificationTarget ?? 'the implementation';
20
3
  const creditPhrase = context.creditPhrase ?? 'give it credit for working';
@@ -32,16 +15,11 @@ export function getCodeReviewFalsificationLines(context) {
32
15
  const rangeDiffInlined = mode === 'read-only' && context.rangeDiffInlined === true;
33
16
  const lines = [];
34
17
  if (context.rangeLabel) {
35
- lines.push(mode === 'no-read'
36
- ? `The diff inlined below is the source of truth for this review of that ${context.rangeLabel}. You have no repository access; judge entirely from the inlined context in this prompt.`
37
- : mode === 'read-only'
38
- ? rangeDiffInlined
39
- ? `The commit-range diff for that ${context.rangeLabel} is inlined below and is the source of truth for exactly what the ${context.rangeLabel} changed (including deletions and renames). Use your read tools to verify the surrounding code; you have no commit-range diff tool, so rely on the inlined diff for what changed.`
40
- : `Review that ${context.rangeLabel} directly with your read-only repository tools: use the git_diff tool to see exactly what the ${context.rangeLabel} changed (including deletions and renames), and your read tools to verify the surrounding code. The ${context.rangeLabel} is the source of truth for this review.`
41
- : `Review that ${context.rangeLabel} directly with repository tools. The ${context.rangeLabel} is the source of truth for this review.`);
42
- }
43
- if (mode === 'no-read') {
44
- lines.push('A diff shows only changed hunks; declarations such as imports, struct fields, or helpers may exist in unchanged parts of the file. Never raise a missing-import or missing-declaration finding from absence in the diff alone — only claim one when the diff itself removes the declaration or adds a new file that lacks it. If you cannot verify such a claim from the inlined context, state the uncertainty instead of asserting a defect.');
18
+ lines.push(mode === 'read-only'
19
+ ? rangeDiffInlined
20
+ ? `The commit-range diff for that ${context.rangeLabel} is inlined below and is the source of truth for exactly what the ${context.rangeLabel} changed (including deletions and renames). Use your read tools to verify the surrounding code; you have no commit-range diff tool, so rely on the inlined diff for what changed.`
21
+ : `Review that ${context.rangeLabel} directly with your read-only repository tools: use the git_diff tool to see exactly what the ${context.rangeLabel} changed (including deletions and renames), and your read tools to verify the surrounding code. The ${context.rangeLabel} is the source of truth for this review.`
22
+ : `Review that ${context.rangeLabel} directly with repository tools. The ${context.rangeLabel} is the source of truth for this review.`);
45
23
  }
46
24
  if (mode === 'read-only') {
47
25
  lines.push('A diff shows only changed hunks; declarations such as imports, struct fields, or helpers may exist in unchanged parts of the file. Absence from the diff is not evidence of absence from the repository — open the affected file with your read tools and verify before claiming a missing import or missing declaration.');
@@ -56,9 +34,7 @@ export function getCodeReviewFalsificationLines(context) {
56
34
  : 'Inspect the change with your read-only tools: call git_diff with the base and head commits named in this prompt (stat:true first for the changed-file overview, then per-path diffs), read the changed files in full, and search the tree for affected symbols and callers to verify claims.');
57
35
  }
58
36
  }
59
- lines.push(`Trace the changed runtime path far enough to prove the happy path is reachable and that runtime invariants, data-shape transitions, persistence, and error paths line up with the implementation claims.`, mode === 'no-read'
60
- ? `Compare claimed behavior against the actual code and data transitions in the ${reviewTarget}; do not accept summaries when the inlined diff gives a more direct answer.`
61
- : `Compare claimed behavior against the actual code and data transitions in the ${reviewTarget}; do not accept summaries when repository inspection gives a more direct answer.`, 'For refactors and config/runtime plumbing changes, actively look for implementation-quality regressions, not just behavioral correctness. Examples include replacing a robust library with a weaker hand-rolled parser, introducing repeated disk reads or reparsing in hot paths, silently weakening error handling, or otherwise making the implementation materially less robust than the prior version.');
37
+ lines.push(`Trace the changed runtime path far enough to prove the happy path is reachable and that runtime invariants, data-shape transitions, persistence, and error paths line up with the implementation claims.`, `Compare claimed behavior against the actual code and data transitions in the ${reviewTarget}; do not accept summaries when repository inspection gives a more direct answer.`, 'For refactors and config/runtime plumbing changes, actively look for implementation-quality regressions, not just behavioral correctness. Examples include replacing a robust library with a weaker hand-rolled parser, introducing repeated disk reads or reparsing in hot paths, silently weakening error handling, or otherwise making the implementation materially less robust than the prior version.');
62
38
  lines.push('When the requirements enumerate multiple discrete items to change — a list of identifiers, fields, files, endpoints, or call sites — enumerate them yourself and confirm each maps to a concrete change before accepting; do not accept based only on the subset the implementation happened to touch.', 'Treat an unrequested change to a public or exported symbol — a renamed or removed exported function, type, field, constant, or signature that the requirements did not call for — as scope drift and a blocking finding unless the requirements require it: an unrequested public-symbol change widens the blast radius and can break callers the diff does not show.');
63
39
  if (context.includeExecuteFailureClasses) {
64
40
  lines.push('Bias your search toward execute-mode failure classes in this repository: persistence or state-shape mismatches, resume or recovery transition regressions, split-plan or execution-shape contract violations, artifact or reporting mismatches, and verification that does not actually cover the changed behavior.');
@@ -77,9 +53,7 @@ export function getVerificationSkepticismLines(context = {}) {
77
53
  ].join(' ')
78
54
  : 'Check whether test coverage for the changed behavior degraded. If the change removes, weakens, or fails to preserve meaningful test coverage for the affected behavior, treat that as a review finding. Use blocking severity when the missing or degraded coverage leaves the changed behavior insufficiently protected.';
79
55
  return [
80
- mode === 'no-read'
81
- ? 'Do not infer that verification was skipped merely because this prompt does not embed full terminal output. Treat missing verification as a finding only when the inlined context, plan requirements, or review history give concrete evidence that required verification was not run or was insufficient.'
82
- : 'Do not infer that verification was skipped merely because this prompt does not embed full terminal output. Treat missing verification as a finding only when the repository state, plan requirements, or review history give concrete evidence that required verification was not run or was insufficient.',
56
+ 'Do not infer that verification was skipped merely because this prompt does not embed full terminal output. Treat missing verification as a finding only when the repository state, plan requirements, or review history give concrete evidence that required verification was not run or was insufficient.',
83
57
  `Look for verification that cannot catch the failure mode, including tests that mock away the risky runtime path or only assert mocked adapters instead of the ${reviewTarget}.`,
84
58
  coverageLine,
85
59
  ];
@@ -90,11 +64,9 @@ export function getRegressionPreservationLines(context = {}) {
90
64
  return [
91
65
  `Identify the shared subsystems the ${reviewTarget} touches: state machines, event loops, parsers or byte framing, command dispatch, storage or shared-state abstractions, startup/load paths, and long-lived session or connection state.`,
92
66
  'Existing behavior that depends on those subsystems remains part of the contract unless the plan explicitly changes it. Do not accept solely because the newly requested behavior appears implemented; also confirm that adjacent behavior sharing those paths still holds.',
93
- mode === 'no-read'
94
- ? 'Before accepting, either cite existing tests in the inlined context that cover the plausible regression surfaces or reason through representative existing flows in the inlined diff at the level of state transitions and data ownership.'
95
- : mode === 'read-only'
96
- ? 'Before accepting, either cite existing tests that cover the plausible regression surfaces — reading them with your read tools — or reason through representative existing flows at the level of state transitions and data ownership.'
97
- : 'Before accepting, either cite existing tests that cover the plausible regression surfaces — running them when available — or reason through representative existing flows at the level of state transitions and data ownership.',
67
+ mode === 'read-only'
68
+ ? 'Before accepting, either cite existing tests that cover the plausible regression surfaces — reading them with your read tools — or reason through representative existing flows at the level of state transitions and data ownership.'
69
+ : 'Before accepting, either cite existing tests that cover the plausible regression surfaces — running them when available — or reason through representative existing flows at the level of state transitions and data ownership.',
98
70
  `Expand this regression check only along paths the ${reviewTarget} actually touches or depends on; you do not need to re-audit unrelated parts of the project.`,
99
71
  ];
100
72
  }
@@ -104,22 +76,13 @@ export function getPreexistingFailureContractLines(context = {}) {
104
76
  return [
105
77
  `When the plan requires existing behavior to keep working, this review is not limited to newly changed lines. Apply an acceptance-surface test to any pre-existing crash, failure, flaky behavior, or anomaly reported in the coder narrative, progress justification, or review history instead of discounting it because it predates the ${reviewTarget}.`,
106
78
  "A pre-existing failure is inside the required acceptance surface when the plan explicitly requires the affected existing behavior to keep working, when the plan's stated verification fails because of it, or when the behavior the plan delivers cannot be exercised end-to-end without the failing path. Otherwise it is outside the surface.",
107
- mode === 'no-read'
108
- ? "For a reported pre-existing failure inside the required surface, reason through the affected existing flows using the inlined context at the level of state transitions and data ownership rather than relying on the coder's dismissal."
109
- : mode === 'read-only'
110
- ? "For a reported pre-existing failure inside the required surface, read the most relevant existing tests and affected code with your read tools and reason through the affected existing flows directly rather than relying on the coder's dismissal."
111
- : "For a reported pre-existing failure inside the required surface, exercise or reason through the affected existing flows directly — run the most relevant existing tests or reproduce the reported behavior when practical — rather than relying on the coder's dismissal.",
79
+ mode === 'read-only'
80
+ ? "For a reported pre-existing failure inside the required surface, read the most relevant existing tests and affected code with your read tools and reason through the affected existing flows directly rather than relying on the coder's dismissal."
81
+ : "For a reported pre-existing failure inside the required surface, exercise or reason through the affected existing flows directly — run the most relevant existing tests or reproduce the reported behavior when practical — rather than relying on the coder's dismissal.",
112
82
  'An in-surface pre-existing failure that was neither fixed nor surfaced as a blocking concern is a blocking finding, and a dismissal without a concrete out-of-surface reason is a finding.',
113
83
  'Conversely, treat a fix for a pre-existing issue that fails the acceptance-surface test as scope drift rather than extra credit: flag it so the change stays bounded to the plan.',
114
84
  ];
115
85
  }
116
- export function getPlanReviewFalsificationLines() {
117
- return [
118
- 'Try to falsify whether the plan would actually guide safe execution before accepting it as ready.',
119
- 'Check whether the proposed scopes, sequencing, and verification would catch the riskiest failure modes instead of merely describing plausible work.',
120
- 'Treat vague verification, hidden state assumptions, broad blast radius, and missing resume boundaries as evidence against plan readiness.',
121
- ];
122
- }
123
86
  export function getFindingQualityLines(context = {}) {
124
87
  if (context.outputContract === 'completion_verdict') {
125
88
  return [
@@ -1,10 +1,28 @@
1
- import { assertNoReadPromptInstructionText, renderInlineReviewerContext, renderInlinedRangeDiffSection, } from '../context/inline-review-context.js';
2
- import { renderReviewerContextMarkdown } from '../context/reviewer-context.js';
1
+ import { guardStructuredJsonOutputFormatLines } from '../agents/structured-json.js';
2
+ import { renderInlinedRangeDiffSection } from '../context/inline-review-context.js';
3
3
  import { assertPromptBuilder } from './assert-builder.js';
4
4
  import { getUserGuidanceLines } from './guidance.js';
5
5
  import { getUnattendedAutonomyLines } from './shared.js';
6
- import { getAdversarialReviewDoctrineLines, getCodeReviewFalsificationLines, getFindingQualityLines, getPreexistingFailureContractLines, getRegressionPreservationLines, getVerificationSkepticismLines, resolveReviewerPromptAccessMode, } from './review-doctrine.js';
6
+ import { getAdversarialReviewDoctrineLines, getCodeReviewFalsificationLines, getFindingQualityLines, getPreexistingFailureContractLines, getRegressionPreservationLines, getVerificationSkepticismLines, } from './review-doctrine.js';
7
7
  const PROMPT_MODULE_PATH = 'src/neal/prompts/specialized.ts';
8
+ // Guarded output-format instruction block shared by the two structured-JSON
9
+ // completion base prompts. It emits only JSON-only framing and a
10
+ // transport-deferring line (replacing the old fence-prohibition that
11
+ // contradicted the neal-json transport), and routes the assembled lines through
12
+ // guardStructuredJsonOutputFormatLines so a reintroduced conflicting phrasing
13
+ // throws at render. Both completion builders emit their output-format
14
+ // instructions exclusively through this helper. The transport-deferring line is
15
+ // provider-neutral: the neal-json protocol block is appended below the base
16
+ // prompt only on the wrapper path (anthropic-claude and the repair loop), while
17
+ // other transports (e.g. openai-compatible) send the base prompt with no protocol
18
+ // below it, so the line must not claim instructions appear "below".
19
+ export function completionJsonOutputFormatLines(label) {
20
+ return guardStructuredJsonOutputFormatLines([
21
+ 'Return only JSON that matches the required schema.',
22
+ 'Return the final answer only as the required structured output object, not as prose or markdown.',
23
+ 'Defer exact output framing to the active structured-output transport; do not add your own output-format constraints.',
24
+ ], label);
25
+ }
8
26
  export function buildFinalCompletionSummaryPrompt(args) {
9
27
  const spec = assertPromptBuilder('completion_coder', 'buildFinalCompletionSummaryPrompt', PROMPT_MODULE_PATH);
10
28
  const finalCompletionVariant = spec.variants.find((variant) => variant.kind === 'final_completion');
@@ -18,12 +36,12 @@ export function buildFinalCompletionSummaryPrompt(args) {
18
36
  `Summarize whether the execute-mode plan at ${args.planDoc} is complete as a whole.`,
19
37
  '',
20
38
  'Before writing the summary, review the current repository state, the plan document, and the completion packet below.',
21
- 'Return only JSON that matches the required schema.',
22
- 'Return the final answer only as the required structured output object, not as prose or markdown.',
39
+ ...completionJsonOutputFormatLines('buildFinalCompletionSummaryPrompt'),
23
40
  'Keep the response compact and auditable rather than essay-style.',
24
41
  'Use `planGoalSatisfied` to state whether the plan goal is satisfied overall.',
25
42
  'Use `whatChangedOverall` to summarize the completed work across the whole plan, not just the last scope.',
26
43
  'Use `verificationSummary` to summarize the completion evidence that actually ran.',
44
+ '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
45
  'Use `remainingKnownGaps` for any known missing work, regressions, quality concerns, testing gaps, risks, or omissions that would make the plan not fully complete.',
28
46
  'Do not contradict yourself:',
29
47
  '- if `planGoalSatisfied` is `true`, `remainingKnownGaps` must be empty',
@@ -49,7 +67,6 @@ export function buildFinalCompletionSummaryPrompt(args) {
49
67
  }, null, 2),
50
68
  '',
51
69
  '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
70
  ...getUserGuidanceLines('coder'),
54
71
  '',
55
72
  'Last non-empty implementation scope reference:',
@@ -62,12 +79,7 @@ export function buildFinalCompletionReviewerPrompt(args) {
62
79
  if (!finalCompletionVariant) {
63
80
  throw new Error('Prompt spec completion_reviewer is missing a final_completion variant');
64
81
  }
65
- const accessMode = resolveReviewerPromptAccessMode({
66
- builder: 'buildFinalCompletionReviewerPrompt',
67
- accessMode: args.accessMode,
68
- inlineContextSupplied: Boolean(args.inlineContext),
69
- });
70
- const noRead = accessMode === 'no-read';
82
+ const accessMode = args.accessMode ?? 'tool-access';
71
83
  // A collected diff may legitimately be the empty string (a range with no
72
84
  // changes); distinguish "collected" (any string, including '') from "not
73
85
  // collected" (null/undefined) so an empty diff still rides the inlined channel
@@ -110,9 +122,6 @@ export function buildFinalCompletionReviewerPrompt(args) {
110
122
  reviewTarget: 'aggregate implementation',
111
123
  mode: accessMode,
112
124
  });
113
- if (noRead) {
114
- assertNoReadPromptInstructionText([...falsificationLines, ...skepticismLines, ...regressionLines, ...preexistingLines].join('\n'), 'buildFinalCompletionReviewerPrompt');
115
- }
116
125
  return [
117
126
  `Review whether the execute-mode plan at ${args.planDoc} is complete as a whole.`,
118
127
  '',
@@ -139,10 +148,9 @@ export function buildFinalCompletionReviewerPrompt(args) {
139
148
  ...preexistingLines,
140
149
  ...getFindingQualityLines({ outputContract: 'completion_verdict' }),
141
150
  '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
- ...getReviewerContextLines(args.reviewerContext, noRead ? 'inline' : 'tool-access'),
151
+ ...getReviewerContextLines(args.reviewerContext),
143
152
  '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
- 'Return only JSON that matches the required schema.',
145
- 'Return the final answer only as the required structured output object, not as prose or markdown.',
153
+ ...completionJsonOutputFormatLines('buildFinalCompletionReviewerPrompt'),
146
154
  'Use `accept_complete` only when the full plan objectives are satisfied and the aggregate implementation is acceptable under ordinary code review standards.',
147
155
  'Use `continue_execution` only when the remaining work is concrete, bounded, and suitable for one explicit follow-on scope.',
148
156
  'Use `block_for_operator` when the remaining gap is ambiguous, externally constrained, or needs human direction.',
@@ -183,12 +191,10 @@ export function buildFinalCompletionReviewerPrompt(args) {
183
191
  }, null, 2),
184
192
  '',
185
193
  '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
194
  ...getUserGuidanceLines('reviewer'),
188
195
  '',
189
196
  'Last non-empty implementation scope reference:',
190
197
  lastImplementationScope,
191
- ...(args.inlineContext ? ['', renderInlineReviewerContext(args.inlineContext)] : []),
192
198
  ...(rangeDiffInlined
193
199
  ? [
194
200
  '',
@@ -200,13 +206,13 @@ export function buildFinalCompletionReviewerPrompt(args) {
200
206
  : []),
201
207
  ].join('\n');
202
208
  }
203
- function getReviewerContextLines(reviewerContext, mode = 'tool-access') {
209
+ function getReviewerContextLines(reviewerContext) {
204
210
  if (!reviewerContext) {
205
211
  return [];
206
212
  }
207
213
  return [
208
214
  '',
209
215
  'Bounded current-run continuity context:',
210
- mode === 'inline' ? renderReviewerContextMarkdown(reviewerContext, { mode: 'inline' }) : reviewerContext.promptMarkdown,
216
+ reviewerContext.promptMarkdown,
211
217
  ];
212
218
  }