@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.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +527 -0
  3. package/SECURITY.md +91 -0
  4. package/config.yml +104 -0
  5. package/dist/neal/activity-footer.js +177 -0
  6. package/dist/neal/activity-reporting.js +1 -0
  7. package/dist/neal/adjudicator/artifacts.js +58 -0
  8. package/dist/neal/adjudicator/blocked-adjudicator.js +223 -0
  9. package/dist/neal/adjudicator/contracts.js +139 -0
  10. package/dist/neal/adjudicator/execute.js +611 -0
  11. package/dist/neal/adjudicator/final-completion.js +104 -0
  12. package/dist/neal/adjudicator/planning.js +145 -0
  13. package/dist/neal/adjudicator/specs.js +453 -0
  14. package/dist/neal/agents/prompts.js +120 -0
  15. package/dist/neal/agents/rounds.js +706 -0
  16. package/dist/neal/agents/schemas.js +832 -0
  17. package/dist/neal/agents/structured-coder.js +82 -0
  18. package/dist/neal/agents/structured-json.js +528 -0
  19. package/dist/neal/agents.js +4 -0
  20. package/dist/neal/atomic-write.js +18 -0
  21. package/dist/neal/blocked-guidance.js +406 -0
  22. package/dist/neal/cli.js +471 -0
  23. package/dist/neal/commands/check.js +401 -0
  24. package/dist/neal/commands/compat.js +807 -0
  25. package/dist/neal/commands/interactive-activity.js +57 -0
  26. package/dist/neal/commands/new-run.js +79 -0
  27. package/dist/neal/commands/plan-and-execute.js +44 -0
  28. package/dist/neal/commands/recovery-guidance.js +217 -0
  29. package/dist/neal/commands/resume-run.js +395 -0
  30. package/dist/neal/commands/review.js +21 -0
  31. package/dist/neal/commands/runtime.js +557 -0
  32. package/dist/neal/commands/setup.js +596 -0
  33. package/dist/neal/commands/squash.js +113 -0
  34. package/dist/neal/commands/status.js +33 -0
  35. package/dist/neal/commands/writer-exit-codes.js +42 -0
  36. package/dist/neal/commit-message.js +17 -0
  37. package/dist/neal/config.js +432 -0
  38. package/dist/neal/context/artifacts.js +140 -0
  39. package/dist/neal/context/context.js +324 -0
  40. package/dist/neal/context/inline-review-context.js +131 -0
  41. package/dist/neal/context/reviewer-context.js +166 -0
  42. package/dist/neal/context/shared.js +117 -0
  43. package/dist/neal/context/types.js +1 -0
  44. package/dist/neal/diagnostic.js +208 -0
  45. package/dist/neal/execute-finalization.js +5 -0
  46. package/dist/neal/final-completion-review.js +188 -0
  47. package/dist/neal/final-completion.js +229 -0
  48. package/dist/neal/git.js +339 -0
  49. package/dist/neal/index.js +135 -0
  50. package/dist/neal/interactive-controls.js +85 -0
  51. package/dist/neal/logger.js +102 -0
  52. package/dist/neal/manual-gates.js +121 -0
  53. package/dist/neal/orchestrator/artifacts.js +70 -0
  54. package/dist/neal/orchestrator/completion.js +531 -0
  55. package/dist/neal/orchestrator/failures.js +31 -0
  56. package/dist/neal/orchestrator/notifications.js +175 -0
  57. package/dist/neal/orchestrator/phases/coder.js +516 -0
  58. package/dist/neal/orchestrator/phases/planning.js +540 -0
  59. package/dist/neal/orchestrator/phases/recovery.js +798 -0
  60. package/dist/neal/orchestrator/phases/review.js +136 -0
  61. package/dist/neal/orchestrator/phases/shared.js +279 -0
  62. package/dist/neal/orchestrator/run-loop.js +113 -0
  63. package/dist/neal/orchestrator/split-plan.js +235 -0
  64. package/dist/neal/orchestrator/transitions.js +309 -0
  65. package/dist/neal/orchestrator.js +215 -0
  66. package/dist/neal/phase-display.js +27 -0
  67. package/dist/neal/plan-doc.js +154 -0
  68. package/dist/neal/plan-queue.js +1092 -0
  69. package/dist/neal/plan-refinement.js +39 -0
  70. package/dist/neal/plan-validation.js +525 -0
  71. package/dist/neal/progress.js +237 -0
  72. package/dist/neal/prompts/assert-builder.js +13 -0
  73. package/dist/neal/prompts/execute.js +290 -0
  74. package/dist/neal/prompts/guidance.js +70 -0
  75. package/dist/neal/prompts/planning.js +313 -0
  76. package/dist/neal/prompts/review-doctrine.js +142 -0
  77. package/dist/neal/prompts/shared.js +101 -0
  78. package/dist/neal/prompts/specialized.js +212 -0
  79. package/dist/neal/prompts/specs.js +572 -0
  80. package/dist/neal/providers/anthropic-claude.js +1599 -0
  81. package/dist/neal/providers/detection.js +139 -0
  82. package/dist/neal/providers/generic-agentic-tools.js +586 -0
  83. package/dist/neal/providers/generic-agentic.js +1238 -0
  84. package/dist/neal/providers/liveness.js +151 -0
  85. package/dist/neal/providers/openai-codex.js +1014 -0
  86. package/dist/neal/providers/openai-compatible.js +654 -0
  87. package/dist/neal/providers/registry.js +389 -0
  88. package/dist/neal/providers/telemetry.js +208 -0
  89. package/dist/neal/providers/types.js +21 -0
  90. package/dist/neal/recovery-artifacts.js +50 -0
  91. package/dist/neal/resume-decision.js +220 -0
  92. package/dist/neal/resume-planner.js +265 -0
  93. package/dist/neal/retrospective.js +391 -0
  94. package/dist/neal/review-debt.js +18 -0
  95. package/dist/neal/review-findings/artifacts.js +173 -0
  96. package/dist/neal/review-findings/prompts.js +172 -0
  97. package/dist/neal/review-findings/provider.js +330 -0
  98. package/dist/neal/review-findings/run.js +373 -0
  99. package/dist/neal/review-findings/types.js +1 -0
  100. package/dist/neal/review-mode.js +67 -0
  101. package/dist/neal/review.js +137 -0
  102. package/dist/neal/run-lock.js +334 -0
  103. package/dist/neal/run-metrics.js +355 -0
  104. package/dist/neal/run-narrative-types.js +1 -0
  105. package/dist/neal/run-narrative.js +1374 -0
  106. package/dist/neal/run-registry.js +218 -0
  107. package/dist/neal/run-status.js +25 -0
  108. package/dist/neal/scopes.js +451 -0
  109. package/dist/neal/sensitive-text.js +8 -0
  110. package/dist/neal/squash-message.js +379 -0
  111. package/dist/neal/squash.js +591 -0
  112. package/dist/neal/state-invariants.js +496 -0
  113. package/dist/neal/state-views.js +344 -0
  114. package/dist/neal/state.js +887 -0
  115. package/dist/neal/status-footer.js +258 -0
  116. package/dist/neal/status.js +1260 -0
  117. package/dist/neal/storage-paths.js +57 -0
  118. package/dist/neal/support.js +58 -0
  119. package/dist/neal/terminal-narrator.js +435 -0
  120. package/dist/neal/types.js +1 -0
  121. package/dist/neal/verification-events.js +81 -0
  122. package/dist/neal/version.js +37 -0
  123. package/dist/neal/worktree-status.js +137 -0
  124. package/dist/notifier.js +44 -0
  125. package/docs/ADJUDICATOR_INVENTORY.md +310 -0
  126. package/docs/PROMPT_SPECS.md +266 -0
  127. package/docs/README.md +22 -0
  128. package/docs/architecture.md +113 -0
  129. package/docs/assets/neal-execution-flow.png +0 -0
  130. package/docs/automation.md +65 -0
  131. package/docs/comparison.md +105 -0
  132. package/docs/compat.md +269 -0
  133. package/docs/compatible-models.md +135 -0
  134. package/docs/demo.md +55 -0
  135. package/docs/maintenance.md +64 -0
  136. package/docs/plan-format.md +213 -0
  137. package/docs/providers.md +751 -0
  138. package/docs/release.md +147 -0
  139. package/docs/state-machine.md +266 -0
  140. package/docs/storage.md +207 -0
  141. package/docs/troubleshooting.md +152 -0
  142. package/examples/compat/add-edit-verify/PLAN.md +29 -0
  143. package/examples/compat/add-edit-verify/broken.diff +8 -0
  144. package/examples/compat/add-edit-verify/good.diff +8 -0
  145. package/examples/compat/add-edit-verify/package.json +5 -0
  146. package/examples/compat/add-edit-verify/src/add.js +2 -0
  147. package/examples/compat/add-edit-verify/test/add.test.js +9 -0
  148. package/examples/compat/is-even-add-test/PLAN.md +30 -0
  149. package/examples/compat/is-even-add-test/broken.diff +11 -0
  150. package/examples/compat/is-even-add-test/good.diff +11 -0
  151. package/examples/compat/is-even-add-test/package.json +5 -0
  152. package/examples/compat/is-even-add-test/src/is-even.js +3 -0
  153. package/examples/compat/is-even-add-test/test/is-even.test.js +9 -0
  154. package/examples/compat/manifest.json +60 -0
  155. package/examples/compat/plan-greeting/ISSUE.md +25 -0
  156. package/examples/compat/plan-greeting/package.json +5 -0
  157. package/examples/compat/plan-greeting/src/greet.js +2 -0
  158. package/examples/compat/plan-greeting/test/greet.test.js +8 -0
  159. package/examples/compat/reverse-grep-edit/PLAN.md +32 -0
  160. package/examples/compat/reverse-grep-edit/broken.diff +12 -0
  161. package/examples/compat/reverse-grep-edit/good.diff +12 -0
  162. package/examples/compat/reverse-grep-edit/package.json +5 -0
  163. package/examples/compat/reverse-grep-edit/src/strings.js +10 -0
  164. package/examples/compat/reverse-grep-edit/test/strings.test.js +15 -0
  165. package/examples/issue-triage-js/PLAN.md +83 -0
  166. package/examples/issue-triage-js/README.md +76 -0
  167. package/examples/issue-triage-js/package.json +9 -0
  168. package/examples/issue-triage-js/src/issue-triage.js +87 -0
  169. package/examples/issue-triage-js/test/issue-triage.test.js +107 -0
  170. package/package.json +70 -0
