@navels/neal 0.2.0 → 0.3.1

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 (114) 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 +230 -38
  7. package/dist/neal/agents/prompts.js +6 -17
  8. package/dist/neal/agents/rounds.js +43 -73
  9. package/dist/neal/agents/schemas.js +47 -13
  10. package/dist/neal/blocked-guidance.js +16 -9
  11. package/dist/neal/cli.js +2 -2
  12. package/dist/neal/commands/check.js +2 -2
  13. package/dist/neal/commands/compat.js +100 -36
  14. package/dist/neal/commands/interactive-activity.js +2 -2
  15. package/dist/neal/commands/recovery-guidance.js +20 -8
  16. package/dist/neal/commands/runtime.js +4 -2
  17. package/dist/neal/config.js +16 -7
  18. package/dist/neal/context/inline-review-context.js +13 -28
  19. package/dist/neal/context/reviewer-context.js +19 -14
  20. package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
  21. package/dist/neal/eval/reviewer-eval.js +155 -0
  22. package/dist/neal/interactive-controls.js +5 -0
  23. package/dist/neal/orchestrator/completion.js +49 -2
  24. package/dist/neal/orchestrator/failures.js +7 -0
  25. package/dist/neal/orchestrator/notifications.js +9 -9
  26. package/dist/neal/orchestrator/phases/planning.js +117 -80
  27. package/dist/neal/orchestrator/phases/recovery.js +151 -115
  28. package/dist/neal/orchestrator/phases/review.js +28 -1
  29. package/dist/neal/orchestrator/transitions.js +7 -1
  30. package/dist/neal/orchestrator.js +3 -0
  31. package/dist/neal/plan-queue.js +29 -1
  32. package/dist/neal/prompts/execute.js +7 -19
  33. package/dist/neal/prompts/planning.js +64 -55
  34. package/dist/neal/prompts/review-doctrine.js +13 -50
  35. package/dist/neal/prompts/specialized.js +7 -17
  36. package/dist/neal/prompts/specs.js +180 -32
  37. package/dist/neal/providers/agent-settings-isolation.js +35 -0
  38. package/dist/neal/providers/anthropic-claude.js +18 -1
  39. package/dist/neal/providers/detection.js +3 -7
  40. package/dist/neal/providers/git-config-isolation.js +83 -0
  41. package/dist/neal/providers/openai-codex.js +25 -3
  42. package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
  43. package/dist/neal/providers/openai-compatible.js +1078 -430
  44. package/dist/neal/providers/openrouter-routing.js +68 -0
  45. package/dist/neal/providers/pricing.js +1 -1
  46. package/dist/neal/providers/rate-card.js +6 -32
  47. package/dist/neal/providers/registry.js +6 -17
  48. package/dist/neal/providers/types.js +12 -0
  49. package/dist/neal/resume-decision.js +7 -6
  50. package/dist/neal/resume-planner.js +9 -0
  51. package/dist/neal/retrospective.js +3 -3
  52. package/dist/neal/review-debt.js +30 -0
  53. package/dist/neal/review-findings/provider.js +17 -29
  54. package/dist/neal/review.js +17 -1
  55. package/dist/neal/state-invariants.js +11 -3
  56. package/dist/neal/state-views.js +27 -5
  57. package/dist/neal/state.js +59 -15
  58. package/dist/neal/status.js +28 -1
  59. package/dist/neal/support.js +3 -3
  60. package/docs/README.md +22 -18
  61. package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
  62. package/docs/architecture.md +79 -83
  63. package/docs/automation.md +11 -11
  64. package/docs/compat.md +91 -61
  65. package/docs/compatible-models.md +145 -121
  66. package/docs/demo.md +15 -5
  67. package/docs/maintenance.md +65 -34
  68. package/docs/plan-format.md +61 -33
  69. package/docs/prompt-evals.md +96 -0
  70. package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
  71. package/docs/providers.md +279 -349
  72. package/docs/release.md +63 -81
  73. package/docs/review-convergence.md +246 -0
  74. package/docs/state-machine.md +61 -59
  75. package/docs/storage.md +40 -31
  76. package/docs/troubleshooting.md +32 -29
  77. package/examples/compat/add-edit-verify/PLAN.md +2 -0
  78. package/examples/compat/add-edit-verify/broken.diff +2 -2
  79. package/examples/compat/add-edit-verify/good.diff +2 -2
  80. package/examples/compat/add-edit-verify/src/add.js +1 -1
  81. package/examples/compat/manifest.json +25 -29
  82. package/examples/compat/sum-grep-edit/PLAN.md +33 -0
  83. package/examples/compat/sum-grep-edit/broken.diff +12 -0
  84. package/examples/compat/sum-grep-edit/good.diff +12 -0
  85. package/examples/compat/sum-grep-edit/package.json +5 -0
  86. package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
  87. package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
  88. package/examples/issue-triage-js/README.md +0 -15
  89. package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
  90. package/examples/reviewer-eval/clean-doc/change.diff +9 -0
  91. package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
  92. package/examples/reviewer-eval/clean-extract/change.diff +13 -0
  93. package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
  94. package/examples/reviewer-eval/dropped-error/change.diff +15 -0
  95. package/examples/reviewer-eval/manifest.json +45 -0
  96. package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
  97. package/examples/reviewer-eval/off-by-one/change.diff +11 -0
  98. package/{config.yml → neal.yml} +1 -1
  99. package/package.json +15 -14
  100. package/dist/neal/providers/generic-agentic.js +0 -1256
  101. package/docs/comparison.md +0 -105
  102. package/docs/issue-pipeline.md +0 -124
  103. package/examples/compat/is-even-add-test/PLAN.md +0 -30
  104. package/examples/compat/is-even-add-test/broken.diff +0 -11
  105. package/examples/compat/is-even-add-test/good.diff +0 -11
  106. package/examples/compat/is-even-add-test/package.json +0 -5
  107. package/examples/compat/is-even-add-test/src/is-even.js +0 -3
  108. package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
  109. package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
  110. package/examples/compat/reverse-grep-edit/broken.diff +0 -12
  111. package/examples/compat/reverse-grep-edit/good.diff +0 -12
  112. package/examples/compat/reverse-grep-edit/package.json +0 -5
  113. package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
  114. package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
