@navels/neal 0.1.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/LICENSE +21 -0
- package/README.md +527 -0
- package/SECURITY.md +91 -0
- package/config.yml +104 -0
- package/dist/neal/activity-footer.js +177 -0
- package/dist/neal/activity-reporting.js +1 -0
- package/dist/neal/adjudicator/artifacts.js +58 -0
- package/dist/neal/adjudicator/blocked-adjudicator.js +223 -0
- package/dist/neal/adjudicator/contracts.js +139 -0
- package/dist/neal/adjudicator/execute.js +611 -0
- package/dist/neal/adjudicator/final-completion.js +104 -0
- package/dist/neal/adjudicator/planning.js +145 -0
- package/dist/neal/adjudicator/specs.js +453 -0
- package/dist/neal/agents/prompts.js +120 -0
- package/dist/neal/agents/rounds.js +706 -0
- package/dist/neal/agents/schemas.js +832 -0
- package/dist/neal/agents/structured-coder.js +82 -0
- package/dist/neal/agents/structured-json.js +528 -0
- package/dist/neal/agents.js +4 -0
- package/dist/neal/atomic-write.js +18 -0
- package/dist/neal/blocked-guidance.js +406 -0
- package/dist/neal/cli.js +471 -0
- package/dist/neal/commands/check.js +401 -0
- package/dist/neal/commands/compat.js +807 -0
- package/dist/neal/commands/interactive-activity.js +57 -0
- package/dist/neal/commands/new-run.js +79 -0
- package/dist/neal/commands/plan-and-execute.js +44 -0
- package/dist/neal/commands/recovery-guidance.js +217 -0
- package/dist/neal/commands/resume-run.js +395 -0
- package/dist/neal/commands/review.js +21 -0
- package/dist/neal/commands/runtime.js +557 -0
- package/dist/neal/commands/setup.js +596 -0
- package/dist/neal/commands/squash.js +113 -0
- package/dist/neal/commands/status.js +33 -0
- package/dist/neal/commands/writer-exit-codes.js +42 -0
- package/dist/neal/commit-message.js +17 -0
- package/dist/neal/config.js +432 -0
- package/dist/neal/context/artifacts.js +140 -0
- package/dist/neal/context/context.js +324 -0
- package/dist/neal/context/inline-review-context.js +131 -0
- package/dist/neal/context/reviewer-context.js +166 -0
- package/dist/neal/context/shared.js +117 -0
- package/dist/neal/context/types.js +1 -0
- package/dist/neal/diagnostic.js +208 -0
- package/dist/neal/execute-finalization.js +5 -0
- package/dist/neal/final-completion-review.js +188 -0
- package/dist/neal/final-completion.js +229 -0
- package/dist/neal/git.js +339 -0
- package/dist/neal/index.js +135 -0
- package/dist/neal/interactive-controls.js +85 -0
- package/dist/neal/logger.js +102 -0
- package/dist/neal/manual-gates.js +121 -0
- package/dist/neal/orchestrator/artifacts.js +70 -0
- package/dist/neal/orchestrator/completion.js +531 -0
- package/dist/neal/orchestrator/failures.js +31 -0
- package/dist/neal/orchestrator/notifications.js +175 -0
- package/dist/neal/orchestrator/phases/coder.js +516 -0
- package/dist/neal/orchestrator/phases/planning.js +540 -0
- package/dist/neal/orchestrator/phases/recovery.js +798 -0
- package/dist/neal/orchestrator/phases/review.js +136 -0
- package/dist/neal/orchestrator/phases/shared.js +279 -0
- package/dist/neal/orchestrator/run-loop.js +113 -0
- package/dist/neal/orchestrator/split-plan.js +235 -0
- package/dist/neal/orchestrator/transitions.js +309 -0
- package/dist/neal/orchestrator.js +215 -0
- package/dist/neal/phase-display.js +27 -0
- package/dist/neal/plan-doc.js +154 -0
- package/dist/neal/plan-queue.js +1092 -0
- package/dist/neal/plan-refinement.js +39 -0
- package/dist/neal/plan-validation.js +525 -0
- package/dist/neal/progress.js +237 -0
- package/dist/neal/prompts/assert-builder.js +13 -0
- package/dist/neal/prompts/execute.js +290 -0
- package/dist/neal/prompts/guidance.js +70 -0
- package/dist/neal/prompts/planning.js +313 -0
- package/dist/neal/prompts/review-doctrine.js +142 -0
- package/dist/neal/prompts/shared.js +101 -0
- package/dist/neal/prompts/specialized.js +212 -0
- package/dist/neal/prompts/specs.js +572 -0
- package/dist/neal/providers/anthropic-claude.js +1599 -0
- package/dist/neal/providers/detection.js +139 -0
- package/dist/neal/providers/generic-agentic-tools.js +586 -0
- package/dist/neal/providers/generic-agentic.js +1238 -0
- package/dist/neal/providers/liveness.js +151 -0
- package/dist/neal/providers/openai-codex.js +1014 -0
- package/dist/neal/providers/openai-compatible.js +654 -0
- package/dist/neal/providers/registry.js +389 -0
- package/dist/neal/providers/telemetry.js +208 -0
- package/dist/neal/providers/types.js +21 -0
- package/dist/neal/recovery-artifacts.js +50 -0
- package/dist/neal/resume-decision.js +220 -0
- package/dist/neal/resume-planner.js +265 -0
- package/dist/neal/retrospective.js +391 -0
- package/dist/neal/review-debt.js +18 -0
- package/dist/neal/review-findings/artifacts.js +173 -0
- package/dist/neal/review-findings/prompts.js +172 -0
- package/dist/neal/review-findings/provider.js +330 -0
- package/dist/neal/review-findings/run.js +373 -0
- package/dist/neal/review-findings/types.js +1 -0
- package/dist/neal/review-mode.js +67 -0
- package/dist/neal/review.js +137 -0
- package/dist/neal/run-lock.js +334 -0
- package/dist/neal/run-metrics.js +355 -0
- package/dist/neal/run-narrative-types.js +1 -0
- package/dist/neal/run-narrative.js +1374 -0
- package/dist/neal/run-registry.js +218 -0
- package/dist/neal/run-status.js +25 -0
- package/dist/neal/scopes.js +451 -0
- package/dist/neal/sensitive-text.js +8 -0
- package/dist/neal/squash-message.js +379 -0
- package/dist/neal/squash.js +591 -0
- package/dist/neal/state-invariants.js +496 -0
- package/dist/neal/state-views.js +344 -0
- package/dist/neal/state.js +887 -0
- package/dist/neal/status-footer.js +258 -0
- package/dist/neal/status.js +1260 -0
- package/dist/neal/storage-paths.js +57 -0
- package/dist/neal/support.js +58 -0
- package/dist/neal/terminal-narrator.js +435 -0
- package/dist/neal/types.js +1 -0
- package/dist/neal/verification-events.js +81 -0
- package/dist/neal/version.js +37 -0
- package/dist/neal/worktree-status.js +137 -0
- package/dist/notifier.js +44 -0
- package/docs/ADJUDICATOR_INVENTORY.md +310 -0
- package/docs/PROMPT_SPECS.md +266 -0
- package/docs/README.md +22 -0
- package/docs/architecture.md +113 -0
- package/docs/assets/neal-execution-flow.png +0 -0
- package/docs/automation.md +65 -0
- package/docs/comparison.md +105 -0
- package/docs/compat.md +269 -0
- package/docs/compatible-models.md +135 -0
- package/docs/demo.md +55 -0
- package/docs/maintenance.md +64 -0
- package/docs/plan-format.md +213 -0
- package/docs/providers.md +751 -0
- package/docs/release.md +147 -0
- package/docs/state-machine.md +266 -0
- package/docs/storage.md +207 -0
- package/docs/troubleshooting.md +152 -0
- package/examples/compat/add-edit-verify/PLAN.md +29 -0
- package/examples/compat/add-edit-verify/broken.diff +8 -0
- package/examples/compat/add-edit-verify/good.diff +8 -0
- package/examples/compat/add-edit-verify/package.json +5 -0
- package/examples/compat/add-edit-verify/src/add.js +2 -0
- package/examples/compat/add-edit-verify/test/add.test.js +9 -0
- package/examples/compat/is-even-add-test/PLAN.md +30 -0
- package/examples/compat/is-even-add-test/broken.diff +11 -0
- package/examples/compat/is-even-add-test/good.diff +11 -0
- package/examples/compat/is-even-add-test/package.json +5 -0
- package/examples/compat/is-even-add-test/src/is-even.js +3 -0
- package/examples/compat/is-even-add-test/test/is-even.test.js +9 -0
- package/examples/compat/manifest.json +60 -0
- package/examples/compat/plan-greeting/ISSUE.md +25 -0
- package/examples/compat/plan-greeting/package.json +5 -0
- package/examples/compat/plan-greeting/src/greet.js +2 -0
- package/examples/compat/plan-greeting/test/greet.test.js +8 -0
- package/examples/compat/reverse-grep-edit/PLAN.md +32 -0
- package/examples/compat/reverse-grep-edit/broken.diff +12 -0
- package/examples/compat/reverse-grep-edit/good.diff +12 -0
- package/examples/compat/reverse-grep-edit/package.json +5 -0
- package/examples/compat/reverse-grep-edit/src/strings.js +10 -0
- package/examples/compat/reverse-grep-edit/test/strings.test.js +15 -0
- package/examples/issue-triage-js/PLAN.md +83 -0
- package/examples/issue-triage-js/README.md +76 -0
- package/examples/issue-triage-js/package.json +9 -0
- package/examples/issue-triage-js/src/issue-triage.js +87 -0
- package/examples/issue-triage-js/test/issue-triage.test.js +107 -0
- package/package.json +70 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, normalize, relative, resolve } from 'node:path';
|
|
3
|
+
const LIKELY_SCRATCH_ROOTS = new Set([
|
|
4
|
+
'build_review',
|
|
5
|
+
'build-review',
|
|
6
|
+
'review_build',
|
|
7
|
+
'review-build',
|
|
8
|
+
'review_scratch',
|
|
9
|
+
'review-scratch',
|
|
10
|
+
'reviewer_scratch',
|
|
11
|
+
'reviewer-scratch',
|
|
12
|
+
'scratch',
|
|
13
|
+
]);
|
|
14
|
+
const WRAPPER_OWNED_PREFIXES = ['.neal/', '.forge/'];
|
|
15
|
+
const WRAPPER_OWNED_PATHS = new Set(['.neal', '.forge', 'CURRENT_PLAN.md']);
|
|
16
|
+
export function toStoredWorktreePath(cwd, path) {
|
|
17
|
+
const absoluteCwd = resolveExistingPath(resolve(cwd));
|
|
18
|
+
const absolutePath = resolveExistingPath(isAbsolute(path) ? resolve(path) : resolve(absoluteCwd, path));
|
|
19
|
+
const relativePath = relative(absoluteCwd, absolutePath);
|
|
20
|
+
if (relativePath && !relativePath.startsWith('..') && !isAbsolute(relativePath)) {
|
|
21
|
+
return normalize(relativePath);
|
|
22
|
+
}
|
|
23
|
+
return absolutePath;
|
|
24
|
+
}
|
|
25
|
+
function resolveExistingPath(path) {
|
|
26
|
+
try {
|
|
27
|
+
return realpathSync.native(path);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return path;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function filterAllowedDirtyPathStatus(cwd, statusOutput, allowedDirtyPaths) {
|
|
34
|
+
const allowedPaths = new Set(allowedDirtyPaths.flatMap((path) => allowedDirtyPathKeys(cwd, path)));
|
|
35
|
+
return statusOutput
|
|
36
|
+
.split('\n')
|
|
37
|
+
.map((line) => line.trimEnd())
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.filter((line) => {
|
|
40
|
+
const paths = parseWorktreeStatusLine(line)?.paths ?? [];
|
|
41
|
+
return paths.length === 0 || paths.some((path) => !allowedPaths.has(toStoredWorktreePath(cwd, path)));
|
|
42
|
+
})
|
|
43
|
+
.join('\n');
|
|
44
|
+
}
|
|
45
|
+
function allowedDirtyPathKeys(cwd, path) {
|
|
46
|
+
const storedPath = toStoredWorktreePath(cwd, path);
|
|
47
|
+
if (isAbsolute(path)) {
|
|
48
|
+
return [storedPath];
|
|
49
|
+
}
|
|
50
|
+
return [storedPath, normalize(path)];
|
|
51
|
+
}
|
|
52
|
+
export function filterWrapperOwnedWorktreeStatus(statusOutput) {
|
|
53
|
+
return statusOutput
|
|
54
|
+
.split('\n')
|
|
55
|
+
.map((line) => line.trimEnd())
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.filter((line) => {
|
|
58
|
+
const paths = parseWorktreeStatusLine(line)?.paths ?? [];
|
|
59
|
+
return paths.length === 0 || paths.some((path) => !isWrapperOwnedPath(path));
|
|
60
|
+
})
|
|
61
|
+
.join('\n');
|
|
62
|
+
}
|
|
63
|
+
export function parseWorktreeStatusLine(line) {
|
|
64
|
+
const raw = line.trimEnd();
|
|
65
|
+
if (!raw) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const pathText = getStatusLinePathText(raw);
|
|
69
|
+
if (!pathText) {
|
|
70
|
+
return {
|
|
71
|
+
raw,
|
|
72
|
+
pathText,
|
|
73
|
+
paths: [],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
raw,
|
|
78
|
+
pathText,
|
|
79
|
+
paths: pathText.split(' -> ').map((path) => normalize(path)),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function getLikelyScratchLeakPaths(statusOutput) {
|
|
83
|
+
const scratchPaths = statusOutput
|
|
84
|
+
.split('\n')
|
|
85
|
+
.map(parseWorktreeStatusLine)
|
|
86
|
+
.filter((entry) => entry !== null)
|
|
87
|
+
.flatMap((entry) => entry.paths)
|
|
88
|
+
.filter(isLikelyProjectScratchPath);
|
|
89
|
+
return [...new Set(scratchPaths)];
|
|
90
|
+
}
|
|
91
|
+
export function formatDirtyWorktreeDiagnostic(args) {
|
|
92
|
+
const scratchPaths = getLikelyScratchLeakPaths(args.statusOutput);
|
|
93
|
+
if (scratchPaths.length === 0) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
const expectedScratchDirs = [...new Set(args.expectedScratchDirs ?? [])].filter(Boolean);
|
|
97
|
+
const lines = [
|
|
98
|
+
'Likely Neal reviewer scratch leakage detected:',
|
|
99
|
+
...scratchPaths.map((path) => `- ${path}`),
|
|
100
|
+
'',
|
|
101
|
+
];
|
|
102
|
+
if (expectedScratchDirs.length > 0) {
|
|
103
|
+
lines.push('Expected Neal scratch location(s):', ...expectedScratchDirs.map((path) => `- ${path}`), '');
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
lines.push('Reviewer scratch should stay under `.neal/runs/<run-id>/scratch/`.', '');
|
|
107
|
+
}
|
|
108
|
+
lines.push('Neal is still blocking because these project-tree paths are not proven Neal-owned. Inspect and move, remove, commit, or stash them outside Neal if appropriate.');
|
|
109
|
+
return lines.join('\n');
|
|
110
|
+
}
|
|
111
|
+
function isLikelyProjectScratchPath(path) {
|
|
112
|
+
if (isAbsolute(path)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const normalized = normalize(path).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
116
|
+
const [root] = normalized.split('/');
|
|
117
|
+
return LIKELY_SCRATCH_ROOTS.has(root);
|
|
118
|
+
}
|
|
119
|
+
export function isWrapperOwnedPath(path) {
|
|
120
|
+
if (isAbsolute(path)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const normalized = normalize(path)
|
|
124
|
+
.replace(/\\/g, '/')
|
|
125
|
+
.replace(/^\.\//, '')
|
|
126
|
+
.replace(/\/+$/, '');
|
|
127
|
+
return WRAPPER_OWNED_PATHS.has(normalized) || WRAPPER_OWNED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
128
|
+
}
|
|
129
|
+
function getStatusLinePathText(line) {
|
|
130
|
+
if (line.length >= 3 && line[2] === ' ') {
|
|
131
|
+
return line.slice(3).trim();
|
|
132
|
+
}
|
|
133
|
+
if (line.length >= 2 && line[1] === ' ') {
|
|
134
|
+
return line.slice(2).trim();
|
|
135
|
+
}
|
|
136
|
+
return line.trim();
|
|
137
|
+
}
|
package/dist/notifier.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { getNotifyBin } from './neal/config.js';
|
|
3
|
+
export const NOTIFICATION_CHECK_MESSAGE = '[neal] check notification test';
|
|
4
|
+
async function runCommand(command, args) {
|
|
5
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
6
|
+
const child = spawn(command, args, {
|
|
7
|
+
shell: false,
|
|
8
|
+
stdio: 'ignore',
|
|
9
|
+
});
|
|
10
|
+
child.on('error', rejectPromise);
|
|
11
|
+
child.on('exit', (code, signal) => {
|
|
12
|
+
if (signal) {
|
|
13
|
+
rejectPromise(new Error(`${command} terminated by signal ${signal}`));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (code === 0) {
|
|
17
|
+
resolvePromise();
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
rejectPromise(new Error(`${command} exited with status ${code}`));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export async function notify(kind, message, cwd = process.cwd()) {
|
|
26
|
+
const notifyPath = getNotifyBin(cwd);
|
|
27
|
+
if (!notifyPath) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await runCommand(notifyPath, [message]);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Notifications are a local convenience; the primary run result wins.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function verifyNotification(cwd = process.cwd(), message = NOTIFICATION_CHECK_MESSAGE) {
|
|
38
|
+
const notifyPath = getNotifyBin(cwd);
|
|
39
|
+
if (!notifyPath) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
await runCommand(notifyPath, [message]);
|
|
43
|
+
return notifyPath;
|
|
44
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Neal Adjudicator Inventory
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
This document is the current contract for Neal's adjudicator layer: the shared
|
|
6
|
+
coder/reviewer loop vocabulary, the typed adjudication specs, the read-only
|
|
7
|
+
loops that reuse the same contract, and the blocked adjudicator.
|
|
8
|
+
|
|
9
|
+
Two things are explicit:
|
|
10
|
+
|
|
11
|
+
- Neal uses the terms `adjudicator` and `adjudication spec`.
|
|
12
|
+
- Only recurring coder/reviewer loop mechanics belong to the shared
|
|
13
|
+
adjudicator. Prompt ownership stays under `src/neal/prompts/` (see
|
|
14
|
+
[PROMPT_SPECS.md](PROMPT_SPECS.md)) and transition semantics stay in the
|
|
15
|
+
orchestrator layers.
|
|
16
|
+
|
|
17
|
+
The typed contract for writer-run adjudication specs lives in
|
|
18
|
+
[src/neal/adjudicator/specs.ts](../src/neal/adjudicator/specs.ts).
|
|
19
|
+
The shared loop vocabulary and side-effect contract live in
|
|
20
|
+
[src/neal/adjudicator/contracts.ts](../src/neal/adjudicator/contracts.ts) and
|
|
21
|
+
are attached to each in-scope writer-run adjudication spec as `loopContract`
|
|
22
|
+
metadata. Read-only `neal review` findings use the same contract-only pattern
|
|
23
|
+
with artifacts under `.neal/reviews/<review-id>/`.
|
|
24
|
+
|
|
25
|
+
## Module Map
|
|
26
|
+
|
|
27
|
+
| Module | Owns |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `src/neal/adjudicator/specs.ts` | `AdjudicationSpec` type, `ADJUDICATION_SPECS` registry, transition-signal validation, `getAdjudicationSpec`, `assertAdjudicationTransitionSignal`, `getReviewerCapability`, `ADJUDICATION_ADJACENT_FLOWS` |
|
|
30
|
+
| `src/neal/adjudicator/contracts.ts` | `AdjudicatedLoopContract` vocabulary (loop kind, side-effect policy, allowed/terminal outcomes, round-cap and terminal-artifact metadata), import-time contract validation, `resolveReviewedDraftLoopStep`, `REVIEW_FINDINGS_ADJUDICATED_LOOP_CONTRACT` |
|
|
31
|
+
| `src/neal/adjudicator/planning.ts` | Planning-family context resolution plus reviewer and plan-response round adjudication for `plan_review` and `derived_plan_review` |
|
|
32
|
+
| `src/neal/adjudicator/execute.ts` | Execute-family context resolution, reviewer and coder-response round adjudication, findings synthesis, convergence detection (reopened canonical, stuck window), disposition resolution |
|
|
33
|
+
| `src/neal/adjudicator/final-completion.ts` | Final-completion context resolution plus summary and reviewer round adjudication |
|
|
34
|
+
| `src/neal/adjudicator/blocked-adjudicator.ts` | Read-only triage of eligible blocked states (see Blocked Adjudicator below) |
|
|
35
|
+
| `src/neal/adjudicator/artifacts.ts` | Resolving the active adjudication contract from run state and rendering it into artifacts |
|
|
36
|
+
|
|
37
|
+
## Ownership Split
|
|
38
|
+
|
|
39
|
+
- Shared loop contract (`contracts.ts`):
|
|
40
|
+
- loop kind, side-effect policy, allowed outcomes, terminal outcomes,
|
|
41
|
+
round-cap semantics, and terminal artifact metadata
|
|
42
|
+
- import-time validation that every in-scope adjudication spec declares the
|
|
43
|
+
shared contract fields, plus validation for the contract-only
|
|
44
|
+
review-findings loop
|
|
45
|
+
- descriptive metadata only; runtime phase routing and state mutation stay in
|
|
46
|
+
the transition layer
|
|
47
|
+
- Adjudication spec (`specs.ts`):
|
|
48
|
+
- coder and reviewer prompt-surface references, validated at import time
|
|
49
|
+
against the prompt-spec registry
|
|
50
|
+
- coder and reviewer schema builder / parser / provider output surfaces
|
|
51
|
+
- artifact and context-assembly contract
|
|
52
|
+
- convergence rule
|
|
53
|
+
- validated allowed transition outcomes declared per spec
|
|
54
|
+
- Adjudicator helpers (`planning.ts`, `execute.ts`, `final-completion.ts`):
|
|
55
|
+
- per-family context resolution from run state
|
|
56
|
+
- reviewer/coder round invocation, including inline-context assembly for
|
|
57
|
+
reviewers without repository read access
|
|
58
|
+
- execute-family findings synthesis, reopened-finding and stuck-window
|
|
59
|
+
detection, and disposition resolution
|
|
60
|
+
- Transition layer (`src/neal/orchestrator.ts`, `src/neal/orchestrator/run-loop.ts`,
|
|
61
|
+
`src/neal/orchestrator/phases/*.ts`, `src/neal/orchestrator/transitions.ts`,
|
|
62
|
+
`src/neal/orchestrator/completion.ts`):
|
|
63
|
+
- runnable-phase registries, phase routing, and state mutation
|
|
64
|
+
- live re-check that the resolved outcome is allowed for the active
|
|
65
|
+
adjudication spec (`assertAdjudicationTransitionSignal`)
|
|
66
|
+
- coder-timeout and fresh-session retry handling
|
|
67
|
+
- notification emission
|
|
68
|
+
- split-plan handling
|
|
69
|
+
- interactive blocked-recovery routing
|
|
70
|
+
- adoption/finalization semantics
|
|
71
|
+
- `createdCommits` and final-commit consequences
|
|
72
|
+
- Prompt-spec layer:
|
|
73
|
+
- prompt identity, builders, and role/task ownership under
|
|
74
|
+
`src/neal/prompts/` (see [PROMPT_SPECS.md](PROMPT_SPECS.md))
|
|
75
|
+
|
|
76
|
+
`transitionSignals` in `specs.ts` are validated allowed outcomes, not a
|
|
77
|
+
dispatch table. Import-time validation checks each spec against one explicit
|
|
78
|
+
family-level runtime contract (`FAMILY_RUNTIME_TRANSITION_SIGNALS`) and a
|
|
79
|
+
per-spec required set (`SPEC_RUNTIME_TRANSITION_SIGNALS`), live routing
|
|
80
|
+
re-checks the resolved outcome against the active adjudication spec, and the
|
|
81
|
+
transition layer still maps those outcomes explicitly in runtime code rather
|
|
82
|
+
than dispatching off `transitionSignals` directly.
|
|
83
|
+
|
|
84
|
+
## Transition Signals
|
|
85
|
+
|
|
86
|
+
| Spec | Family | Validated transition signals |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `plan_review` | `plan_review` | `accept_plan`, `request_revision`, `optional_revision`, `block_for_operator` |
|
|
89
|
+
| `derived_plan_review` | `plan_review` | `accept_derived_plan`, `request_revision`, `optional_revision`, `block_for_operator` |
|
|
90
|
+
| `execute_review` | `execute_review` | `accept_scope`, `request_revision`, `optional_revision`, `block_for_operator`, `replace_plan`, `advance_parent` |
|
|
91
|
+
| `final_completion_review` | `final_completion` | `accept_complete`, `continue_execution`, `block_for_operator` |
|
|
92
|
+
|
|
93
|
+
The `AdjudicationTransitionSignal` union also declares `leave_adjacent`, which
|
|
94
|
+
no in-scope spec or family currently uses.
|
|
95
|
+
|
|
96
|
+
## Loop Contracts
|
|
97
|
+
|
|
98
|
+
Every in-scope spec declares allowed outcomes
|
|
99
|
+
`accepted, revise, blocked, failed, cap_reached` with terminal outcomes
|
|
100
|
+
`accepted, blocked, failed, cap_reached`.
|
|
101
|
+
|
|
102
|
+
| Spec | Loop kind | Side effects | Round cap source | Terminal artifact |
|
|
103
|
+
| --- | --- | --- | --- | --- |
|
|
104
|
+
| `plan_review` | `plan` | `plan_doc_only` | `state.maxRounds` (review iterations) | `plan_document` at `state.planDoc` |
|
|
105
|
+
| `derived_plan_review` | `plan` | `plan_doc_only` | `derivedPlan.counters.maxDerivedPlanReviewRounds` (review iterations) | `derived_plan_document` at `state.derivedPlanPath` |
|
|
106
|
+
| `execute_review` | `execute` | `code_changes` | `state.maxRounds` (review iterations) | `implementation_scope` at `state.createdCommits` plus `state.reviewMarkdownPath` |
|
|
107
|
+
| `final_completion_review` | `final_completion` | `code_changes` | `state.finalCompletionContinueExecutionMax` (continued execution) | `final_completion_review` at `FINAL_COMPLETION_REVIEW.md` and `state.finalCompletionReviewVerdict` |
|
|
108
|
+
|
|
109
|
+
## In-Scope Adjudication Specs
|
|
110
|
+
|
|
111
|
+
### `plan_review`
|
|
112
|
+
|
|
113
|
+
- Current loop surfaces: `runCoderPlanRound`, `runPlanReviewerRound`, `runCoderPlanResponseRound(reviewMode=plan)`
|
|
114
|
+
- Artifact under review: top-level Neal-executable plan markdown
|
|
115
|
+
- Prompt surfaces:
|
|
116
|
+
- coder primary: `plan_author.primary` via `buildPlanningPrompt`
|
|
117
|
+
- coder response: `plan_author.response` via `buildCoderPlanResponsePrompt`
|
|
118
|
+
- reviewer: `plan_reviewer.primary` via `buildPlanReviewerPrompt`
|
|
119
|
+
- Output contracts:
|
|
120
|
+
- coder primary: `buildCoderPlanSchema` / `validateCoderPlanPayload`
|
|
121
|
+
- coder response: `buildCoderPlanResponseSchema` / `validateCoderPlanResponsePayload`
|
|
122
|
+
- reviewer: `buildPlanReviewerSchema` / `PlanReviewerPayload`
|
|
123
|
+
- provider surfaces: coder primary/response use `coder_structured_schema`; reviewer uses `structured_advisor_schema`
|
|
124
|
+
- Convergence rule:
|
|
125
|
+
- settle when reviewer returns no open findings and the plan remains structurally valid
|
|
126
|
+
- revise when findings route back to required or optional plan response
|
|
127
|
+
- block when coder returns structured `action=blocked`; round code still renders compatibility markers for downstream state
|
|
128
|
+
- Transition targets: `accept_plan`, `request_revision`, `optional_revision`, `block_for_operator`
|
|
129
|
+
|
|
130
|
+
### `derived_plan_review`
|
|
131
|
+
|
|
132
|
+
- Current loop surfaces: `runPlanReviewerRound(mode=derived-plan)`, `runCoderPlanResponseRound(reviewMode=derived-plan)`
|
|
133
|
+
- Artifact under review: derived replacement plan for one stale execute scope
|
|
134
|
+
- Prompt surfaces:
|
|
135
|
+
- coder response family: `plan_author.response` via `buildCoderPlanResponsePrompt` (`reviewMode=derived-plan`)
|
|
136
|
+
- reviewer: `plan_reviewer.derived_plan` via `buildPlanReviewerPrompt`
|
|
137
|
+
- Output contracts:
|
|
138
|
+
- coder response: `buildCoderPlanResponseSchema` / `validateCoderPlanResponsePayload`
|
|
139
|
+
- reviewer: `buildPlanReviewerSchema` / `PlanReviewerPayload`
|
|
140
|
+
- provider surfaces: coder response uses `coder_structured_schema`; reviewer uses `structured_advisor_schema`
|
|
141
|
+
- Convergence rule:
|
|
142
|
+
- settle when reviewer returns no open findings and the derived plan is safe to adopt
|
|
143
|
+
- revise through the same planning-family response loop as ordinary plan review
|
|
144
|
+
- block when coder returns structured `action=blocked` or the derived plan remains invalid
|
|
145
|
+
- Transition targets: `accept_derived_plan`, `request_revision`, `optional_revision`, `block_for_operator`
|
|
146
|
+
|
|
147
|
+
### `execute_review`
|
|
148
|
+
|
|
149
|
+
- Current loop surfaces: `runCoderScopeRound`, `runReviewerRound`, `runCoderResponseRound`
|
|
150
|
+
- Artifact under review: execute-mode scope diff plus persisted meaningful-progress history for the active parent objective
|
|
151
|
+
- Prompt surfaces:
|
|
152
|
+
- coder primary: `scope_coder.primary` via `buildScopePrompt`
|
|
153
|
+
- coder response: `scope_coder.response` via `buildCoderResponsePrompt`
|
|
154
|
+
- reviewer: `scope_reviewer.primary` via `buildReviewerPrompt`
|
|
155
|
+
- reviewer capability: `scope_reviewer.meaningful_progress` via `buildReviewerPrompt`
|
|
156
|
+
- Output contracts:
|
|
157
|
+
- coder primary: `buildCoderScopeSchema` / `validateCoderScopePayload`
|
|
158
|
+
- coder response: `buildCoderResponseSchema` / `validateCoderResponsePayload`
|
|
159
|
+
- reviewer: `buildReviewerSchema` / `ReviewerPayload`
|
|
160
|
+
- provider surfaces: coder primary/response use `coder_structured_schema`; reviewer uses `structured_advisor_schema`
|
|
161
|
+
- Convergence rule:
|
|
162
|
+
- settle when the reviewer returns no blocking findings and
|
|
163
|
+
`meaningfulProgressAction === accept`, including top-level scopes already
|
|
164
|
+
satisfied by prior accepted work (an eligible already-satisfied
|
|
165
|
+
`advance_parent` is downgraded to `accept` with an explanatory rationale)
|
|
166
|
+
- `advance_parent` is a distinct empty-derived-scope parent-advancement
|
|
167
|
+
signal: it settles by finalizing the parent objective only when
|
|
168
|
+
deterministic classification finds it eligible; an eligible empty-derived
|
|
169
|
+
classification can also upgrade a findings-free `block_for_operator` to
|
|
170
|
+
`advance_parent`
|
|
171
|
+
- revise when findings reopen `coder_response` or `coder_optional_response`
|
|
172
|
+
- block when reviewer returns `block_for_operator`, `replace_plan`, or an
|
|
173
|
+
unsafe `advance_parent`, or coder returns structured `action=blocked` /
|
|
174
|
+
`action=split_plan`; round code still renders compatibility markers for
|
|
175
|
+
downstream state
|
|
176
|
+
- Transition targets: `accept_scope`, `request_revision`, `optional_revision`, `block_for_operator`, `replace_plan`, `advance_parent`
|
|
177
|
+
- Public review note:
|
|
178
|
+
- `neal review` external ranges use the contract-only read-only
|
|
179
|
+
review-findings loop under `.neal/reviews/<review-id>/`, not the execute
|
|
180
|
+
writer-run adjudication family.
|
|
181
|
+
|
|
182
|
+
### `final_completion_review`
|
|
183
|
+
|
|
184
|
+
- Current loop surfaces: `runCoderFinalCompletionSummaryRound`, `runReviewerFinalCompletionRound`
|
|
185
|
+
- Artifact under review: whole-plan completion packet assembled after the terminal execute scope settles, including aggregate review context for `initialBaseCommit..finalCommit` when Neal can read that range
|
|
186
|
+
- Prompt surfaces:
|
|
187
|
+
- coder: `completion_coder.final_completion` via `buildFinalCompletionSummaryPrompt`
|
|
188
|
+
- reviewer: `completion_reviewer.final_completion` via `buildFinalCompletionReviewerPrompt`
|
|
189
|
+
- Output contracts:
|
|
190
|
+
- coder: `buildFinalCompletionSummarySchema` / `parseFinalCompletionSummaryPayload`
|
|
191
|
+
- reviewer: `buildFinalCompletionReviewerSchema` / `parseFinalCompletionReviewerPayload`
|
|
192
|
+
- provider surfaces: coder and reviewer both use `structured_advisor_schema`
|
|
193
|
+
- Convergence rule:
|
|
194
|
+
- settle when reviewer returns one of the three whole-plan decisions
|
|
195
|
+
- no coder-response revision round exists; `continue_execution` hands control
|
|
196
|
+
back to execute transitions rather than mutating the adjudicator loop
|
|
197
|
+
- Transition targets: `accept_complete`, `continue_execution`, `block_for_operator`
|
|
198
|
+
- Aggregate review context:
|
|
199
|
+
- `buildFinalCompletionPacket()` derives the aggregate range from the run's
|
|
200
|
+
`initialBaseCommit` and resolved final commit, then records commit
|
|
201
|
+
subjects, diff stat, and changed files when available
|
|
202
|
+
- if the aggregate range cannot be read, the packet records an explicit
|
|
203
|
+
unavailable reason; the reviewer prompt treats that as a completion-review
|
|
204
|
+
evidence gap rather than proof of correctness
|
|
205
|
+
- the final completion reviewer shares the adversarial falsification,
|
|
206
|
+
verification-skepticism, regression-preservation, and
|
|
207
|
+
pre-existing-failure acceptance-surface doctrine from
|
|
208
|
+
`src/neal/prompts/review-doctrine.ts`, but keeps the final-completion
|
|
209
|
+
verdict schema instead of the ordinary scope-review findings schema
|
|
210
|
+
|
|
211
|
+
## Contract-Only Read-Only Loops
|
|
212
|
+
|
|
213
|
+
### `review`
|
|
214
|
+
|
|
215
|
+
- Current loop surface: `runNealReviewCli` in `src/neal/review-findings/run.ts`
|
|
216
|
+
- Artifact under review: findings draft for a selected local commit range
|
|
217
|
+
- Prompt surfaces (own prompts, not shared with `scope_reviewer` or
|
|
218
|
+
`review-doctrine.ts`): `buildReviewFindingsDraftPrompt`,
|
|
219
|
+
`buildReviewFindingsReviewPrompt` in `src/neal/review-findings/prompts.ts`
|
|
220
|
+
- Output contracts:
|
|
221
|
+
- draft: `ReviewFindingsDraft`
|
|
222
|
+
- review: `ReviewFindingsReview` with verdict `accepted`, `revise`, or `blocked`
|
|
223
|
+
- Convergence rule (resolved through `resolveReviewedDraftLoopStep`):
|
|
224
|
+
- settle when reviewer returns `accepted` with final markdown
|
|
225
|
+
- revise when reviewer returns concrete findings
|
|
226
|
+
- block when reviewer returns `blocked`, provider validation fails, or
|
|
227
|
+
protected writer state changes; `cap_reached` when the configured
|
|
228
|
+
review-round cap is reached without acceptance
|
|
229
|
+
- Loop contract (`REVIEW_FINDINGS_ADJUDICATED_LOOP_CONTRACT` in `contracts.ts`):
|
|
230
|
+
- loop kind: `review`
|
|
231
|
+
- side-effect policy: `read_only`
|
|
232
|
+
- round cap source: `neal.max_review_rounds`
|
|
233
|
+
- terminal artifact: `.neal/reviews/<review-id>/REVIEW_FINAL.md`
|
|
234
|
+
- State ownership:
|
|
235
|
+
- review artifacts live under `.neal/reviews/<review-id>/`
|
|
236
|
+
- review never becomes a writer-run command path; a read-only guard asserts
|
|
237
|
+
that `.neal/current.json`, `.neal/current-queue.json`, and run-local
|
|
238
|
+
`RUN_STATE.json` files are unchanged after the loop
|
|
239
|
+
|
|
240
|
+
## Blocked Adjudicator
|
|
241
|
+
|
|
242
|
+
`src/neal/adjudicator/blocked-adjudicator.ts` (`runBlockedAdjudicator`) is a
|
|
243
|
+
read-only triage step for blocked writer runs. It decides whether a block is an
|
|
244
|
+
autonomously recoverable misunderstanding — resolvable within the existing
|
|
245
|
+
scope with no new authorization, external state, or scope expansion — or a
|
|
246
|
+
genuine wall (`authorization`, `external_precondition`, `impossible_task`)
|
|
247
|
+
that must escalate to a human. It is not an adjudication spec: it makes no
|
|
248
|
+
commits and no file edits, and only returns a `BlockedAdjudicatorVerdict`.
|
|
249
|
+
|
|
250
|
+
Gates, enforced at the recovery chokepoint
|
|
251
|
+
(`src/neal/orchestrator/phases/recovery.ts`); any failed gate falls through to
|
|
252
|
+
generic recovery with no adjudicator invocation:
|
|
253
|
+
|
|
254
|
+
- Source-phase eligibility: `ADJUDICATOR_ELIGIBLE_SOURCE_PHASES` is
|
|
255
|
+
`reviewer_scope`, `reviewer_plan`, `coder_scope`, `coder_response`,
|
|
256
|
+
`coder_optional_response`. Reviewer phases are eligible only for structural
|
|
257
|
+
`review_stuck:` reasons; any coder block on the coder phases is eligible.
|
|
258
|
+
- Per-scope budget: `state.reviewStuckArbiterCount`, bounded by
|
|
259
|
+
`neal.review_stuck_arbiter_max_attempts` (default `1`; `0` disables) and
|
|
260
|
+
reset to `0` at scope boundaries. One invocation consumes one unit whether it
|
|
261
|
+
auto-acts (unattended) or only produces advice (attended, where the verdict
|
|
262
|
+
is surfaced as operator advice and never auto-applied).
|
|
263
|
+
- Anti-thrash window: a block that repeats a `state.recentBlocks` record with
|
|
264
|
+
the same scope identity (`scopeNumber` + `derivedScopeIndex`), the same
|
|
265
|
+
`sourcePhase`, and the same normalized blocker key — with no new evidence —
|
|
266
|
+
short-circuits to a non-recoverable `impossible_task` verdict without
|
|
267
|
+
running an LLM round. The chokepoint is the sole writer of
|
|
268
|
+
`state.recentBlocks`; the adjudicator module's window helpers are pure.
|
|
269
|
+
|
|
270
|
+
Verdict schema constraints (`buildBlockedAdjudicatorSchema` /
|
|
271
|
+
`validateBlockedAdjudicatorVerdictPayload` in `src/neal/agents/schemas.ts`):
|
|
272
|
+
`recoverable=true` is valid only with `triageCategory=misunderstanding` plus a
|
|
273
|
+
non-empty `resolutionDirective`; `recoverable=false` must not pair with
|
|
274
|
+
`misunderstanding`; `rationale` must be non-empty; `targetCanonicalIds` is
|
|
275
|
+
optional and defaults to `[]`. A malformed verdict is rejected so it can never
|
|
276
|
+
drive an autonomous recovery.
|
|
277
|
+
|
|
278
|
+
Prompt and round: `buildBlockedAdjudicatorPrompt` in
|
|
279
|
+
`src/neal/agents/prompts.ts` (a single no-read-safe variant that judges
|
|
280
|
+
entirely from Neal-inlined context) run through `runBlockedAdjudicatorRound`
|
|
281
|
+
in `src/neal/agents/rounds.ts`.
|
|
282
|
+
|
|
283
|
+
## Adjacent Or Non-Adjudicator Flows
|
|
284
|
+
|
|
285
|
+
- `interactive_blocked_recovery` (declared in `ADJUDICATION_ADJACENT_FLOWS`):
|
|
286
|
+
- current entrypoint: `runInteractiveBlockedRecoveryPhase`
|
|
287
|
+
- reason: operator input, coder dispositions, and recovery routing mix rather
|
|
288
|
+
than forming a pure coder/reviewer adjudication loop, so this remains
|
|
289
|
+
transition-layer-owned
|
|
290
|
+
|
|
291
|
+
## Spec Family Mapping
|
|
292
|
+
|
|
293
|
+
- Planning family: `plan_review`, `derived_plan_review`
|
|
294
|
+
- Execute family: `execute_review` (meaningful-progress remains a capability of
|
|
295
|
+
this family, not a separate adjudication spec)
|
|
296
|
+
- Final-completion family: `final_completion_review`
|
|
297
|
+
- Contract-only read-only loops: `review`
|
|
298
|
+
- Adjacent read-only triage: the blocked adjudicator (not a spec)
|
|
299
|
+
|
|
300
|
+
Shared reviewer doctrine does not collapse these families. It is prompt wording
|
|
301
|
+
reused across execute-scope review, plan review, and final completion review;
|
|
302
|
+
schemas, allowed transition outcomes, and artifact/context packets remain
|
|
303
|
+
family-specific.
|
|
304
|
+
|
|
305
|
+
New loop variants should extend the adjudication-spec family or remain
|
|
306
|
+
explicitly adjacent; they should not reintroduce bespoke coder/reviewer loop
|
|
307
|
+
wiring inside `src/neal/orchestrator.ts`. When adding or changing a spec, keep
|
|
308
|
+
`transitionSignals`, the family runtime contract, and the loop contract in
|
|
309
|
+
sync — `specs.ts` and `contracts.ts` validate all three at import time, so
|
|
310
|
+
drift fails fast in tests and at startup.
|