package/docs/compat.md ADDED
@@ -0,0 +1,269 @@
1
+ # `neal compat` — model compatibility smoke test
2
+
3
+ `neal compat` answers one question per model: **can this OpenAI-compatible model
4
+ drive neal's loop at all** — as a coder, a reviewer, or a planner? It is a
5
+ **compatibility smoke test, not a performance benchmark**. There is no
6
+ resolve-rate, no score, no ranking, and no real SWE-bench repo involved. The
7
+ fixtures are deliberately trivial; any working agent should pass them. A failure
8
+ therefore means something is **fundamentally wrong** with the model's ability to
9
+ operate neal's contract — not that a task was hard.
10
+
11
+ `neal compat` is fully self-contained in the neal repo. The only things an
12
+ operator needs to qualify a model are the neal checkout plus an API key — no
13
+ external benchmark assets, no dataset download, no remote runner.
14
+
15
+ ## Compatibility, not skill
16
+
17
+ - **Measured:** whether the model can follow neal's contract end to end (valid
18
+ tool calls + structured output, no max-step loops, no unresolved operator
19
+ blocks, a clean finalization) **and** solve a trivial bundled fixture.
20
+ - **Not measured:** skill. No resolve-rate, no scores, no rankings, no real
21
+ SWE-bench repos — trivial fixtures only.
22
+ - **Outcome:** a binary **PASS / FAIL per (model, role)**, with a structural
23
+ failure mode recorded for every FAIL.
24
+
25
+ ## How it layers with `neal check`
26
+
27
+ `neal check` and `neal compat` are two distinct gates:
28
+
29
+ - **`neal check`** validates the effective config and confirms each configured
30
+ role's provider can answer one small structured probe
31
+ (`provider_check_payload`). It proves *connectivity and basic protocol*, not
32
+ that the model can complete a full neal run. When the resolved **coder or
33
+ reviewer** role uses a non-native (generic-agentic / openai-compatible)
34
+ provider, `neal check` prints a one-line pointer: *"This is a generic-agentic
35
+ model - run `neal compat` to confirm it can drive the full loop."* Native
36
+ adapters (`openai-codex`, `anthropic-claude`) do not get the pointer. The
37
+ planner inherits the coder provider by default, so the coder check covers the
38
+ common case; an explicitly configured non-native planner paired with native
39
+ coder/reviewer is not separately flagged (run `neal compat --role planner`
40
+ to qualify it).
41
+ - **`neal compat`** runs the same structured pre-filter first (it reuses
42
+ `check`'s `verifyConfiguredProviders`), then drives the model through complete
43
+ neal runs against the bundled fixtures and produces a PASS/FAIL matrix.
44
+
45
+ So: `neal check` is the cheap connectivity gate; `neal compat` is the full-loop
46
+ qualification gate that `check` points generic-agentic users toward.
47
+
48
+ ## Usage
49
+
50
+ ```bash
51
+ neal compat [--model <slug>] [--role coder|reviewer|planner|all] [--reference openai-codex|anthropic-claude|generic-agentic:<slug>] [--json]
52
+ ```
53
+
54
+ - `--role` (default `all`) selects which role(s) to test: `coder`, `reviewer`,
55
+ `planner`, or `all`.
56
+ - `--model <slug>` runs the slug on the **`generic-agentic`** provider in the
57
+ candidate role (provider forced to `generic-agentic`, any configured effort
58
+ dropped, so the slug drives a clean OpenRouter call). When omitted, the
59
+ candidate role uses its configured provider/model unchanged.
60
+ - `--reference <id>` names the provider for the **non-candidate** roles so a
61
+ FAIL is attributable to the candidate in the tested role, not to a weak (and
62
+ possibly flaky) partner. Accepted forms: a native provider id — `openai-codex`
63
+ (the default when omitted while `--model` is set) or `anthropic-claude` — or
64
+ `generic-agentic:<openrouter-model>` to run the reference roles on an
65
+ OpenRouter model. A bare model slug is rejected. Native reference roles run on
66
+ that adapter's default model (`gpt-5.5` for `openai-codex`, `claude-opus-4-8`
67
+ for `anthropic-claude`). Prefer a native reference for whitelist
68
+ qualification; see [compatible-models.md](compatible-models.md) for why
69
+ OpenRouter references proved unreliable as qualification partners. When
70
+ **neither** `--model` nor `--reference` is given, every role stays on its
71
+ configured provider/model (pure pass-through).
72
+ - `--json` prints the stable machine-readable matrix instead of the human table
73
+ (schema below).
74
+
75
+ The command resolves its base config via the same writer-provider resolver the
76
+ other commands use, then derives a candidate config by cloning it and routing the
77
+ tested role onto `generic-agentic` (when `--model` is set) and the non-candidate
78
+ roles onto the native reference adapter (when either flag is set). It runs
79
+ against **throwaway git copies** of the bundled fixtures and **never mutates** a
80
+ committed fixture under `examples/compat/`. The process exits non-zero when the
81
+ overall result is FAIL.
82
+
83
+ Example (Phase B operational usage, OpenRouter paid slug with a native reference):
84
+
85
+ ```bash
86
+ neal compat --model deepseek/deepseek-chat --role all --reference openai-codex --json
87
+ ```
88
+
89
+ ## Bundled fixtures (`examples/compat/`)
90
+
91
+ Each fixture is a tiny self-contained project. `examples/compat/manifest.json`
92
+ is the single data source that makes the command data-driven. The shipped
93
+ fixtures are:
94
+
95
+ | Fixture | Roles | Contract surface |
96
+ | --- | --- | --- |
97
+ | `add-edit-verify` | coder, reviewer | edit + verify + commit |
98
+ | `reverse-grep-edit` | coder, reviewer | read/grep-then-edit |
99
+ | `is-even-add-test` | coder, reviewer | add-a-test |
100
+ | `plan-greeting` | planner | emit a `one_shot` plan |
101
+
102
+ Each coder fixture bundles buggy source, a test that fails against the buggy
103
+ source, and a `PLAN.md` declaring `executionShape: one_shot` that describes the
104
+ smallest complete fix. The reviewer fixtures reuse the same projects, each paired
105
+ with two pre-made unified diffs (`good.diff`, `broken.diff`). The planner fixture
106
+ provides an issue-statement seed (`ISSUE.md`) and no reviewer diffs.
107
+
108
+ `examples/issue-triage-js` remains an optional tier-2 "real-ish" check above the
109
+ trivial gate; `neal compat` does not require it.
110
+
111
+ ### Manifest schema
112
+
113
+ ```jsonc
114
+ {
115
+ "fixtures": [
116
+ {
117
+ "id": "add-edit-verify",
118
+ "roles": ["coder", "reviewer"], // which roles use this fixture
119
+ "projectDir": "add-edit-verify", // relative to examples/compat/
120
+ "planDoc": "add-edit-verify/PLAN.md", // coder run plan (one_shot)
121
+ "verifyCommand": "node --test test/add.test.js", // coder PASS check
122
+ "referenceFix": { "file": "src/add.js", "from": "a - b", "to": "a + b" },
123
+ "reviewer": {
124
+ "goodDiff": "add-edit-verify/good.diff", // reviewer must ACCEPT
125
+ "brokenDiff": "add-edit-verify/broken.diff" // reviewer must FLAG/REJECT
126
+ }
127
+ }
128
+ ]
129
+ }
130
+ ```
131
+
132
+ A planner fixture sets `"roles": ["planner"]` and provides `"issuePrompt"`
133
+ instead of `reviewer`. Any fixture carrying the `reviewer` role must also define
134
+ `verifyCommand`: reviewer fixtures reuse the coder project's command as the
135
+ deterministic good/broken oracle — `goodDiff` must make it pass, `brokenDiff`
136
+ must make it fail. The `referenceFix` is used only by the structural fixture
137
+ self-test (`test/compat-fixtures.test.ts`), never by a model run.
138
+
139
+ ## Definition of PASS (per model, per role)
140
+
141
+ Across **every** fixture for the role, the model must (1) **complete the run
142
+ cleanly** — the run reaches `status: 'done'` (not `'failed'` / `'blocked'`), does
143
+ not emit an `unattended.block_unresolved` event, and does not exceed its
144
+ step/round budget — **and** (2) produce the **role-correct result**:
145
+
146
+ - **coder:** `finalState.status === 'done'` **and** the fixture's
147
+ `verifyCommand` exits `0` in the throwaway worktree (the failing test now
148
+ passes).
149
+ - **reviewer:** the review-findings loop converges (`outcome === 'accepted'`) for
150
+ **both** diffs, and the converged findings discriminate them: **zero** blocking
151
+ findings for the good diff and **≥1** blocking finding (a `ReviewFindingItem`
152
+ with `severity === 'blocking'`) for the broken diff. `outcome` alone is not
153
+ sufficient — it is `'accepted'` whenever the findings artifact converges,
154
+ including a zero-finding artifact — so the good/broken verdict is scored on
155
+ `draft.findings` severities.
156
+ - **planner** (secondary): the emitted plan document validates via
157
+ `validatePlanDocument` as a schema-conformant `one_shot` plan.
158
+
159
+ Any single fundamental failure on any fixture for the role → **FAIL** for that
160
+ role, with the mode recorded.
161
+
162
+ `neal compat` forces **unattended mode** on every run so a model is never
163
+ penalized for a halt that a human operator would simply have resumed.
164
+
165
+ ## Failure-mode taxonomy
166
+
167
+ Each FAIL records a mode derived from **structural run state** — `finalState.status`,
168
+ emitted run events, `ReviewFindingsOutcome`, and `validatePlanDocument` — never
169
+ from substring-matching model prose. When more than one applies, the earliest in
170
+ this list (most specific cause first) is recorded:
171
+
172
+ - `protocol` — the `verifyConfiguredProviders` pre-filter threw for the candidate
173
+ (it could not emit one valid `provider_check_payload`); fixtures were skipped.
174
+ - `provider_failed` — the run failed with a model-attributable provider error
175
+ event (transport/auth/transient/other) rather than a clean completion, **except**
176
+ a provider error whose `errorKind` is a structured-output kind, which is recorded
177
+ as `structured_output` (below). This is also the bucket for a **writer** run that
178
+ ends `status: 'failed'` without a more specific structural signal — including
179
+ step/round-budget exhaustion, which the current runtime does not surface to compat
180
+ as a distinct cap event, so writer step-cap exhaustion is reported here rather than
181
+ as `max_step_loop`.
182
+ - `block_unresolved` — the run emitted `unattended.block_unresolved` (the model
183
+ escalated to an operator block unattended mode could not resolve within budget),
184
+ or the review loop's outcome was `'blocked'`.
185
+ - `max_step_loop` — the **reviewer** loop's outcome was `'cap_reached'` (the
186
+ review-findings convergence cap was hit). Writer (coder/planner) step-cap
187
+ exhaustion is not separately distinguishable under the current runtime and is
188
+ classified as `provider_failed` (above).
189
+ - `wrong_or_empty_output` — coder: the run reached `done` but `verifyCommand`
190
+ exited non-zero (or the diff was empty); reviewer: the verdict was wrong
191
+ (accepted the broken diff with no blocking finding, or flagged the good diff
192
+ with a blocking finding); planner: the emitted plan was not a schema-conformant
193
+ `one_shot` plan.
194
+ - `structured_output` — the model could not produce or honor **schema-enforced**
195
+ JSON. This covers a reviewer/planner round whose payload failed schema validation
196
+ (surfaced as the corresponding round error) **and** a coder/reviewer provider
197
+ error whose `errorKind` is `structured_output_invalid` or
198
+ `structured_output_missing` — e.g. an OpenRouter (generic-agentic) gateway that
199
+ rejects the `type: 'json_schema'` request (HTTP 400) or returns a missing/invalid
200
+ object. This is distinct from `provider_failed` (transport/auth/other transport
201
+ errors) and from `wrong_or_empty_output` (a syntactically valid verdict/output
202
+ that was substantively wrong). Both the coder (`classifyWriterFailure`) and
203
+ reviewer (`classifyReviewerThrownFailure`) paths attribute these kinds here.
204
+ - `finalization_error` — the run failed during finalization/artifact writing, or
205
+ the run produced no final document where one was expected.
206
+
207
+ When no structural signal is conclusive, `provider_failed` is recorded with the
208
+ terminal error message attached.
209
+
210
+ ## Output
211
+
212
+ By default `neal compat` prints a human-readable matrix: one row per
213
+ role × fixture cell (with the reviewer `good`/`broken` diff kind), a per-role
214
+ roll-up, and an overall verdict.
215
+
216
+ ### `--json` schema
217
+
218
+ `--json` prints a single JSON object. This shape is stable and feeds the Phase B
219
+ whitelist (`docs/compatible-models.md`):
220
+
221
+ ```jsonc
222
+ {
223
+ "schemaVersion": 1,
224
+ "model": "deepseek/deepseek-chat", // candidate --model, or null when omitted
225
+ "reference": null, // native provider id (openai-codex | anthropic-claude), or null when omitted
226
+ "role": "all", // coder | reviewer | planner | all
227
+ "candidateProviders": { // provider each role used as the candidate: "generic-agentic" per role when --model is set, else the configured provider
228
+ "coder": "generic-agentic",
229
+ "reviewer": "generic-agentic",
230
+ "planner": "generic-agentic"
231
+ },
232
+ "cells": [
233
+ {
234
+ "role": "coder", // coder | reviewer | planner
235
+ "fixtureId": "add-edit-verify", // manifest id; "provider:<role>" for a protocol pre-filter FAIL
236
+ "diffKind": null, // "good" | "broken" for reviewer cells, else null
237
+ "pass": true,
238
+ "failureMode": null, // one of the taxonomy modes when pass=false, else null
239
+ "detail": null // human-readable explanation when pass=false, else null
240
+ }
241
+ ],
242
+ "roles": [
243
+ {
244
+ "role": "coder",
245
+ "pass": true,
246
+ "cellCount": 3, // cells for this role
247
+ "passCount": 3 // cells that passed
248
+ }
249
+ ],
250
+ "overallPass": true // true iff every targeted role's roll-up passed
251
+ }
252
+ ```
253
+
254
+ A role roll-up is PASS iff it has at least one cell and every one of its cells
255
+ passed. `overallPass` is true iff every targeted role roll-up passed. The process
256
+ exits non-zero when `overallPass` is false.
257
+
258
+ ## Cost
259
+
260
+ Cheap by construction: trivial fixtures and cheap models. If qualifying a model
261
+ gets expensive, the fixture set is too big.
262
+
263
+ ## Phase B — building the whitelist
264
+
265
+ With a provider configured (OpenRouter **paid** slugs only, never `:free`) and a
266
+ known-good `--reference`, run `neal compat --model <slug> --role all --json` for
267
+ each candidate and fill the dated [`compatible-models.md`](compatible-models.md)
268
+ matrix from the JSON output. Date and version the matrix — model behavior drifts,
269
+ so re-run on version bumps.
@@ -0,0 +1,135 @@
1
+ # Compatible models
2
+
3
+ > **Last updated:** 2026-06-18 · **Reference:** `openai-codex` (gpt-5.5) · **Pool:** 79 OpenRouter models
4
+ >
5
+ > The whitelist from running [`neal compat`](compat.md) across an OpenRouter candidate
6
+ > pool. It records **compatibility, not skill**: a PASS means the model can drive neal's
7
+ > loop on the trivial bundled fixtures in that role — it says nothing about how well the
8
+ > model performs on real work. Choose among the PASSes by your own cost/quality needs.
9
+
10
+ ## How to read this
11
+
12
+ - A PASS means the model drove neal under **real schema-enforced structured output**
13
+ (`type: 'json_schema'`), with `openai-codex` (gpt-5.5) running the non-tested roles as a
14
+ known-good partner. Role cells = passed/total fixtures (coder 3, reviewer 6, planner 1).
15
+ - A FAIL is **attributable** to the model via its [failure mode](compat.md#failure-mode-taxonomy)
16
+ (e.g. `structured_output`, not a generic `provider_failed`).
17
+ - **Single-run results are noisy at the margin.** Models that fail only 1–2 cells, or fail
18
+ with `provider error`, often flip on a re-run — treat the "borderline" table as *re-run
19
+ candidates*, not verdicts.
20
+ - Native adapters (`openai-codex`, `anthropic-claude`) are supported by construction and
21
+ need no compat run.
22
+
23
+ ## ✅ Compatible (25) — verified across all roles
24
+
25
+ Sorted cheapest first ($/Mtok in·out, from the live OpenRouter catalog).
26
+
27
+ | Model | Coder | Reviewer | Planner | $/Mtok | ctx |
28
+ |---|---|---|---|---|---|
29
+ | `google/gemma-4-26b-a4b-it` | 3/3 | 6/6 | 1/1 | 0.06·0.33 | 262k |
30
+ | `inclusionai/ling-2.6-1t` | 3/3 | 6/6 | 1/1 | 0.07·0.62 | 262k |
31
+ | `deepseek/deepseek-v4-flash` | 3/3 | 6/6 | 1/1 | 0.09·0.18 | 1048k |
32
+ | `nvidia/nemotron-3-super-120b-a12b` | 3/3 | 6/6 | 1/1 | 0.09·0.45 | 1000k |
33
+ | `google/gemma-4-31b-it` | 3/3 | 6/6 | 1/1 | 0.12·0.35 | 262k |
34
+ | `qwen/qwen3.6-35b-a3b` | 3/3 | 6/6 | 1/1 | 0.14·1.00 | 262k |
35
+ | `minimax/minimax-m2.5` | 3/3 | 6/6 | 1/1 | 0.15·0.90 | 204k |
36
+ | `deepseek/deepseek-chat-v3.1` | 3/3 | 6/6 | 1/1 | 0.21·0.79 | 163k |
37
+ | `deepseek/deepseek-v3.2` | 3/3 | 6/6 | 1/1 | 0.23·0.34 | 131k |
38
+ | `bytedance-seed/seed-2.0-lite` | 3/3 | 6/6 | 1/1 | 0.25·2.00 | 262k |
39
+ | `google/gemini-3.1-flash-lite` | 3/3 | 6/6 | 1/1 | 0.25·1.50 | 1048k |
40
+ | `minimax/minimax-m2.7` | 3/3 | 6/6 | 1/1 | 0.25·1.00 | 204k |
41
+ | `google/gemini-3.1-flash-lite-preview` | 3/3 | 6/6 | 1/1 | 0.25·1.50 | 1048k |
42
+ | `minimax/minimax-m2` | 3/3 | 6/6 | 1/1 | 0.26·1.00 | 204k |
43
+ | `deepseek/deepseek-v3.1-terminus` | 3/3 | 6/6 | 1/1 | 0.27·0.95 | 163k |
44
+ | `minimax/minimax-m2.1` | 3/3 | 6/6 | 1/1 | 0.29·0.95 | 204k |
45
+ | `minimax/minimax-m3` | 3/3 | 6/6 | 1/1 | 0.30·1.20 | 1048k |
46
+ | `qwen/qwen3.7-plus` | 3/3 | 6/6 | 1/1 | 0.32·1.28 | 1000k |
47
+ | `z-ai/glm-5` | 3/3 | 6/6 | 1/1 | 0.60·1.92 | 202k |
48
+ | `z-ai/glm-5.1` | 3/3 | 6/6 | 1/1 | 0.98·3.08 | 202k |
49
+ | `qwen/qwen3.6-max-preview` | 3/3 | 6/6 | 1/1 | 1.04·6.24 | 262k |
50
+ | `x-ai/grok-4.20` | 3/3 | 6/6 | 1/1 | 1.25·2.50 | 2000k |
51
+ | `x-ai/grok-4.3` | 3/3 | 6/6 | 1/1 | 1.25·2.50 | 1000k |
52
+ | `google/gemini-3.5-flash` | 3/3 | 6/6 | 1/1 | 1.50·9.00 | 1048k |
53
+ | `google/gemini-3.1-pro-preview-customtools` | 3/3 | 6/6 | 1/1 | 2.00·12.00 | 1048k |
54
+
55
+ ## ⚠️ Borderline — re-run candidates (failed 1–2 cells, or transient)
56
+
57
+ These drove most of neal but tripped on one or two cells. `provider error` rows are
58
+ transient (re-run likely flips them); `bad review output` / `can't emit schema JSON` are
59
+ single-run signals that often flip too. Re-run before excluding.
60
+
61
+ | Model | Coder | Reviewer | Planner | Tripped on |
62
+ |---|---|---|---|---|
63
+ | `mistralai/devstral-2512` | 3/3 | 6/6 | 0/1 | planner: provider error |
64
+ | `nvidia/nemotron-3-ultra-550b-a55b` | 3/3 | 5/6 | 1/1 | reviewer: provider error |
65
+ | `x-ai/grok-build-0.1` | 3/3 | 5/6 | 1/1 | reviewer: provider error |
66
+ | `nvidia/nemotron-3-nano-30b-a3b` | 3/3 | 5/6 | 1/1 | reviewer: bad review output |
67
+ | `deepseek/deepseek-v3.2-exp` | 3/3 | 5/6 | 1/1 | reviewer: bad review output |
68
+ | `mistralai/mistral-medium-3-5` | 3/3 | 5/6 | 1/1 | reviewer: bad review output |
69
+ | `qwen/qwen3.5-plus-20260420` | 3/3 | 5/6 | 1/1 | reviewer: bad review output |
70
+ | `qwen/qwen3.7-max` | 3/3 | 5/6 | 1/1 | reviewer: bad review output |
71
+ | `qwen/qwen3.6-flash` | 3/3 | 5/6 | 1/1 | reviewer: can't emit schema JSON |
72
+ | `deepseek/deepseek-v4-pro` | 3/3 | 4/6 | 1/1 | reviewer: can't emit schema JSON |
73
+ | `meta-llama/llama-4-maverick` | 1/3 | 6/6 | 1/1 | coder: left block unresolved |
74
+ | `moonshotai/kimi-k2.5` | 2/3 | 6/6 | 1/1 | coder: can't emit schema JSON |
75
+ | `stepfun/step-3.7-flash` | 2/3 | 6/6 | 1/1 | coder: provider error |
76
+ | `inclusionai/ring-2.6-1t` | 2/3 | 5/6 | 1/1 | coder+reviewer: schema JSON |
77
+ | `z-ai/glm-5.2` | 2/3 | 5/6 | 1/1 | coder+reviewer: schema JSON |
78
+ | `inclusionai/ling-2.6-flash` | 0/3 | 4/6 | 1/1 | coder+reviewer: provider error |
79
+ | `meta-llama/llama-3.3-70b-instruct` | 0/3 | 3/6 | 0/1 | provider error |
80
+ | `bytedance-seed/seed-1.6` | 0/3 | 5/6 | 1/1 | coder: can't emit schema JSON |
81
+ | `bytedance-seed/seed-1.6-flash` | 0/3 | 5/6 | 0/1 | coder: can't emit schema JSON |
82
+
83
+ > Notable: the flagship `deepseek-v4-pro` FAILs (reviewer schema), but the cheaper
84
+ > `deepseek-v4-flash` / `v3.2` PASS cleanly — and v4-pro has flaked stochastically before,
85
+ > so it's a re-run candidate, not a hard FAIL.
86
+
87
+ ## ❌ Incompatible — fail the provider handshake (no tool-use / unparsable JSON)
88
+
89
+ These can't drive neal's agentic loop on OpenRouter at all (`protocol` failure on the
90
+ provider check — 0/1 every role): mostly small models and ones whose OR endpoints don't
91
+ support tool use.
92
+
93
+ `amazon/nova-{micro,lite,2-lite,pro,premier}-v1`, `bytedance-seed/seed-2.0-mini`,
94
+ `cohere/{command-a,command-r-08-2024,command-r-plus-08-2024,command-r7b-12-2024}`,
95
+ `meta-llama/{llama-3.1-8b,llama-3.2-1b,llama-3.2-3b}-instruct`, `meta-llama/llama-4-scout`,
96
+ `microsoft/{phi-4,phi-4-mini-instruct,wizardlm-2-8x22b}`, `minimax/minimax-m2-her`,
97
+ `mistralai/{ministral-3b,ministral-8b,ministral-14b}-2512`, `mistralai/mistral-small-2603`,
98
+ `moonshotai/{kimi-k2,kimi-k2-0905,kimi-k2-thinking,kimi-k2.6,kimi-k2.7-code}`,
99
+ `nvidia/llama-3.3-nemotron-super-49b-v1.5`, `openai/{gpt-oss-120b,gpt-oss-20b,gpt-oss-safeguard-20b}`,
100
+ `stepfun/step-3.5-flash`, `x-ai/grok-4.20-multi-agent`, `z-ai/{glm-4.7-flash,glm-5-turbo}`.
101
+
102
+ ## Native adapters (supported, no compat run required)
103
+
104
+ | Provider id | Coder | Reviewer | Planner |
105
+ | --- | --- | --- | --- |
106
+ | `openai-codex` | supported | supported | supported |
107
+ | `anthropic-claude` | supported | supported | supported |
108
+
109
+ ## Reproducing / extending
110
+
111
+ ```
112
+ neal compat --model <slug> --role all --reference openai-codex --json
113
+ ```
114
+
115
+ `neal compat` also accepts an OpenRouter model as the reference
116
+ (`--reference generic-agentic:<slug>`) to escape codex capacity limits, but
117
+ **no OpenRouter model has validated as a drop-in reference** — two were tried on
118
+ 2026-06-18 and both failed:
119
+
120
+ - `deepseek/deepseek-v4-flash` (cheap/fast) reproduced only **9** of the codex-ref
121
+ **25** PASSes (13 dropped, confirmed in isolation — not a rate-limit artifact):
122
+ too weak, it emits coder diffs the candidate-reviewer flags and blocks good
123
+ candidate work as partner-reviewer.
124
+ - `google/gemini-3.1-pro-preview-customtools` (strong/expensive) was worse in
125
+ practice: of the 25 codex-PASS models only **1** reproduced, and it **wedged
126
+ under load** (hung reference calls at 6-wide, timeouts/no-result on the
127
+ PASS-class models at 3-wide). A reference that can't run wide defeats the
128
+ throughput purpose of going off-codex in the first place.
129
+
130
+ **Conclusion:** `openai-codex` (gpt-5.5) is the authoritative reference. To extend
131
+ the whitelist beyond this pool, run additional candidates on codex-ref directly
132
+ (throttle to about three concurrent `neal compat` invocations for codex
133
+ capacity); the OpenRouter-reference path is not a viable shortcut today.
134
+
135
+ Paid slugs only — never `:free`. Re-run when a slug's backing model version changes.
package/docs/demo.md ADDED
@@ -0,0 +1,55 @@
1
+ # Demo Recording
2
+
3
+ Use `asciinema` for public terminal demos when a recording is useful. It is an
4
+ optional recording tool, not a package dependency.
5
+
6
+ Record from a disposable repository or throwaway worktree, preferably under
7
+ `/tmp`, so the demo does not expose real project history, paths, or artifacts.
8
+
9
+ ## Suggested Script
10
+
11
+ Create a small demo plan in the disposable repository:
12
+
13
+ ```bash
14
+ throwaway="$(mktemp -d /tmp/neal-demo.XXXXXX)"
15
+ recording_dir="$(mktemp -d /tmp/neal-demo-recording.XXXXXX)"
16
+ recording="$recording_dir/neal-demo.cast"
17
+ cd "$throwaway"
18
+ git init
19
+ printf '# Neal demo target\n' > README.md
20
+ git add README.md
21
+ git -c user.name='Neal Demo' -c user.email='neal-demo@example.invalid' commit -m 'Initial demo baseline'
22
+ mkdir -p tmp
23
+ $EDITOR tmp/DEMO_PLAN.md
24
+ ```
25
+
26
+ Configure providers and run `neal check` before recording, or record those
27
+ steps only if the output is safe to share. Use current Neal commands while
28
+ recording:
29
+
30
+ ```bash
31
+ asciinema rec "$recording"
32
+ neal run tmp/DEMO_PLAN.md
33
+ neal status
34
+ exit
35
+ ```
36
+
37
+ Keep the recording output outside the demo repository so it does not appear as
38
+ unrelated dirty work while Neal starts the writer run.
39
+
40
+ If you specifically want to show recovery from a run that is waiting for
41
+ operator guidance, record that as a separate segment with the selected run id:
42
+
43
+ ```bash
44
+ neal resume --run <run-id> --message "Continue with the smallest safe follow-up."
45
+ ```
46
+
47
+ ## Scrubbing Checklist
48
+
49
+ Before sharing a recording, review the terminal output for local paths, private
50
+ project names, provider output, credentials, tokens, provider environment
51
+ variables, and `.neal/` artifact contents. Re-record from a clean disposable
52
+ repository if the output exposes anything sensitive.
53
+
54
+ This repository should commit recording instructions only, not `.cast` files or
55
+ generated terminal transcripts.
@@ -0,0 +1,64 @@
1
+ # Maintenance: dependencies & versioning
2
+
3
+ neal's behavior is defined in large part by the agent SDKs it drives, so
4
+ dependency updates are a first-class concern, not routine hygiene. This document
5
+ is the policy.
6
+
7
+ ## Two dependency tiers
8
+
9
+ | Tier | Packages | Pinning | Update posture |
10
+ | --- | --- | --- | --- |
11
+ | **Behavior-defining** | `@openai/codex-sdk`, `@anthropic-ai/claude-agent-sdk`, `@anthropic-ai/sdk`, `ai`, `@ai-sdk/openai-compatible`, `zod` | **exact** (enforced by `scripts/validate-release.mjs`) | **deliberate** — qualify before adopting; never auto-merge |
12
+ | **Utility** | `dotenv`, `yaml`, `@types/node`, `tsx`, `typescript` | caret OK | routine — auto-merge on green CI after a soak period |
13
+
14
+ The asymmetry exists because an agentic-SDK bump can change tool-calling,
15
+ structured output, or sandbox behavior — i.e. break neal's loop **without**
16
+ breaking compilation. Utility deps cannot.
17
+
18
+ ## The update flow
19
+
20
+ 1. **Detect.** [Renovate](../renovate.json) opens PRs weekly: utility deps grouped
21
+ (auto-merge), agentic SDKs one-per-PR (labelled `agentic-sdk` /
22
+ `needs-qualification`, auto-merge **off**, pinned exact).
23
+ 2. **Verify (automatic).** CI (`.github/workflows/ci.yml`) runs typecheck + unit
24
+ tests + package verification — catches **API-shape / contract** breaks.
25
+ 3. **Verify (behavioral).**
26
+ - The **AI-SDK tier** (`ai`, `@ai-sdk/openai-compatible`, `zod`) is exercised
27
+ automatically by the live smoke (`.github/workflows/smoke.yml`): a real
28
+ `neal compat` run against a cheap OpenRouter model through `generic-agentic`.
29
+ - The **native tier** (`@openai/codex-sdk`, `@anthropic-ai/*`) cannot be smoked
30
+ in CI (subscription auth isn't available there), so qualify it **locally**
31
+ before merging:
32
+ ```
33
+ neal compat --model <a-known-good-slug> --role all
34
+ ```
35
+ and ideally a one-fixture end-to-end run.
36
+ 4. **Adopt.** Merge, bump neal's version, add a CHANGELOG entry noting the bump +
37
+ any behavior change, and cut a release via the existing workflow.
38
+
39
+ ## Versioning
40
+
41
+ SemVer, treated as a **CLI/application** contract:
42
+
43
+ - **MAJOR** — breaking CLI/behavior: a command or flag removed, a plan-format or
44
+ config break, a provider-contract change users depend on.
45
+ - **MINOR** — new backward-compatible surface: a new command, flag, or provider.
46
+ - **PATCH** — bug fixes, no new surface.
47
+
48
+ Mapping a dependency bump to neal's version:
49
+
50
+ - Invisible to users (pin hygiene, internal) → **patch**.
51
+ - Adds a capability neal now exposes → **minor**.
52
+ - Changes observable behavior in a breaking way → **major** (or minor pre-1.0).
53
+
54
+ **Pre-1.0:** while on `0.x`, minor may break (the honest "still evolving"
55
+ contract). Move to **`1.0.0`** deliberately, once the CLI surface, plan format,
56
+ and provider contracts are stable enough to promise compatibility — not before.
57
+
58
+ ## Changelog discipline
59
+
60
+ Every release updates [CHANGELOG.md](../CHANGELOG.md) (Keep a Changelog format).
61
+ Agentic-SDK bumps get an explicit line with the from→to versions and a one-line
62
+ behavior note ("re-qualified with `neal compat`, no behavior change", or the
63
+ specific change observed). The dependency churn *is* the risk surface, so it is
64
+ recorded, not buried.