@@ -1,29 +1,29 @@
1
- # Neal State Machine
1
+ # State machine
2
2
 
3
3
  The product-level storage layout, artifact classifications, run pointers,
4
4
  retention guidance, and no-global-index decision are documented in
5
- [storage.md](storage.md). This document focuses on the persisted ledgers and the
6
- state invariants Neal enforces when reading and writing them.
5
+ [storage.md](storage.md). What follows covers the persisted ledgers and the
6
+ state invariants neal enforces when reading and writing them.
7
7
 
8
- Neal keeps two persisted ledgers:
8
+ neal keeps two persisted ledgers:
9
9
 
10
10
  - `OrchestrationState` is the child-run ledger. It lives in each run directory as `RUN_STATE.json` and records one plan or execute writer run.
11
11
  - `PlanAndExecuteQueueState` is the parent queue ledger. It lives under `.neal/queues/<queue-id>/QUEUE_STATE.json` and tracks serial `neal run` children, their active stage, child run ids, and queue-level stop status.
12
12
 
13
- These ledgers are related but separate. A queue item points at child run state paths; child runs do not own the queue's status, item ordering, or current queue pointer.
13
+ These ledgers are related but separate. A queue item points at child run state paths. Child runs do not own the queue's status, item ordering, or current queue pointer.
14
14
 
15
- ## Hydration And Validation
15
+ ## Hydration and validation
16
16
 
17
- `src/neal/state.ts` owns v1 child-run hydration. It reads JSON, checks the basic shape, requires the current v1 fields Neal writes, and rejects unknown enum-like strings while hydrating phase references. Missing or malformed required child-run fields fail load instead of receiving invented defaults.
17
+ `src/neal/state.ts` owns v1 child-run hydration. It reads JSON, checks the basic shape, requires the current v1 fields neal writes, and rejects unknown enum-like strings while hydrating phase references. Missing or malformed required child-run fields fail load instead of receiving invented defaults.
18
18
 
19
19
  `src/neal/state-invariants.ts` owns semantic validation after hydration and before save. `assertValidOrchestrationState` is pure: it has no filesystem, git, provider, logger, or clock dependency. The same invariant layer runs when `loadState` returns hydrated state and when `saveState` builds the timestamped next state.
20
20
 
21
21
  Queue state has its own parser and invariants in `src/neal/plan-queue.ts`, centered on `parsePlanAndExecuteQueueState` and related queue item parsing helpers.
