@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
package/docs/storage.md CHANGED
@@ -1,15 +1,15 @@
1
- # Neal Storage Contract
1
+ # Storage contract
2
2
 
3
- Neal uses project-local `.neal/` storage as the source of truth for runs, queues,
3
+ neal uses project-local `.neal/` storage as the source of truth for runs, queues,
4
4
  review artifacts, recovery artifacts, progress, and audit history. User-level
5
5
  storage is reserved for configuration, guidance, caches, logs, and future
6
6
  optional discovery helpers.
7
7
 
8
- This document describes the product-level storage contract. The state transition
9
- and validation details for run and queue ledgers are documented separately in
8
+ The storage contract here is product-level. Run and queue ledger state
9
+ transition and validation details are documented separately in
10
10
  [state-machine.md](state-machine.md).
11
11
 
12
- ## Public Automation Surface
12
+ ## Public automation surface
13
13
 
14
14
  `neal status --json` and `neal status --json --all` are the stable automation
15
15
  contracts. Scripts should prefer them over parsing raw files under `.neal/`.
@@ -37,9 +37,9 @@ know a run id. Important stable fields include:
37
37
  - `providerError` with the latest provider failure or unclassified phase error,
38
38
  including timestamp, provider id, role, label, session handle, normalized
39
39
  error kind, bounded message, and retryability where available.
40
- - `build` with Neal package version, Neal source Git SHA when available, Node
40
+ - `build` with neal package version, neal source Git SHA when available, Node
41
41
  version, source marker (`meta` or `live_fallback`), and the persisted
42
- coder/reviewer agent config.
42
+ planner/coder/reviewer agent config.
43
43
  - `artifacts` with run-local paths for the human narrative, review, progress,
44
44
  support, and related diagnostic artifacts.
45
45
 
@@ -61,30 +61,38 @@ command output.
61
61
  Storage classifications used below are: stable CLI surface, user-facing human
62
62
  artifact, support/debug artifact, internal state, and lock/concurrency artifact.
63
63
  The stable CLI surfaces in this storage contract are `neal status --json` and
64
- `neal status --json --all`; the project-local files are artifacts that support
65
- Neal operations and diagnostics.
64
+ `neal status --json --all`. The project-local files are artifacts that support
65
+ neal operations and diagnostics.
66
66
 
67
- ## Project-Local Layout
67
+ ## Project-local layout
68
68
 
69
69
  | Path | Classification | Contract |
70
70
  | --- | --- | --- |
71
- | `.neal/runs/<run-id>/RUN_STATE.json` | Internal state | Child-run ledger. Neal validates current v1 state on read, but scripts should use `neal status --json` rather than depend on this file as a public API. |
71
+ | `.neal/runs/<run-id>/RUN_STATE.json` | Internal state | Child-run ledger. neal validates current v1 state on read, but scripts should use `neal status --json` rather than depend on this file as a public API. |
72
72
  | `.neal/runs/<run-id>/events.ndjson` | Support/debug artifact | Append-only event log for audit, diagnostics, command output references, and provider/runtime events. Readers should tolerate malformed or partial final lines where implemented. |
73
73
  | `.neal/runs/<run-id>/stderr.log` | Support/debug artifact | Append-only stderr transcript for writer runs. It includes visible narrative lines plus low-level detail such as provider/tool telemetry, command output, reviewer context, and heartbeat diagnostics that may be hidden from the normal terminal stream. |
74
- | `.neal/runs/<run-id>/meta.json` | Support/debug artifact | Run metadata used for diagnostics. New writes include `version: 1`; metadata alone cannot make a run selectable for squash or status. It is not the stable automation surface. |
74
+ | `.neal/runs/<run-id>/meta.json` | Support/debug artifact | Run metadata used for diagnostics. New writes include `version: 1`. Metadata alone cannot make a run selectable for squash or status. It is not the stable automation surface. |
75
75
  | `.neal/runs/<run-id>/PLAN_ORIGINAL.md` | Support/debug artifact | Original plan document backup for `neal plan` runs, written before in-place plan refinement so the pre-refinement input remains inspectable. |
