amicus 1.0.0 → 1.2.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.
Files changed (55) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +19 -0
  3. package/CHANGELOG.md +86 -0
  4. package/LICENSE +22 -1
  5. package/README.md +14 -3
  6. package/bin/amicus.js +17 -162
  7. package/electron/ipc-setup.js +30 -9
  8. package/electron/main.js +13 -5
  9. package/electron/preload.js +30 -10
  10. package/electron/setup-ui-keys.js +9 -0
  11. package/electron/setup-ui-model.js +33 -23
  12. package/electron/setup-ui-styles.js +6 -1
  13. package/electron/setup-ui.js +91 -38
  14. package/electron/toolbar.js +4 -5
  15. package/package.json +7 -5
  16. package/scripts/postinstall.js +16 -7
  17. package/skills/second-opinion/COUNCIL-DESIGN.md +36 -34
  18. package/skills/second-opinion/MODEL-NOTES.md +23 -17
  19. package/skills/second-opinion/SKILL.md +84 -51
  20. package/{skill → skills/sidecar}/SKILL.md +14 -4
  21. package/src/cli-handlers-council.js +59 -0
  22. package/src/cli-handlers-doctor.js +173 -0
  23. package/src/cli-handlers-run.js +196 -0
  24. package/src/cli-handlers.js +66 -1
  25. package/src/cli.js +16 -2
  26. package/src/council/findings.js +48 -0
  27. package/src/council/ledger.js +82 -0
  28. package/src/council/tally.js +108 -0
  29. package/src/council/verdict.js +48 -0
  30. package/src/headless.js +43 -149
  31. package/src/mcp-server.js +6 -0
  32. package/src/sidecar/budget.js +83 -0
  33. package/src/sidecar/conversation-mirror.js +128 -0
  34. package/src/sidecar/fanout-leg.js +4 -1
  35. package/src/sidecar/fanout.js +34 -7
  36. package/src/sidecar/interactive-mirror.js +66 -0
  37. package/src/sidecar/interactive.js +35 -21
  38. package/src/sidecar/models.js +41 -10
  39. package/src/sidecar/session-finalize.js +26 -0
  40. package/src/sidecar/session-utils.js +5 -5
  41. package/src/sidecar/setup.js +55 -42
  42. package/src/sidecar/start.js +19 -6
  43. package/src/utils/activity-poller.js +47 -0
  44. package/src/utils/alias-resolver.js +1 -1
  45. package/src/utils/config.js +4 -4
  46. package/src/utils/curated-models.js +88 -45
  47. package/src/utils/error-doc.js +55 -0
  48. package/src/utils/lifecycle.js +1 -1
  49. package/src/utils/model-catalog.js +1 -1
  50. package/src/utils/model-fetcher.js +16 -2
  51. package/src/utils/pricing.js +93 -0
  52. package/src/utils/quick-picks.js +81 -0
  53. package/src/utils/result-schema.js +21 -2
  54. package/src/utils/session-abort.js +40 -13
  55. package/src/utils/validators.js +17 -17
