amicus 4.8.0 → 4.9.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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +355 -0
- package/README.md +10 -5
- package/docs/CITATIONS.md +13 -5
- package/docs/ROADMAP.md +101 -10
- package/docs/configuration.md +55 -5
- package/docs/council.md +102 -14
- package/docs/troubleshooting.md +9 -2
- package/docs/usage.md +128 -12
- package/electron/ipc-setup.js +39 -2
- package/electron/main.js +46 -3
- package/electron/offer-session.js +51 -0
- package/electron/setup-ui-model.js +99 -9
- package/electron/setup-ui-styles.js +22 -0
- package/electron/setup-ui.js +244 -32
- package/electron/workspace-ui/live-dead-seats.js +163 -91
- package/electron/workspace-ui/live-seats.js +4 -4
- package/electron/workspace-ui/workspace-banners.js +30 -7
- package/electron/workspace-ui/workspace-matrix.js +23 -3
- package/electron/workspace-ui/workspace-seats.js +95 -79
- package/package.json +2 -1
- package/schemas/council-run.schema.json +2 -2
- package/schemas/council-tally.schema.json +17 -1
- package/schemas/council-verdict.schema.json +12 -4
- package/schemas/run.schema.json +6 -1
- package/skills/second-opinion/COUNCIL-DESIGN.md +1 -1
- package/skills/second-opinion/MANUAL-ORCHESTRATION.md +1 -1
- package/skills/second-opinion/MODEL-NOTES.md +88 -9
- package/skills/second-opinion/SEAT-BRIEFS.md +36 -4
- package/skills/second-opinion/SKILL.md +151 -36
- package/src/cli-council-run-bench.js +98 -6
- package/src/cli-handlers-council-run.js +18 -6
- package/src/cli-handlers-council.js +57 -7
- package/src/cli-handlers-doctor.js +12 -15
- package/src/cli.js +3 -1
- package/src/council/anonymize.js +2 -1
- package/src/council/briefings-chair-task.js +161 -0
- package/src/council/briefings-chair.js +33 -8
- package/src/council/briefings-debate.js +79 -13
- package/src/council/briefings-stage2-task.js +236 -0
- package/src/council/briefings-stage2.js +103 -26
- package/src/council/briefings-task.js +167 -0
- package/src/council/briefings.js +41 -4
- package/src/council/chair-fallback.js +95 -0
- package/src/council/debate.js +38 -21
- package/src/council/findings.js +3 -2
- package/src/council/ledger.js +2 -2
- package/src/council/parse-stage2.js +64 -16
- package/src/council/report-cost.js +61 -0
- package/src/council/report-html.js +26 -4
- package/src/council/report-md.js +30 -2
- package/src/council/report.js +40 -37
- package/src/council/run-assemble.js +21 -6
- package/src/council/run-chair.js +44 -95
- package/src/council/run-debate-revote.js +81 -49
- package/src/council/run-debate.js +51 -34
- package/src/council/run-finish.js +5 -3
- package/src/council/run-retry-keys.js +4 -4
- package/src/council/run-retry-launch.js +4 -4
- package/src/council/run-retry-notes.js +72 -15
- package/src/council/run-stage1-launch.js +4 -4
- package/src/council/run-stage1-rows.js +9 -6
- package/src/council/run-stage2.js +81 -47
- package/src/council/run-stages.js +9 -21
- package/src/council/run-stats-entry.js +46 -1
- package/src/council/run.js +28 -13
- package/src/council/seats.js +2 -2
- package/src/council/stage1-bind.js +3 -2
- package/src/council/verdict-seat-loss.js +124 -0
- package/src/council/verdict.js +108 -99
- package/src/headless.js +256 -49
- package/src/mcp-council-bench.js +64 -3
- package/src/mcp-council-run.js +10 -3
- package/src/mcp-server.js +52 -12
- package/src/mcp-tools.js +41 -5
- package/src/observe/council-legs.js +2 -2
- package/src/opencode-client.js +19 -1
- package/src/pack/pack-forward.js +15 -12
- package/src/pack/pack-resolve.js +1 -1
- package/src/prompt-builder.js +17 -1
- package/src/sidecar/fanout-leg.js +26 -0
- package/src/sidecar/fanout.js +1 -1
- package/src/sidecar/list-council.js +178 -0
- package/src/sidecar/list-limit.js +3 -1
- package/src/sidecar/list-search.js +2 -1
- package/src/sidecar/models.js +8 -1
- package/src/sidecar/read.js +34 -10
- package/src/sidecar/setup.js +124 -0
- package/src/template/render.js +16 -7
- package/src/utils/alias-audit.js +81 -3
- package/src/utils/alias-shadow-writer.js +220 -0
- package/src/utils/alias-shadow.js +294 -0
- package/src/utils/config.js +1 -1
- package/src/utils/curated-models.js +16 -8
- package/src/utils/degrade.js +12 -5
- package/src/utils/doctor-alias-check.js +149 -0
- package/src/utils/engine-log-parse.js +289 -0
- package/src/utils/engine-log-tail.js +114 -0
- package/src/utils/engine-log.js +250 -0
- package/src/utils/engine-skew-records.js +146 -0
- package/src/utils/engine-skew.js +300 -0
- package/src/utils/gateway-router.js +10 -2
- package/src/utils/model-canonicalization.js +64 -0
- package/src/utils/model-catalog.js +1 -1
- package/src/utils/model-shortlist.js +100 -0
- package/src/utils/provider-default-picker.js +93 -45
- package/src/utils/provider-default-prompt.js +1 -1
- package/src/utils/quick-picks.js +2 -2
- package/src/utils/remediation-hints.js +24 -0
- package/src/utils/result-schema.js +10 -0
- package/src/utils/text-sanitize.js +81 -0
- package/src/utils/ttft.js +57 -0
- package/src/utils/untrusted-fence.js +111 -1
- package/src/workspace/fold-format.js +28 -7
- package/src/workspace/live-normalize.js +2 -1
- package/src/workspace/matrix-model.js +6 -2
- package/src/workspace/run-detail.js +35 -9
- package/src/workspace/seat-space.js +10 -6
|
@@ -18,6 +18,15 @@ non-Claude chair, per the council's core rule)._
|
|
|
18
18
|
(`src/council/briefings.js` + `briefings-stage2.js` + `briefings-debate.js`). This file stays
|
|
19
19
|
authoritative for the manual path and for element *semantics*.
|
|
20
20
|
|
|
21
|
+
**v4.9 — everything below is REVIEW wording.** Task mode (`--intent task`, SKILL.md §5.5) has an
|
|
22
|
+
engine-composed twin of every block here (`briefings-task.js`, `briefings-stage2-task.js`,
|
|
23
|
+
`briefings-chair-task.js`, and the task frames in `briefings-debate.js`), so a fast-path task run
|
|
24
|
+
needs nothing from this file. Only the **manual path** has to swap the wording by hand: the seat
|
|
25
|
+
role becomes *do the work, do not review the briefing*; "findings" become the **load-bearing claims**
|
|
26
|
+
the deliverable rests on (same JSON shape, same severity enum, `location` = source, computation, or
|
|
27
|
+
the word `assumption`); the judge's ranking axis becomes *how well the work was done* rather than
|
|
28
|
+
how accurate a critique was; and the chair closes on the ANSWER scale below.
|
|
29
|
+
|
|
21
30
|
---
|
|
22
31
|
|
|
23
32
|
## Standard anti-sycophancy clause (ALL Stage-1 briefings — not an optional element)
|
|
@@ -173,9 +182,11 @@ verbatim:
|
|
|
173
182
|
|
|
174
183
|
---
|
|
175
184
|
|
|
176
|
-
## Chair
|
|
185
|
+
## Chair closing-scale addendum
|
|
177
186
|
|
|
178
|
-
Append to the chair packet (`_tmp-chair-packet.md`)
|
|
187
|
+
Append to the chair packet (`_tmp-chair-packet.md`). ⚠️ **No longer an opt-in element** — the engine
|
|
188
|
+
makes every chair close on a scale, so the manual path must too; SKILL.md's Stage-0 menu says the
|
|
189
|
+
same. Review runs use the VERDICT scale below; task runs use the ANSWER twin after it.
|
|
179
190
|
|
|
180
191
|
> After your synthesis, add two closing sections:
|
|
181
192
|
>
|
|
@@ -192,5 +203,26 @@ Append to the chair packet (`_tmp-chair-packet.md`) when the element is toggled
|
|
|
192
203
|
> Name the gaps or the structural problems in the synthesis ABOVE, not on the VERDICT
|
|
193
204
|
> line itself — that line carries the phrase and nothing else.
|
|
194
205
|
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
### Task-mode twin — the ANSWER scale
|
|
207
|
+
|
|
208
|
+
Same two closing sections, same "final line, alone" rule, same HARD QUESTIONS item (all three are
|
|
209
|
+
scale-independent). Only the phrase list and its gloss change:
|
|
210
|
+
|
|
211
|
+
> `ANSWER: Converged` | `ANSWER: Split` | `ANSWER: Insufficient`
|
|
212
|
+
>
|
|
213
|
+
> Pick one. "Converged" = the bench substantially agrees and the synthesis above is
|
|
214
|
+
> well-supported. "Split" = material disagreement remains; the synthesis states both positions
|
|
215
|
+
> and what would settle them. "Insufficient" = the bench's work cannot support an answer —
|
|
216
|
+
> missing information, an unsound premise, or too little usable output. Name which, in the
|
|
217
|
+
> synthesis ABOVE, not on the ANSWER line.
|
|
218
|
+
|
|
219
|
+
The task chair's synthesis instruction also differs: adopt the strongest response, merge
|
|
220
|
+
complementary ones, or refuse the premise if the bench showed it unsound — then close the synthesis
|
|
221
|
+
with a **RESIDUAL RISK** section naming the disputed claims the answer still depends on, or the
|
|
222
|
+
single line `RESIDUAL RISK: none — no load-bearing claim was disputed.` when that is the truth.
|
|
223
|
+
(Drop the RESIDUAL RISK close when the bench declared no claims at all: it is defined as "the claims
|
|
224
|
+
peers disputed", and asking for it over an empty set is an unfollowable instruction.)
|
|
225
|
+
|
|
226
|
+
**Orchestration note:** surface the chair's closing line — `VERDICT:` or `ANSWER:`, whichever the
|
|
227
|
+
chair actually wrote — verbatim at the top of `report.md` and in the inline chat presentation of
|
|
228
|
+
the results. Never translate one scale into the other.
|
|
@@ -1,31 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: second-opinion
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
opinion", "multi-model review", "council review", "have other models
|
|
4
|
+
A structured, multi-model "council" that either REVIEWS material against criteria or
|
|
5
|
+
DOES the open-ended work asked for, turning either into decisions. Models work
|
|
6
|
+
independently, then anonymously rank and adjudicate each other's output; a non-Claude
|
|
7
|
+
"chair" synthesizes the verdict or answer — Claude orchestrates, never synthesizes.
|
|
8
|
+
Trigger on "second opinion", "multi-model review", "council review", "have other models
|
|
10
9
|
review/critique/evaluate this", "cross-check this against the research",
|
|
11
|
-
"red-team/stress-test this doc", "what would other models conclude about this", or
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
another model — use the sidecar
|
|
10
|
+
"red-team/stress-test this doc", "what would other models conclude about this", or any
|
|
11
|
+
request to have external models review material and turn it into accept/deny decisions —
|
|
12
|
+
even without the word "sidecar". Also TASK mode: "have the council work this out", or
|
|
13
|
+
any request for several models to independently produce an analysis, answer, or artifact
|
|
14
|
+
and reconcile them. NOT for quick or exploratory single-model chats — "ask
|
|
15
|
+
Gemini…", brainstorming, or forking a chat with another model — use the sidecar
|
|
16
|
+
skill instead.
|
|
17
17
|
---
|
|
18
18
|
|
|
19
19
|
# Second Opinion (LLM Council)
|
|
20
20
|
|
|
21
|
-
Independent, multi-model review of material the user provides
|
|
21
|
+
Independent, multi-model work — either a review of material the user provides or the work itself — turned into decisions. Any single model — including the one running this conversation — has consistent blind spots. Routing the same brief through models from *different* families surfaces disagreements, missed issues, and overstated claims that one model alone won't catch.
|
|
22
22
|
|
|
23
23
|
Four principles govern this skill:
|
|
24
24
|
|
|
25
25
|
1. **Secondary tool.** By the time this skill runs, Claude has already given its opinion in the main conversation. The skill exists to bring in *independent outside* views — it does not replace or re-run Claude's upstream analysis.
|
|
26
26
|
2. **The council is the non-Claude bench by default.** Council members are models from families other than the orchestrator (Gemini, DeepSeek, GPT, etc.). Claude is not a first-opinion council member unless the optional "Claude in the council" toggle is on — and even then it is judged but does not vote or chair.
|
|
27
27
|
3. **Claude orchestrates; Claude does not synthesize the verdict.** Claude preps material, recommends the council, drives the run, presents accept/deny decisions, and applies them. A designated non-Claude chair model synthesizes the final verdict. Claude's role ends at presenting it.
|
|
28
|
-
4. **The subject of cross-review is the other reviews, not the artifact again.** In the peer cross-review stage, models critique and rank *each other's reviews* — not re-review the original artifact. This is the mechanism that surfaces reviewer blind spots and inflated confidence.
|
|
28
|
+
4. **The subject of cross-review is the other reviews, not the artifact again.** In the peer cross-review stage, models critique and rank *each other's reviews* — not re-review the original artifact (in task mode: each other's *responses*; the subject is never the briefing again). This is the mechanism that surfaces reviewer blind spots and inflated confidence.
|
|
29
|
+
|
|
30
|
+
**Two intents, one machine (v4.9).** The council runs in one of two intents, and choosing between
|
|
31
|
+
them is a Stage-0 decision, not a wording preference:
|
|
32
|
+
|
|
33
|
+
- **Review (default).** The user supplies material and the bench reviews it against criteria. Seats
|
|
34
|
+
raise findings; judges rank how accurate each review was; the chair closes
|
|
35
|
+
`VERDICT: Ship it | Fix these first | Fundamental rethink`.
|
|
36
|
+
- **Task** (`--intent task`). The user asks for work to be DONE, and the bench does it — each seat
|
|
37
|
+
independently produces the analysis, answer, or artifact the briefing requests, plus the
|
|
38
|
+
load-bearing claims it rests on. Judges rank the responses by *how well each did the work* and
|
|
39
|
+
adjudicate those claims; the chair synthesizes an ANSWER across them and closes
|
|
40
|
+
`ANSWER: Converged | Split | Insufficient`.
|
|
41
|
+
|
|
42
|
+
Everything else is shared: same seats, same anonymized bundle, same tier machinery (though a tier
|
|
43
|
+
means peer *concurrence* on a task run, not peer verification of a defect), same commands, same
|
|
44
|
+
artifacts under the same filenames (a seat's deliverable still lands in `review-<seat>.md`). Task
|
|
45
|
+
mode is not a lesser mode or a fallback — see *Key mechanics → §5.5*.
|
|
29
46
|
|
|
30
47
|
**The engine runs the mechanics (v4.1).** Stages 1–3 and the deterministic Stage-5 artifacts are
|
|
31
48
|
ONE `amicus council run` call: the engine composes every model-facing briefing, runs the review
|
|
@@ -47,7 +64,8 @@ Operating lessons from each run fold back into `MODEL-NOTES.md` (with approval),
|
|
|
47
64
|
- The user provides documents, artifacts, or links **and** an analysis request **and** criteria, and wants other models to weigh in independently.
|
|
48
65
|
- They want a fact-check, critique, research-backed evaluation, red-team, or "what would another model conclude about this?"
|
|
49
66
|
- They need actionable decisions (accept / defer / deny) on the material, not just a summary of findings.
|
|
50
|
-
-
|
|
67
|
+
- **Task mode:** the ask is open-ended work — produce the analysis, the answer, the artifact — and they want several independent models to do it and the disagreements reconciled rather than one model's draft. Run it with `--intent task`; §5.5 has the mechanics.
|
|
68
|
+
- A thorough **single-model** pass is wanted — scales down gracefully; the boundary vs the `sidecar` skill is **intent** (structured, criteria-bound work reconciled into decisions), not model count.
|
|
51
69
|
|
|
52
70
|
## When NOT to use
|
|
53
71
|
|
|
@@ -68,7 +86,15 @@ human-in-the-loop; the engine call itself is one background command you do not p
|
|
|
68
86
|
|
|
69
87
|
### Stage 0 — Intake & prep
|
|
70
88
|
|
|
71
|
-
|
|
89
|
+
**Settle the intent first — it changes what the inputs are.** Ask which the user wants only when
|
|
90
|
+
the ask is genuinely ambiguous; usually it is plain. Review when there is a *thing* and the ask is a
|
|
91
|
+
judgment on it ("is this right", "what did I miss", "red-team this"). Task when the deliverable does
|
|
92
|
+
not exist yet and the ask is to produce it ("work out X", "draft the plan", "analyze this market").
|
|
93
|
+
An ask that supplies material but wants new work built *from* it ("take these notes and write the
|
|
94
|
+
strategy") is a TASK — the material is input, not the subject. State the intent in the run-shape
|
|
95
|
+
disclosure below so the user can correct you before any spend.
|
|
96
|
+
|
|
97
|
+
Then confirm the three inputs before doing anything else — **review:** **source material**, **the analysis** (the thing to be reviewed), and **the criteria** (what quality/correctness means for this material); **task:** **the brief** (the work to be done, stated precisely enough that two models can do it without asking), any **material** it should draw on, and **the criteria** (what a good answer must do — scope, depth, form, what counts as evidence). Ask only for what is missing; don't re-ask for what is already provided.
|
|
72
98
|
|
|
73
99
|
**Establish the run folder first:** `output/<stem>-council/` (or `./second-opinion/<stem>-council/`
|
|
74
100
|
if no `output/` directory exists). Create it now — it is both your working directory and the
|
|
@@ -82,14 +108,19 @@ its absolute path in all path arguments.
|
|
|
82
108
|
- Small, clean text → feed inline in the briefing.
|
|
83
109
|
|
|
84
110
|
**Author ONE briefing file: `<run-folder>/briefing.md`.** This is the only briefing Claude
|
|
85
|
-
writes, and it carries only the *review request
|
|
111
|
+
writes, and it carries only the *request* — the review request, or in task mode the work request:
|
|
86
112
|
|
|
87
113
|
- the material — inline when small and clean, otherwise the **absolute path** of the extracted
|
|
88
114
|
clean-text file (Stage-1 seats run agentically in the invoking cwd and can read referenced files);
|
|
89
|
-
- the analysis request;
|
|
115
|
+
- the analysis request (task mode: the brief — what to produce, and in what form);
|
|
90
116
|
- the criteria;
|
|
91
117
|
- any material-specific cautions ("the appendix is out of scope", "treat the numbers as given").
|
|
92
118
|
|
|
119
|
+
**Task mode: write the brief, not the answer.** The seats execute this file. Do not sketch the
|
|
120
|
+
conclusion, pre-select an approach, or attach your own draft — every one of those collapses the
|
|
121
|
+
independence the bench exists for, and unlike a leaked review contract the engine cannot detect it.
|
|
122
|
+
State the goal, the constraints, the audience and the form; leave the work to the bench.
|
|
123
|
+
|
|
93
124
|
**Do not restate output contracts in `briefing.md`.** The anti-sycophancy clause, the findings-JSON
|
|
94
125
|
schema, the judge tasks, the no-tools preambles, the chair packet and the verdict-scale addendum
|
|
95
126
|
are all the engine's (`briefings.js` templates) — duplicated contracts drift and the engine's copy
|
|
@@ -147,15 +178,19 @@ seats**; a 1-model run is the scale-down path below, not an engine run.
|
|
|
147
178
|
bench and chair are picked and before asking for launch confirmation, present this menu once
|
|
148
179
|
(adjust the run-shape numbers to the actual bench):
|
|
149
180
|
|
|
150
|
-
> Optional council elements — all OFF unless you name them. Reply with any you want (e.g. "1 and 3", "critic + debate mode", or "none"). Note the chair's **verdict scale is now standard** — the engine always makes the chair close with `VERDICT: Ship it | Fix these first | Fundamental rethink` plus its hard questions, so it is no longer something to opt into:
|
|
181
|
+
> Optional council elements — all OFF unless you name them. Reply with any you want (e.g. "1 and 3", "critic + debate mode", or "none"). Note the chair's **verdict scale is now standard** — the engine always makes the chair close with `VERDICT: Ship it | Fix these first | Fundamental rethink` plus its hard questions, so it is no longer something to opt into (a task run closes on the ANSWER twin instead — `Converged | Split | Insufficient`):
|
|
151
182
|
>
|
|
152
183
|
> 1. **Critic seat** (`--critic <model>`) — one reviewer, which must be one of the bench seats, swaps to an adversarial brief (adversarial pass, edge-case hunt, consistency check, executability test). Same review count. Trade-off: that reviewer can recognize its own review during cross-review (disclosed in the report).
|
|
153
184
|
> 2. **Expert lenses** (`--lenses s1,s2,s3`) — each reviewer gets a distinct expert perspective; you pick the panel domain (business, technical, customer, financial, custom), one lens per seat. Trade-offs: weakens cross-review anonymity (disclosed) and the run is **not** recorded to the reliability ledger.
|
|
154
185
|
> 3. **Debate mode** (`--debate`) — after cross-review, Contested and Disputed findings go back to their raisers to defend, amend, or withdraw, and the disputing judges re-vote before the final tally. Adds 1–2 short waves (up to ~2N extra calls, ~+5 min).
|
|
155
|
-
> 4. **Claude in the council** (`--claude-review`) — I add my own fresh review to the bundle so the bench can rank and adjudicate it; I'm judged but do not vote or chair, so the verdict stays independent. +1 review in the bundle, no extra council calls.
|
|
186
|
+
> 4. **Claude in the council** (`--claude-review`) — **review runs only.** I add my own fresh review to the bundle so the bench can rank and adjudicate it; I'm judged but do not vote or chair, so the verdict stays independent. +1 review in the bundle, no extra council calls.
|
|
156
187
|
|
|
157
188
|
Rules for this menu:
|
|
158
189
|
|
|
190
|
+
- **On a task run, offer 1–3 only.** `--claude-review` enters a *review* as review N+1 and has no
|
|
191
|
+
task-mode meaning; the engine rejects the pair pre-flight, exit 1 before any spend. Don't offer
|
|
192
|
+
it and then have the launch fail.
|
|
193
|
+
|
|
159
194
|
- **Never enable an element the user did not explicitly name.** Silence, "no", or "none" = all off. Do not infer opt-in from the nature of the material ("this doc could use a critic…") — offer, don't decide.
|
|
160
195
|
- If elements were **pre-requested in the invoking command** (e.g. `/council … with a critic seat and debate mode`), confirm them back by name ("Critic seat and debate mode are ON per your request; the others are off") instead of re-asking.
|
|
161
196
|
- **The launch confirmation must enumerate the enabled elements by name** — an element not named in the confirmation is off. Restate its cost/shape impact there.
|
|
@@ -169,10 +204,16 @@ Rules for this menu:
|
|
|
169
204
|
this file and fails the run *before any spend* if it is malformed, so a bad file costs nothing but a
|
|
170
205
|
relaunch. See §5.4.
|
|
171
206
|
|
|
172
|
-
**Disclose the run shape up front**, naming any enabled elements and their cost
|
|
207
|
+
**Disclose the run shape up front**, naming the intent and any enabled elements and their cost
|
|
208
|
+
impact — e.g.:
|
|
173
209
|
|
|
174
210
|
> This run uses 3 council models across 2 engine waves + 1 chair call (~7 model runs), ~10 min.
|
|
175
211
|
|
|
212
|
+
or, in task mode:
|
|
213
|
+
|
|
214
|
+
> **Task run** — the 3 models each produce the analysis independently, then rank and adjudicate each
|
|
215
|
+
> other's; the chair synthesizes one answer. 2 engine waves + 1 chair call (~7 model runs), ~10 min.
|
|
216
|
+
|
|
176
217
|
or, with elements enabled:
|
|
177
218
|
|
|
178
219
|
> This run uses 3 council models across 2 engine waves + 1 chair call, with **critic seat + debate mode ON** (~7 base runs + up to 6 rebuttal calls), ~15 min.
|
|
@@ -190,17 +231,19 @@ The scale-down levels count **non-Claude judges**; `--claude-review` adds a judg
|
|
|
190
231
|
|
|
191
232
|
### The engine run — Stages 1–3 plus the Stage-5 artifacts
|
|
192
233
|
|
|
193
|
-
ONE call. The engine executes the Stage-1
|
|
234
|
+
ONE call. The engine executes the Stage-1 wave (reviews, or in task mode the seats' deliverables),
|
|
235
|
+
the per-leg findings validation and bounded
|
|
194
236
|
repair loop, anonymization and run-global finding-id rewriting, the identical judge bundle and
|
|
195
237
|
cross-review wave, the optional rebuttal round, the tally (which appends the reliability ledger
|
|
196
|
-
**once**), the chair synthesis with the
|
|
197
|
-
`verdict.json` + `report.html` — checkpointing `run.json` as it goes.
|
|
238
|
+
**once** — never on a task run, see §5.5), the chair synthesis with the standard closing scale, and
|
|
239
|
+
the deterministic `verdict.json` + `report.html` — checkpointing `run.json` as it goes.
|
|
198
240
|
|
|
199
241
|
**Canonical launch (shell contexts):**
|
|
200
242
|
|
|
201
243
|
```
|
|
202
244
|
amicus council run --prompt-file <run-folder>/briefing.md \
|
|
203
245
|
--models "<m1,m2,m3>" --chair <chair> --out-dir <run-folder> --json \
|
|
246
|
+
[--intent task] \
|
|
204
247
|
[--critic <m>] [--lenses s1,s2,s3] [--debate] \
|
|
205
248
|
[--claude-review <run-folder>/review-claude.md] \
|
|
206
249
|
[--max-cost <$> | --no-cost-gate] [--timeout <min>] [--gateway auto|direct|openrouter]
|
|
@@ -224,8 +267,10 @@ Stage-1 wave, the repair re-prompts, the Stage-2 judge wave, the debate legs, an
|
|
|
224
267
|
so a single invocation replaces the old per-call pass-through footgun entirely.
|
|
225
268
|
|
|
226
269
|
**Cowork / no-Bash environments:** use the MCP tools instead. `amicus_council_run`
|
|
227
|
-
`{briefingFile, models|council, chair, critic?, lenses?, debate?, claudeReviewFile?, outDir,
|
|
270
|
+
`{briefingFile, models|council, chair, intent?, critic?, lenses?, debate?, claudeReviewFile?, outDir,
|
|
228
271
|
maxCost?|noCostGate?, timeoutMinutes?, gateway?}` returns `{runId, runDir}` immediately.
|
|
272
|
+
`intent: 'task'` is the MCP twin of `--intent task`; omit it for a review run — absence IS review,
|
|
273
|
+
and passing `'review'` explicitly is accepted but never stored.
|
|
229
274
|
Preferred: call `amicus_wait` with the runId — one blocking call; re-call it while it returns
|
|
230
275
|
`timedOut: true`. Fallback: poll `amicus_status`, which shows stage progression. Then read the
|
|
231
276
|
run-folder artifacts with the host's file tools. Council JSON returned by the MCP tools arrives
|
|
@@ -282,7 +327,16 @@ withdrew it in the rebuttal round (`findings[].debate.action === 'withdrawn'` in
|
|
|
282
327
|
**auto-recorded `denied` in `decisions.json` and never presented for a user decision** — just note
|
|
283
328
|
it as withdrawn when walking the tiers.
|
|
284
329
|
|
|
285
|
-
**
|
|
330
|
+
**Task mode:** the findings are the **load-bearing claims** each response rests on, not defects — so
|
|
331
|
+
a tier says how many peers CONCURRED with a claim, never that anyone verified it, and models
|
|
332
|
+
correlate on priors. Say so when presenting, exactly as the rendered report does. Decide them as
|
|
333
|
+
claims: `accepted` = the answer may rest on this, `denied` = it may not, and anything downstream of
|
|
334
|
+
it goes with it, `modified` = it holds only in a narrower form, and the narrowing is recorded. A
|
|
335
|
+
clean bench is a valid result here too — an answer whose reasoning is fully inline can declare no
|
|
336
|
+
adjudicable claims at all, in which case there is nothing to tier and Stage 4 is one decision:
|
|
337
|
+
does the chair's answer stand as written.
|
|
338
|
+
|
|
339
|
+
**Consensus tier — Confirmed findings** (≥ 2 peer agreements with agrees dominating — or a lone corroborating peer with zero disputes)
|
|
286
340
|
|
|
287
341
|
- Present the full list in one block: id, claim, severity, and which models raised / endorsed it.
|
|
288
342
|
- Offer one **bulk accept/deny decision** over the whole tier:
|
|
@@ -322,6 +376,16 @@ Do not advance to Stage 5 until every finding has a recorded decision.
|
|
|
322
376
|
- Do not attempt to produce a modified copy.
|
|
323
377
|
- Write a **standalone reviewed report** instead: the full decision log, the chair's verdict, and clear callouts of what should be changed and where — formatted so the user can apply the changes manually.
|
|
324
378
|
|
|
379
|
+
**Task mode** (there is no source to revise — the deliverable is the output):
|
|
380
|
+
- The deliverable is the **chair's synthesized answer**, constrained by the Stage-4 decisions:
|
|
381
|
+
strike anything that rested on a `denied` claim, narrow anything a `modified` claim narrowed.
|
|
382
|
+
- Write it as `<run-folder>/answer.md` — or, when the brief asked for a specific artifact
|
|
383
|
+
(a spec, a plan, a letter), as that artifact under the name the brief asked for, next to the
|
|
384
|
+
material it drew on.
|
|
385
|
+
- **Never re-do the work yourself.** If the answer needs material the bench did not produce, say so
|
|
386
|
+
and offer a re-run with a sharper brief; silently writing your own version is the failure this
|
|
387
|
+
skill exists to prevent, and it is invisible to the user once the chair's prose is edited.
|
|
388
|
+
|
|
325
389
|
**Run-folder artifacts.** The engine already wrote the deterministic set (see *Output & naming*).
|
|
326
390
|
Two artifacts are yours:
|
|
327
391
|
|
|
@@ -354,7 +418,11 @@ Two artifacts are yours:
|
|
|
354
418
|
`render: true` and `outDir: <run-folder>` — this refreshes `<outDir>/report.html` on disk and
|
|
355
419
|
returns the Markdown rendering for `report.md` below; it still does **not** write `verdict.json`.
|
|
356
420
|
- `report.md` — Claude-authored: the chair's synthesis (read verbatim from
|
|
357
|
-
`<run-folder>/chair-output.md`, including its closing
|
|
421
|
+
`<run-folder>/chair-output.md`, including its closing scale line at the top of the report —
|
|
422
|
+
`VERDICT: Ship it | Fix these first | Fundamental rethink` on a review run,
|
|
423
|
+
`ANSWER: Converged | Split | Insufficient` on a task run. Reproduce the line the chair actually
|
|
424
|
+
wrote; never translate one scale into the other, and a run whose chair produced none says
|
|
425
|
+
`VERDICT: none` / `ANSWER: none` rather than inventing a phrase) +
|
|
358
426
|
the full Stage-4 decision log (one row per finding: `id` + claim + decision — the claim text
|
|
359
427
|
comes from `tally-input.json`, joined on `id` exactly as in Stage 4, since `decisions.json`
|
|
360
428
|
and `tally.json` both carry only the id) + a summary of what was applied (+ the "How Claude's
|
|
@@ -385,7 +453,7 @@ The fast path retires the manual path's two hand-written artifacts: there is no
|
|
|
385
453
|
`report.html`/`report.md`) and no `verdict.md` (the chair's prose is `chair-output.md`, written by
|
|
386
454
|
the engine). Do not recreate them — `MANUAL-ORCHESTRATION.md` is where they still live.
|
|
387
455
|
|
|
388
|
-
Tell the user exactly which files were written and where, leading with `report.html`, **and present the verdict inline in chat** — the chair's overall assessment (verbatim or lightly trimmed) plus the tier counts (Confirmed/Disputed/Contested/Singleton) and what was applied. Never hand over only file paths.
|
|
456
|
+
Tell the user exactly which files were written and where, leading with `report.html`, **and present the verdict inline in chat** — the chair's overall assessment (verbatim or lightly trimmed), its closing `VERDICT:`/`ANSWER:` line, plus the tier counts (Confirmed/Disputed/Contested/Singleton) and what was applied. On a task run lead with the answer itself, not the process. Never hand over only file paths.
|
|
389
457
|
|
|
390
458
|
---
|
|
391
459
|
|
|
@@ -405,8 +473,8 @@ Draft new or updated entries for the per-model sections of `MODEL-NOTES.md` that
|
|
|
405
473
|
**Ledger — already appended; do not touch it.** The engine's finalize tally appended one row per
|
|
406
474
|
(run × model × resolved executable) to the append-only `council-ledger.jsonl` under `getConfigDir()`
|
|
407
475
|
as part of the run — one row per model on an ordinary bench, and since v4.8 one row per executable
|
|
408
|
-
when an alias's seats resolved differently (expert-lens runs
|
|
409
|
-
fast path, never run `council tally` yourself.** The ledger is append-only, so a second tally over
|
|
476
|
+
when an alias's seats resolved differently (expert-lens runs **and task runs** are deliberately
|
|
477
|
+
excluded — see §5.5). **In the fast path, never run `council tally` yourself.** The ledger is append-only, so a second tally over
|
|
410
478
|
the same run double-appends: that permanently doubles every affected model's `conformance` histogram
|
|
411
479
|
in `amicus council stats` and cannot be undone. (It does *not* skew the lifetime averages — a
|
|
412
480
|
duplicated set of rows has the same mean — and since v4.8 it no longer inflates `runs`/`low-N`
|
|
@@ -516,6 +584,51 @@ in `--critic`, which must be a bench seat) with `council_claude_review_invalid`.
|
|
|
516
584
|
|
|
517
585
|
---
|
|
518
586
|
|
|
587
|
+
### §5.5 Task mode (`--intent task`, default off)
|
|
588
|
+
|
|
589
|
+
The bench does the work instead of judging someone else's. Every stage still runs; what each stage
|
|
590
|
+
is *about* changes.
|
|
591
|
+
|
|
592
|
+
**What the seats are told.** Execute the briefing — "produce the analysis, answer, or artifact it
|
|
593
|
+
requests — you are not reviewing the briefing" — and close with the same trailing JSON block review
|
|
594
|
+
mode uses, carrying the **load-bearing claims** the deliverable rests on instead of defects found in
|
|
595
|
+
someone's work. The fenced JSON shape, the `blocker|major|minor|nit` enum and the required non-empty
|
|
596
|
+
`location` are unchanged; here `location` is the grounding discipline — the source, the computation,
|
|
597
|
+
or the literal word `assumption`. An empty claims list under a real answer is a valid result. Critic and lens seats
|
|
598
|
+
have task twins (argue against the easy answer *while doing the work*; do the work through one
|
|
599
|
+
expert perspective), so menu items 1–3 are all available.
|
|
600
|
+
|
|
601
|
+
**What the judges rank.** Review mode ranks how ACCURATE a critique was; there is no critique here,
|
|
602
|
+
so the axis is how well the asked-for work was DONE. Adjudication is unchanged — agree / dispute /
|
|
603
|
+
neutral, per claim.
|
|
604
|
+
|
|
605
|
+
**What the chair produces.** An ANSWER, not a verdict: adopt the strongest response, merge
|
|
606
|
+
complementary ones, or refuse the premise if the bench showed it unsound; state the consensus and
|
|
607
|
+
which way each disagreement went; close with a RESIDUAL RISK section naming the disputed claims the
|
|
608
|
+
answer still depends on. Then the final line, alone — `ANSWER: Converged` (the bench substantially
|
|
609
|
+
agrees and the synthesis is well-supported), `ANSWER: Split` (material disagreement remains; the
|
|
610
|
+
synthesis states both positions and what would settle them), or `ANSWER: Insufficient` (the bench's
|
|
611
|
+
work cannot support an answer — missing information, an unsound premise, or too little usable
|
|
612
|
+
output; *which* is named in the synthesis, never on the ANSWER line).
|
|
613
|
+
|
|
614
|
+
**What the mode refuses.** `--claude-review` is rejected pre-flight, exit 1 before any spend — it
|
|
615
|
+
enters a REVIEW as review N+1 and has no task-mode meaning. There is no task twin, and that is the
|
|
616
|
+
point: Claude contributing its own deliverable would be doing the work the bench was convened for.
|
|
617
|
+
|
|
618
|
+
**What is not recorded.** A task run is kept out of the reliability ledger, for the reason
|
|
619
|
+
expert-lens runs are: cred earned doing work is not comparable with cred earned reviewing. So
|
|
620
|
+
`amicus council stats` never moves on a task run, its numbers stay a review-mode signal — still the
|
|
621
|
+
right basis for bench selection — and a task run's rankings must not be read as if they fed it.
|
|
622
|
+
|
|
623
|
+
**How it surfaces.** `intent: 'task'` is emit-when-task and rides `run.json`, the tally `meta` and
|
|
624
|
+
`verdict.json`; absence means review, and `'review'` is never materialized. That one key is what
|
|
625
|
+
makes `report.html`/`report.md` head their summary **Answer summary** rather than *Verdict summary*,
|
|
626
|
+
print the peer-concurrence caveat, and makes the Workspace fold read `ANSWER:`. If a task run's fold
|
|
627
|
+
or panels say *verdict*, the intent did not reach that artifact — report it, do not relabel it by
|
|
628
|
+
hand.
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
519
632
|
## Model-recommendation heuristics
|
|
520
633
|
|
|
521
634
|
Use these together with `amicus council stats` (the ledger — authoritative quantitative reliability data: runs, avg peers-only street-cred, confirm-rate, fact-error rate) and the qualitative quirks in `MODEL-NOTES.md`:
|
|
@@ -536,13 +649,14 @@ Always **rank recommendations by fit**, state the trade-off for each option, and
|
|
|
536
649
|
## Output & naming
|
|
537
650
|
|
|
538
651
|
- Run folder: `output/<stem>-council/` (or `./second-opinion/<stem>-council/` if no `output/` exists), passed to the engine as `--out-dir`. After a fast-path run it holds:
|
|
539
|
-
- `briefing.md` — the Stage-0
|
|
652
|
+
- `briefing.md` — the Stage-0 request Claude authored — the review request, or in task mode the work brief (run provenance, not a temp file)
|
|
540
653
|
- `review-claude.md` — Claude's own fresh review, only when "Claude in the council" is on
|
|
541
654
|
- `run.json` — the engine's run manifest: stage log, wave ids, degradation, `runStats`, cost
|
|
542
655
|
- `review-<seat>.md` ×N and `judge-<seat>.md` ×N — the raw engine legs. `<seat>` is the bench
|
|
543
656
|
seat's id, which **is** the model alias for any bench that names each alias once; seat one and
|
|
544
657
|
seat two of a repeated alias write `…-<alias>-1.md` and `…-<alias>-2.md` instead of sharing
|
|
545
|
-
one file
|
|
658
|
+
one file. The names are intent-independent: on a task run `review-<seat>.md` holds that seat's
|
|
659
|
+
**deliverable**, not a review
|
|
546
660
|
- `briefing-stage1.md`, `bundle-stage2.md`, `chair-packet.md` — the model-facing briefings the engine composed
|
|
547
661
|
- `tally-input.json` and `tally.json` — the assembled input and the tiered record (plus `tally-provisional.json` and `debate.json` when `--debate` was on)
|
|
548
662
|
- `rebuttal-<seat>.md` ×(raising seats) and `revote-bundle.md` + `revote-<seat>.md` ×(disputing seats) — the debate round's raw defense and re-vote leg outputs plus the shared re-vote prompt, only when `--debate` was on. Both waves are sized in seats, so a bench that repeats an alias buys up to two extra billed legs per duplicated pair per round (one defense solo, one re-vote leg), each of which may draw its own bounded repair solo
|
|
@@ -550,8 +664,9 @@ Always **rank recommendations by fit**, state the trade-off for each option, and
|
|
|
550
664
|
- `decisions.json` — the Stage-4 decision array Claude writes
|
|
551
665
|
- `verdict.json` — schema-stamped machine-readable record: tally output + Stage-4 decisions, written via `amicus council verdict` at Stage 5 (replacing the engine's undecided version)
|
|
552
666
|
- `report.md` — Claude-authored; full contract defined once in *Stage 5 → Run-folder artifacts* above (chair's synthesis + Stage-4 decision log + run-stats table).
|
|
553
|
-
- `report.html` — a **separate, deterministic** artifact rendered from `verdict.json` (no chair prose, no decision-log narrative — see Stage 5's *Renderer* note); the default artifact to share.
|
|
554
|
-
-
|
|
667
|
+
- `report.html` — a **separate, deterministic** artifact rendered from `verdict.json` (no chair prose, no decision-log narrative — see Stage 5's *Renderer* note); the default artifact to share. On a task run it heads **Answer summary** and carries the peer-concurrence caveat, off `verdict.json`'s `intent: 'task'`.
|
|
668
|
+
- `answer.md` — task runs only: the chair's answer as constrained by the Stage-4 decisions (or the named artifact the brief asked for, wherever the brief asked for it). See *Stage 5 → Task mode*.
|
|
669
|
+
- Reviewed copy: `<stem>-reviewed.<ext>`, next to the source (review runs only — a task run revises nothing).
|
|
555
670
|
- The only working file Claude writes in the fast path is the Stage-6 proposed MODEL-NOTES diff (`_tmp-proposed-model-notes-update.md`), cleaned up once the approval decision is resolved. The manual fallback's `_tmp-*.md` files are documented in `MANUAL-ORCHESTRATION.md`.
|
|
556
671
|
|
|
557
672
|
---
|
|
@@ -560,5 +675,5 @@ Always **rank recommendations by fit**, state the trade-off for each option, and
|
|
|
560
675
|
|
|
561
676
|
- `MANUAL-ORCHESTRATION.md` — the **fallback path**: the hand-driven Stage 1/2/2.5/3 mechanics and the Stage-5 artifacts the engine replaced. **Read it when the engine is unavailable, too old, or misbehaving; when a seat needs a fully custom brief beyond `--critic`/`--lenses`; or when you need to inspect or intervene mid-stage.**
|
|
562
677
|
- `MODEL-NOTES.md` — operating rules, per-model qualitative quirks, cost guardrail, and structural-conformance notes. **Read it before Stage 0 (council selection and launch); update qualitative notes (with approval) in Stage 6.** Quantitative reliability data (runs, avg street-cred, confirm-rate, fact-error rate) comes from `amicus council stats`, not this file. This copy is machine-local (never overwritten on update); the shipped seed lives in the amicus repo and absorbs durable lessons at release time.
|
|
563
|
-
- `SEAT-BRIEFS.md` — the semantics of the optional council elements (critic seat, expert lenses, rebuttal round, chair
|
|
564
|
-
- `COUNCIL-DESIGN.md` — the design spec this skill implements (§12 covers the optional council elements). Consult it if a mechanics question arises that the skill prose does not resolve.
|
|
678
|
+
- `SEAT-BRIEFS.md` — the semantics of the optional council elements (critic seat, expert lenses, rebuttal round, chair closing scale) plus the standard anti-sycophancy clause. Its blocks are REVIEW wording; the task twins are engine-composed, and the file names the swaps the manual path has to make (including the ANSWER-scale addendum). The engine composes its own stricter-JSON variants of all of them headlessly; this file stays authoritative for the manual path and for what each element *means*. **Read it whenever any element is toggled on at Stage 0.**
|
|
679
|
+
- `COUNCIL-DESIGN.md` — the design spec this skill implements (§12 covers the optional council elements). Consult it if a mechanics question arises that the skill prose does not resolve. It predates task mode and does not describe it — §5.5 above is the authority there.
|
|
@@ -1,20 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bench and input resolution for the council run command.
|
|
3
3
|
*
|
|
4
|
-
* Exports parseList, sanitizeCouncilName, resolveBench
|
|
5
|
-
* from cli-handlers-council-run.js (v4.7 PR0). ⚠️
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Exports parseList, sanitizeCouncilName, resolveBench — extracted verbatim
|
|
5
|
+
* from cli-handlers-council-run.js (v4.7 PR0). ⚠️ v4.9 W13 split `resolveBench`
|
|
6
|
+
* into a private `resolveBenchCore` (the extracted body, still verbatim) plus a
|
|
7
|
+
* thin exported wrapper that owns the CLI's alias-shadow notice site (the MCP
|
|
8
|
+
* surface has its own, in mcp-council-bench.js — PR #207 round 2, A1); the
|
|
9
|
+
* export name, its arguments and its return shapes are unchanged.
|
|
10
|
+
* ⚠️ The top-level cli*.js name is LOAD-BEARING: the known-flags source scan
|
|
11
|
+
* covers only src/cli*.js, and resolveBenchCore reads args['dropped-members'].
|
|
8
12
|
*/
|
|
9
13
|
|
|
10
14
|
'use strict';
|
|
11
15
|
|
|
12
16
|
const { failJson, ERROR_CODES } = require('./utils/error-doc');
|
|
13
17
|
|
|
18
|
+
/**
|
|
19
|
+
* The chair a council gets when nobody names one.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ Lives HERE, in the bench/seat-resolution leaf, and is imported by
|
|
22
|
+
* `cli-handlers-council-run.js` (which re-exports it, so existing importers are
|
|
23
|
+
* unchanged) and by `mcp-council-run.js`. PR #203 round 1 (A6) needed the
|
|
24
|
+
* default at this seam to audit the chair; two spellings already existed and a
|
|
25
|
+
* third would have been the "wrong lever" mistake — one owner, three readers.
|
|
26
|
+
*/
|
|
27
|
+
const CHAIR_DEFAULT = 'deepseek';
|
|
28
|
+
|
|
14
29
|
function parseList(value) {
|
|
15
30
|
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
16
31
|
}
|
|
17
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The chair this run will use: a trimmed `--chair`, else CHAIR_DEFAULT.
|
|
35
|
+
* Extracted so the alias audit below and the handler that enforces the
|
|
36
|
+
* chair-not-in-bench rule read ONE definition. @param {object} args
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function resolveChair(args) {
|
|
40
|
+
return (typeof args.chair === 'string' && args.chair.trim()) ? args.chair.trim() : CHAIR_DEFAULT;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The critic seat, or null when this run has none. Same one-definition
|
|
44
|
+
* rationale as resolveChair. @param {object} args @returns {string|null} */
|
|
45
|
+
function resolveCritic(args) {
|
|
46
|
+
return (typeof args.critic === 'string' && args.critic.trim()) ? args.critic.trim() : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
18
49
|
/**
|
|
19
50
|
* Sanitize the internal `--council-name` passthrough before it can reach the
|
|
20
51
|
* spend ledger's `councilName` column (v4.3 Task 4 review fix, spec §7.3:
|
|
@@ -43,8 +74,12 @@ function sanitizeCouncilName(name) {
|
|
|
43
74
|
* They have already diverged (this side has a third guard for a valueless
|
|
44
75
|
* --council, and the min-seat rule lives in both callers, not here) — change
|
|
45
76
|
* a validation rule on one side, change the other.
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ Not exported directly — `resolveBench` below wraps it so this transport's
|
|
79
|
+
* alias-shadow notice has exactly ONE site. Keep new return shapes going through
|
|
80
|
+
* that wrapper.
|
|
46
81
|
*/
|
|
47
|
-
function
|
|
82
|
+
function resolveBenchCore(args, useJson) {
|
|
48
83
|
const hasModels = typeof args.models === 'string' && args.models.trim();
|
|
49
84
|
const hasCouncil = args.council !== undefined && args.council !== false;
|
|
50
85
|
if (hasModels && hasCouncil) {
|
|
@@ -83,4 +118,61 @@ function resolveBench(args, useJson) {
|
|
|
83
118
|
return { bench: parseList(args.models), presetName: null, droppedMembers: dm };
|
|
84
119
|
}
|
|
85
120
|
|
|
86
|
-
|
|
121
|
+
/**
|
|
122
|
+
* `resolveBenchCore` plus the CLI's alias-shadow notice site (v4.9 W13 Task B,
|
|
123
|
+
* BACKLOG C5). This is the shared bench-resolution helper BOTH council
|
|
124
|
+
* transports execute: `mcp-council-run.js` always spawns the CLI child with an
|
|
125
|
+
* already-expanded `--models` list (never `--council`), so the MCP path
|
|
126
|
+
* re-enters here exactly like a hand-typed `amicus council run`. Measured, not
|
|
127
|
+
* assumed — see tests/alias-shadow.test.js's header.
|
|
128
|
+
*
|
|
129
|
+
* ⚠️ EXECUTES on both transports, SURFACES on one. The child's stderr is a
|
|
130
|
+
* `debug.log` fd, so the line this site writes never reaches an MCP client;
|
|
131
|
+
* `mcp-council-bench.js :: auditBenchAliases` is the parallel site that puts it
|
|
132
|
+
* on the tool result (PR #207 round 2, A1). See alias-shadow.js's own docblock.
|
|
133
|
+
*
|
|
134
|
+
* Wrapped rather than called from each `return`, so the rule cannot grow a twin
|
|
135
|
+
* as branches are added. A rejected bench carries no `bench` key and is
|
|
136
|
+
* therefore silent by construction: nothing was resolved, so nothing is
|
|
137
|
+
* diagnosed. Diagnosis only — the returned value is byte-identical to what
|
|
138
|
+
* `resolveBenchCore` produced.
|
|
139
|
+
*
|
|
140
|
+
* v4.9 W13, PR #203 council round 1:
|
|
141
|
+
* A5 — `auditAliasShadows` (not `noteAliasShadows`) opens a fresh notice scope
|
|
142
|
+
* first, so a host process that resolves two councils in a row audits
|
|
143
|
+
* BOTH instead of silently auditing only the first. This call is reached
|
|
144
|
+
* exactly once per council run, which is what makes "one scope" mean
|
|
145
|
+
* "one run".
|
|
146
|
+
* A6 — the audited names are the bench PLUS the chair (explicit or default)
|
|
147
|
+
* and the critic. All three resolve through the same alias table, so a
|
|
148
|
+
* shadow on any of them is equally invisible downstream. Order matters
|
|
149
|
+
* only cosmetically (rows come back in the order given), and the audit
|
|
150
|
+
* de-dups, so a critic — which a valid run always draws from the bench —
|
|
151
|
+
* adds a row only when it is outside it.
|
|
152
|
+
*/
|
|
153
|
+
function resolveBench(args, useJson) {
|
|
154
|
+
const res = resolveBenchCore(args, useJson);
|
|
155
|
+
if (Array.isArray(res.bench)) {
|
|
156
|
+
const critic = resolveCritic(args);
|
|
157
|
+
require('./utils/alias-shadow').auditAliasShadows(
|
|
158
|
+
[...res.bench, resolveChair(args), ...(critic ? [critic] : [])]);
|
|
159
|
+
}
|
|
160
|
+
return res;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ⚠️ THIS ORDER IS LOAD-BEARING, and only for the docs (PR #207 round 4, B3).
|
|
164
|
+
// `scripts/generate-docs-helpers.js:107` keeps the FIRST FIVE names in source
|
|
165
|
+
// order for CLAUDE.md's Key Exports column, and this module exports six — so
|
|
166
|
+
// whichever name sits last is the one the generated table never shows.
|
|
167
|
+
// `CHAIR_DEFAULT` is DEFINED here and merely re-exported by
|
|
168
|
+
// cli-handlers-council-run.js, whose row (3 exports, all inside the cap) always
|
|
169
|
+
// listed it; the round-4 council read the definer's silence beside the
|
|
170
|
+
// re-exporter's mention as the docs disagreeing about where the constant lives.
|
|
171
|
+
// Moving it inside the cap makes the definer the visible source. The resolve
|
|
172
|
+
// trio stays intact and `sanitizeCouncilName` — the one name here that is a
|
|
173
|
+
// generic string helper rather than part of this module's headline — is the one
|
|
174
|
+
// that falls off the end. Nothing else depends on this order: consumers
|
|
175
|
+
// destructure by name.
|
|
176
|
+
module.exports = {
|
|
177
|
+
resolveBench, resolveChair, resolveCritic, CHAIR_DEFAULT, parseList, sanitizeCouncilName,
|
|
178
|
+
};
|
|
@@ -17,11 +17,15 @@ const { GATEWAY_MODES } = require('./utils/model-descriptor');
|
|
|
17
17
|
// file re-exports it below so every existing require() of this path still
|
|
18
18
|
// resolves it unchanged.
|
|
19
19
|
const { renderRunHuman } = require('./cli-council-run-render');
|
|
20
|
-
|
|
20
|
+
// v4.9 W13 (PR #203 round 1, A6): CHAIR_DEFAULT and the two seat resolvers moved
|
|
21
|
+
// to cli-council-run-bench.js so the alias audit at the resolveBench seam and the
|
|
22
|
+
// validation below read ONE definition of each. Re-exported at the bottom of this
|
|
23
|
+
// file, so `require('./cli-handlers-council-run').CHAIR_DEFAULT` still resolves.
|
|
24
|
+
const {
|
|
25
|
+
parseList, sanitizeCouncilName, resolveBench, resolveChair, resolveCritic, CHAIR_DEFAULT,
|
|
26
|
+
} = require('./cli-council-run-bench');
|
|
21
27
|
const { applyTemplateForArgs } = require('./cli-template-args');
|
|
22
28
|
|
|
23
|
-
const CHAIR_DEFAULT = 'deepseek';
|
|
24
|
-
|
|
25
29
|
/**
|
|
26
30
|
* Default real helpers; tests override via depsOverride (mirrors
|
|
27
31
|
* cli-handlers-spend.js's realDeps()/depsOverride convention).
|
|
@@ -132,14 +136,13 @@ async function handleCouncilRun(args, depsOverride = {}) {
|
|
|
132
136
|
message: 'Error: a council needs at least 2 seats (fanout semantics)' });
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
const chair = (
|
|
136
|
-
? args.chair.trim() : CHAIR_DEFAULT;
|
|
139
|
+
const chair = resolveChair(args);
|
|
137
140
|
if (bench.includes(chair)) {
|
|
138
141
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
139
142
|
message: `Error: chair '${chair}' is a bench seat — the chair must not review${packSuffix('chair')}`,
|
|
140
143
|
hint: `pick a chair outside --models (default: ${CHAIR_DEFAULT}), or remove '${chair}' from the bench` });
|
|
141
144
|
}
|
|
142
|
-
const critic = (
|
|
145
|
+
const critic = resolveCritic(args);
|
|
143
146
|
if (critic && !bench.includes(critic)) {
|
|
144
147
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
145
148
|
message: `Error: critic '${critic}' must be one of the bench seats${packSuffix('critic')}`,
|
|
@@ -189,6 +192,13 @@ async function handleCouncilRun(args, depsOverride = {}) {
|
|
|
189
192
|
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: tagCheck.error });
|
|
190
193
|
}
|
|
191
194
|
}
|
|
195
|
+
// v4.9 W5.2 (spec §5.3): emit-when-'task' everywhere — 'review' is the
|
|
196
|
+
// default spelled out and is never materialized on the options object.
|
|
197
|
+
if (args.intent !== undefined && args.intent !== 'review' && args.intent !== 'task') {
|
|
198
|
+
return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
|
|
199
|
+
message: 'Error: --intent must be review or task',
|
|
200
|
+
hint: "review (the default) may be omitted; only '--intent task' changes the run" });
|
|
201
|
+
}
|
|
192
202
|
let runId;
|
|
193
203
|
if (args['run-id']) {
|
|
194
204
|
const check = validateTaskId(String(args['run-id']));
|
|
@@ -238,6 +248,8 @@ async function handleCouncilRun(args, depsOverride = {}) {
|
|
|
238
248
|
template: templateMeta, // F9 (v4.5): null when no --template; additive on the run.json seed (run-state.js).
|
|
239
249
|
pack: packRecord, // v4.5 Task 12 (B7/F5): null when no --pack; additive on the run.json seed (run-state.js).
|
|
240
250
|
tag: args.tag, // v4.7 F8: undefined when no --tag; Task 3 stores it on the run.json seed.
|
|
251
|
+
// v4.9 W5.2: o.intent is 'task' or ABSENT, never 'review' (validated above).
|
|
252
|
+
...(args.intent === 'task' ? { intent: 'task' } : {}),
|
|
241
253
|
droppedMembers: benchRes.droppedMembers, // v4.5 Wave 2: [] when nothing dropped; additive on the run.json seed (run-state.js).
|
|
242
254
|
// v4.1 §4.5b/§4.5d. `--claude-review` is resolved here but VALIDATED by the
|
|
243
255
|
// engine's preflightClaudeReview (run-assemble.js): the reserved-seat and
|