76
- | `.neal/runs/<run-id>/scratch/` | Support/debug artifact | Run-local scratch root for execute-scope and final-completion reviewer verification artifacts such as copied tests, scratch builds, logs, and modified throwaway files. It is not durable state, but it remains project-local `.neal/` data for retention and privacy purposes. |
77
- | `.neal/runs/<run-id>/plan-progress.json` | Internal state | Machine-readable v1 progress artifact used by Neal context and summaries. |
76
+ | `.neal/runs/<run-id>/DERIVED_PLAN_SCOPE_<scope>.md` | User-facing human artifact | Replacement execution plan produced when the coder splits an active scope. |
77
+ | `.neal/runs/<run-id>/SCOPE_<scope>_INVALID_DERIVED_PLAN.md` | Support/debug artifact | Rejected split-plan payload and its validation errors. Written only when the returned replacement plan is invalid. |
78
+ | `.neal/runs/<run-id>/SCOPE_<scope>_DISCARDED.diff` | Support/debug artifact | Scope work preserved before neal resets it while adopting a replacement plan. |
79
+ | `.neal/runs/<run-id>/GATE-<id>.md` | User-facing human artifact | Instructions and resume checks for an active manual gate. |
80
+ | `.neal/runs/<run-id>/scratch/` | Support/debug artifact | Reserved run-local scratch root for execute-scope and final-completion review. Read-only reviewer prompts do not use it. It is not durable state, but it remains project-local `.neal/` data for retention and privacy purposes. |
81
+ | `.neal/runs/<run-id>/plan-progress.json` | Internal state | Machine-readable v1 progress artifact used by neal context and summaries. |
78
82
  | `.neal/runs/<run-id>/PLAN_PROGRESS.md` | User-facing human artifact | Human-readable progress summary for the active plan or scope. |
79
83
  | `.neal/runs/<run-id>/RETROSPECTIVE.md` | User-facing human artifact | Human-readable retrospective or checkpoint summary. Archived variants such as `RETROSPECTIVE-scope-*.md` may also exist. |
84
+ | `.neal/runs/<run-id>/RUN_METRICS.json` | Support/debug artifact | Machine-readable metrics paired with the current retrospective. Archived variants such as `RUN_METRICS-scope-*.json` may also exist. |
80
85
  | `.neal/runs/<run-id>/RUN_NARRATIVE.md` | User-facing human artifact | Human-readable run narrative. It includes a benchmark trace section with status, patch-policy, provider-error, and reproducibility summaries suitable for public result bundles. |
81
- | `.neal/runs/<run-id>/RUN_NARRATIVE.json` | Internal state | Narrative source data that Neal may read to update the human narrative. It is not a public trace artifact. |
86
+ | `.neal/runs/<run-id>/RUN_NARRATIVE.json` | Internal state | Narrative source data that neal may read to update the human narrative. It is not a public trace artifact. |
82
87
  | `.neal/runs/<run-id>/REVIEW.md` | User-facing human artifact | Scope or plan review history and findings. |
88
+ | `.neal/runs/<run-id>/REVIEW-<commit>.md` | User-facing human artifact | Archived review history for an accepted scope commit. |
89
+ | `.neal/runs/<run-id>/REVIEWER_CONTEXT.md` | Support/debug artifact | Bounded reviewer-continuity packet rendered for inspection. |
90
+ | `.neal/runs/<run-id>/REVIEWER_CONTEXT.json` | Support/debug artifact | Machine-readable reviewer-continuity packet without the rendered prompt markdown. |
83
91
  | `.neal/runs/<run-id>/RECOVERY.md` | User-facing human artifact | Interactive blocked-recovery transcript/history for a run. |
84
92
  | `.neal/runs/<run-id>/FINAL_COMPLETION_REVIEW.md` | User-facing human artifact | Whole-plan final completion review. |
85
93
  | `.neal/runs/<run-id>/SQUASH_RESULT.json` | Support/debug artifact | Versioned audit artifact from `neal squash`. It records the squash decision/result but is not the stable automation surface. |
86
94
  | `.neal/runs/<run-id>/QUEUE_LINK.json` | Internal state | Link from a child run back to its parent plan-and-execute queue item. |
87
- | `.neal/queues/<queue-id>/QUEUE_STATE.json` | Internal state | Parent queue ledger. Neal validates it on read, but it is not a public JSON API. |
95
+ | `.neal/queues/<queue-id>/QUEUE_STATE.json` | Internal state | Parent queue ledger. neal validates it on read, but it is not a public JSON API. |
88
96
  | `.neal/queues/<queue-id>/QUEUE_SUMMARY.md` | User-facing human artifact | Human-readable summary for a plan-and-execute queue. |
89
97
  | `.neal/reviews/<review-id>/meta.json` | Support/debug artifact | Review metadata for a read-only `neal review` request. |
90
98
  | `.neal/reviews/<review-id>/events.ndjson` | Support/debug artifact | Append-only review event log. |
@@ -97,19 +105,20 @@ Neal operations and diagnostics.
97
105
  | `.neal/current.json` | Internal state | Default writer-run pointer for commands that need the current run. |
