@navels/neal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +527 -0
- package/SECURITY.md +91 -0
- package/config.yml +104 -0
- package/dist/neal/activity-footer.js +177 -0
- package/dist/neal/activity-reporting.js +1 -0
- package/dist/neal/adjudicator/artifacts.js +58 -0
- package/dist/neal/adjudicator/blocked-adjudicator.js +223 -0
- package/dist/neal/adjudicator/contracts.js +139 -0
- package/dist/neal/adjudicator/execute.js +611 -0
- package/dist/neal/adjudicator/final-completion.js +104 -0
- package/dist/neal/adjudicator/planning.js +145 -0
- package/dist/neal/adjudicator/specs.js +453 -0
- package/dist/neal/agents/prompts.js +120 -0
- package/dist/neal/agents/rounds.js +706 -0
- package/dist/neal/agents/schemas.js +832 -0
- package/dist/neal/agents/structured-coder.js +82 -0
- package/dist/neal/agents/structured-json.js +528 -0
- package/dist/neal/agents.js +4 -0
- package/dist/neal/atomic-write.js +18 -0
- package/dist/neal/blocked-guidance.js +406 -0
- package/dist/neal/cli.js +471 -0
- package/dist/neal/commands/check.js +401 -0
- package/dist/neal/commands/compat.js +807 -0
- package/dist/neal/commands/interactive-activity.js +57 -0
- package/dist/neal/commands/new-run.js +79 -0
- package/dist/neal/commands/plan-and-execute.js +44 -0
- package/dist/neal/commands/recovery-guidance.js +217 -0
- package/dist/neal/commands/resume-run.js +395 -0
- package/dist/neal/commands/review.js +21 -0
- package/dist/neal/commands/runtime.js +557 -0
- package/dist/neal/commands/setup.js +596 -0
- package/dist/neal/commands/squash.js +113 -0
- package/dist/neal/commands/status.js +33 -0
- package/dist/neal/commands/writer-exit-codes.js +42 -0
- package/dist/neal/commit-message.js +17 -0
- package/dist/neal/config.js +432 -0
- package/dist/neal/context/artifacts.js +140 -0
- package/dist/neal/context/context.js +324 -0
- package/dist/neal/context/inline-review-context.js +131 -0
- package/dist/neal/context/reviewer-context.js +166 -0
- package/dist/neal/context/shared.js +117 -0
- package/dist/neal/context/types.js +1 -0
- package/dist/neal/diagnostic.js +208 -0
- package/dist/neal/execute-finalization.js +5 -0
- package/dist/neal/final-completion-review.js +188 -0
- package/dist/neal/final-completion.js +229 -0
- package/dist/neal/git.js +339 -0
- package/dist/neal/index.js +135 -0
- package/dist/neal/interactive-controls.js +85 -0
- package/dist/neal/logger.js +102 -0
- package/dist/neal/manual-gates.js +121 -0
- package/dist/neal/orchestrator/artifacts.js +70 -0
- package/dist/neal/orchestrator/completion.js +531 -0
- package/dist/neal/orchestrator/failures.js +31 -0
- package/dist/neal/orchestrator/notifications.js +175 -0
- package/dist/neal/orchestrator/phases/coder.js +516 -0
- package/dist/neal/orchestrator/phases/planning.js +540 -0
- package/dist/neal/orchestrator/phases/recovery.js +798 -0
- package/dist/neal/orchestrator/phases/review.js +136 -0
- package/dist/neal/orchestrator/phases/shared.js +279 -0
- package/dist/neal/orchestrator/run-loop.js +113 -0
- package/dist/neal/orchestrator/split-plan.js +235 -0
- package/dist/neal/orchestrator/transitions.js +309 -0
- package/dist/neal/orchestrator.js +215 -0
- package/dist/neal/phase-display.js +27 -0
- package/dist/neal/plan-doc.js +154 -0
- package/dist/neal/plan-queue.js +1092 -0
- package/dist/neal/plan-refinement.js +39 -0
- package/dist/neal/plan-validation.js +525 -0
- package/dist/neal/progress.js +237 -0
- package/dist/neal/prompts/assert-builder.js +13 -0
- package/dist/neal/prompts/execute.js +290 -0
- package/dist/neal/prompts/guidance.js +70 -0
- package/dist/neal/prompts/planning.js +313 -0
- package/dist/neal/prompts/review-doctrine.js +142 -0
- package/dist/neal/prompts/shared.js +101 -0
- package/dist/neal/prompts/specialized.js +212 -0
- package/dist/neal/prompts/specs.js +572 -0
- package/dist/neal/providers/anthropic-claude.js +1599 -0
- package/dist/neal/providers/detection.js +139 -0
- package/dist/neal/providers/generic-agentic-tools.js +586 -0
- package/dist/neal/providers/generic-agentic.js +1238 -0
- package/dist/neal/providers/liveness.js +151 -0
- package/dist/neal/providers/openai-codex.js +1014 -0
- package/dist/neal/providers/openai-compatible.js +654 -0
- package/dist/neal/providers/registry.js +389 -0
- package/dist/neal/providers/telemetry.js +208 -0
- package/dist/neal/providers/types.js +21 -0
- package/dist/neal/recovery-artifacts.js +50 -0
- package/dist/neal/resume-decision.js +220 -0
- package/dist/neal/resume-planner.js +265 -0
- package/dist/neal/retrospective.js +391 -0
- package/dist/neal/review-debt.js +18 -0
- package/dist/neal/review-findings/artifacts.js +173 -0
- package/dist/neal/review-findings/prompts.js +172 -0
- package/dist/neal/review-findings/provider.js +330 -0
- package/dist/neal/review-findings/run.js +373 -0
- package/dist/neal/review-findings/types.js +1 -0
- package/dist/neal/review-mode.js +67 -0
- package/dist/neal/review.js +137 -0
- package/dist/neal/run-lock.js +334 -0
- package/dist/neal/run-metrics.js +355 -0
- package/dist/neal/run-narrative-types.js +1 -0
- package/dist/neal/run-narrative.js +1374 -0
- package/dist/neal/run-registry.js +218 -0
- package/dist/neal/run-status.js +25 -0
- package/dist/neal/scopes.js +451 -0
- package/dist/neal/sensitive-text.js +8 -0
- package/dist/neal/squash-message.js +379 -0
- package/dist/neal/squash.js +591 -0
- package/dist/neal/state-invariants.js +496 -0
- package/dist/neal/state-views.js +344 -0
- package/dist/neal/state.js +887 -0
- package/dist/neal/status-footer.js +258 -0
- package/dist/neal/status.js +1260 -0
- package/dist/neal/storage-paths.js +57 -0
- package/dist/neal/support.js +58 -0
- package/dist/neal/terminal-narrator.js +435 -0
- package/dist/neal/types.js +1 -0
- package/dist/neal/verification-events.js +81 -0
- package/dist/neal/version.js +37 -0
- package/dist/neal/worktree-status.js +137 -0
- package/dist/notifier.js +44 -0
- package/docs/ADJUDICATOR_INVENTORY.md +310 -0
- package/docs/PROMPT_SPECS.md +266 -0
- package/docs/README.md +22 -0
- package/docs/architecture.md +113 -0
- package/docs/assets/neal-execution-flow.png +0 -0
- package/docs/automation.md +65 -0
- package/docs/comparison.md +105 -0
- package/docs/compat.md +269 -0
- package/docs/compatible-models.md +135 -0
- package/docs/demo.md +55 -0
- package/docs/maintenance.md +64 -0
- package/docs/plan-format.md +213 -0
- package/docs/providers.md +751 -0
- package/docs/release.md +147 -0
- package/docs/state-machine.md +266 -0
- package/docs/storage.md +207 -0
- package/docs/troubleshooting.md +152 -0
- package/examples/compat/add-edit-verify/PLAN.md +29 -0
- package/examples/compat/add-edit-verify/broken.diff +8 -0
- package/examples/compat/add-edit-verify/good.diff +8 -0
- package/examples/compat/add-edit-verify/package.json +5 -0
- package/examples/compat/add-edit-verify/src/add.js +2 -0
- package/examples/compat/add-edit-verify/test/add.test.js +9 -0
- package/examples/compat/is-even-add-test/PLAN.md +30 -0
- package/examples/compat/is-even-add-test/broken.diff +11 -0
- package/examples/compat/is-even-add-test/good.diff +11 -0
- package/examples/compat/is-even-add-test/package.json +5 -0
- package/examples/compat/is-even-add-test/src/is-even.js +3 -0
- package/examples/compat/is-even-add-test/test/is-even.test.js +9 -0
- package/examples/compat/manifest.json +60 -0
- package/examples/compat/plan-greeting/ISSUE.md +25 -0
- package/examples/compat/plan-greeting/package.json +5 -0
- package/examples/compat/plan-greeting/src/greet.js +2 -0
- package/examples/compat/plan-greeting/test/greet.test.js +8 -0
- package/examples/compat/reverse-grep-edit/PLAN.md +32 -0
- package/examples/compat/reverse-grep-edit/broken.diff +12 -0
- package/examples/compat/reverse-grep-edit/good.diff +12 -0
- package/examples/compat/reverse-grep-edit/package.json +5 -0
- package/examples/compat/reverse-grep-edit/src/strings.js +10 -0
- package/examples/compat/reverse-grep-edit/test/strings.test.js +15 -0
- package/examples/issue-triage-js/PLAN.md +83 -0
- package/examples/issue-triage-js/README.md +76 -0
- package/examples/issue-triage-js/package.json +9 -0
- package/examples/issue-triage-js/src/issue-triage.js +87 -0
- package/examples/issue-triage-js/test/issue-triage.test.js +107 -0
- package/package.json +70 -0
package/docs/release.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
This is the canonical tracked release guide for Neal. Releases are manual,
|
|
4
|
+
versioned npm operations. Normal CI remains verification-only for pushes and
|
|
5
|
+
pull requests; publishing is isolated to the GitHub Actions `Publish` workflow,
|
|
6
|
+
which is triggered manually with `workflow_dispatch`.
|
|
7
|
+
|
|
8
|
+
## Versioning Policy
|
|
9
|
+
|
|
10
|
+
Neal uses Semantic Versioning. `package.json.version` changes only in an
|
|
11
|
+
intentional release-preparation pull request, not in ordinary implementation
|
|
12
|
+
commits. Release versions are exact SemVer strings without a leading `v`, and
|
|
13
|
+
an npm version must never be reused.
|
|
14
|
+
|
|
15
|
+
Before `1.0.0`, minor releases may include meaningful behavior changes while
|
|
16
|
+
the CLI, configuration, exit-code, and artifact contracts settle. After
|
|
17
|
+
`1.0.0`, breaking changes to documented CLI commands, flags, configuration,
|
|
18
|
+
exit codes, or durable run artifacts require a major version.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
|
|
22
|
+
- Patch: `0.1.1` fixes a defect or compatibility issue without intentionally
|
|
23
|
+
changing documented behavior.
|
|
24
|
+
- Minor before `1.0.0`: `0.2.0` adds a feature or meaningful behavior change
|
|
25
|
+
while the public contract is still settling.
|
|
26
|
+
- Major: `1.0.0` establishes the stable public contract; later breaking
|
|
27
|
+
contract changes require `2.0.0`, `3.0.0`, and so on.
|
|
28
|
+
|
|
29
|
+
## SDK Dependency Policy
|
|
30
|
+
|
|
31
|
+
Direct coding-agent and AI SDK runtime dependencies must use exact SemVer specs
|
|
32
|
+
in `package.json`. Do not use `latest`, caret ranges, tilde ranges, wildcards,
|
|
33
|
+
workspace specs, file specs, or link specs for these direct dependencies in a
|
|
34
|
+
published CLI package:
|
|
35
|
+
|
|
36
|
+
- `@anthropic-ai/claude-agent-sdk`
|
|
37
|
+
- `@openai/codex-sdk`
|
|
38
|
+
- `ai`
|
|
39
|
+
- `@ai-sdk/openai-compatible`
|
|
40
|
+
- `zod`
|
|
41
|
+
|
|
42
|
+
An SDK update should land as a normal dependency pull request that updates both
|
|
43
|
+
`package.json` and `pnpm-lock.yaml`, runs normal CI, and records any provider
|
|
44
|
+
compatibility findings. The resulting Neal release should match the user-facing
|
|
45
|
+
impact: use a patch release for a compatibility fix that preserves documented
|
|
46
|
+
behavior, a minor release for behavior changes before `1.0.0`, and a major
|
|
47
|
+
release after `1.0.0` if a documented public contract breaks.
|
|
48
|
+
|
|
49
|
+
## Local Release Readiness
|
|
50
|
+
|
|
51
|
+
Prepare a release with a normal pull request that changes `package.json.version`
|
|
52
|
+
to the intended exact SemVer version:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
pnpm version <version> --no-git-tag-version
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run the local release-readiness gates from the repository root:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
RELEASE_VERSION=<version> RELEASE_DRY_RUN=true pnpm run validate:release
|
|
62
|
+
pnpm typecheck
|
|
63
|
+
pnpm test
|
|
64
|
+
pnpm build
|
|
65
|
+
node scripts/verify-package.mjs
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`validate:release` checks that the workflow input version matches
|
|
69
|
+
`package.json.version`, validates required package metadata, checks npm version
|
|
70
|
+
availability, and checks remote `v<version>` tag availability when an `origin`
|
|
71
|
+
remote is configured. Package verification confirms the built CLI and packed
|
|
72
|
+
tarball.
|
|
73
|
+
|
|
74
|
+
## Manual Publish Workflow
|
|
75
|
+
|
|
76
|
+
After the release-preparation pull request is merged and normal CI passes, run
|
|
77
|
+
the `Publish` workflow manually from `main` with:
|
|
78
|
+
|
|
79
|
+
- `version`: the exact `package.json.version`, without a leading `v`
|
|
80
|
+
- `dry_run`: `true`
|
|
81
|
+
|
|
82
|
+
The workflow is guarded to `refs/heads/main`, uses the `npm-publish`
|
|
83
|
+
environment, grants `contents: read` and `id-token: write`, installs with
|
|
84
|
+
`pnpm install --frozen-lockfile`, and runs these gates before any publish step:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
RELEASE_VERSION=<version> RELEASE_DRY_RUN=<dry_run> pnpm run validate:release
|
|
88
|
+
pnpm typecheck
|
|
89
|
+
pnpm test
|
|
90
|
+
pnpm build
|
|
91
|
+
node scripts/verify-package.mjs
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Review the dry-run result before any real publish. Run `Publish` again with
|
|
95
|
+
`dry_run: false` only when a first-publish or later release-preparation plan has
|
|
96
|
+
explicitly authorized the real publish.
|
|
97
|
+
|
|
98
|
+
## First-Publish Boundary
|
|
99
|
+
|
|
100
|
+
`@navels/neal` is not yet published to the npm registry. Local validation and
|
|
101
|
+
the workflow run dry-run release checks, including `npm publish --dry-run
|
|
102
|
+
--access public`. A real public publish (`npm publish --access public`) happens
|
|
103
|
+
only on the `dry_run: false` path, under the configured npm trusted publishing
|
|
104
|
+
and an explicit authorization from a first-publish or later release-preparation
|
|
105
|
+
plan.
|
|
106
|
+
|
|
107
|
+
## Trusted Publishing Setup
|
|
108
|
+
|
|
109
|
+
The publish workflow relies on trusted publishing/OIDC and intentionally has no
|
|
110
|
+
npm-token fallback. It grants `id-token: write` for OIDC, keeps repository
|
|
111
|
+
contents read-only, uses the `npm-publish` GitHub environment, and publishes
|
|
112
|
+
with `npm publish --access public` only on the real-publish path.
|
|
113
|
+
|
|
114
|
+
Use this npm CLI setup command as the trusted publisher reference:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npm install -g npm@^11.10.0
|
|
118
|
+
npm trust github @navels/neal --repo navels/neal --file publish.yml --env npm-publish --allow-publish
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Current first-publish limitation: `@navels/neal` is not currently published, and
|
|
122
|
+
the `npm trust github` command may be blocked until the package already exists
|
|
123
|
+
on the npm registry. The npm trusted publisher configuration must match the
|
|
124
|
+
repository workflow: package `@navels/neal`, owner/repo `navels/neal`, workflow
|
|
125
|
+
filename `publish.yml`, environment `npm-publish`, and allowed action
|
|
126
|
+
`npm publish`.
|
|
127
|
+
|
|
128
|
+
## Release Boundaries
|
|
129
|
+
|
|
130
|
+
The current workflow checks that the remote `v<version>` tag is available, but
|
|
131
|
+
it does not create tags or GitHub releases. Do not promise or perform release
|
|
132
|
+
marker creation as part of the current publish workflow. If tags or GitHub
|
|
133
|
+
releases are needed after a successful publish, add them in a separate plan with
|
|
134
|
+
the required permissions and explicit operator approval.
|
|
135
|
+
|
|
136
|
+
Recovery rule for later real publishes: if `npm publish` succeeds but a later
|
|
137
|
+
release-marker step fails in a separate plan, do not rerun the publish blindly.
|
|
138
|
+
First confirm the npm version exists, then create only the missing marker.
|
|
139
|
+
|
|
140
|
+
Official references:
|
|
141
|
+
|
|
142
|
+
- GitHub `workflow_dispatch` inputs:
|
|
143
|
+
<https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow>
|
|
144
|
+
- npm trusted publishing:
|
|
145
|
+
<https://docs.npmjs.com/trusted-publishers/>
|
|
146
|
+
- npm trust command:
|
|
147
|
+
<https://docs.npmjs.com/cli/v11/commands/npm-trust/>
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Neal State Machine
|
|
2
|
+
|
|
3
|
+
The product-level storage layout, artifact classifications, run pointers,
|
|
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.
|
|
7
|
+
|
|
8
|
+
Neal keeps two persisted ledgers:
|
|
9
|
+
|
|
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
|
+
- `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
|
+
|
|
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
|
+
|
|
15
|
+
## Hydration And Validation
|
|
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.
|
|
18
|
+
|
|
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
|
+
|
|
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
|
+
|
|
23
|
+
## Schema Versions
|
|
24
|
+
|
|
25
|
+
Persisted JSON that Neal reads as durable schema is versioned at v1.
|
|
26
|
+
`RUN_STATE.json` hydrates through `normalizeStateV1`; `.neal/current.json`,
|
|
27
|
+
queue state, `.neal/current-queue.json`, queue child links, run narratives,
|
|
28
|
+
squash audit results, and the active writer lock all have v1 write shapes or
|
|
29
|
+
parsers.
|
|
30
|
+
|
|
31
|
+
Run `meta.json` is support data, not child-run state. Optional context artifacts
|
|
32
|
+
such as `plan-progress.json` are written as v1 and read defensively so malformed
|
|
33
|
+
or unsupported support data does not replace the canonical run ledger.
|
|
34
|
+
|
|
35
|
+
## State Views And Public Lifecycle
|
|
36
|
+
|
|
37
|
+
Persisted v1 child-run state remains record-shaped, but most callers should not
|
|
38
|
+
treat `OrchestrationState` as one large nullable programming model.
|
|
39
|
+
`src/neal/state-views.ts` exposes typed views over the current v1 fields without
|
|
40
|
+
changing the stored JSON shape:
|
|
41
|
+
|
|
42
|
+
- shared metadata and public lifecycle views
|
|
43
|
+
- plan and execute run views
|
|
44
|
+
- interactive blocked-recovery views
|
|
45
|
+
- derived-plan and final-completion views
|
|
46
|
+
|
|
47
|
+
The public lifecycle view treats `status` as the product-facing lifecycle owner
|
|
48
|
+
and treats `phase` as an internal runnable cursor or diagnostic detail.
|
|
49
|
+
`src/neal/run-status.ts` and status rendering build on that lifecycle view so
|
|
50
|
+
human output can say `waiting_for_guidance`, `paused`, `blocked`, `failed`, or
|
|
51
|
+
`done` without requiring users to interpret raw phase names.
|
|
52
|
+
|
|
53
|
+
`neal status --json` remains the stable automation surface. It still exposes raw
|
|
54
|
+
`phase` and `status` fields for diagnostics, and it preserves derived public
|
|
55
|
+
fields such as `effectiveStatus`, `waitingForOperatorGuidance`, and
|
|
56
|
+
`pendingOperatorGuidance`.
|
|
57
|
+
|
|
58
|
+
## Phase And Status
|
|
59
|
+
|
|
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
|
+
|
|
62
|
+
The enforced phase/status relationship is intentionally small:
|
|
63
|
+
|
|
64
|
+
- `phase === 'done'` if and only if `status === 'done'`.
|
|
65
|
+
- `phase === 'blocked'` may use `status === 'blocked'` or `status === 'failed'`.
|
|
66
|
+
- `status === 'running'` is invalid with terminal `done` or `blocked` phases.
|
|
67
|
+
- Failed states may preserve the phase that failed, because provider, reviewer, coder, or artifact errors are useful to resume and diagnostic tooling only when the failing phase remains visible.
|
|
68
|
+
|
|
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
|
+
|
|
71
|
+
## Recovery State
|
|
72
|
+
|
|
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
|
+
|
|
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.
|
|
81
|
+
|
|
82
|
+
Public resume eligibility is classified by `src/neal/resume-decision.ts` before
|
|
83
|
+
any recovery mutation. That read-only decision layer combines loaded child-run
|
|
84
|
+
state with lock, queue, and retrospective evidence, then returns the shared
|
|
85
|
+
vocabulary used by `neal resume`, `neal status`, and run narratives: continue,
|
|
86
|
+
needs message, pending message, already running, done, or cannot resume.
|
|
87
|
+
|
|
88
|
+
Blocked resume eligibility still depends on the planner actions from
|
|
89
|
+
`src/neal/resume-planner.ts`, with the orchestrator recovery code applying the
|
|
90
|
+
selected actions only after the selected run has been classified as executable.
|
|
91
|
+
`state-invariants.ts` mirrors the allowed phase sets so changes to recovery
|
|
92
|
+
behavior are visible in focused tests.
|
|
93
|
+
|
|
94
|
+
## Unattended Mode
|
|
95
|
+
|
|
96
|
+
`--unattended` / `agent.unattended` resolves to a persisted
|
|
97
|
+
`OrchestrationState.unattended` boolean (default `false`) for both plan and
|
|
98
|
+
execute top-level modes, so a separate `neal resume` process and the `neal run`
|
|
99
|
+
plan→execute hand-off see it without re-passing a flag. The flag overrides the
|
|
100
|
+
config key. The resolved value is also threaded into the planner, reviewer,
|
|
101
|
+
coder, final-completion, and plan-reviewer prompts, where it adds one autonomy
|
|
102
|
+
line only when true; with `unattended` false the rendered prompts are
|
|
103
|
+
byte-identical to attended output.
|
|
104
|
+
|
|
105
|
+
Unattended changes only the three structural operator-block sites; it never
|
|
106
|
+
weakens verification, authorization, or squash/grading, and never removes
|
|
107
|
+
`block_for_operator` from any decision surface. Every unattended branch gates on
|
|
108
|
+
structural state (`state.unattended`, `actionResolution.effectiveAction`,
|
|
109
|
+
`phase`, `blockedFromPhase`, the bounded auto-resume counter), never on
|
|
110
|
+
substring-matching assistant or guidance text.
|
|
111
|
+
|
|
112
|
+
- **Site A — execute-mode interactive recovery.** All fresh blocks funnel
|
|
113
|
+
through `enterInteractiveBlockedRecovery` (`src/neal/orchestrator/phases/recovery.ts`).
|
|
114
|
+
Under unattended, while `unattendedAutoResumeCount < UNATTENDED_MAX_AUTO_RESUMES`
|
|
115
|
+
(a module constant, reconciled so it never pushes past
|
|
116
|
+
`interactiveBlockedRecovery.maxTurns`), it appends a synthesized conservative
|
|
117
|
+
guidance turn (`UNATTENDED_AUTO_RESUME_GUIDANCE` from
|
|
118
|
+
`src/neal/blocked-guidance.ts`) via the same turn-recording helper a human
|
|
119
|
+
message uses, increments the persisted counter, and lets the run proceed into
|
|
120
|
+
the recovery phase. Past the cap (or the `maxTurns` boundary) it runs the
|
|
121
|
+
shared terminal-fail action instead of waiting.
|
|
122
|
+
- **Generalized blocked adjudicator (bounded, both modes).** Inside
|
|
123
|
+
`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
|
+
running through the same no-write reviewer plumbing the review/final-completion
|
|
127
|
+
reviewers use, making zero commits and zero file edits). Eligible source phases
|
|
128
|
+
(`ADJUDICATOR_ELIGIBLE_SOURCE_PHASES`) are the coder-block phases (`coder_scope`
|
|
129
|
+
/ `coder_response` / `coder_optional_response`, which also carry the split-plan
|
|
130
|
+
invalid-payload block) and the reviewer `review_stuck` phases (`reviewer_scope` /
|
|
131
|
+
`reviewer_plan`). The adjudicator first applies an anti-thrash guard
|
|
132
|
+
(`recentBlocks`, keyed on scope identity + source phase + normalized blocker key +
|
|
133
|
+
evidence fingerprint): a same-scope repeat with no new evidence short-circuits to
|
|
134
|
+
`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
|
|
145
|
+
`unattendedAutoResumeCount` or `interactiveBlockedRecovery.maxTurns`. Every other
|
|
146
|
+
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
|
+
The decisions are auditable from the structured event log via the
|
|
150
|
+
`review_stuck_arbiter.{start,verdict,resolved,declined,exhausted}` events, which
|
|
151
|
+
carry `scopeNumber`, `sourcePhase`, `blockedReason`, and (on `verdict`/`resolved`)
|
|
152
|
+
`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
|
+
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
|
+
unattended they run the shared terminal-fail action immediately rather than
|
|
159
|
+
saving `status:'blocked'`; there is no auto-resume and no synthesized
|
|
160
|
+
`pendingPlanReviewGuidance`. Site C edits only the `topLevelMode !== 'execute'`
|
|
161
|
+
branch of `finalizeBlockedPlanReviewResponse`; execute-mode derived-plan-review
|
|
162
|
+
blocks (`topLevelMode === 'execute'`) re-enter site A and are handled there.
|
|
163
|
+
|
|
164
|
+
The shared terminal-fail action is `persistUnattendedBlockUnresolvedFailure`
|
|
165
|
+
(`src/neal/orchestrator/phases/shared.ts`), which mirrors the
|
|
166
|
+
`persistCoderFailureState` failed-run shape: save `status:'failed'` (preserving
|
|
167
|
+
`phase`/`blockedFromPhase` for diagnostics), re-render execution artifacts, write
|
|
168
|
+
a `failed` checkpoint retrospective, and emit the classified
|
|
169
|
+
`unattended.block_unresolved` log event (`reason:'unattended_block_unresolved'`
|
|
170
|
+
plus the `UnattendedBlockSite` origin) — deliberately without `notifyBlocked`,
|
|
171
|
+
which is the attended wait notification. The run exits with writer code `3`, and
|
|
172
|
+
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
|
|
174
|
+
retrospective. Attended runs are unchanged and still wait for
|
|
175
|
+
`neal resume --message` at all three sites.
|
|
176
|
+
|
|
177
|
+
## Resume Planning
|
|
178
|
+
|
|
179
|
+
New-run initialization and existing-run resume reconciliation are separate.
|
|
180
|
+
`loadOrInitialize` still provides the command-facing entrypoint, but resume
|
|
181
|
+
reconciliation is modeled as explicit `ResumeAction` values from
|
|
182
|
+
`planResumeActions` and applied by `applyResumeActions`.
|
|
183
|
+
|
|
184
|
+
Current resume actions cover stopped-status normalization, restoring a resumable
|
|
185
|
+
blocked source phase, keeping non-resumable blocked runs blocked, promoting
|
|
186
|
+
accepted or pending unexecuted derived plans, blocking rejected abandoned derived
|
|
187
|
+
plans, flushing derived-plan notifications, recovering clean committed
|
|
188
|
+
scope work that is waiting for review, waiting for operator guidance, processing
|
|
189
|
+
pending operator guidance, and no-op completion for done runs.
|
|
190
|
+
|
|
191
|
+
`neal resume` is the safe first recovery command for mechanical interruptions.
|
|
192
|
+
The command gathers read-only context, asks the shared decision layer what should
|
|
193
|
+
happen, and calls `loadRunForResume()` only for decisions that should execute
|
|
194
|
+
the selected run. `loadRunForResume()` remains the mutation boundary for resume
|
|
195
|
+
normalization, event logging, pointer writes, and execution artifacts.
|
|
196
|
+
|
|
197
|
+
`neal resume --run <run-id> --message "..."` is the operator-input path only
|
|
198
|
+
when the shared decision says interactive recovery is waiting for guidance.
|
|
199
|
+
Pending guidance resumes with plain `neal resume --run <run-id>`. Queue
|
|
200
|
+
continuation still belongs to `continuePlanAndExecuteQueueFromChildRun` after
|
|
201
|
+
the resumed child run finishes; selecting a child run for resume does not
|
|
202
|
+
advance or repair the parent queue by itself.
|
|
203
|
+
|
|
204
|
+
## Derived Plans
|
|
205
|
+
|
|
206
|
+
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
|
+
|
|
208
|
+
- A non-null `derivedPlanStatus` requires a non-null `derivedPlanPath`.
|
|
209
|
+
- A `derivedScopeIndex` requires execute mode, an accepted derived plan, a parent scope number, and a plan path.
|
|
210
|
+
- `awaiting_derived_plan_execution` requires an accepted derived plan that has not started executing yet and no active created commits.
|
|
211
|
+
- Pending or rejected derived plans cannot have a `derivedScopeIndex`.
|
|
212
|
+
|
|
213
|
+
Derived-plan execution should continue to flow through the existing orchestrator transition helpers before any future shape refactor changes the persisted fields.
|
|
214
|
+
|
|
215
|
+
## Final Completion
|
|
216
|
+
|
|
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.
|
|
218
|
+
|
|
219
|
+
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
|
+
|
|
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.
|
|
222
|
+
|
|
223
|
+
## Atomic Writes And Locks
|
|
224
|
+
|
|
225
|
+
`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
|
+
|
|
227
|
+
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
|
+
|
|
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.
|
|
230
|
+
|
|
231
|
+
## Change Checklist
|
|
232
|
+
|
|
233
|
+
When adding a child-run phase:
|
|
234
|
+
|
|
235
|
+
- Add the phase to `OrchestrationPhase` in `src/neal/types.ts`.
|
|
236
|
+
- Add it to `ORCHESTRATION_PHASES` in `src/neal/state-invariants.ts`.
|
|
237
|
+
- If it is runnable, add it to `RUNNABLE_PHASE_REGISTRY` in `src/neal/orchestrator/run-loop.ts`.
|
|
238
|
+
- Update any recovery or blocked-resume phase sets that should include or exclude it.
|
|
239
|
+
- Add tests in `test/state-invariants.test.ts` or `test/run-loop.test.ts` for the new membership and dispatch contract.
|
|
240
|
+
|
|
241
|
+
When adding a child-run persisted field:
|
|
242
|
+
|
|
243
|
+
- Add the type in `OrchestrationState`.
|
|
244
|
+
- Initialize it in `createInitialState`.
|
|
245
|
+
- Parse it in `normalizeStateV1`.
|
|
246
|
+
- Validate semantic combinations in `assertValidOrchestrationState` when the field can make state invalid.
|
|
247
|
+
- Extend state round-trip or invariant tests.
|
|
248
|
+
|
|
249
|
+
When adding a queue-state field:
|
|
250
|
+
|
|
251
|
+
- Add the type in `PlanAndExecuteQueueState` or `PlanAndExecuteQueueItem`.
|
|
252
|
+
- Initialize it in queue creation or child transition helpers.
|
|
253
|
+
- Parse and validate it in `parsePlanAndExecuteQueueState` or `parsePlanAndExecuteQueueItem`.
|
|
254
|
+
- Include it in queue summary or current queue pointer output only when it is operator-facing or needed for resume.
|
|
255
|
+
|
|
256
|
+
## Future Refactor Path
|
|
257
|
+
|
|
258
|
+
Do not jump directly from the current record-shaped `OrchestrationState` to a
|
|
259
|
+
persisted discriminated union. The lower-risk path now in place is to keep v1
|
|
260
|
+
hydration isolated, keep semantic checks centralized in `state-invariants.ts`,
|
|
261
|
+
and route new logic through typed state views and the resume planner.
|
|
262
|
+
|
|
263
|
+
After those views have stabilized in real runs and tests, a v2 envelope or
|
|
264
|
+
discriminated internal TypeScript model can be considered separately. Any future
|
|
265
|
+
shape should preserve `neal status --json` as the automation contract and keep
|
|
266
|
+
v1 run-local ledgers readable unless an explicit migration command exists.
|
package/docs/storage.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Neal Storage Contract
|
|
2
|
+
|
|
3
|
+
Neal uses project-local `.neal/` storage as the source of truth for runs, queues,
|
|
4
|
+
review artifacts, recovery artifacts, progress, and audit history. User-level
|
|
5
|
+
storage is reserved for configuration, guidance, caches, logs, and future
|
|
6
|
+
optional discovery helpers.
|
|
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
|
|
10
|
+
[state-machine.md](state-machine.md).
|
|
11
|
+
|
|
12
|
+
## Public Automation Surface
|
|
13
|
+
|
|
14
|
+
`neal status --json` and `neal status --json --all` are the stable automation
|
|
15
|
+
contracts. Scripts should prefer them over parsing raw files under `.neal/`.
|
|
16
|
+
The status JSON includes public status and phase strings, next action, commit
|
|
17
|
+
and patch summaries, provider-error metadata, build metadata, and active writer
|
|
18
|
+
lock evidence through `lock.kind`.
|
|
19
|
+
|
|
20
|
+
The single-run JSON is the authoritative read model for wrappers that already
|
|
21
|
+
know a run id. Important stable fields include:
|
|
22
|
+
|
|
23
|
+
- `runId`, `status`, `effectiveStatus`, `publicStatus`, `phase`,
|
|
24
|
+
`publicPhase`, and `nextAction` for classification and follow-up.
|
|
25
|
+
- `waitingForOperatorGuidance`, `pendingOperatorGuidance`, `blocker`,
|
|
26
|
+
`manualGate`, `resumeDecision`, `health`, and `lock` for blocked, waiting,
|
|
27
|
+
paused, timed-out, live-lock, stale-lock, and manually gated states.
|
|
28
|
+
- `commits` with `initialBaseCommit`, `baseCommit`, `finalCommit`,
|
|
29
|
+
`createdCommitCount`, and accepted-scope final commits.
|
|
30
|
+
- `squash` with the run-local audit artifact path, summary status
|
|
31
|
+
(`missing`, `pending`, `complete`, or `malformed`), replacement/final-head
|
|
32
|
+
commits, original base/final commits, and an unavailable reason when the
|
|
33
|
+
summary cannot provide complete metadata.
|
|
34
|
+
- `patch` with the conservative default-submission decision, reason, patch
|
|
35
|
+
source, base/head/range, commit count, changed-file count, changed files, and
|
|
36
|
+
unavailable reason.
|
|
37
|
+
- `providerError` with the latest provider failure or unclassified phase error,
|
|
38
|
+
including timestamp, provider id, role, label, session handle, normalized
|
|
39
|
+
error kind, bounded message, and retryability where available.
|
|
40
|
+
- `build` with Neal package version, Neal source Git SHA when available, Node
|
|
41
|
+
version, source marker (`meta` or `live_fallback`), and the persisted
|
|
42
|
+
coder/reviewer agent config.
|
|
43
|
+
- `artifacts` with run-local paths for the human narrative, review, progress,
|
|
44
|
+
support, and related diagnostic artifacts.
|
|
45
|
+
|
|
46
|
+
`neal status --json --all` is the discovery surface. Each listed run is built
|
|
47
|
+
from the same status snapshot path, so list entries retain the public status,
|
|
48
|
+
phase, next action, commit, squash, provider-error, build, patch, lock, and
|
|
49
|
+
artifact summaries. Scripts that need a final decision for one run should still
|
|
50
|
+
read that run with `--run <run-id>`.
|
|
51
|
+
|
|
52
|
+
Status reads are best-effort for optional artifacts. Missing or malformed
|
|
53
|
+
`SQUASH_RESULT.json`, unreadable patch ranges, missing run metadata, and old
|
|
54
|
+
runs without build metadata are represented in the JSON summary instead of
|
|
55
|
+
making status fail. The canonical run ledger still must be readable.
|
|
56
|
+
|
|
57
|
+
Raw `.neal` files are inspectable support artifacts. Internal JSON files are not
|
|
58
|
+
public JSON APIs unless this repository documents that guarantee for a specific
|
|
59
|
+
command output.
|
|
60
|
+
|
|
61
|
+
Storage classifications used below are: stable CLI surface, user-facing human
|
|
62
|
+
artifact, support/debug artifact, internal state, and lock/concurrency artifact.
|
|
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.
|
|
66
|
+
|
|
67
|
+
## Project-Local Layout
|
|
68
|
+
|
|
69
|
+
| Path | Classification | Contract |
|
|
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. |
|
|
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
|
+
| `.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. |
|
|
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. |
|
|
78
|
+
| `.neal/runs/<run-id>/PLAN_PROGRESS.md` | User-facing human artifact | Human-readable progress summary for the active plan or scope. |
|
|
79
|
+
| `.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. |
|
|
80
|
+
| `.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. |
|
|
82
|
+
| `.neal/runs/<run-id>/REVIEW.md` | User-facing human artifact | Scope or plan review history and findings. |
|
|
83
|
+
| `.neal/runs/<run-id>/RECOVERY.md` | User-facing human artifact | Interactive blocked-recovery transcript/history for a run. |
|
|
84
|
+
| `.neal/runs/<run-id>/FINAL_COMPLETION_REVIEW.md` | User-facing human artifact | Whole-plan final completion review. |
|
|
85
|
+
| `.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
|
+
| `.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. |
|
|
88
|
+
| `.neal/queues/<queue-id>/QUEUE_SUMMARY.md` | User-facing human artifact | Human-readable summary for a plan-and-execute queue. |
|
|
89
|
+
| `.neal/reviews/<review-id>/meta.json` | Support/debug artifact | Review metadata for a read-only `neal review` request. |
|
|
90
|
+
| `.neal/reviews/<review-id>/events.ndjson` | Support/debug artifact | Append-only review event log. |
|
|
91
|
+
| `.neal/reviews/<review-id>/REVIEW_REQUEST.md` | User-facing human artifact | Original review request, selected range, and loop prompts. |
|
|
92
|
+
| `.neal/reviews/<review-id>/REVIEW_CONTEXT.json` | Support/debug artifact | Resolved local commit range, changed files, diff stat, and diff used for findings. |
|
|
93
|
+
| `.neal/reviews/<review-id>/REVIEW_DRAFT.md` | User-facing human artifact | Human-readable draft findings history for each review loop round. |
|
|
94
|
+
| `.neal/reviews/<review-id>/REVIEW_REVIEW.json` | Support/debug artifact | Versioned review loop summary, including outcome, cap, and round reviews. |
|
|
95
|
+
| `.neal/reviews/<review-id>/REVIEW_ROUNDS.json` | Support/debug artifact | Versioned round-by-round review draft/review prompt and response history. |
|
|
96
|
+
| `.neal/reviews/<review-id>/REVIEW_FINAL.md` | User-facing human artifact | Accepted final reviewed findings artifact, written only after reviewer acceptance. |
|
|
97
|
+
| `.neal/current.json` | Internal state | Default writer-run pointer for commands that need the current run. |
|
|
98
|
+
| `.neal/current-queue.json` | Internal state | Preferred current plan-and-execute queue pointer. |
|
|
99
|
+
| `.neal/active-run.lock` | Lock/concurrency artifact | Active writer-run lock. It prevents unrelated writer commands from mutating the same checkout concurrently. |
|
|
100
|
+
|
|
101
|
+
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
|
|
103
|
+
and terminate the group, because provider-owned child processes are outside the
|
|
104
|
+
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
|
|
107
|
+
still running, cleaned up, live, stale, cross-host, or unreadable.
|
|
108
|
+
|
|
109
|
+
## Patch Automation Policy
|
|
110
|
+
|
|
111
|
+
Default public prediction submission should use only
|
|
112
|
+
`patch.defaultSubmissionEligible: true`. Neal sets that value only for clean
|
|
113
|
+
completed execute runs with a non-empty readable patch range.
|
|
114
|
+
|
|
115
|
+
When a completed run has a successful squash artifact, the status read model
|
|
116
|
+
prefers `squash.originalBaseCommit..squash.replacementCommit` and reports
|
|
117
|
+
`patch.source: "squash_replacement"`. Unsquashed completed execute runs fall
|
|
118
|
+
back to `commits.initialBaseCommit ?? commits.baseCommit` through
|
|
119
|
+
`commits.finalCommit` and report `patch.source: "final_commit"`.
|
|
120
|
+
|
|
121
|
+
Failed, blocked, paused, running, waiting, provider-error, timed-out,
|
|
122
|
+
manual-gate, malformed-squash, pending-squash, unreadable-range, and empty-patch
|
|
123
|
+
runs may still report patch-bearing metadata for diagnostics or private
|
|
124
|
+
analysis. They are not default-submission eligible, and `patch.reason` explains
|
|
125
|
+
why.
|
|
126
|
+
|
|
127
|
+
## Source Of Truth
|
|
128
|
+
|
|
129
|
+
Project-local `.neal/` remains the source of truth for run, queue, review,
|
|
130
|
+
recovery, progress, and audit artifacts.
|
|
131
|
+
|
|
132
|
+
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
|
|
134
|
+
pointer, not a copy of the ledger.
|
|
135
|
+
|
|
136
|
+
There is no required global run index in v1. Future global discovery, if added,
|
|
137
|
+
must be optional and rebuildable from project-local data. It must not become the
|
|
138
|
+
only place where run or queue history can be recovered.
|
|
139
|
+
|
|
140
|
+
## Ledgers And Pointers
|
|
141
|
+
|
|
142
|
+
`.neal/runs/<run-id>/RUN_STATE.json` and
|
|
143
|
+
`.neal/queues/<queue-id>/QUEUE_STATE.json` are persisted ledgers. Neal validates
|
|
144
|
+
them on read. Child-run state uses strict current v1 hydration: missing or
|
|
145
|
+
malformed required child-run fields fail instead of receiving defaults. These
|
|
146
|
+
ledgers are not public JSON APIs.
|
|
147
|
+
|
|
148
|
+
Neal does not write, read, migrate, or repair a session mirror. Use
|
|
149
|
+
`.neal/current.json` as the default writer-run pointer, `neal status --all` to
|
|
150
|
+
discover run IDs, and `--run <run-id>` when selecting a specific run for
|
|
151
|
+
`resume` or `status`.
|
|
152
|
+
|
|
153
|
+
Squash discovery requires readable run-local state. Run metadata and progress
|
|
154
|
+
artifacts can help humans inspect a run, but they cannot make a run selectable
|
|
155
|
+
for `neal squash` without `.neal/runs/<run-id>/RUN_STATE.json`.
|
|
156
|
+
|
|
157
|
+
## Schema Versions And Writes
|
|
158
|
+
|
|
159
|
+
Neal-owned JSON schemas use `version: 1` where they are read back as durable
|
|
160
|
+
state or audit data. That includes child-run state, current run pointers, queue
|
|
161
|
+
state, current queue pointers, queue child links, progress summaries, run
|
|
162
|
+
narratives, squash audit results, review artifacts, and the active writer lock.
|
|
163
|
+
|
|
164
|
+
Run `meta.json` is diagnostic support data. New writes include `version: 1`, but
|
|
165
|
+
status, resume, and squash selection derive canonical run facts from run-local
|
|
166
|
+
state.
|
|
167
|
+
|
|
168
|
+
Important replacement artifacts under `.neal/` are written through per-file
|
|
169
|
+
atomic replacement helpers so a reader should see either the old complete file
|
|
170
|
+
or the new complete file. This is still not a multi-file transaction: related
|
|
171
|
+
files such as state, progress, reviews, and pointers can briefly disagree if a
|
|
172
|
+
process stops between writes. Append-only event logs remain append-only, and the
|
|
173
|
+
active writer lock keeps exclusive file creation for acquisition.
|
|
174
|
+
|
|
175
|
+
## Retention And Privacy
|
|
176
|
+
|
|
177
|
+
`.neal/` may contain prompts, local paths, command output, diffs, review text,
|
|
178
|
+
provider responses, user guidance diagnostics, reviewer scratch files, copied
|
|
179
|
+
tests, build logs, and project-specific context. Review `.neal/` before sharing
|
|
180
|
+
archives, bug reports, or support bundles.
|
|
181
|
+
|
|
182
|
+
Public automation bundles should copy `neal status --json` output and
|
|
183
|
+
`RUN_NARRATIVE.md` when a safe per-run trace is needed. Do not publish raw run
|
|
184
|
+
directories, `RUN_NARRATIVE.json`, `events.ndjson`, `stderr.log`, or internal
|
|
185
|
+
state ledgers as benchmark traces.
|
|
186
|
+
|
|
187
|
+
Run and review artifacts are intentionally inspectable, but they should be
|
|
188
|
+
treated as project data. Apply the same retention and access controls you use
|
|
189
|
+
for source, logs, and local debugging output.
|
|
190
|
+
|
|
191
|
+
## Manual Cleanup
|
|
192
|
+
|
|
193
|
+
Manual cleanup is safe only when you no longer need resume, history, diagnostics,
|
|
194
|
+
or audit data for the item being removed.
|
|
195
|
+
|
|
196
|
+
Reasonable manual cleanup options:
|
|
197
|
+
|
|
198
|
+
- remove old `.neal/runs/<run-id>/` directories when resume and run history for
|
|
199
|
+
those runs are no longer needed
|
|
200
|
+
- remove old `.neal/queues/<queue-id>/` directories when queue resume and queue
|
|
201
|
+
history are no longer needed
|
|
202
|
+
- remove stale `.neal/current.json` or `.neal/current-queue.json` pointers only
|
|
203
|
+
after confirming they do not point to work you still intend to resume
|
|
204
|
+
|
|
205
|
+
Deleting run or queue state removes Neal's resume and history for that run or
|
|
206
|
+
queue. Prefer keeping the full directory until the related work has been merged,
|
|
207
|
+
archived, or otherwise recorded somewhere durable.
|