22
22
 
23
- ## Schema Versions
23
+ ## Schema versions
24
24
 
25
- Persisted JSON that Neal reads as durable schema is versioned at v1.
26
- `RUN_STATE.json` hydrates through `normalizeStateV1`; `.neal/current.json`,
25
+ Persisted JSON that neal reads as durable schema is versioned at v1.
26
+ `RUN_STATE.json` hydrates through `normalizeStateV1`. `.neal/current.json`,
27
27
  queue state, `.neal/current-queue.json`, queue child links, run narratives,
28
28
  squash audit results, and the active writer lock all have v1 write shapes or
29
29
  parsers.
@@ -32,7 +32,7 @@ Run `meta.json` is support data, not child-run state. Optional context artifacts
32
32
  such as `plan-progress.json` are written as v1 and read defensively so malformed
33
33
  or unsupported support data does not replace the canonical run ledger.
34
34
 
35
- ## State Views And Public Lifecycle
35
+ ## State views and public lifecycle
36
36
 
37
37
  Persisted v1 child-run state remains record-shaped, but most callers should not
38
38
  treat `OrchestrationState` as one large nullable programming model.
@@ -55,7 +55,7 @@ human output can say `waiting_for_guidance`, `paused`, `blocked`, `failed`, or
55
55
  fields such as `effectiveStatus`, `waitingForOperatorGuidance`, and
56
56
  `pendingOperatorGuidance`.
57
57
 
58
- ## Phase And Status
58
+ ## Phase and status
59
59
 
60
60
  Known child-run phases and statuses are centralized in `state-invariants.ts` through `ORCHESTRATION_PHASES`, `ORCHESTRATION_STATUSES`, and their runtime guard helpers. Runnable dispatch is authored in `src/neal/orchestrator/run-loop.ts` as purpose-specific registries for plan, execute, interactive recovery, and execute finalization phases. Those registries are composed into `RUNNABLE_PHASE_REGISTRY` for dispatch and exposed through top-level-mode helpers so tests can assert which phases are runnable for plan and execute runs.
61
61
 
@@ -68,16 +68,18 @@ The enforced phase/status relationship is intentionally small:
68
68
 
69
69
  Read-only `neal review` is not a writer-run mode. It writes isolated findings artifacts under `.neal/reviews/<review-id>/` and does not create `OrchestrationState`.
70
70
 
71
- ## Recovery State
71
+ ## Recovery state
72
72
 
73
73
  Interactive blocked recovery is owned by `interactiveBlockedRecovery` while the active phase is `interactive_blocked_recovery`. The invariant layer validates execute-mode ownership, supported source phases, bounded turn counters, contiguous turn numbers, and disposition result phases.
74
74
 
75
- Every block class coder-blocked signals, reviewer `review_stuck` deadlocks, and
76
- the split-plan invalid-payload block funnels through the single
77
- `enterInteractiveBlockedRecovery` chokepoint, where the generalized blocked
78
- adjudicator triages it (see Site A below). The adjudicator is read-only: it never
79
- grants authorization, expands scope, or waives verification gates. Its verdict
80
- auto-acts only under unattended runs; attended runs surface it as advice and yield.
75
+ Every block class (coder-blocked signals, reviewer `review_stuck` deadlocks, and
76
+ the split-plan invalid-payload block) funnels through the single
77
+ `enterInteractiveBlockedRecovery` chokepoint, where the consultant triages it (see
78
+ Site A below). The consultant is read-only: it never grants authorization, expands
79
+ scope, or waives verification gates. A recoverable verdict acts automatically under
80
+ both run modes. The modes differ only on a non-recoverable verdict, which finalizes
81
+ the run terminally under unattended runs and yields to the operator (carrying the
82
+ verdict as advice) under attended runs.
81
83
 
82
84
  Public resume eligibility is classified by `src/neal/resume-decision.ts` before
83
85
  any recovery mutation. That read-only decision layer combines loaded child-run
@@ -91,7 +93,7 @@ selected actions only after the selected run has been classified as executable.
91
93
  `state-invariants.ts` mirrors the allowed phase sets so changes to recovery
92
94
  behavior are visible in focused tests.
93
95
 
94
- ## Unattended Mode
96
+ ## Unattended mode
95
97
 
