@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.
- package/README.md +206 -209
- package/SECURITY.md +16 -19
- package/dist/neal/adjudicator/{blocked-adjudicator.js → consultant.js} +21 -21
- package/dist/neal/adjudicator/execute.js +33 -33
- package/dist/neal/adjudicator/final-completion.js +2 -31
- package/dist/neal/adjudicator/planning.js +230 -38
- package/dist/neal/agents/prompts.js +6 -17
- package/dist/neal/agents/rounds.js +43 -73
- package/dist/neal/agents/schemas.js +47 -13
- package/dist/neal/blocked-guidance.js +16 -9
- package/dist/neal/cli.js +2 -2
- package/dist/neal/commands/check.js +2 -2
- package/dist/neal/commands/compat.js +100 -36
- package/dist/neal/commands/interactive-activity.js +2 -2
- package/dist/neal/commands/recovery-guidance.js +20 -8
- package/dist/neal/commands/runtime.js +4 -2
- package/dist/neal/config.js +16 -7
- package/dist/neal/context/inline-review-context.js +13 -28
- package/dist/neal/context/reviewer-context.js +19 -14
- package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
- package/dist/neal/eval/reviewer-eval.js +155 -0
- package/dist/neal/interactive-controls.js +5 -0
- package/dist/neal/orchestrator/completion.js +49 -2
- package/dist/neal/orchestrator/failures.js +7 -0
- package/dist/neal/orchestrator/notifications.js +9 -9
- package/dist/neal/orchestrator/phases/planning.js +117 -80
- package/dist/neal/orchestrator/phases/recovery.js +151 -115
- package/dist/neal/orchestrator/phases/review.js +28 -1
- package/dist/neal/orchestrator/transitions.js +7 -1
- package/dist/neal/orchestrator.js +3 -0
- package/dist/neal/plan-queue.js +29 -1
- package/dist/neal/prompts/execute.js +7 -19
- package/dist/neal/prompts/planning.js +64 -55
- package/dist/neal/prompts/review-doctrine.js +13 -50
- package/dist/neal/prompts/specialized.js +7 -17
- package/dist/neal/prompts/specs.js +180 -32
- package/dist/neal/providers/agent-settings-isolation.js +35 -0
- package/dist/neal/providers/anthropic-claude.js +18 -1
- package/dist/neal/providers/detection.js +3 -7
- package/dist/neal/providers/git-config-isolation.js +83 -0
- package/dist/neal/providers/openai-codex.js +25 -3
- package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
- package/dist/neal/providers/openai-compatible.js +1078 -430
- package/dist/neal/providers/openrouter-routing.js +68 -0
- package/dist/neal/providers/pricing.js +1 -1
- package/dist/neal/providers/rate-card.js +6 -32
- package/dist/neal/providers/registry.js +6 -17
- package/dist/neal/providers/types.js +12 -0
- package/dist/neal/resume-decision.js +7 -6
- package/dist/neal/resume-planner.js +9 -0
- package/dist/neal/retrospective.js +3 -3
- package/dist/neal/review-debt.js +30 -0
- package/dist/neal/review-findings/provider.js +17 -29
- package/dist/neal/review.js +17 -1
- package/dist/neal/state-invariants.js +11 -3
- package/dist/neal/state-views.js +27 -5
- package/dist/neal/state.js +59 -15
- package/dist/neal/status.js +28 -1
- package/dist/neal/support.js +3 -3
- package/docs/README.md +22 -18
- package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
- package/docs/architecture.md +79 -83
- package/docs/automation.md +11 -11
- package/docs/compat.md +91 -61
- package/docs/compatible-models.md +145 -121
- package/docs/demo.md +15 -5
- package/docs/maintenance.md +65 -34
- package/docs/plan-format.md +61 -33
- package/docs/prompt-evals.md +96 -0
- package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
- package/docs/providers.md +279 -349
- package/docs/release.md +63 -81
- package/docs/review-convergence.md +246 -0
- package/docs/state-machine.md +61 -59
- package/docs/storage.md +40 -31
- package/docs/troubleshooting.md +32 -29
- package/examples/compat/add-edit-verify/PLAN.md +2 -0
- package/examples/compat/add-edit-verify/broken.diff +2 -2
- package/examples/compat/add-edit-verify/good.diff +2 -2
- package/examples/compat/add-edit-verify/src/add.js +1 -1
- package/examples/compat/manifest.json +25 -29
- package/examples/compat/sum-grep-edit/PLAN.md +33 -0
- package/examples/compat/sum-grep-edit/broken.diff +12 -0
- package/examples/compat/sum-grep-edit/good.diff +12 -0
- package/examples/compat/sum-grep-edit/package.json +5 -0
- package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
- package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
- package/examples/issue-triage-js/README.md +0 -15
- package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
- package/examples/reviewer-eval/clean-doc/change.diff +9 -0
- package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
- package/examples/reviewer-eval/clean-extract/change.diff +13 -0
- package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
- package/examples/reviewer-eval/dropped-error/change.diff +15 -0
- package/examples/reviewer-eval/manifest.json +45 -0
- package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
- package/examples/reviewer-eval/off-by-one/change.diff +11 -0
- package/{config.yml → neal.yml} +1 -1
- package/package.json +15 -14
- package/dist/neal/providers/generic-agentic.js +0 -1256
- package/docs/comparison.md +0 -105
- package/docs/issue-pipeline.md +0 -124
- package/examples/compat/is-even-add-test/PLAN.md +0 -30
- package/examples/compat/is-even-add-test/broken.diff +0 -11
- package/examples/compat/is-even-add-test/good.diff +0 -11
- package/examples/compat/is-even-add-test/package.json +0 -5
- package/examples/compat/is-even-add-test/src/is-even.js +0 -3
- package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
- package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
- package/examples/compat/reverse-grep-edit/broken.diff +0 -12
- package/examples/compat/reverse-grep-edit/good.diff +0 -12
- package/examples/compat/reverse-grep-edit/package.json +0 -5
- package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
- package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
package/docs/comparison.md
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Why neal instead of X?
|
|
2
|
-
|
|
3
|
-
neal is a harness, not an agent. It runs three independent roles — planner,
|
|
4
|
-
coder, reviewer — each bound to whatever provider and model you choose
|
|
5
|
-
([providers.md](providers.md)), and drives them with a deterministic state
|
|
6
|
-
machine ([architecture.md](architecture.md)) rather than letting a model decide
|
|
7
|
-
the control flow. Verdicts are schema-validated structured output, not prose an
|
|
8
|
-
orchestrator model interprets. Every run is persisted under `.neal/` and
|
|
9
|
-
resumable after a crash ([storage.md](storage.md)). The reviewer is read-only by
|
|
10
|
-
construction: the provider registry rejects any reviewer capability that
|
|
11
|
-
declares write or shell access, so a reviewer structurally cannot mutate the
|
|
12
|
-
repo regardless of prompt. Most alternatives are agents; neal is the loop that
|
|
13
|
-
holds agents accountable to a plan.
|
|
14
|
-
|
|
15
|
-
## Claude Code
|
|
16
|
-
|
|
17
|
-
What it's great at: interactive, conversational coding with a single very
|
|
18
|
-
capable agent. Its subagents delegate work, plan mode gates execution behind an
|
|
19
|
-
approved plan, and hooks let you inject your own checks into the tool loop.
|
|
20
|
-
|
|
21
|
-
The structural difference: Claude Code is a single-vendor agent product.
|
|
22
|
-
Its subagents are prompt-scoped delegates inside one product, sharing a vendor
|
|
23
|
-
and orchestrated by a model — the top-level agent decides when to delegate and
|
|
24
|
-
how to weigh what comes back. There is no cross-vendor adversarial review: the
|
|
25
|
-
judge and the judged are the same vendor, often the same model family. neal is
|
|
26
|
-
not a competitor to Claude Code; it is a consumer of it. The `anthropic-claude`
|
|
27
|
-
adapter drives Claude Code's SDK as a coder or reviewer, and the common
|
|
28
|
-
configuration is exactly the adversarial split Claude Code cannot express:
|
|
29
|
-
Codex codes, Claude reviews, and a deterministic orchestrator — not a model —
|
|
30
|
-
decides what happens with the verdict.
|
|
31
|
-
|
|
32
|
-
Pick Claude Code when the work is exploratory or conversational, when you want
|
|
33
|
-
a human in the loop turn by turn, or when you have no plan yet. neal wants a
|
|
34
|
-
plan document; Claude Code is a good place to figure one out.
|
|
35
|
-
|
|
36
|
-
## Aider (architect/editor mode)
|
|
37
|
-
|
|
38
|
-
What it's great at: fast, terminal-native pair programming. Its
|
|
39
|
-
architect/editor mode was an early demonstration that splitting reasoning from
|
|
40
|
-
editing across two models improves results.
|
|
41
|
-
|
|
42
|
-
The structural difference: Aider's two models cooperate on one change — the
|
|
43
|
-
architect proposes, the editor applies — and the control flow between them is
|
|
44
|
-
mediated by the models within a chat session. There is no independent reviewer
|
|
45
|
-
producing a structured verdict that a state machine acts on, no scoped plan
|
|
46
|
-
queue where each scope starts the coder from a fresh context, and continuity is
|
|
47
|
-
conversation-shaped rather than a persisted run ledger. neal's roles are
|
|
48
|
-
adversarial rather than cooperative: the reviewer judges committed diffs it had
|
|
49
|
-
no hand in producing, findings route back to the coder until a schema-validated
|
|
50
|
-
accept, and `neal resume` reconstructs an interrupted run from the `.neal/`
|
|
51
|
-
ledgers rather than replaying a chat.
|
|
52
|
-
|
|
53
|
-
Pick Aider for tight interactive edits where you are the reviewer and the loop
|
|
54
|
-
is you. neal earns its overhead on multi-scope work where drift over a long
|
|
55
|
-
session is the failure mode.
|
|
56
|
-
|
|
57
|
-
## OpenHands and autonomous agents
|
|
58
|
-
|
|
59
|
-
What they're great at: sandboxed, hands-off execution — hand an issue to an
|
|
60
|
-
agent in an isolated runtime and come back to a pull request. The sandbox makes
|
|
61
|
-
broad autonomy defensible.
|
|
62
|
-
|
|
63
|
-
The structural difference: these systems bet on maximizing agent autonomy and
|
|
64
|
-
containing it with a sandbox. neal bets the opposite way: local-first execution
|
|
65
|
-
in your checkout, a human-authored plan as the contract for what may happen,
|
|
66
|
-
and bounded autonomy with structural escalation — blocked states that wait for
|
|
67
|
-
`neal resume --message`, manual gates for work only a human can do, and a
|
|
68
|
-
bounded adjudicator that resolves a narrow recoverable class and escalates
|
|
69
|
-
genuine walls. Even `--unattended` mode only bounds how long neal persists
|
|
70
|
-
before failing cleanly; it never widens authorization.
|
|
71
|
-
|
|
72
|
-
Pick an autonomous agent when you want isolation and don't need to constrain
|
|
73
|
-
the path taken. Pick neal when the plan is the point — when you want to
|
|
74
|
-
specify the decomposition, the verification per scope, and the stop conditions,
|
|
75
|
-
and have the machine enforce them.
|
|
76
|
-
|
|
77
|
-
## If you just want CI review
|
|
78
|
-
|
|
79
|
-
PR-review bots comment on diffs after they reach the forge. neal's review is
|
|
80
|
-
inside the loop: every scope's committed diff is judged before the run moves
|
|
81
|
-
on, and a final-completion review checks the whole plan before the squash — so
|
|
82
|
-
findings are fixed by the same coder in the same run, not triaged later.
|
|
83
|
-
`neal review` also works standalone against any committed range, role-symmetric
|
|
84
|
-
(the coder provider drafts findings, the read-only reviewer adjudicates them),
|
|
85
|
-
local, and provider-agnostic — no forge integration or CI required. The two are
|
|
86
|
-
complementary: neal reviews before you push, bots review after.
|
|
87
|
-
|
|
88
|
-
## What neal does not do
|
|
89
|
-
|
|
90
|
-
- **No GUI.** neal is a CLI with narrative terminal output; state lives in
|
|
91
|
-
files under `.neal/`.
|
|
92
|
-
- **No sandbox of its own for the coder.** Writer providers run with broad
|
|
93
|
-
local permissions (Codex under `danger-full-access`, Claude under
|
|
94
|
-
`bypassPermissions`, the generic writer's `run` tool is unsandboxed shell).
|
|
95
|
-
Bring your own isolation — disposable checkout, container, or VM. See
|
|
96
|
-
[SECURITY.md](../SECURITY.md).
|
|
97
|
-
- **Serial scopes only, today.** Scopes execute one at a time, and `neal run`
|
|
98
|
-
queues run plans serially. There is no parallel scope execution.
|
|
99
|
-
- **It requires a plan document.** There is no "just fix it" mode — the
|
|
100
|
-
authored plan, with its mandatory `## Execution Shape` section
|
|
101
|
-
([plan-format.md](plan-format.md)), is the product's opinion. If writing a
|
|
102
|
-
plan feels like overhead, neal is the wrong tool for that task.
|
|
103
|
-
- **No provider plugins yet.** Adapters are built-in (`openai-codex`,
|
|
104
|
-
`anthropic-claude`, `openai-compatible`, `generic-agentic`); external
|
|
105
|
-
provider plugins are future work.
|
package/docs/issue-pipeline.md
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# GitHub Issue Pipeline
|
|
2
|
-
|
|
3
|
-
Assign a GitHub issue to neal and get a reviewed pull request back. A maintainer
|
|
4
|
-
applies a trigger label to an issue; a GitHub Actions runner turns the issue
|
|
5
|
-
into a neal plan, runs neal's planner/coder/reviewer loop to completion, and
|
|
6
|
-
opens a pull request assigned to a human — or reports back on the issue if neal
|
|
7
|
-
blocks. Humans own merge; the pipeline never pushes to a protected branch.
|
|
8
|
-
|
|
9
|
-
This is a self-hosting use of neal itself: neal already *is* the middle of the
|
|
10
|
-
loop (cross-model review, deterministic gating, bounded recovery), so the
|
|
11
|
-
pipeline is a thin trigger plus a thin publish around `neal run`.
|
|
12
|
-
|
|
13
|
-
## What it does
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
maintainer applies the trigger label to an issue
|
|
17
|
-
│ (gate: the labeler must have write access — the issue author is irrelevant)
|
|
18
|
-
▼
|
|
19
|
-
issue title + body ──▶ seed .neal/PLAN.md ──▶ neal run --unattended
|
|
20
|
-
│ (Opus 4.8 writes, sol reviews each diff)
|
|
21
|
-
├─ exit 0 ──▶ push neal/issue-<n>, open a PR, assign the maintainer
|
|
22
|
-
├─ exit 2 ──▶ comment the blocker on the issue, keep the branch
|
|
23
|
-
└─ exit 3 ──▶ comment the failure on the issue, open no PR
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
The plan is seeded inside `.neal/` — wrapper-owned, gitignored territory — so
|
|
27
|
-
it can never collide with a tracked file or appear in review diffs, and it is
|
|
28
|
-
never committed. The refined plan travels with the outcome instead: the PR
|
|
29
|
-
body (or the blocked/failed issue comment) embeds it in a collapsed details
|
|
30
|
-
block, which outlives the 3-day `.neal/` artifact retention.
|
|
31
|
-
|
|
32
|
-
The outcome also carries a cost ledger — per-provider turns, tokens, and
|
|
33
|
-
dollars aggregated from the run's `RUN_METRICS-final*.json` files. Claude
|
|
34
|
-
cost is provider-reported; reviewer cost is rate-computed from neal's
|
|
35
|
-
vendored published rate card (see `docs/providers.md`) and marked as such.
|
|
36
|
-
|
|
37
|
-
## Running an issue locally
|
|
38
|
-
|
|
39
|
-
`scripts/neal-issue-local.sh <issue-number> [plan-doc]` is the pipeline's
|
|
40
|
-
local twin for subscription billing: the coder rides the Claude CLI login and
|
|
41
|
-
the reviewer the codex CLI login (per `~/.neal/config.yml`) instead of the
|
|
42
|
-
API keys CI uses. It shares the plan seed and PR-body construction with
|
|
43
|
-
`pipeline/run.sh` through `pipeline/lib.sh`, so a local run produces the same
|
|
44
|
-
branch name, the same PR format, and the same embedded plan and cost ledger.
|
|
45
|
-
The optional second argument substitutes a pre-authored plan (for example a
|
|
46
|
-
`multi_scope` plan spanning several issues) for the seeded `one_shot` one.
|
|
47
|
-
The script unsets `OPENAI_API_KEY`/`ANTHROPIC_API_KEY` for the run so a stray
|
|
48
|
-
exported key cannot silently switch billing from subscription to API.
|
|
49
|
-
|
|
50
|
-
Every run executes in its own git worktree under `<repo>-worktrees/issue-<n>`,
|
|
51
|
-
branched from `origin/main`. Runs on different issues can proceed in parallel
|
|
52
|
-
(git enforces one worktree per branch), and the main checkout stays free for
|
|
53
|
-
your own work throughout. On success the worktree is removed after the push —
|
|
54
|
-
the branch lives on the PR. On blocked/failed outcomes the worktree is kept:
|
|
55
|
-
it is the resume state (`cd` in and `neal resume`). The practical concurrency
|
|
56
|
-
cap is your subscriptions, not git: parallel runs share your Claude and
|
|
57
|
-
ChatGPT rate limits.
|
|
58
|
-
|
|
59
|
-
- **Coder:** Claude Opus 4.8 (`anthropic-claude` adapter, `ANTHROPIC_API_KEY`).
|
|
60
|
-
- **Reviewer:** GPT-5.6-sol via the `openai-compatible` adapter pointed at the
|
|
61
|
-
OpenAI API (`OPENAI_API_KEY`). The reviewer judges neal-inlined diff/context;
|
|
62
|
-
it has no repository tools, which keeps the runner setup to two env vars and
|
|
63
|
-
no extra CLI.
|
|
64
|
-
|
|
65
|
-
## Setup
|
|
66
|
-
|
|
67
|
-
One-time, by a maintainer. The workflow no-ops safely until this is done.
|
|
68
|
-
|
|
69
|
-
1. **Secrets** (repo → Settings → Secrets → Actions, on the `neal-pipeline`
|
|
70
|
-
environment): `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`. Use short-lived,
|
|
71
|
-
minimally-scoped keys and rotate them (see Security below).
|
|
72
|
-
2. **Environment**: create a `neal-pipeline` environment (Settings →
|
|
73
|
-
Environments). Optionally add required reviewers so each run needs a manual
|
|
74
|
-
click before the secrets are exposed — recommended for a public repo.
|
|
75
|
-
3. **Reviewer model**: set the repo variable `NEAL_REVIEWER_MODEL` to the sol
|
|
76
|
-
model slug your OpenAI key serves. Before the first run, qualify it:
|
|
77
|
-
`neal compat --model <slug> --role reviewer --reference anthropic-claude`
|
|
78
|
-
(locally, with the same key). If sol is not reachable over the OpenAI
|
|
79
|
-
Chat Completions API, point `NEAL_REVIEWER_BASE_URL`/`NEAL_REVIEWER_MODEL` at
|
|
80
|
-
an endpoint that serves it, or switch the reviewer to the `openai-codex`
|
|
81
|
-
adapter in `pipeline/config.yml`.
|
|
82
|
-
4. **Trigger label**: create a label (default `neal:go`; override with the repo
|
|
83
|
-
variable `NEAL_TRIGGER_LABEL`). Optionally set `NEAL_READY_LABEL` (e.g.
|
|
84
|
-
`ready-for-human`) to tag the PRs neal opens.
|
|
85
|
-
5. **Branch protection**: protect `main` (require PR + review). The pipeline can
|
|
86
|
-
push `neal/issue-*` branches and open PRs, but must never be able to merge or
|
|
87
|
-
push to `main`.
|
|
88
|
-
|
|
89
|
-
## Security model
|
|
90
|
-
|
|
91
|
-
This runs on a public repository, so the trust boundary is "arbitrary internet
|
|
92
|
-
text becomes instructions to an agent with push access." The controls:
|
|
93
|
-
|
|
94
|
-
- **Maintainer-gated trigger.** The gate checks the *labeler's* repo permission
|
|
95
|
-
(`write`/`maintain`/`admin`), not the issue author. An untrusted issue cannot
|
|
96
|
-
start a run — only a maintainer applying the label can.
|
|
97
|
-
- **Untrusted issue input.** The issue title/body are passed as environment
|
|
98
|
-
variables (never interpolated into a shell command) and written to the seed
|
|
99
|
-
plan with `printf '%s'`, so they cannot inject shell commands. neal then
|
|
100
|
-
treats the seed as plan input, refined through the planner/reviewer loop.
|
|
101
|
-
- **Least privilege.** The workflow token can push `neal/issue-*` branches, open
|
|
102
|
-
PRs, and comment — nothing else. Branch protection keeps the human merge gate.
|
|
103
|
-
- **Boundaries in the plan.** The seed forbids edits to `.github/**` /
|
|
104
|
-
`pipeline/**` and forbids reading or printing secrets — defense-in-depth on
|
|
105
|
-
top of the token scope.
|
|
106
|
-
- **Secret exposure caveat.** The coder runs with shell in the runner, where
|
|
107
|
-
`ANTHROPIC_API_KEY` and `OPENAI_API_KEY` live in the environment. A
|
|
108
|
-
prompt-injected coder could try to exfiltrate them; the maintainer gate is
|
|
109
|
-
what makes that acceptable (only a maintainer's labeled issue ever runs). Use
|
|
110
|
-
short-lived, minimally-scoped keys, the `neal-pipeline` environment's approval
|
|
111
|
-
gate, and rotate on any suspicion.
|
|
112
|
-
|
|
113
|
-
## v1 limitations
|
|
114
|
-
|
|
115
|
-
- **Seed shape is `one_shot`.** Best for well-scoped issues ("fix X", "add Y").
|
|
116
|
-
For large multi-part work neal's coder will split the scope into a sub-plan;
|
|
117
|
-
if it can't be done in scope, neal blocks and comments asking for a smaller
|
|
118
|
-
issue. Write issues with a clear, bounded objective for best results.
|
|
119
|
-
- **Reviewer reads inlined context, not the repo.** The `openai-compatible`
|
|
120
|
-
reviewer judges the diff neal inlines, not the working tree. Switch to an
|
|
121
|
-
`openai-codex` reviewer (OS-sandboxed, repo-reading) once sol is confirmed on
|
|
122
|
-
that adapter's API path, if repo-reading review is wanted.
|
|
123
|
-
- **No comment-driven iteration yet.** On a blocked run you re-apply the label to
|
|
124
|
-
retry; a maintainer-comment → `neal resume --message` loop is a v2 addition.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Compat fixture: replace the placeholder test
|
|
2
|
-
|
|
3
|
-
## Execution Shape
|
|
4
|
-
|
|
5
|
-
executionShape: one_shot
|
|
6
|
-
|
|
7
|
-
## Objective
|
|
8
|
-
|
|
9
|
-
`src/is-even.js` exports a correct `isEven(n)` helper. Its test in
|
|
10
|
-
`test/is-even.test.js` is only a placeholder that always fails
|
|
11
|
-
(`assert.ok(false, ...)`).
|
|
12
|
-
|
|
13
|
-
Replace the placeholder assertion with a real test that exercises `isEven` —
|
|
14
|
-
asserting `isEven(4)` is `true` is enough — so the test suite passes. Keep the
|
|
15
|
-
existing `test(...)` block; only the placeholder assertion needs to change.
|
|
16
|
-
|
|
17
|
-
## Boundaries
|
|
18
|
-
|
|
19
|
-
Allowed paths:
|
|
20
|
-
|
|
21
|
-
- `test/is-even.test.js`
|
|
22
|
-
|
|
23
|
-
Forbidden:
|
|
24
|
-
|
|
25
|
-
- Do not edit `src/is-even.js` (it is already correct).
|
|
26
|
-
- Do not add dependencies or new files.
|
|
27
|
-
|
|
28
|
-
## Verification
|
|
29
|
-
|
|
30
|
-
Run `node --test test/is-even.test.js`; it must exit `0`.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
diff --git a/test/is-even.test.js b/test/is-even.test.js
|
|
2
|
-
index 52c2a94..0e9eec5 100644
|
|
3
|
-
--- a/test/is-even.test.js
|
|
4
|
-
+++ b/test/is-even.test.js
|
|
5
|
-
@@ -5,5 +5,5 @@ import { isEven } from '../src/is-even.js';
|
|
6
|
-
|
|
7
|
-
test('isEven identifies even numbers', () => {
|
|
8
|
-
// TODO: replace this placeholder with a real assertion on isEven.
|
|
9
|
-
- assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
|
|
10
|
-
+ assert.equal(isEven(4), false);
|
|
11
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
diff --git a/test/is-even.test.js b/test/is-even.test.js
|
|
2
|
-
index 52c2a94..28d1d73 100644
|
|
3
|
-
--- a/test/is-even.test.js
|
|
4
|
-
+++ b/test/is-even.test.js
|
|
5
|
-
@@ -5,5 +5,5 @@ import { isEven } from '../src/is-even.js';
|
|
6
|
-
|
|
7
|
-
test('isEven identifies even numbers', () => {
|
|
8
|
-
// TODO: replace this placeholder with a real assertion on isEven.
|
|
9
|
-
- assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
|
|
10
|
-
+ assert.equal(isEven(4), true);
|
|
11
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
|
|
4
|
-
import { isEven } from '../src/is-even.js';
|
|
5
|
-
|
|
6
|
-
test('isEven identifies even numbers', () => {
|
|
7
|
-
// TODO: replace this placeholder with a real assertion on isEven.
|
|
8
|
-
assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
|
|
9
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Compat fixture: fix the broken string helper
|
|
2
|
-
|
|
3
|
-
## Execution Shape
|
|
4
|
-
|
|
5
|
-
executionShape: one_shot
|
|
6
|
-
|
|
7
|
-
## Objective
|
|
8
|
-
|
|
9
|
-
`src/strings.js` exports several small string helpers. Exactly one of them is
|
|
10
|
-
buggy: `reverse(s)` is supposed to return the string with its characters
|
|
11
|
-
reversed, but it returns the string unchanged. The `reverse` test in
|
|
12
|
-
`test/strings.test.js` fails because of this.
|
|
13
|
-
|
|
14
|
-
Read `src/strings.js`, locate the faulty `reverse` helper, and make the smallest
|
|
15
|
-
complete change so it returns the reversed string. Do not touch the other
|
|
16
|
-
helpers, which already pass their tests.
|
|
17
|
-
|
|
18
|
-
## Boundaries
|
|
19
|
-
|
|
20
|
-
Allowed paths:
|
|
21
|
-
|
|
22
|
-
- `src/strings.js`
|
|
23
|
-
|
|
24
|
-
Forbidden:
|
|
25
|
-
|
|
26
|
-
- Do not edit the test file.
|
|
27
|
-
- Do not change the other (already-correct) helpers.
|
|
28
|
-
- Do not add dependencies or new files.
|
|
29
|
-
|
|
30
|
-
## Verification
|
|
31
|
-
|
|
32
|
-
Run `node --test test/strings.test.js`; it must exit `0`.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
diff --git a/src/strings.js b/src/strings.js
|
|
2
|
-
index c338691..fbfe71b 100644
|
|
3
|
-
--- a/src/strings.js
|
|
4
|
-
+++ b/src/strings.js
|
|
5
|
-
@@ -5,6 +5,6 @@ export const upper = (s) => s.toUpperCase();
|
|
6
|
-
export const lower = (s) => s.toLowerCase();
|
|
7
|
-
|
|
8
|
-
// BUG: this does not actually reverse the string.
|
|
9
|
-
-export const reverse = (s) => s.split('').join('');
|
|
10
|
-
+export const reverse = (s) => s.split('').join('-');
|
|
11
|
-
|
|
12
|
-
export const repeat = (s, n) => s.repeat(n);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
diff --git a/src/strings.js b/src/strings.js
|
|
2
|
-
index c338691..d17fc16 100644
|
|
3
|
-
--- a/src/strings.js
|
|
4
|
-
+++ b/src/strings.js
|
|
5
|
-
@@ -5,6 +5,6 @@ export const upper = (s) => s.toUpperCase();
|
|
6
|
-
export const lower = (s) => s.toLowerCase();
|
|
7
|
-
|
|
8
|
-
// BUG: this does not actually reverse the string.
|
|
9
|
-
-export const reverse = (s) => s.split('').join('');
|
|
10
|
-
+export const reverse = (s) => s.split('').reverse().join('');
|
|
11
|
-
|
|
12
|
-
export const repeat = (s, n) => s.repeat(n);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Trivial compat fixture: several string helpers live here. Exactly one of them
|
|
2
|
-
// is buggy, so an agent must read/grep to locate the faulty function.
|
|
3
|
-
export const upper = (s) => s.toUpperCase();
|
|
4
|
-
|
|
5
|
-
export const lower = (s) => s.toLowerCase();
|
|
6
|
-
|
|
7
|
-
// BUG: this does not actually reverse the string.
|
|
8
|
-
export const reverse = (s) => s.split('').join('');
|
|
9
|
-
|
|
10
|
-
export const repeat = (s, n) => s.repeat(n);
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
|
|
4
|
-
import { lower, repeat, reverse, upper } from '../src/strings.js';
|
|
5
|
-
|
|
6
|
-
test('upper/lower/repeat helpers are correct', () => {
|
|
7
|
-
assert.equal(upper('abc'), 'ABC');
|
|
8
|
-
assert.equal(lower('ABC'), 'abc');
|
|
9
|
-
assert.equal(repeat('ab', 3), 'ababab');
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('reverse returns the string reversed', () => {
|
|
13
|
-
assert.equal(reverse('abc'), 'cba');
|
|
14
|
-
assert.equal(reverse('neal'), 'laen');
|
|
15
|
-
});
|