@agentsdance/codejury 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zzxwill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # Code Jury
2
+
3
+ **Multiple reviewers. One clean PR.**
4
+
5
+ A convergence loop that runs independent review agents against a pushed commit, applies the findings
6
+ that survive verification, and repeats until every agent reports nothing new.
7
+
8
+ Derived from a real run on `worker-pool` #128 (exponential CreateSandbox retry backoff).
9
+ Three rounds, two agents (`codex`, `droid`), converged. See [CASE-STUDY.md](CASE-STUDY.md) for the
10
+ findings and the numbers.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install -g @agentsdance/codejury # then: jury <pr-url>
16
+ npx @agentsdance/codejury <pr-url> # or without installing
17
+ ```
18
+
19
+ The GitHub PR URL is authoritative. `jury` resolves its base, source branch and exact
20
+ head commit, reviews an isolated temporary checkout, and pushes accepted fixes
21
+ back to the source branch. It is safe to invoke from `master` or from outside
22
+ the target repository; the caller's working tree is not switched or modified.
23
+
24
+ Node 20 or newer. The reviewers are separate CLIs you install yourself — `jury` spawns whatever you
25
+ have and skips the rest:
26
+
27
+ ```bash
28
+ jury agents # which are installed, and which role each holds
29
+ ```
30
+
31
+ `jury` reads `jury.config.json` from the repo you are reviewing, if present; copy
32
+ [`jury.config.example.json`](jury.config.example.json) to start. Without one, the built-in registry is
33
+ used. The former `cr.config.json` and `macr.config.json` names remain readable for compatibility.
34
+
35
+ The former `cr` command remains available as a compatibility alias.
36
+
37
+ ## The loop
38
+
39
+ ```
40
+ round N
41
+ ├─ worktree at the pushed HEAD
42
+ ├─ launch every agent in parallel, in the background
43
+
44
+ ├─ per agent: triage each finding
45
+ │ ├─ reproduce it against the code ← before touching anything
46
+ │ ├─ fix, and add a regression test
47
+ │ └─ reintroduce the bug, confirm the test fails
48
+
49
+ ├─ gofmt + vet + full suite → amend → push --force-with-lease
50
+ ├─ append newly-settled items to the prompt
51
+
52
+ └─ stop iff every agent emitted the stop token on the SAME commit
53
+ else round N+1
54
+ ```
55
+
56
+ Two properties make it terminate rather than churn:
57
+
58
+ - **A growing settled list.** Without it each fresh agent rediscovers the same deferred issues every
59
+ round, forever.
60
+ - **A literal stop token.** Termination is a `grep`, not a judgement call.
61
+
62
+ ## Watching it happen
63
+
64
+ A round is a reviewer talking for several minutes and then a wall of text. `--web` starts the console
65
+ in the same process as the run, so the conversation streams as it is spoken rather than arriving at
66
+ the end:
67
+
68
+ ```
69
+ jury --web https://github.com/owner/repo/pull/1 # review, with the console open on it
70
+ jury --web --port 3099 --rounds 3 # the current branch, on another port
71
+ ```
72
+
73
+ The console outlives the loop — it stays up until you ctrl-c, which is the point: the run finishing
74
+ is when there is finally something worth reading. `jury web` still serves the same console
75
+ standalone, against runs that already exist.
76
+
77
+ ## Who writes, and who only reads
78
+
79
+ Exactly one agent has role `main` — **Claude Code**, the session driving the loop. It owns the working
80
+ tree and the commit, triages every finding, and is the only writer. Its registry entry carries an
81
+ empty `argv` because it is never spawned; `jury agents` shows it as in-process:
82
+
83
+ ```
84
+ ok claude main (in-process)
85
+ ok codex reviewer /usr/local/bin/codex
86
+ ok agy reviewer /usr/local/bin/agy
87
+ ```
88
+
89
+ Reviewers only read and report. A registry with two `main` entries is refused outright — two agents
90
+ both believing they own the commit corrupts a run rather than merely failing it.
91
+
92
+ The main agent then holds **one conversation per reviewer**, concurrently, each about that reviewer's
93
+ own findings and nothing else:
94
+
95
+ ```
96
+ claude ──▶ codex its 5 findings, resumed session
97
+ └──▶ agy its 5 findings, fresh run with its own words quoted back
98
+ ```
99
+
100
+ Never a broadcast. Two reviewers that see each other's findings stop being independent, and their
101
+ agreement stops being evidence — which is the only reason to run more than one. On aigit #48 both
102
+ independently found the same Windows `os.Rename` bug; neither was told the other had. `jury reply`
103
+ redacts other agents' names and finding ids out of the verdict text, so independence does not depend
104
+ on the operator remembering.
105
+
106
+ ## More than one PR
107
+
108
+ Each PR is its own run directory — `runs/<repo>-<id>/` — with its own event log, findings, and settled
109
+ list. Nothing is shared, so reviewing several at once needs no coordination:
110
+
111
+ ```
112
+ jury runs # every PR under review, with its slug
113
+ jury finding list --run agentsdance-aigit-48
114
+ ```
115
+
116
+ With one run, `--run` is optional. With several it is required, and the commands that need it refuse
117
+ with the available slugs rather than guessing. The console serves them all and shows a chip per PR;
118
+ picking one does not disturb a review running on another.
119
+
120
+ The one thing genuinely shared is the machine: reviewers are subprocesses, so two PRs reviewing at
121
+ once run two of every agent. The slowest still sets each round's wall clock.
122
+
123
+ ## Agents are configuration
124
+
125
+ The loop is agent-agnostic; `codex` and `droid` are just the two entries that happen to be enabled.
126
+ Adding a third is a config change, not a code change. See [config.example.yaml](config.example.yaml).
127
+
128
+ Adding `agy` for the aigit #48 run was exactly that — a `jury.config.json` entry, no code:
129
+
130
+ ```json
131
+ { "name": "agy", "promptDelivery": "argv", "cwd": "worktree",
132
+ "argv": ["agy", "--dangerously-skip-permissions", "--add-dir", "{{worktree}}", "--print", "{{promptText}}"],
133
+ "report": "whole", "expectSeconds": 300 }
134
+ ```
135
+
136
+ Two of its quirks are worth knowing, because both cost a wasted run: its permission flag must precede
137
+ `--print` or every file read is auto-denied and it returns an empty report, and it picks its own
138
+ working directory, so the worktree needs `--add-dir` *and* a mention in the prompt.
139
+
140
+ Only three things actually vary between agents, and all three bit during the reference run:
141
+
142
+ | dimension | values | why it matters |
143
+ |---|---|---|
144
+ | `promptDelivery` | `argv` \| `file` \| `stdin` | codex takes the prompt as an argument, droid needs `-f <path>` |
145
+ | `cwd` | `worktree` \| `flag` | codex runs *in* the worktree, droid takes `--cwd` and ignores process cwd |
146
+ | `resume.supported` | `true` \| `false` | decides whether feedback is a real conversation or a fresh run with prior findings quoted back |
147
+
148
+ ```yaml
149
+ agents:
150
+ - name: codex
151
+ promptDelivery: argv
152
+ cwd: worktree
153
+ argv: ["codex", "exec", "--skip-git-repo-check", "{{promptText}}"]
154
+ resume: { supported: true, argv: ["codex", "exec", "resume", "--last", "{{promptText}}"] }
155
+ expect: { latencySeconds: 1100, verbose: true }
156
+
157
+ - name: droid
158
+ promptDelivery: file
159
+ cwd: flag
160
+ argv: ["droid", "exec", "--cwd", "{{worktree}}", "--auto", "medium", "-f", "{{promptFile}}"]
161
+ resume: { supported: false, reason: "exec text output carries no session id" }
162
+ expect: { latencySeconds: 130, verbose: false }
163
+ ```
164
+
165
+ Run every enabled agent concurrently and in the background — the slowest sets the round's wall clock,
166
+ so serialising a 20-minute agent behind a 2-minute one wastes most of it.
167
+
168
+ One parsing subtlety worth encoding per agent: a verbose agent's stdout is a **full transcript that
169
+ contains the prompt**, so grepping it for the stop token matches the instruction that asked for the
170
+ token. Parse the final report block, not the whole stream — hence `parse.reportFrom`.
171
+
172
+ ## Prompt anatomy
173
+
174
+ [`prompts/review-round.md`](prompts/review-round.md) — four sections, in this order:
175
+
176
+ 1. **What the change does** — the design in a few bullets, so the agent does not have to infer intent.
177
+ 2. **ALREADY SETTLED — do NOT re-report** — a numbered list, appended to after every round. For items
178
+ you are deliberately *deferring*, include the reasoning, so an agent can argue with the reasoning
179
+ instead of re-proposing a fix you already rejected.
180
+ 3. **What you want** — the specific classes of defect. Always include the test-quality question:
181
+ *"would each assertion actually fail if the behaviour it guards regressed? Read the assertions,
182
+ not the test names."* That question produced every finding in rounds 2 and 3.
183
+ 4. **The stop token** — `say exactly "NO NEW FINDINGS" on its own line`.
184
+
185
+ Plus `DO NOT edit any files`. You want findings to triage, not competing patches to merge.
186
+
187
+ [`prompts/feedback.md`](prompts/feedback.md) — structured per finding as
188
+ **ACCEPTED / AGREE-BUT-DEFERRED / REJECTED**, each with reasoning, each ending in a direct question.
189
+ Closing the loop this way is what turned two "you should fix this" items into explicit agreement to
190
+ defer them.
191
+
192
+ ## Triage discipline
193
+
194
+ This is the part that carries the value. In the reference run **roughly a third of suggestions did not
195
+ survive verification**, and the loop also surfaced three of the operator's own mistakes.
196
+
197
+ Rules that earned their place:
198
+
199
+ 1. **Reproduce before fixing.** Every accepted finding was demonstrated against the real code first.
200
+ 2. **Every fix gets a regression test, and the test gets verified by reintroducing the bug.** A test
201
+ that passes both with and without the fix is decoration.
202
+ 3. **A test that hangs on regression is worse than one that fails.** One deterministic test drained a
203
+ buffered channel and then blocked forever; it burned the CI timeout and read as an infra fault.
204
+ Close the channel so the regression terminates.
205
+ 4. **Distinguish "no hits" from "broken query."** When verifying against logs or metrics, run a control
206
+ that you know should return rows. A zero is not evidence until you have proven the pipe works.
207
+ 5. **An agent's suggested fix can be wrong even when its finding is right.** One reviewer correctly
208
+ identified a cancellation gap, then proposed a guard that consumed a queued job without releasing
209
+ its accounting reservation — trading a bounded extra RPC for a permanent resource leak.
210
+
211
+ ## Known traps
212
+
213
+ - **Diff base.** `git diff origin/master` shows commits master gained *since* your branch point as
214
+ deletions in your diff. Always diff against `git merge-base HEAD origin/master`. This produced a
215
+ confident, completely spurious finding on round zero.
216
+ - **Prompt-induced findings are your bug, not theirs.** Fix the prompt and say so.
217
+ - **`timeout` does not exist on macOS.** Wrapping a verification in it exits 127 and the command never
218
+ runs — you get an empty result that looks like a pass.
219
+ - **Removing a call can orphan an import**, turning "the test caught it" into a compile error. Keep the
220
+ import used when simulating a regression.
221
+ - **Mock frameworks may not re-apply inside a loop.** Build the mock once outside; re-`Build()`ing per
222
+ iteration silently kept the real implementation in the reference run (mockey).
223
+ - **Agents recycle suggestions when they have nothing left.** A verbatim repeat of a prior cosmetic note
224
+ is a decent signal of convergence.
225
+ - **Skipping fenced text to avoid quoted material will also skip real findings.** The prompt shows the
226
+ `FINDING:`/`WHERE:` header inside a fence, so agents answer in the same shape — on aigit #48 that
227
+ silently dropped all five of one reviewer's findings while its report sat there in full. A fence
228
+ containing *only* the header pair is the reviewer speaking; one holding a diff or code is not.
229
+ - **Two review processes on one round corrupt the record.** Relaunching without killing the first run
230
+ gave round 1 two codex reports and merged their findings. Kill the prior run, or start a new round.
231
+
232
+ ## What a system built on this would need
233
+
234
+ The manual run was the loop above driven by hand. To automate:
235
+
236
+ - an agent registry: invocation, cwd handling, whether sessions resume, expected latency
237
+ - prompt assembly: template + accumulated settled list, versioned per round
238
+ - a findings model: `{id, agent, round, severity, file, line, claim, status}` where status is one of
239
+ `accepted | deferred | rejected | superseded`, so the settled list generates itself
240
+ - the reproduce/fix/verify gate as an explicit state machine — the step most worth enforcing, since it
241
+ is the step most easily skipped
242
+ - convergence detection across N agents on a single commit sha
243
+ - a per-round budget, because the long-tail agent dominates wall clock
244
+
245
+ Deliberately **not** automated in the reference run: applying a fix without first reproducing the
246
+ finding. That gate is where the value was.