96
98
  `--unattended` / `agent.unattended` resolves to a persisted
97
99
  `OrchestrationState.unattended` boolean (default `false`) for both plan and
@@ -99,17 +101,17 @@ execute top-level modes, so a separate `neal resume` process and the `neal run`
99
101
  plan→execute hand-off see it without re-passing a flag. The flag overrides the
100
102
  config key. The resolved value is also threaded into the planner, reviewer,
101
103
  coder, final-completion, and plan-reviewer prompts, where it adds one autonomy
102
- line only when true; with `unattended` false the rendered prompts are
104
+ line only when true. With `unattended` false the rendered prompts are
103
105
  byte-identical to attended output.
104
106
 
105
- Unattended changes only the three structural operator-block sites; it never
107
+ Unattended changes only the three structural operator-block sites. It never
106
108
  weakens verification, authorization, or squash/grading, and never removes
107
109
  `block_for_operator` from any decision surface. Every unattended branch gates on
108
110
  structural state (`state.unattended`, `actionResolution.effectiveAction`,
109
111
  `phase`, `blockedFromPhase`, the bounded auto-resume counter), never on
110
112
  substring-matching assistant or guidance text.
111
113
 
112
- - **Site A execute-mode interactive recovery.** All fresh blocks funnel
114
+ - **Site A: execute-mode interactive recovery.** All fresh blocks funnel
113
115
  through `enterInteractiveBlockedRecovery` (`src/neal/orchestrator/phases/recovery.ts`).
114
116
  Under unattended, while `unattendedAutoResumeCount < UNATTENDED_MAX_AUTO_RESUMES`