98
106
  | `.neal/current-queue.json` | Internal state | Preferred current plan-and-execute queue pointer. |
99
107
  | `.neal/active-run.lock` | Lock/concurrency artifact | Active writer-run lock. It prevents unrelated writer commands from mutating the same checkout concurrently. |
108
+ | `.neal/NOTES.md` | User-facing human artifact | Optional operator-authored notes included in bounded context packets. neal reads this file but does not create it. |
100
109
 
101
110
  Writer processes remove their own active lock during normal shutdown and on
102
- `SIGINT`/`SIGTERM`. Timeout wrappers should still launch Neal in a process group
111
+ `SIGINT`/`SIGTERM`. Timeout wrappers should still launch neal in a process group
103
112
  and terminate the group, because provider-owned child processes are outside the
104
113
  lock file contract. After a timeout, call `neal status --json --run <run-id>`
105
- when the run id is known; treat the wrapper timeout as the primary result while
106
- using `status`, `health`, and `lock.kind` to record whether Neal's run state is
114
+ when the run id is known. Treat the wrapper timeout as the primary result while
115
+ using `status`, `health`, and `lock.kind` to record whether neal's run state is
107
116
  still running, cleaned up, live, stale, cross-host, or unreadable.
108
117
 
109
- ## Patch Automation Policy
118
+ ## Patch automation policy
110
119
 
111
120
  Default public prediction submission should use only
112
- `patch.defaultSubmissionEligible: true`. Neal sets that value only for clean
121
+ `patch.defaultSubmissionEligible: true`. neal sets that value only for clean
113
122
  completed execute runs with a non-empty readable patch range.
114
123
 
115
124
  When a completed run has a successful squash artifact, the status read model
@@ -124,28 +133,28 @@ runs may still report patch-bearing metadata for diagnostics or private
124
133
  analysis. They are not default-submission eligible, and `patch.reason` explains
125
134
  why.
126
135
 
127
- ## Source Of Truth
136
+ ## Source of truth
128
137
 
129
138
  Project-local `.neal/` remains the source of truth for run, queue, review,
130
139
  recovery, progress, and audit artifacts.
131
140
 
132
141
  Run-local `.neal/runs/<run-id>/RUN_STATE.json` is the only writer-run ledger
133
- path Neal writes. `.neal/current.json` points at the default writer run; it is a
142
+ path neal writes. `.neal/current.json` points at the default writer run. It is a
134
143
  pointer, not a copy of the ledger.
135
144
 
136
145
  There is no required global run index in v1. Future global discovery, if added,
137
146
  must be optional and rebuildable from project-local data. It must not become the
138
147
  only place where run or queue history can be recovered.
139
148
 
140
- ## Ledgers And Pointers
149
+ ## Ledgers and pointers
141
150
 
142
151
  `.neal/runs/<run-id>/RUN_STATE.json` and
143
- `.neal/queues/<queue-id>/QUEUE_STATE.json` are persisted ledgers. Neal validates
152
+ `.neal/queues/<queue-id>/QUEUE_STATE.json` are persisted ledgers. neal validates
144
153
  them on read. Child-run state uses strict current v1 hydration: missing or
145
154
  malformed required child-run fields fail instead of receiving defaults. These
146
155
  ledgers are not public JSON APIs.
147
156
 
148
- Neal does not write, read, migrate, or repair a session mirror. Use
157
+ neal does not write, read, migrate, or repair a session mirror. Use
149
158
  `.neal/current.json` as the default writer-run pointer, `neal status --all` to
150
159
  discover run IDs, and `--run <run-id>` when selecting a specific run for
151
160
  `resume` or `status`.
@@ -154,9 +163,9 @@ Squash discovery requires readable run-local state. Run metadata and progress
154
163
  artifacts can help humans inspect a run, but they cannot make a run selectable
155
164
  for `neal squash` without `.neal/runs/<run-id>/RUN_STATE.json`.
156
165
 
157
- ## Schema Versions And Writes
166
+ ## Schema versions and writes
158
167
 
159
- Neal-owned JSON schemas use `version: 1` where they are read back as durable
168
+ neal-owned JSON schemas use `version: 1` where they are read back as durable
160
169
  state or audit data. That includes child-run state, current run pointers, queue
161
170
  state, current queue pointers, queue child links, progress summaries, run
162
171
  narratives, squash audit results, review artifacts, and the active writer lock.
@@ -172,7 +181,7 @@ files such as state, progress, reviews, and pointers can briefly disagree if a
172
181
  process stops between writes. Append-only event logs remain append-only, and the