@@ -33,7 +33,9 @@ _Last updated: 2026-06-10 (v3 migration: engine workarounds pruned — see chang
33
33
  names — so rankings are directly comparable (one fanout wave distributes it).
34
34
  - **Require a `FINAL RANKING:` block** at the end of the response (e.g. `1. Review C / 2. Review
35
35
  A …`), plus a per-finding `agree | dispute | neutral` verdict with a one-line reason for each
36
- finding referenced by label+id (e.g. `A2`).
36
+ finding referenced by run-global label id (e.g. `A2` = Review A's 2nd finding).
37
+ - After de-anonymizing, assemble the tally input (see SKILL.md Stage 2 assembly recipe) and run
38
+ `amicus council tally <input.json> --json` — do not hand-tally tiers or street-cred numbers.
37
39
 
38
40
  ## Per-model notes
39
41
 
@@ -59,28 +61,32 @@ _Last updated: 2026-06-10 (v3 migration: engine workarounds pruned — see chang
59
61
  - Opus / o-series etc. are reachable via amicus **if their API keys are configured**. Add notes
60
62
  here the first time each is used.
61
63
 
62
- ## Reviewer-reliability table
64
+ ## Reviewer-reliability
63
65
 
64
- Consulted in Stage 0 (council selection) and updated with approval in Stage 6.
66
+ Quantitative reliability data (runs, avg peers-only street-cred, confirm-rate, fact-error rate, conformance distribution) is now generated from the append-only `council-ledger.jsonl` via:
65
67
 
66
- - **avg street-cred** — rolling average of this model's per-run street-cred (mean rank position
67
- across judges' `FINAL RANKING:` blocks; lower = better).
68
- - **confirm-rate** — share of this model's findings that reached the **Confirmed** tier (agrees
69
- outweigh disputes, ≥ 2 judges engaged).
68
+ ```
69
+ amicus council stats [--json]
70
+ ```
70
71
 
71
- | model | runs | avg street-cred | confirm-rate | notes |
72
- | --- | --- | --- | --- | --- |
73
- | deepseek | 1 | 2.33 | 100% (12/12) | strong synthesis, resilient; chaired well |
74
- | gpt | 1 | 2.67 | 92% (23/25) | thorough but verbose; self-ranked #1 → discount; OpenRouter |
75
- | gemini | 1 | 3.67 | 89% (8/9) | fast, large-context; more absolute/adversarial ("blocker" inflation); ranked lowest |
72
+ **Do not hand-edit reliability numbers here.** The ledger is the authoritative source; `amicus council stats` aggregates it and flags low-N models (`runs < 3`). Consult `amicus council stats` in Stage 0 for bench recommendations. In Stage 6 the ledger row is appended automatically — no manual update needed.
76
73
 
77
- _Scale note: the 2026-06-04 run used a 4-review pool (Claude in-council), so street-cred is on a
78
- 1–4 scale rather than 1–3 — treat these as run-1 baselines, not directly comparable to future
79
- 3-model runs. Merge/prune rather than append._
74
+ This section keeps only per-model **qualitative quirks** and **structural-conformance notes** (`clean` / `repaired` / `unstructured`), which cannot be captured by the ledger:
75
+
76
+ ### Qualitative notes (hand-curated)
77
+
78
+ - **deepseek** — strong synthesis, resilient; occasional transient 502 → re-run the leg. Proven chair. Conforms cleanly.
79
+ - **gpt** — thorough but verbose; peers have dinged it for volume-over-judgment. Self-ranked its own review #1 in the 2026-06-04 run → the peers-only street-cred rule (now enforced by `tally`) mitigates this. Conforms cleanly. Accessible via OpenRouter.
80
+ - **gemini** — fast, very large context; tends toward absolute severity labels ("blocker" inflation vs peers). Conforms cleanly; watch for preamble narration — instruct it to emit the JSON block verbatim after the prose.
80
81
 
81
82
  ## Cost guardrail
82
- - **Never** use `o3` / `o3-pro` unless the user explicitly asks for it by name — these cost
83
- roughly $10–60+ per request. Warn about cost before proceeding even when asked.
83
+ - The budget gate enforces this in code: a per-$/Mtok threshold (ON by default)
84
+ refuses o3/o3-pro-class models before a wave launches. This replaces the old
85
+ "remember not to" rule — it can no longer be forgotten.
86
+ - To run `o3`/`o3-pro` (≈ $10–60+/request) the user must ask by name AND you
87
+ pass `--no-cost-gate` (disables both guards) for that run. Still warn first.
88
+ - `--max-cost <$>` raises only the soft total ceiling; it does not unblock an
89
+ over-threshold model.
84
90
 
85
91
  ## General
86
92
  - Model citations are usually real but **verify any load-bearing reference before publishing**;
@@ -54,11 +54,11 @@ in this run is written here. Use its absolute path in all `--prompt-file` argume
54
54
  `--prompt-file` — never inline a briefing as a CLI argument. All `_tmp-*` files are cleaned up
55
55
  after the run.
56
56
 
57
- **Pick the council.** Default: **3 models from different families (non-Claude)**. Recommend them ranked by fit, consulting the reviewer-reliability table in `MODEL-NOTES.md`. State the estimated cost. **Disclose the run shape up front** before asking for confirmation — e.g.:
57
+ **Pick the council.** Default: **3 models from different families (non-Claude)**. Recommend them ranked by fit, consulting both the reviewer-reliability data from `amicus council stats` (the authoritative quantitative source — runs, avg peers-only street-cred, confirm-rate, fact-error rate) and the qualitative quirks in `MODEL-NOTES.md`. State the estimated cost. The estimate is the budget gate's pre-flight figure (per-$/Mtok pricing from the cached catalog; direct-provider legs without catalog pricing are disclosed as "cost unknown"). State it as an estimate, not a guarantee. **Disclose the run shape up front** before asking for confirmation — e.g.:
58
58
 
59
59
  > This run uses 3 council models across 2 fanout waves + 1 chair call (~7 model runs), ~10 min.
60
60
 
61
- Then **wait for confirmation**. Never launch without it. Honor the cost guardrail in `MODEL-NOTES.md` (no `o3`/`o3-pro` without explicit ask-by-name).
61
+ Then **wait for confirmation**. Never launch without it. The budget gate enforces the cost guardrail in code: by default it refuses any leg whose price exceeds the per-$/Mtok threshold (the o3/o3-pro guard). To run an intentionally expensive model the user explicitly asked for by name, pass `--no-cost-gate`; to raise only the total ceiling, pass `--max-cost <$>`.
62
62
 
63
63
  **Scale-down is explicit — state which mode applies:**
64
64
  - **1 model** → thorough single pass; Stage 2 (cross-review) and Stage 3 (chair synthesis) are skipped entirely; Claude synthesizes directly. Transport: a single solo `amicus start --no-ui --json` (no fanout).
@@ -110,18 +110,32 @@ equivalent.
110
110
 
111
111
  **Required structured output from every model.** Instruct each council model to produce:
112
112
 
113
- 1. A **findings list** — every finding contains:
114
- - `id` — sequential integer within this review (1, 2, 3…)
115
- - `claim` — the specific issue or observation
116
- - `severity` — one of: `blocker | major | minor | nit`
117
- - `location` — section heading or verbatim quote identifying where in the artifact
118
- - `rationale` — why this is a problem or worth noting
113
+ 1. A **prose review** — the reviewer's full narrative assessment of the artifact.
119
114
 
120
- 2. A **short overall take** 2–4 sentences summarizing the reviewer's overall assessment.
115
+ 2. A **trailing fenced ` ```json ` block** immediately after the prose, containing:
116
+ ```json
117
+ {
118
+ "overall": "one-paragraph take",
119
+ "findings": [
120
+ { "id": 1, "severity": "blocker",
121
+ "claim": "…", "location": "…", "rationale": "…" }
122
+ ]
123
+ }
124
+ ```
125
+ - `id` — sequential integer within this review (`1..n`); at Stage-2 assembly Claude rewrites each into a **run-global label id** (`A1`, `B1`, …) by prefixing the review's anonymized label.
126
+ - `severity ∈ {blocker, major, minor, nit}`
127
+ - `claim`, `location`, `rationale` — non-empty strings.
121
128
 
122
- Instruct models to emit the structured output verbatim, without preamble, so it reads cleanly.
129
+ Instruct models to emit the structured JSON verbatim after the prose, without preamble, so it parses cleanly.
123
130
 
124
- When the wave returns, save each leg's `summary` to the run folder as `review-<model>.md`
131
+ **After the wave returns, validate each leg's findings block** using `validateFindings` (Unit A `src/council/findings.js`). If a leg's JSON fails validation:
132
+ 1. Issue a **solo `start --json`** re-prompt to that one model: "re-emit only the findings JSON, fixing: \<errors\>." Keep the first-pass prose. (Solo `start` is **not** subject to the WS-2 fanout cost gate, so a repair cannot be refused mid-council.)
133
+ 2. If still malformed, retry **once more** (cap = **2** re-prompts total).
134
+ 3. If still malformed after 2 retries, mark the review `unstructured` and hand-parse its prose into the schema. The review proceeds — never dropped for a formatting miss.
135
+
136
+ Record per-model **conformance** (`clean` | `repaired` | `unstructured`) for inclusion in the tally input's `runStats` and the Stage-6 MODEL-NOTES note.
137
+
138
+ Save each leg's full output (prose + findings block) to the run folder as `review-<model>.md`
125
139
  (one file per reviewer) before moving on.
126
140
 
127
141
  **"Claude in the council" (when toggled on):** Claude also produces a **fresh** Stage-1 review on the artifact in the identical findings format — a new structured pass on the artifact, not a formalization of anything said upstream. This review is added to the bundle as one more anonymous entry. Claude does not rank or adjudicate in Stage 2 (it holds the label map), and does not chair in Stage 3. Save it as `review-claude.md`.
@@ -172,7 +186,21 @@ FINAL RANKING:
172
186
 
173
187
  **Task B — Adjudicate findings.** For every finding in the bundle, state: `agree | dispute | neutral` plus one-line reason. Reference each finding as **review-label + finding-id** — for example, `A2` means Review A's 2nd finding, `B1` means Review B's 1st finding. An "I missed this — it's valid" counts as `agree`.
174
188
 
175
- As each judge's ranking + adjudication response returns, collect it (the raw per-judge responses are working intermediates, not separate run-folder artifacts). Once all are in, de-anonymize and tally them into the single `crossreview-matrix.md` — the adjudication grid plus the street-cred table (see *Output & naming*). This de-anonymized data feeds Stage 3 (chair briefing), the scoring/street-cred table, and the cross-review matrix artifact — but is never re-anonymized or forwarded to any council model.
189
+ As each judge's ranking + adjudication response returns, collect it (the raw per-judge responses are working intermediates, not separate run-folder artifacts). Once all are in, **assemble the de-anonymized tally input** and then call `amicus council tally`:
190
+
191
+ **Stage-2 → tally assembly recipe (Claude's work before calling `tally`):**
192
+ 1. **Rewrite finding ids to run-global label ids.** Each Stage-1 review's local integer ids (`1`, `2`, `3`…) become `A1`, `A2`, `A3`… (where `A` is that review's anonymized label). The label↔model map (`Review A → deepseek`, etc.) is the key.
193
+ 2. **Build `adjudications`** — for every judge across all findings: `findingId` = run-global label id; `judge` = the model id (de-anonymized via the map); `verdict ∈ {agree, dispute, neutral}`. Include every judge's verdict on every finding. The raiser's own adjudication of its own finding is **included in the input** (the tally engine excludes it when computing peers-only tiers — do not pre-filter it).
194
+ 3. **Translate each judge's `FINAL RANKING:` block** — convert the label order (`1. Review C / 2. Review A / 3. Review B`) into a model `order` array via the same map (e.g. `{C→mistral, A→deepseek, B→gpt}` ⇒ `order: ["mistral","deepseek","gpt"]`). This is each entry in `rankings[]`.
195
+ 4. **Populate `runStats`** from the per-leg run documents emitted by `fanout --json` (and any solo red-team/chair `start --json` docs): copy `model`, `status`, `durationMs`, `usage` verbatim. Any leg with no run doc gets `durationMs: null` and `usage: null` — never invent a value. Attach `role` (`council` | `redteam` | `claude`), `wasChair`, and `conformance` (`clean` | `repaired` | `unstructured`) as council-domain labels.
196
+
197
+ Then call:
198
+
199
+ ```
200
+ amicus council tally <run-folder>/tally-input.json --json
201
+ ```
202
+
203
+ The output `record` carries the deterministic tiers (Disputed / Confirmed / Contested / Singleton), `confidence` (`solid` | `thin`), both street-cred numbers (`withSelf` and `peersOnly`), the validated `runStats`, and `tierCounts`. **Claude may override a `thin`-confidence tier at the margins** before Stage 4 — record the override in `tierOverride: {from, to, reason}`; the matrix and `verdict.json` surface it. De-anonymize and write the tally results to `crossreview-matrix.md` — the adjudication grid plus the street-cred table. This data feeds Stage 3 (chair briefing) and is never re-anonymized or forwarded to any council model.
176
204
 
177
205
  ---
178
206
 
@@ -180,7 +208,7 @@ As each judge's ranking + adjudication response returns, collect it (the raw per
180
208
 
181
209
  A designated **non-Claude** chair synthesizes the verdict across all reviews, rankings, and adjudications. The chair produces an independent verdict that Claude then presents — Claude does not paraphrase, edit, or re-synthesize it.
182
210
 
183
- **Chair selection (confirmed in Stage 0).** Default: Claude recommends the strongest reasoner in the council (guided by the reviewer-reliability table in `MODEL-NOTES.md`) and the user confirms before the run launches. The chair may be a council member who already participated in Stages 1 and 2 — it receives the de-anonymized full bundle, all ranking outputs, and all adjudications so it has the complete picture.
211
+ **Chair selection (confirmed in Stage 0).** Default: Claude recommends the strongest reasoner in the council (guided by `amicus council stats` (peers-only street-cred) and the qualitative quirks in `MODEL-NOTES.md`) and the user confirms before the run launches. The chair may be a council member who already participated in Stages 1 and 2 — it receives the de-anonymized full bundle, all ranking outputs, and all adjudications so it has the complete picture.
184
212
 
185
213
  **Fallback order if the chair fails:**
186
214
  1. Re-run the chair call (transient failure — `MODEL-NOTES.md` mitigations apply).
@@ -212,11 +240,11 @@ Save the chair's output to the run folder as `verdict.md`.
212
240
 
213
241
  ### Stage 4 — Tiered decisions (peer-validated)
214
242
 
215
- All findings from the bundle are sorted into two tiers based on the **peer-confidence tier derived from the Stage 2 adjudication data**. These tiers are **derived from the Stage 2 adjudications** — a judgment call, not a rigid formula (see *Key mechanics → §5.2 Scoring* for the full rule): a finding is **Confirmed** when agrees clearly outweigh disputes (≥ 2 judges engaged), **Contested** when there is a meaningful split or explicit disputes, and **Singleton** when only its original raiser stands behind it. Present the tiers in this order.
243
+ All findings from the bundle are sorted into tiers based on the **peer-confidence tier assigned by `amicus council tally`** (see *Key mechanics → §5.2 Scoring* in COUNCIL-DESIGN.md for the full cascade): **Disputed** (strong peer pushback `d 2` and `d > a`), **Confirmed** (≥ 2 peer agreements, agrees dominate), **Contested** (at least one live dispute), **Singleton** (at most one endorsement, no pushback). `confidence: thin` cells `(0,0)/(1,0)/(0,1)` are override-eligible (Claude records any override in `tierOverride`). Present the tiers in this order: Confirmed first (bulk decision), then Disputed and Contested and Singleton individually in the judgment tier.
216
244
 
217
245
  **Scale-down:** In a 1-model run, Stage 2 was skipped — there is no peer-confidence data, so present every finding individually for decision (no tiers). In a 2-model run, the Confirmed tier rests on thin cross-review (one ranker per review, per Stage 0) — say so when presenting it.
218
246
 
219
- **Consensus tier — Confirmed findings** (peers agree clearly outweigh disputes, with ≥ 2 judges engaged)
247
+ **Consensus tier — Confirmed findings** ( 2 peer agreements, agrees dominate)
220
248
 
221
249
  - Present the full list in one block: id, claim, severity, and which models raised / endorsed it.
222
250
  - Offer one **bulk accept/deny decision** over the whole tier:
@@ -225,12 +253,13 @@ All findings from the bundle are sorted into two tiers based on the **peer-confi
225
253
 
226
254
  - The user may accept the block, deny the block, or enumerate exceptions. Handle exceptions individually before moving on.
227
255
 
228
- **Judgment tier — Contested and Singleton findings**
256
+ **Judgment tier — Disputed, Contested, and Singleton findings**
229
257
 
230
- This is one tier with two sub-types presented separately. Present each finding individually. Handle the two sub-types distinctly:
258
+ This is one tier with three sub-types presented separately. Present each finding individually. Handle the sub-types distinctly:
231
259
 
232
- - **Contested** (meaningful split or explicit disputes): For each finding show the claim and severity, which model raised it, who agreed, who disputed, and the one-line reasons from the adjudications. Ask for a decision before proceeding to the next: **accept / deny / modify**.
233
- - **Singleton** (only the original raiser; no other judge engaged — neutral or silent): For each finding show the claim and severity and that no other judge engaged with it. Name the sole raiser. Ask for a decision before proceeding to the next: **accept / deny / modify**.
260
+ - **Disputed** (`d 2` and `d > a` — strong peer pushback, the finding itself may be wrong): For each finding show the claim, severity, which model raised it, which peers dispute it and why. Ask for a decision before proceeding to the next: **accept / deny / modify**.
261
+ - **Contested** (`d 1` with a meaningful split): For each finding show the claim and severity, which model raised it, who agreed, who disputed, and the one-line reasons from the adjudications. Ask for a decision before proceeding to the next: **accept / deny / modify**.
262
+ - **Singleton** (only the original raiser; all other judges were neutral or silent — `d = 0` and `a < 2`): For each finding show the claim and severity and that no other judge engaged with it. Name the sole raiser. Ask for a decision before proceeding to the next: **accept / deny / modify**.
234
263
 
235
264
  **Recording decisions.** Keep a running decision log throughout this stage — every finding's outcome (accepted / denied / modified, with any modification noted). This log feeds Stage 5 (only accepted changes go into the reviewed copy) and Stage 6 (the run-folder report).
236
265
 
@@ -249,15 +278,19 @@ Do not advance to Stage 5 until every finding in both tiers has a recorded decis
249
278
  - Do not attempt to produce a modified copy.
250
279
  - 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.
251
280
 
252
- **Run-folder artifacts — always write these** regardless of source type. The full artifact set and naming conventions are defined in the *Output & naming* section of this skill; write every artifact specified there. The four canonical run-folder files are:
281
+ **Run-folder artifacts — always write these** regardless of source type. The full artifact set and naming conventions are defined in the *Output & naming* section of this skill; write every artifact specified there. The canonical run-folder files are:
253
282
  - `review-<model>.md` × N (already saved in Stage 1)
254
283
  - `crossreview-matrix.md` — the de-anonymized adjudication grid and street-cred table
255
284
  - `verdict.md` (already saved in Stage 3)
285
+ - `verdict.json` — write via `buildVerdict(record, decisions)` (`src/council/verdict.js`): pass the tally `record` from Stage 2 and the Stage-4 decision map (accepted / denied / modified / deferred per finding, plus any `duplicateOf` links Claude identified). This is the schema-stamped machine-readable record of the full run. Write it with an atomic tmp+rename to the run folder.
256
286
  - `report.md` — the chair's synthesis + the full Stage-4 decision log + a summary of what was
257
287
  applied (+ the "How Claude's review fared" readout when "Claude in the council" is on) + a
258
288
  **run-stats table**: one row per model call — **stage** (which stage you launched the call for)
259
- plus **model, status, durationMs** read from the wave/run JSON documents. The schema carries no
260
- cost data do not invent cost figures.
289
+ plus **model, status, durationMs, and cost** read from the wave/run JSON `usage`
290
+ block. Cost is `usage.cost.amount` (USD); mark it with its `usage.cost.source`
291
+ — exact for `reported`, `~` for `estimated`, `?` for `unknown` — and never
292
+ invent a figure. Add a wave **total cost** row from the wave document's
293
+ `usage.cost` (`source: reported|estimated|mixed|unknown`). Any leg with no run doc → `durationMs: null`, `usage: null`; never invent a value.
261
294
 
262
295
  Tell the user exactly which files were written and where.
263
296
 
@@ -274,18 +307,15 @@ This stage updates `MODEL-NOTES.md` to make future runs better. **Nothing is wri
274
307
 
275
308
  Draft new or updated entries for the per-model sections of `MODEL-NOTES.md` that capture what was learned.
276
309
 
277
- **Update the reviewer-reliability table.** After every completed council run, update the rolling table in `MODEL-NOTES.md` (the "Reviewer reliability" table) for each council model that participated:
278
- - **avg street-cred** — incorporate this run's rank position into each model's running average.
279
- - **confirm-rate** — incorporate this run's share of each model's findings that ended up Confirmed.
280
- - Merge into the existing row for that model; prune the notes column to stay tight.
310
+ **Ledger auto-append (automatic — no approval required).** After `verdict.json` is written, the ledger row is appended automatically when the tally record is finalized — `ledger.appendRun(record)` writes one row per (run × model) to the append-only `council-ledger.jsonl` under `getConfigDir()`. The run summary shows the appended row. This is a deterministic, content-free model-level record (no finding text, no claim strings, no artifact body content). The quantitative reviewer-reliability data in `MODEL-NOTES.md` is now sourced entirely from `amicus council stats` (which aggregates the ledger) **do not hand-edit reliability numbers in MODEL-NOTES**.
281
311
 
282
- **Compose the proposed MODEL-NOTES diff.** Combine the run-lessons updates and the reviewer-reliability table updates into a single proposed diff (old → new for every changed section). Show it to the user in full.
312
+ **Compose the proposed MODEL-NOTES diff.** Combine the run-lessons updates and the reviewer-reliability table updates into a single proposed diff (old → new for every changed section). **Write the full diff to a file in the run folder** — `_tmp-proposed-model-notes-update.md` — so the user can open and review it before deciding. Presenting the diff as chat text alone is **not sufficient**: an approval dialog can hide the chat transcript, so the user may be asked to decide on a diff they never saw.
283
313
 
284
- **Wait for explicit approval before writing anything.** Present the diff and ask:
314
+ **Wait for explicit approval before writing anything.** Ask, with the diff file's path inside the approval prompt itself:
285
315
 
286
- > Approve this MODEL-NOTES update? (yes / no / edit)
316
+ > Proposed MODEL-NOTES update written to `<run-folder>/_tmp-proposed-model-notes-update.md` — open it to review. Approve this MODEL-NOTES update? (yes / no / edit)
287
317
 
288
- If the user approves, write the changes. If they say "edit", incorporate their corrections and show the revised diff before writing. Do not write any partial update — write only after the full diff is approved.
318
+ If the user approves, write the changes. If they say "edit", incorporate their corrections, rewrite the diff file, and re-present its path for approval before writing. Do not write any partial update — write only after the full diff is approved.
289
319
 
290
320
  **Keep MODEL-NOTES tight.** Do not append new bullets when an existing entry covers the same ground — merge or reword instead. If a note has been superseded by a better mitigation, prune the old one. The goal is a compact, authoritative reference, not a changelog.
291
321
 
@@ -309,23 +339,21 @@ Claude **de-anonymizes only** at two points: when computing scores and when writ
309
339
 
310
340
  ### §5.2 Scoring
311
341
 
312
- Claude tallies two scoring signals from the Stage-2 outputs. No code is required; Claude works through the structured output directly.
313
-
314
- **Street-cred** = each model's **average rank position** across all judges' `FINAL RANKING:` blocks (lower is better). For example, if three judges rank DeepSeek 1st, 2nd, and 1st, its street-cred score is 1.33. Surface this as a compact table in the cross-review matrix and report. Street-cred drives the chair's weighting of reviewer findings in Stage 3 and feeds the reviewer-reliability table updated in Stage 6.
342
+ `amicus council tally` computes the two scoring signals from the assembled tally input. Claude's role is to assemble the input (Stage-2 assembly recipe in Stage 2 above) and to exercise judgment on `thin`-confidence overrides.
315
343
 
316
- **Per-finding peer-confidence tier** = a qualitative label derived from the Stage-2 adjudications for each finding:
344
+ **Street-cred** computed two ways:
345
+ - **withSelf** = each model's mean rank position across **all** judges' `FINAL RANKING:` blocks (lower is better).
346
+ - **peersOnly** = mean rank excluding the model's own ranking of itself.
317
347
 
318
- - **Confirmed** agrees clearly outweigh disputes, with at least 2 judges having engaged with the finding.
319
- - **Contested** — a meaningful split exists or explicit disputes were recorded.
320
- - **Singleton** — only the original raiser stands behind it; all other judges were neutral or silent.
348
+ Both are surfaced in `crossreview-matrix.md` and `report.md`. The ledger and Stage-0 bench recommendations use **peersOnly** only.
321
349
 
322
- These three tiers drive the Stage-4 decision flow. Assigning a tier is a **judgment call, not a rigid formula** Claude reads the adjudication signals and makes the call at the margins, especially when engagement is sparse or agreements and disputes are close in number. When in doubt, downgrade toward Contested or Singleton rather than overstate confidence.
350
+ **Per-finding peer-confidence tier** assigned by the peers-only cascade in `amicus council tally` (see COUNCIL-DESIGN.md §5.2 for the full table): **Disputed** **Confirmed** **Contested** **Singleton**. The raiser's own adjudication is excluded from the cascade. `confidence: thin` when total engaged peers `a + d ≤ 1` — cells `(0,0)`, `(1,0)`, `(0,1)`. **Claude may override a `thin` tier at the margins** before presenting Stage 4 — the override is recorded in `tierOverride: {from, to, reason}` and surfaced in the matrix and `verdict.json`.
323
351
 
324
352
  ---
325
353
 
326
354
  ### §5.3 Chair selection & fallback
327
355
 
328
- The default is for Claude to **recommend a non-Claude chair** from the council — typically the model with the strongest reasoning capability or the best reviewer-reliability score in `MODEL-NOTES.md` — and the user confirms this recommendation before the run launches (Stage 0). The chair **may** be a council member who already participated in Stages 1 and 2; it receives the full de-anonymized picture (all reviews with model attribution, all rankings, all adjudications) so it can synthesize from a complete view.
356
+ The default is for Claude to **recommend a non-Claude chair** from the council — typically the model with the strongest reasoning capability or the best peers-only street-cred from `amicus council stats` — and the user confirms this recommendation before the run launches (Stage 0). The chair **may** be a council member who already participated in Stages 1 and 2; it receives the full de-anonymized picture (all reviews with model attribution, all rankings, all adjudications) so it can synthesize from a complete view.
329
357
 
330
358
  **Fallback chain if the chair call fails:**
331
359
 
@@ -346,8 +374,8 @@ Enabling this toggle lets the bench judge Claude's own take, so you can see how
346
374
  **Always fresh.** When the toggle is on, Claude performs a new structured Stage-1 review on the artifact — a fresh pass in the required findings format, not a formalization or summary of anything said earlier in the main conversation. Upstream feedback does not seed or constrain this review.
347
375
 
348
376
  **"How Claude's review fared" readout.** Included in both `crossreview-matrix.md` and `report.md` when the toggle is on:
349
- - Claude's street-cred rank among peers (its average rank position in the judges' `FINAL RANKING:` blocks).
350
- - The Confirmed / Contested / Singleton split of Claude's findings — how many of its claims the bench endorsed, contested, or ignored.
377
+ - Claude's street-cred rank among peers (its `peersOnly` average rank position in the judges' `FINAL RANKING:` blocks — `withSelf == peersOnly` for Claude since it never casts rankings).
378
+ - The Disputed / Confirmed / Contested / Singleton split of Claude's findings — how many of its claims the bench pushed back on, endorsed, disputed, or ignored.
351
379
 
352
380
  **Integrity.** When Claude presents results — including the bench's assessment of its own review — it reports the verdict at face value. Claude does not defend, contextualize away, or re-litigate findings the bench disputed or ranked poorly. The point of the toggle is an honest external read on Claude's review; undermining that defeats the purpose.
353
381
 
@@ -355,16 +383,16 @@ Enabling this toggle lets the bench judge Claude's own take, so you can see how
355
383
 
356
384
  ## Model-recommendation heuristics
357
385
 
358
- Use these together with the reviewer-reliability table in `MODEL-NOTES.md`, which holds live performance data from prior runs:
386
+ 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`:
359
387
 
360
388
  - **Large or long material, broad coverage sweep** → favor a large-context model (e.g., Gemini) that won't truncate or degrade on the full source.
361
389
  - **Reasoning-heavy critique, structured argument evaluation, citations** → favor a strong reasoner (e.g., DeepSeek, GPT, Opus) that will interrogate claims rather than accept them.
362
390
  - **Code review** → favor a code-strong model (e.g., DeepSeek, GPT, Opus); general-purpose models often miss implementation-level issues.
363
391
  - **Independence matters** → pick models from **different families**; two models from the same family produce correlated opinions and reduce the value of the cross-review.
364
392
  - **Contrarian / red-team value** → when material is persuasive, consensus-prone, or high-stakes, assign one model an explicit red-team brief: argue against the others, hunt for what they will miss. This is especially valuable when the default council is likely to agree.
365
- - **Consult the reviewer-reliability table** in `MODEL-NOTES.md` — a model's historical confirm-rate and avg street-cred are the best predictors of council value for a given run type.
393
+ - **Consult `amicus council stats`** — a model's historical confirm-rate and avg peers-only street-cred (from the ledger) are the best predictors of council value for a given run type.
366
394
 
367
- Always **rank recommendations by fit**, state the trade-off for each option, and surface the estimated cost. Never present a single option without explanation.
395
+ Always **rank recommendations by fit**, state the trade-off for each option, and surface the estimated cost (an estimate, not a guarantee; unpriced legs disclosed as "cost unknown"). Never present a single option without explanation.
368
396
 
369
397
  ---
370
398
 
@@ -373,17 +401,22 @@ Always **rank recommendations by fit**, state the trade-off for each option, and
373
401
  - Run folder: `output/<stem>-council/` (or `./second-opinion/<stem>-council/` if no `output/` exists), containing:
374
402
  - `review-<model>.md` ×N — raw Stage 1 reviews (plus `review-claude.md` when "Claude in the council" is on)
375
403
  - `crossreview-matrix.md` — adjudication grid + de-anonymized street-cred table
376
- - `verdict.md` — the chair's synthesis
404
+ - `verdict.md` — the chair's synthesis (prose)
405
+ - `verdict.json` — schema-stamped machine-readable record: tally output + Stage-4 decisions, written via `buildVerdict(record, decisions)` at Stage 5
377
406
  - `report.md` — synthesis + decision log + what was applied (+ the "How Claude's review fared" readout when the toggle is on) + a
378
- **run-stats table**: one row per model call — **stage** (which stage you launched the call for) plus **model, status, durationMs** read from the wave/run JSON documents. The schema carries no cost data — do not invent cost figures.
407
+ **run-stats table**: one row per model call — **stage** (which stage you launched the call for) plus **model, status, durationMs, and cost** read from the wave/run JSON `usage`
408
+ block. Cost is `usage.cost.amount` (USD); mark it with its `usage.cost.source`
409
+ — exact for `reported`, `~` for `estimated`, `?` for `unknown` — and never
410
+ invent a figure. Add a wave **total cost** row from the wave document's
411
+ `usage.cost` (`source: reported|estimated|mixed|unknown`). Any leg with no run doc → `durationMs: null`, `usage: null`.
379
412
  - Reviewed copy: `<stem>-reviewed.<ext>`, next to the source.
380
- - Temp working files (`_tmp-*.md`: extracts, stage briefings, red-team brief, bundle, chair packet) live in the
381
- run folder and are cleaned up at the end of the run.
413
+ - Temp working files (`_tmp-*.md`: extracts, stage briefings, red-team brief, bundle, chair packet, proposed
414
+ MODEL-NOTES diff) live in the run folder and are cleaned up at the end of the run — the proposed-diff file
415
+ only after the Stage-6 approval decision is resolved.
382
416
 
383
417
  ---
384
418
 
385
419
  ## Files
386
420
 
387
- - `MODEL-NOTES.md` — operating rules, per-model quirks, cost guardrail, and the reviewer-reliability rolling table. **Read it before Stage 0 (council selection and launch); update it (with approval) in Stage 6.**
388
- - `COUNCIL-DESIGN.md` — the design spec this skill implements (v3). Consult it if a mechanics
389
- question arises that the skill prose does not resolve.
421
+ - `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.
422
+ - `COUNCIL-DESIGN.md` — the design spec this skill implements (v3 + WS-3). Consult it if a mechanics question arises that the skill prose does not resolve.
@@ -24,6 +24,10 @@ description: >
24
24
  --prompt-file <path> --json` (one headless wave, one JSON result) instead of N
25
25
  separate start calls. Different prompts per model → separate parallel
26
26
  `amicus start --no-ui` calls.
27
+ (7) For a SINGLE-model sidecar, DEFAULT to interactive — omit --no-ui so the
28
+ Electron UI opens and the user can watch, converse, and click Fold. Use --no-ui
29
+ for a single model only when the user asks for headless/autonomous, or for
30
+ unattended bulk automation. Interactive launches still use run_in_background: true.
27
31
  ---
28
32
 
29
33
  # Amicus: Multi-Model Sidecar Tool
@@ -608,13 +612,15 @@ amicus start --model gemini --prompt "Implement the login feature" --agent Build
608
612
 
609
613
  ### Interactive (Default)
610
614
 
615
+ **The default for single-model sidecars — omit `--no-ui`.** Reach for headless only when the user asks for it, the run is part of a multi-model wave, or the task is unattended bulk automation.
616
+
611
617
  - Opens a GUI window
612
618
  - User can converse with the sidecar
613
619
  - **Model Picker:** Click the model name in the input area to switch models mid-conversation
614
620
  - Click **FOLD** when done to generate summary
615
621
  - Summary returns to your context via stdout
616
622
 
617
- **Use for:** Debugging, exploration, architectural discussions
623
+ **Use for:** Any single-model sidecar — debugging, exploration, second opinions, reviews, architectural discussions
618
624
 
619
625
  **Mid-Conversation Model Switching:**
620
626
  In interactive mode, you can change models without restarting:
@@ -635,6 +641,7 @@ This is useful when you want to:
635
641
  - Summary returns automatically
636
642
  - **Default agent is `build`** — `chat` agent requires interactive UI and will stall in headless mode
637
643
  - **Always use headless when spawning multiple sidecars at once** (see Multi-LLM rule below)
644
+ - **Not the default for single-model runs** — a single-model sidecar opens the UI unless the user asks for headless or the task is unattended bulk work
638
645
 
639
646
  **Multi-LLM Rule:** When the SAME prompt goes to N models, use `amicus fanout` (see [Fan Out One Prompt to N Models](#fan-out-one-prompt-to-n-models)) — one headless wave, one JSON result. When prompts differ per model, use separate parallel `amicus start --no-ui` calls with `run_in_background: true`. Only switch to interactive if the user explicitly asks.
640
647
 
@@ -656,7 +663,7 @@ amicus start --model gemini --prompt "..." --agent chat --no-ui
656
663
  # → Error: --agent chat requires interactive mode (remove --no-ui or use --agent build)
657
664
  ```
658
665
 
659
- **Use for:** Bulk tasks, test generation, documentation, linting
666
+ **Use for:** Multi-model waves, bulk tasks, test generation, documentation, linting — or when the user explicitly asks for headless
660
667
 
661
668
  ```bash
662
669
  amicus start \
@@ -679,11 +686,14 @@ amicus start \
679
686
  **Example invocation pattern:**
680
687
  ```
681
688
  Bash tool:
682
- command: "amicus start --model gemini --prompt '...' --no-ui"
689
+ command: "amicus start --model gemini --prompt '...'"
683
690
  run_in_background: true
684
691
  ```
685
692
 
686
- After launching, tell the user:
693
+ After launching an interactive sidecar, tell the user:
694
+ > "The Amicus window is open — chat with it there and click FOLD when you're done. I'll pick up the summary here."
695
+
696
+ After launching a headless sidecar, tell the user:
687
697
  > "Amicus is running in the background. I'll share the results when it completes."
688
698
 
689
699
  **When the background task completes**, you will be automatically notified. Use the `TaskOutput` tool with the task ID to read the sidecar's summary output, then present it to the user. Do NOT poll or sleep — the notification arrives automatically.
@@ -0,0 +1,59 @@
1
+ // src/cli-handlers-council.js
2
+ 'use strict';
3
+ const fs = require('fs');
4
+ const { tally } = require('./council/tally');
5
+ const { deriveReliability } = require('./council/ledger');
6
+ const { failJson, ERROR_CODES } = require('./utils/error-doc');
7
+
8
+ function runTally(inputPath, useJson) {
9
+ if (!inputPath) {
10
+ return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: 'council tally needs an <input.json> path',
11
+ hint: 'amicus council tally <input.json> [--json]' });
12
+ }
13
+ let input;
14
+ try { input = JSON.parse(fs.readFileSync(inputPath, 'utf-8')); }
15
+ catch (e) {
16
+ return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: `cannot read ${inputPath}: ${e.message}`,
17
+ hint: 'pass a valid tally input JSON file' });
18
+ }
19
+ let record;
20
+ try { record = tally(input); }
21
+ catch (e) {
22
+ return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: `malformed tally input: ${e.message}`,
23
+ hint: 'input needs meta.models, findings[], adjudications[], rankings[]' });
24
+ }
25
+ process.stdout.write(useJson ? JSON.stringify(record, null, 2) + '\n' : renderRecord(record));
26
+ return 0;
27
+ }
28
+
29
+ function runStats(useJson) {
30
+ const agg = deriveReliability();
31
+ process.stdout.write(useJson ? JSON.stringify(agg, null, 2) + '\n' : renderStats(agg));
32
+ return 0;
33
+ }
34
+
35
+ function renderRecord(r) {
36
+ const t = r.tierCounts;
37
+ return `Council tally (${r.meta.runId})\n` +
38
+ ` Confirmed ${t.Confirmed} Contested ${t.Contested} Singleton ${t.Singleton} Disputed ${t.Disputed}\n`;
39
+ }
40
+ function renderStats(agg) {
41
+ if (!agg.length) { return 'No council runs recorded yet.\n'; }
42
+ return 'model runs avg-cred confirm fact-err notes\n' +
43
+ agg.map(a => `${a.model.padEnd(16)} ${String(a.runs).padStart(4)} ` +
44
+ `${fmt(a.avgStreetCredPeersOnly)} ${fmt(a.lifetimeConfirmRate)} ${fmt(a.lifetimeFactErrorRate)}` +
45
+ `${a.lowN ? ' low-N' : ''}`).join('\n') + '\n';
46
+ }
47
+ function fmt(v) { return (v === null || v === undefined) ? ' — ' : v.toFixed(2); }
48
+
49
+ /** @param {{_:string[], json?:boolean}} args @returns {Promise<number>} */
50
+ async function handleCouncil(args) {
51
+ const sub = args._[1];
52
+ const useJson = !!args.json;
53
+ if (sub === 'tally') { return runTally(args._[2], useJson); }
54
+ if (sub === 'stats') { return runStats(useJson); }
55
+ return failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
56
+ message: `unknown council subcommand '${sub || ''}'`, hint: 'amicus council tally|stats' });
57
+ }
58
+
59
+ module.exports = { handleCouncil };