115
117
  (a module constant, reconciled so it never pushes past
@@ -119,46 +121,46 @@ substring-matching assistant or guidance text.
119
121
  message uses, increments the persisted counter, and lets the run proceed into
120
122
  the recovery phase. Past the cap (or the `maxTurns` boundary) it runs the
121
123
  shared terminal-fail action instead of waiting.
122
- - **Generalized blocked adjudicator (bounded, both modes).** Inside
124
+ - **The consultant (bounded, both modes).** Inside
123
125
  `enterInteractiveBlockedRecovery`, *before* the generic auto-resume / yield
124
- decision, eligible blocks are triaged by a read-only adjudicator
125
- (`runBlockedAdjudicator`, in `src/neal/adjudicator/blocked-adjudicator.ts`,
126
+ decision, eligible blocks are triaged by the read-only consultant
127
+ (`runConsultant`, in `src/neal/adjudicator/consultant.ts`,
126
128
  running through the same no-write reviewer plumbing the review/final-completion
127
129
  reviewers use, making zero commits and zero file edits). Eligible source phases
128
- (`ADJUDICATOR_ELIGIBLE_SOURCE_PHASES`) are the coder-block phases (`coder_scope`
130
+ (`CONSULTANT_ELIGIBLE_SOURCE_PHASES`) are the coder-block phases (`coder_scope`
129
131
  / `coder_response` / `coder_optional_response`, which also carry the split-plan
130
132
  invalid-payload block) and the reviewer `review_stuck` phases (`reviewer_scope` /
131
- `reviewer_plan`). The adjudicator first applies an anti-thrash guard
133
+ `reviewer_plan`). The consultant first applies an anti-thrash guard
132
134
  (`recentBlocks`, keyed on scope identity + source phase + normalized blocker key +
133
135
  evidence fingerprint): a same-scope repeat with no new evidence short-circuits to
134
136
  `recoverable:false` without an LLM round. Otherwise it returns a verdict
135
- `{ recoverable, triageCategory, resolutionDirective, rationale }`. Under
136
- unattended, a `recoverable` `misunderstanding` verdict with a concrete in-scope
137
- directive enters recovery with that directive injected as the pending turn
138
- (consumed exactly like a human `neal resume --message`); a `recoverable:false`
139
- genuine wall (`authorization` / `external_precondition` / `impossible_task`) runs
140
- the shared terminal-fail action. Under attended, the same gated adjudicator runs
141
- read-only, persists its verdict as `interactiveBlockedRecovery.adjudicatorAdvice`,
142
- and the run yields for the operator (no auto-apply). It is bounded by the counter
143
- `reviewStuckArbiterCount` against the `review_stuck_arbiter_max_attempts` knob
144
- (default `1`, `0` disables) -- a SEPARATE budget that never touches
137
+ `{ recoverable, triageCategory, resolutionDirective, rationale }`. A `recoverable`
138
+ `misunderstanding` verdict with a concrete in-scope directive enters recovery with
139
+ that directive injected as the pending turn (consumed exactly like a human
140
+ `neal resume --message`) under both run modes. The modes differ only on a
141
+ `recoverable:false` genuine blocker (`authorization` / `external_precondition` /
142
+ `impossible_task`): unattended runs the shared terminal-fail action, while attended
143
+ persists the verdict as `interactiveBlockedRecovery.consultantAdvice` and yields for
144
+ the operator. It is bounded by the counter
145
+ `consultantAttemptCount` against the `consultant_max_attempts` knob
146
+ (default `1`, `0` disables). It's a separate budget that never touches
145
147
  `unattendedAutoResumeCount` or `interactiveBlockedRecovery.maxTurns`. Every other
146
148
  case (ineligible source phase, disabled/exhausted cap, turn cap, or any
147
- adjudicator error) falls through to the generic auto-resume / yield path
148
- unchanged, writing neither `recentBlocks` nor `adjudicatorAdvice`.
149
+ consultant error) falls through to the generic auto-resume / yield path
150
+ unchanged, writing neither `recentBlocks` nor `consultantAdvice`.
149
151
  The decisions are auditable from the structured event log via the
150
- `review_stuck_arbiter.{start,verdict,resolved,declined,exhausted}` events, which
152
+ `consultant.{start,verdict,resolved,declined}` events, which
151
153
  carry `scopeNumber`, `sourcePhase`, `blockedReason`, and (on `verdict`/`resolved`)
152
154
  `recoverable`, `triageCategory`, `targetCanonicalIds`, and the post-increment
153
- `reviewStuckArbiterCount`.
154
- - **Sites B and C final-completion review and the top-level plan-review gate.**
155
+ `consultantAttemptCount`.
156
+ - **Sites B and C: final-completion review and the top-level plan-review gate.**
155
157
  These gates block directly (bypassing the recovery chokepoint), and their own
156
- budgets the final-completion continue-execution cap and the
157
- review-round/convergence cap already bounded the autonomous effort. Under
158
+ budgets (the final-completion continue-execution cap and the
159
+ review-round/convergence cap) already bounded the autonomous effort. Under
158
160
  unattended they run the shared terminal-fail action immediately rather than
159
- saving `status:'blocked'`; there is no auto-resume and no synthesized
161
+ saving `status:'blocked'`. There is no auto-resume and no synthesized
160
162
  `pendingPlanReviewGuidance`. Site C edits only the `topLevelMode !== 'execute'`
161
- branch of `finalizeBlockedPlanReviewResponse`; execute-mode derived-plan-review
163
+ branch of `finalizeBlockedPlanReviewResponse`. Execute-mode derived-plan-review
162
164
  blocks (`topLevelMode === 'execute'`) re-enter site A and are handled there.
163
165
 
164
166
  The shared terminal-fail action is `persistUnattendedBlockUnresolvedFailure`
@@ -167,14 +169,14 @@ The shared terminal-fail action is `persistUnattendedBlockUnresolvedFailure`
167
169
  `phase`/`blockedFromPhase` for diagnostics), re-render execution artifacts, write
168
170
  a `failed` checkpoint retrospective, and emit the classified
169
171
  `unattended.block_unresolved` log event (`reason:'unattended_block_unresolved'`
170
- plus the `UnattendedBlockSite` origin) deliberately without `notifyBlocked`,
172
+ plus the `UnattendedBlockSite` origin), deliberately without `notifyBlocked`,
171
173
  which is the attended wait notification. The run exits with writer code `3`, and
172
174
  any produced diff/plan is left unsubmitted as an artifact. There is no top-level
173
- reason field on `OrchestrationState`; the classification rides the log event and
175
+ reason field on `OrchestrationState`. The classification lives in the log event and
174
176
  retrospective. Attended runs are unchanged and still wait for
175
177
  `neal resume --message` at all three sites.
176
178
 
177
- ## Resume Planning
179
+ ## Resume planning
178
180
 
179
181
  New-run initialization and existing-run resume reconciliation are separate.
180
182
  `loadOrInitialize` still provides the command-facing entrypoint, but resume