173
182
  active writer lock keeps exclusive file creation for acquisition.
174
183
 
175
- ## Retention And Privacy
184
+ ## Retention and privacy
176
185
 
177
186
  `.neal/` may contain prompts, local paths, command output, diffs, review text,
178
187
  provider responses, user guidance diagnostics, reviewer scratch files, copied
@@ -188,7 +197,7 @@ Run and review artifacts are intentionally inspectable, but they should be
188
197
  treated as project data. Apply the same retention and access controls you use
189
198
  for source, logs, and local debugging output.
190
199
 
191
- ## Manual Cleanup
200
+ ## Manual cleanup
192
201
 
193
202
  Manual cleanup is safe only when you no longer need resume, history, diagnostics,
194
203
  or audit data for the item being removed.
@@ -202,6 +211,6 @@ Reasonable manual cleanup options:
202
211
  - remove stale `.neal/current.json` or `.neal/current-queue.json` pointers only
203
212
  after confirming they do not point to work you still intend to resume
204
213
 
205
- Deleting run or queue state removes Neal's resume and history for that run or
214
+ Deleting run or queue state removes neal's resume and history for that run or
206
215
  queue. Prefer keeping the full directory until the related work has been merged,
207
216
  archived, or otherwise recorded somewhere durable.
@@ -6,7 +6,7 @@ prints. When in doubt: `neal check`, then `.neal/runs/<run-id>/stderr.log`.
6
6
  ## Install and first run
7
7
 
8
8
  **Symptom:** install or startup fails on an old Node.
9
- **Cause:** neal requires Node.js >= 22.13 (`engines`); `.nvmrc` pins `24`.
9
+ **Cause:** neal requires Node.js >= 24.18.0 (`engines`). `.nvmrc` pins `24.18.0`.
10
10
  **Fix:** `nvm use` in this repo, or upgrade Node before `npm install -g @navels/neal`.
11
11
 
12
12
  **Symptom:** `pnpm: command not found` when running from source.
@@ -20,13 +20,13 @@ After source changes, `pnpm build` again so the linked command sees `dist/`.
20
20
 
21
21
  **Symptom:** `neal setup` lists a provider with `- runtime not found`.
22
22
  **Cause:** setup detects local runtime surfaces only (the `@openai/codex-sdk`
23
- entrypoint; an SDK-bundled or on-PATH `claude` executable) never auth.
23
+ entrypoint, an SDK-bundled or on-PATH `claude` executable), never auth.
24
24
  **Fix:** install the provider's SDK/CLI, then rerun `neal setup` and `neal check`.
25
25
 
26
26
  ## Provider auth failures (`neal check`)
27
27
 
28
28
  `neal check` sends one small prompt per unique configured provider/model
29
- (interactive TTY only — non-interactive input prints
29
+ (interactive TTY only. Non-interactive input prints
30
30
  `Provider verification skipped: non-interactive input.`). Known failures:
31
31
 
32
32
  **Symptom:** `Claude Code refused bypass-permissions mode while running as root.`
@@ -40,31 +40,34 @@ Claude Code refuses under root/sudo.
40
40
  directory (e.g. run the `codex` CLI there once), then `neal check` again.
41
41
 
42
42
  **Symptom:** `OpenAI Codex could not persist the check session.`
43
- **Fix:** transient; run `neal check` again. If it repeats, redo the provider's
43
+ **Fix:** transient. Run `neal check` again. If it repeats, redo the provider's
44
44
  local setup.
45
45
 
46
46
  **Symptom:** a `not logged in` message from either vendor CLI.
47
- **Cause:** provider auth is provider-owned; neal never collects credentials.
47
+ **Cause:** provider auth is provider-owned, so neal never collects credentials.
48
48
  **Fix:** complete the provider's normal local auth setup (the `codex` / `claude`
49
49
  login flows), then `neal check` again.
50
50
 
51
51
  **Symptom:** `Set providers.openai_compatible.base_url (or OPENAI_COMPATIBLE_BASE_URL) and OPENAI_COMPATIBLE_API_KEY before running Neal.`
52
52
  (or `No model is resolvable for the openai-compatible ... role`).