@@ -198,10 +200,10 @@ normalization, event logging, pointer writes, and execution artifacts.
198
200
  when the shared decision says interactive recovery is waiting for guidance.
199
201
  Pending guidance resumes with plain `neal resume --run <run-id>`. Queue
200
202
  continuation still belongs to `continuePlanAndExecuteQueueFromChildRun` after
201
- the resumed child run finishes; selecting a child run for resume does not
203
+ the resumed child run finishes. Selecting a child run for resume does not
202
204
  advance or repair the parent queue by itself.
203
205
 
204
- ## Derived Plans
206
+ ## Derived plans
205
207
 
206
208
  Derived-plan fields are child-run state because they describe replacement execution for the active child run, not the parent queue. The invariant layer enforces these ownership rules:
207
209
 
@@ -212,23 +214,23 @@ Derived-plan fields are child-run state because they describe replacement execut
212
214
 
213
215
  Derived-plan execution should continue to flow through the existing orchestrator transition helpers before any future shape refactor changes the persisted fields.
214
216
 
215
- ## Final Completion
217
+ ## Final completion
216
218
 
217
- Final-completion review is execute-mode only. The `final_completion_review` phase requires a `finalCompletionSummary`. When a reviewer verdict exists, `finalCompletionResolvedAction` must match the effective action, including the continue-execution cap case where Neal resolves to operator blocking instead of starting another scope.
219
+ Final-completion review is execute-mode only. The `final_completion_review` phase requires a `finalCompletionSummary`. When a reviewer verdict exists, `finalCompletionResolvedAction` must match the effective action, including the continue-execution cap case where neal resolves to operator blocking instead of starting another scope.
218
220
 
219
221
  When final completion review asks to continue execution, the orchestrator may clear summary and verdict fields and reopen `coder_scope`. That reopened running state is valid.
220
222
 
221
- Accepted execute scopes also pass through execute finalization before Neal either opens the next scope or starts final-completion review. The persisted internal runnable cursor for that step is `execute_finalization`, which is separate from the public `neal squash` command. Public `neal squash` is a post-run command with selection, preview, and interactive TTY confirmation before it rewrites history.
223
+ Accepted execute scopes also pass through execute finalization before neal either opens the next scope or starts final-completion review. The persisted internal runnable cursor for that step is `execute_finalization`, which is separate from the public `neal squash` command. Public `neal squash` is a post-run command with selection, preview, and interactive TTY confirmation before it rewrites history.
222
224
 
223
- ## Atomic Writes And Locks
225
+ ## Atomic writes and locks
224
226
 
225
227
  `src/neal/atomic-write.ts` provides per-file atomic replacement helpers. State, current run pointers, queue state, queue pointers, queue links, queue summaries, run metadata, progress artifacts, review/final-completion/recovery/split-plan/retrospective artifacts, run narratives, and default squash audit writes use temp-file-plus-rename writes where in scope.
226
228
 
227
229
  This is not a multi-file transaction. If a process stops between writes, each individual JSON or text file should be either the previous complete file or the next complete file, but related files can briefly disagree. Resume and status commands must continue to tolerate that by resolving run paths and validating loaded state.
228
230
 
229
- The active-run lock is separate. `src/neal/run-lock.ts` uses exclusive creation for `.neal/active-run.lock`; that acquisition path should not be converted to atomic rename because the exclusive-create behavior is the mutual exclusion mechanism.
231
+ The active-run lock is separate. `src/neal/run-lock.ts` uses exclusive creation for `.neal/active-run.lock`. That acquisition path should not be converted to atomic rename because the exclusive-create behavior is the mutual exclusion mechanism.
230
232
 
231
- ## Change Checklist
233
+ ## Change checklist
232
234
 
233
235
  When adding a child-run phase:
234
236
 
@@ -253,7 +255,7 @@ When adding a queue-state field:
253
255
  - Parse and validate it in `parsePlanAndExecuteQueueState` or `parsePlanAndExecuteQueueItem`.
254
256
  - Include it in queue summary or current queue pointer output only when it is operator-facing or needed for resume.
255
257
 
256
- ## Future Refactor Path
258
+ ## Future refactor path
257
259
 
258
260
  Do not jump directly from the current record-shaped `OrchestrationState` to a
259
261
  persisted discriminated union. The lower-risk path now in place is to keep v1
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;