53
- **Cause:** `openai-compatible` / `generic-agentic` resolve settings config-first
54
- with env fallbacks: `base_url` → `OPENAI_COMPATIBLE_BASE_URL`; the key is read
55
- from the env var named by `api_key_env` (default `OPENAI_COMPATIBLE_API_KEY`);
56
- model from `agent.<role>.model` → `default_model` → `OPENAI_COMPATIBLE_MODEL`.
53
+ **Cause:** `openai-compatible` resolves settings from config first,
54
+ with env fallbacks: `base_url` → `OPENAI_COMPATIBLE_BASE_URL`. The key is read
55
+ from the env var named by `api_key_env` (default `OPENAI_COMPATIBLE_API_KEY`).
56
+ Model from `agent.<role>.model` → `default_model` → `OPENAI_COMPATIBLE_MODEL`.
57
57
  **Fix:** set the missing key. For OpenRouter: `base_url: https://openrouter.ai/api/v1`,
58
58
  `api_key_env: OPENROUTER_API_KEY`, and export `OPENROUTER_API_KEY`. (OpenRouter
59
- wraps upstream 429s in HTTP-200 bodies; neal unwraps and retries them.)
59
+ wraps upstream 429s in HTTP-200 bodies, so neal unwraps and retries them.)
60
60
 
61
61
  ## Runs that won't start
62
62
 
63
- **Symptom:** plan validation errors such as
64
- `Missing required `## Execution Shape` section.` or
65
- ``## Execution Shape` must contain exactly one non-empty line.`
63
+ **Symptom:** plan validation errors such as:
64
+
65
+ ```text
66
+ Missing required `## Execution Shape` section.
67
+ `## Execution Shape` must contain exactly one non-empty line.
68
+ ```
66
69
  **Cause:** every executable plan must declare exactly one of
67
- `executionShape: one_shot | multi_scope | multi_scope_unknown`; `multi_scope`
70
+ `executionShape: one_shot | multi_scope | multi_scope_unknown`. `multi_scope`
68
71
  also requires an `## Execution Queue` with contiguous `### Scope N:` headings,
69
72
  each carrying `- Goal:` / `- Verification:` / `- Success Condition:` bullets.
70
73
  **Fix:** see [plan-format.md](plan-format.md), or let `neal plan` refine the
@@ -72,23 +75,23 @@ document into executable shape.
72
75
 
73
76
  **Symptom:** a missing-config error naming `agent.coder.provider` or
74
77
  `agent.reviewer.provider`.
75
- **Cause:** fresh writer runs require explicit coder and reviewer providers;
76
- built-in defaults are not enough.
78
+ **Cause:** fresh writer runs require explicit coder and reviewer providers.
79
+ Built-in defaults are not enough.
77
80
  **Fix:** `neal setup`, then `neal check`.
78
81
 
79
82
  **Symptom:** `Cannot start neal execute with a dirty worktree:` followed by
80
83
  `git status` lines (queues: `Cannot continue neal run with a dirty worktree:`).
81
- **Cause:** writer admission requires a clean worktree; only the selected plan
84
+ **Cause:** writer admission requires a clean worktree. Only the selected plan
82
85
  document and neal-owned paths (`.neal/`) are exempt. Leftover reviewer scratch
83
86
  (`build_review/`, `scratch/`, …) gets a
84
- `Likely Neal reviewer scratch leakage detected:` diagnostic but still blocks
85
- the paths are not proven neal-owned.
87
+ `Likely Neal reviewer scratch leakage detected:` diagnostic but still blocks,
88
+ because the paths are not proven neal-owned.
86
89
  **Fix:** use `neal resume` for in-progress scope work, or commit, stash, move,
87
90
  or remove the dirty paths and start clean.
88
91
 
89
92
  **Symptom:** a Git precondition error before any provider runs.
90
93
  **Fix:** writer commands require a Git repository with an existing `HEAD`
91
- commit; create the initial baseline commit first.
94
+ commit. Create the initial baseline commit first.
92
95
 
93
96
  ## Stuck or blocked runs
94
97
 
@@ -100,26 +103,26 @@ something it may not resolve alone, and attended runs wait for guidance.
100
103
  accepted in that waiting-for-guidance state.
101
104
 
102
105
  **Symptom:** `effectiveStatus: "waiting_for_manual_gate"`.
103
- **Cause:** the scope reached expected human work; instructions are in the
106
+ **Cause:** the scope reached expected human work. Instructions are in the
104
107
  run-local `GATE-<id>.md` file shown by `neal status`.
105
- **Fix:** do the manual step, then `neal resume --run <run-id>` it re-runs
108
+ **Fix:** do the manual step, then `neal resume --run <run-id>`, which re-runs
106
109
  the gate's checks and resumes the scope when they pass. No `--message` here.
107
110
 
108
111
  **Symptom:** a run seems hung or died silently.
109
112
  **Where to look:** raw detail is persisted even when hidden from the terminal:
110
113
  `.neal/runs/<run-id>/stderr.log` (full transcript) and
111
- `.neal/runs/<run-id>/events.ndjson` (event log; startup-silence retries appear
112
- as `provider.turn_liveness_*` events). On a live TTY, press `v` for the
114
+ `.neal/runs/<run-id>/events.ndjson` (event log, where startup-silence retries
115
+ appear as `provider.turn_liveness_*` events). On a live TTY, press `v` for the
113
116
  low-level detail view. `neal status --json --run <run-id>` carries the latest
114
117
  `providerError` classification.
115
118
 
116
119
  ## Lock issues
117
120
 
118
- The writer lock is `.neal/active-run.lock`; writer processes remove it on
121
+ The writer lock is `.neal/active-run.lock`. Writer processes remove it on
119
122
  normal shutdown and on SIGINT/SIGTERM.
120
123
 
121
124
  **Symptom:** `another Neal writer run is active in this checkout`.
122
- **Fix:** as printed `neal resume --run <run-id>` to continue that run, or
125
+ **Fix:** as printed: `neal resume --run <run-id>` to continue that run, or
123
126
  wait for it to finish.
124
127
 
125
128
  **Symptom:** `another Neal process is already resuming this run` (with
@@ -128,17 +131,17 @@ wait for it to finish.
128
131
 
129
132
  **Symptom:** `stale Neal writer lock found in this checkout` /
130
133
  `no process with that PID is running on this host.`
131
- **Fix:** `neal resume --run <run-id>` clears stale same-host locks itself; if
134
+ **Fix:** `neal resume --run <run-id>` clears stale same-host locks itself. If
132
135
  you're starting different work instead, inspect the run, then remove the lock
133
136
  file the message names.
134
137
 
135
138
  **Symptom:** `Neal writer lock belongs to another host`.
136
- **Fix:** inspect the other host before removing the lock; the message prints
139
+ **Fix:** inspect the other host before removing the lock. The message prints
137
140
  the exact resume command for that checkout.
138
141
 
139
142
  **Symptom:** `could not read the active Neal writer lock`.
140
143
  **Fix:** as printed: inspect the named lock file before starting another
141
- writer run; remove it only after confirming no writer process is active.
144
+ writer run. Remove it only after confirming no writer process is active.
142
145
 
143
146
  ## "It squashed when I didn't want it to"
144
147
 
@@ -1,5 +1,7 @@
1
1
  # Compat fixture: fix `add`
2
2
 
3
+ > Fixture note: this plan describes the pre-fix state and is intentionally not updated after the fix lands.
4
+
3
5
  ## Execution Shape
4
6
 
5
7
  executionShape: one_shot
@@ -1,8 +1,8 @@
1
1
  diff --git a/src/add.js b/src/add.js
2
- index 4125f75..a3e4914 100644
2
+ index 6946b9a..e9fa11d 100644
3
3
  --- a/src/add.js
4
4
  +++ b/src/add.js
5
5
  @@ -1,2 +1,2 @@
6
- // Trivial compat fixture: this addition helper is intentionally buggy.
6
+ // Trivial compat fixture: an arithmetic helper exercised by test/add.test.js.
7
7
  -export const add = (a, b) => a - b;
8
8
  +export const add = (a, b) => a * b;
@@ -1,8 +1,8 @@
1
1
  diff --git a/src/add.js b/src/add.js
2
- index 4125f75..0088c50 100644
2
+ index 6946b9a..ee66238 100644
3
3
  --- a/src/add.js
4
4
  +++ b/src/add.js
5
5
  @@ -1,2 +1,2 @@
6
- // Trivial compat fixture: this addition helper is intentionally buggy.
6
+ // Trivial compat fixture: an arithmetic helper exercised by test/add.test.js.
7
7
  -export const add = (a, b) => a - b;
8
8
  +export const add = (a, b) => a + b;
@@ -1,2 +1,2 @@
1
- // Trivial compat fixture: this addition helper is intentionally buggy.
1
+ // Trivial compat fixture: an arithmetic helper exercised by test/add.test.js.
2
2
  export const add = (a, b) => a - b;
@@ -2,51 +2,47 @@
2
2
  "fixtures": [
3
3
  {
4
4
  "id": "add-edit-verify",
5
- "roles": ["coder", "reviewer"],
5
+ "roles": [
6
+ "coder",
7
+ "reviewer"
8
+ ],
6
9
  "projectDir": "add-edit-verify",
7
10
  "planDoc": "add-edit-verify/PLAN.md",
8
11
  "verifyCommand": "node --test test/add.test.js",
9
- "referenceFix": { "file": "src/add.js", "from": "a - b", "to": "a + b" },
10
- "reviewer": {
11
- "goodDiff": "add-edit-verify/good.diff",
12
- "brokenDiff": "add-edit-verify/broken.diff"
13
- }
14
- },
15
- {
16
- "id": "reverse-grep-edit",
17
- "roles": ["coder", "reviewer"],
18
- "projectDir": "reverse-grep-edit",
19
- "planDoc": "reverse-grep-edit/PLAN.md",
20
- "verifyCommand": "node --test test/strings.test.js",
21
12
  "referenceFix": {
22
- "file": "src/strings.js",
23
- "from": "s.split('').join('')",
24
- "to": "s.split('').reverse().join('')"
13
+ "file": "src/add.js",
14
+ "from": "a - b",
15
+ "to": "a + b"
25
16
  },
26
17
  "reviewer": {
27
- "goodDiff": "reverse-grep-edit/good.diff",
28
- "brokenDiff": "reverse-grep-edit/broken.diff"
18
+ "goodDiff": "add-edit-verify/good.diff",
19
+ "brokenDiff": "add-edit-verify/broken.diff"
29
20
  }
30
21
  },
31
22
  {
32
- "id": "is-even-add-test",
33
- "roles": ["coder", "reviewer"],
34
- "projectDir": "is-even-add-test",
35
- "planDoc": "is-even-add-test/PLAN.md",
36
- "verifyCommand": "node --test test/is-even.test.js",
23
+ "id": "sum-grep-edit",
24
+ "roles": [
25
+ "coder",
26
+ "reviewer"
27
+ ],
28
+ "projectDir": "sum-grep-edit",
29
+ "planDoc": "sum-grep-edit/PLAN.md",
30
+ "verifyCommand": "node --test test/numbers.test.js",
37
31
  "referenceFix": {
38
- "file": "test/is-even.test.js",
39
- "from": "assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');",
40
- "to": "assert.equal(isEven(4), true);"
32
+ "file": "src/numbers.js",
33
+ "from": "(total, n) => total",
34
+ "to": "(total, n) => total + n"
41
35
  },
42
36
  "reviewer": {
43
- "goodDiff": "is-even-add-test/good.diff",
44
- "brokenDiff": "is-even-add-test/broken.diff"
37
+ "goodDiff": "sum-grep-edit/good.diff",
38
+ "brokenDiff": "sum-grep-edit/broken.diff"
45
39
  }
46
40
  },
47
41
  {
48
42
  "id": "plan-greeting",
49
- "roles": ["planner"],
43
+ "roles": [
44
+ "planner"
45
+ ],
50
46
  "projectDir": "plan-greeting",
51
47
  "issuePrompt": "plan-greeting/ISSUE.md",
52
48
  "verifyCommand": "node --test test/greet.test.js",
@@ -0,0 +1,33 @@
1
+ # Compat fixture: fix the number helper that never adds
2
+
3
+ > Fixture note: this plan describes the pre-fix state and is intentionally not updated after the fix lands.
4
+
5
+ ## Execution Shape
6
+
7
+ executionShape: one_shot
8
+
9
+ ## Objective
10
+
11
+ `src/numbers.js` exports several small number helpers. One of them is faulty:
12
+ `sumAll(ns)` is supposed to return the total of the list, but it always returns
13
+ `0`. The `sumAll` test in `test/numbers.test.js` fails because of this.
14
+
15
+ Read `src/numbers.js`, locate the faulty `sumAll` helper, and make the smallest
16
+ complete change so it returns the sum of the list. Do not touch the other
17
+ helpers, which already pass their tests.
18
+
19
+ ## Boundaries
20
+
21
+ Allowed paths:
22
+
23
+ - `src/numbers.js`
24
+
25
+ Forbidden:
26
+
27
+ - Do not edit the test file.
28
+ - Do not change the other (already-correct) helpers.
29
+ - Do not add dependencies or new files.
30
+
31
+ ## Verification
32
+
33
+ Run `node --test test/numbers.test.js`; it must exit `0`.
@@ -0,0 +1,12 @@
1
+ diff --git a/src/numbers.js b/src/numbers.js
2
+ index e6677cc..6a5102c 100644
3
+ --- a/src/numbers.js
4
+ +++ b/src/numbers.js
5
+ @@ -4,6 +4,6 @@ export const double = (n) => n * 2;
6
+
7
+ export const negate = (n) => -n;
8
+
9
+ -export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
10
+ +export const sumAll = (ns) => ns.reduce((total, n) => total + 1, 0);
11
+
12
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,12 @@
1
+ diff --git a/src/numbers.js b/src/numbers.js
2
+ index e6677cc..89f58ba 100644
3
+ --- a/src/numbers.js
4
+ +++ b/src/numbers.js
5
+ @@ -4,6 +4,6 @@ export const double = (n) => n * 2;
6
+
7
+ export const negate = (n) => -n;
8
+
9
+ -export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
10
+ +export const sumAll = (ns) => ns.reduce((total, n) => total + n, 0);
11
+
12
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-sum-grep-edit",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,9 @@
1
+ // Trivial compat fixture: small number helpers. test/numbers.test.js is the
2
+ // source of truth for their behavior.
3
+ export const double = (n) => n * 2;
4
+
5
+ export const negate = (n) => -n;
6
+
7
+ export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
8
+
9
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { countOf, double, negate, sumAll } from '../src/numbers.js';
5
+
6
+ test('double/negate/countOf helpers are correct', () => {
7
+ assert.equal(double(4), 8);
8
+ assert.equal(negate(3), -3);
9
+ assert.equal(countOf([1, 2, 3]), 3);
10
+ });
11
+
12
+ test('sumAll returns the total of the list', () => {
13
+ assert.equal(sumAll([1, 2, 3]), 6);
14
+ assert.equal(sumAll([]), 0);
15
+ });
@@ -59,18 +59,3 @@ guarantee that a live provider-backed run will complete successfully.
59
59
 
60
60
  Normal repository CI validates this example through non-live tests and package
61
61
  checks without running providers.
62
-
63
- ## Manual Canary Workflow
64
-
65
- Maintainers can dispatch the `Example Canary` GitHub Actions workflow when they
66
- want a live provider-backed smoke test for this example. The workflow is
67
- manual-only, uses the `example-canary` environment, and requires the
68
- environment to provide `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or both depending
69
- on the selected coder and reviewer providers.
70
-
71
- The canary builds Neal, writes a temporary `~/.neal/config.yml` from the
72
- selected provider roles, runs the example baseline tests, runs
73
- `node ../../dist/neal/index.js run PLAN.md --no-squash` from this directory,
74
- and uploads only curated markdown support artifacts from the example-local
75
- `.neal/` directory. Scheduled canaries are deferred until cost and flakiness are
76
- understood from intentional manual runs.
@@ -0,0 +1,3 @@
1
+ export function clamp(value: number, lo: number, hi: number): number {
2
+ return Math.max(lo, Math.min(hi, value));
3
+ }
@@ -0,0 +1,9 @@
1
+ diff --git a/src/clamp.ts b/src/clamp.ts
2
+ index 58793c4..c7a624d 100644
3
+ --- a/src/clamp.ts
4
+ +++ b/src/clamp.ts
5
+ @@ -1,3 +1,4 @@
6
+ +/** Restrict `value` to the inclusive range [lo, hi]. */
7
+ export function clamp(value: number, lo: number, hi: number): number {
8
+ return Math.max(lo, Math.min(hi, value));
9
+ }
@@ -0,0 +1,3 @@
1
+ export function greet(name: string): string {
2
+ return 'Hello, ' + name.trim() + '!';
3
+ }
@@ -0,0 +1,13 @@
1
+ diff --git a/src/greet.ts b/src/greet.ts
2
+ index 1c1e070..b768208 100644
3
+ --- a/src/greet.ts
4
+ +++ b/src/greet.ts
5
+ @@ -1,3 +1,7 @@
6
+ +function normalize(name: string): string {
7
+ + return name.trim();
8
+ +}
9
+ +
10
+ export function greet(name: string): string {
11
+ - return 'Hello, ' + name.trim() + '!';
12
+ + return 'Hello, ' + normalize(name) + '!';
13
+ }
@@ -0,0 +1,4 @@
1
+ export async function loadConfig(read: () => Promise<string>): Promise<string> {
2
+ const raw = await read();
3
+ return raw.trim();
4
+ }
@@ -0,0 +1,15 @@
1
+ diff --git a/src/load.ts b/src/load.ts
2
+ index 3da1e73..d7835c0 100644
3
+ --- a/src/load.ts
4
+ +++ b/src/load.ts
5
+ @@ -1,4 +1,8 @@
6
+ export async function loadConfig(read: () => Promise<string>): Promise<string> {
7
+ - const raw = await read();
8
+ - return raw.trim();
9
+ + try {
10
+ + const raw = await read();
11
+ + return raw.trim();
12
+ + } catch {
13
+ + return '';
14
+ + }
15
+ }