amicus 4.5.4 → 4.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +113 -0
  3. package/README.md +1 -1
  4. package/commands/council.md +1 -1
  5. package/docs/DISTRIBUTION.md +38 -11
  6. package/docs/ROADMAP.md +40 -8
  7. package/docs/publishing.md +1 -1
  8. package/docs/usage.md +1 -1
  9. package/package.json +3 -2
  10. package/schemas/council-run.schema.json +20 -0
  11. package/schemas/council-verdict.schema.json +20 -0
  12. package/schemas/doctor.schema.json +23 -1
  13. package/skills/second-opinion/MODEL-NOTES.md +182 -35
  14. package/src/cli-council-run-render.js +51 -0
  15. package/src/cli-handlers-council-run.js +45 -44
  16. package/src/cli-handlers-council.js +9 -3
  17. package/src/cli-handlers-doctor.js +16 -37
  18. package/src/cli-handlers-watch.js +1 -1
  19. package/src/cli.js +1 -1
  20. package/src/council/ledger.js +5 -1
  21. package/src/council/report-html.js +16 -1
  22. package/src/council/report.js +25 -1
  23. package/src/council/run-assemble.js +25 -7
  24. package/src/council/run-budget.js +14 -8
  25. package/src/council/run-chair.js +21 -4
  26. package/src/council/run-debate-stage.js +115 -0
  27. package/src/council/run-degrade.js +44 -0
  28. package/src/council/run-finalize.js +18 -3
  29. package/src/council/run-launch.js +4 -0
  30. package/src/council/run-retry-notes.js +74 -0
  31. package/src/council/run-retry.js +280 -0
  32. package/src/council/run-server.js +24 -7
  33. package/src/council/run-stage2.js +10 -2
  34. package/src/council/run-stages.js +59 -27
  35. package/src/council/run.js +39 -67
  36. package/src/council/verdict.js +81 -8
  37. package/src/mcp-council-bench.js +45 -0
  38. package/src/mcp-council-run.js +11 -28
  39. package/src/mcp-server.js +22 -3
  40. package/src/mcp-tools.js +13 -1
  41. package/src/utils/degrade.js +69 -0
  42. package/src/utils/doctor-degrade.js +51 -0
  43. package/src/utils/doctor-electron-mcp-check.js +64 -5
  44. package/src/utils/doctor-engine-check.js +14 -3
  45. package/src/utils/doctor-mcp-checks.js +10 -3
  46. package/src/utils/known-flags.js +2 -1
  47. package/src/utils/remediation-hints.js +20 -14
  48. package/src/utils/result-schema.js +6 -2
  49. package/src/utils/session-index-tmp-sweep.js +2 -1
  50. package/src/utils/update-notice.js +171 -0
  51. package/src/workspace/run-scan.js +5 -1
@@ -4,8 +4,10 @@ This file is the `second-opinion` skill's evolving memory of **how to actually d
4
4
  well**. Read it before Stage 0 (council selection and launch); update it, with the user's
5
5
  approval, at the end of each run (Stage 6). Keep it tight — merge and prune rather than append.
6
6
 
7
- _Last updated: 2026-07-26 (v4.4.0 fold-back: alias-resolution hygiene, `council run --run-id`,
8
- debate defense/re-vote waves now exercised, haiku and glm notes; see changelog)._
7
+ _Last updated: 2026-08-03 (per-section fold-back, both directions the haiku "hard-404"
8
+ re-diagnosed as a `/v1`-less `ANTHROPIC_BASE_URL`, pre-degrade-era claims re-grounded in the
9
+ announcement contract (`degrades[]`/`seatLoss`/exit 2/one Stage-1 retry), three model sections and
10
+ the peer-consensus≠evidence rule upstreamed from the field ledger; see changelog)._
9
11
 
10
12
  ## Global operating rules (all models)
11
13
  - **Fast path:** `council run` applies `--agent Plan` / `--no-context` / `--summary-length
@@ -22,14 +24,33 @@ debate defense/re-vote waves now exercised, haiku and glm notes; see changelog).
22
24
  defined in SKILL.md Stage 0) — no size cap, no shell-quoting hazards. Never inline a briefing as
23
25
  a CLI argument.
24
26
  - **Run in the background (`run_in_background: true`); you're notified on completion. Don't poll.**
27
+ - **A stale MCP server kills councils with a distinctive signature — zero output on every leg.**
28
+ If any amicus MCP tool result carries the "Amicus was upgraded on disk (running vX, on-disk vY).
29
+ Restart your MCP client" notice, do NOT launch councils through the MCP tools — relaunch via the
30
+ CLI (`amicus council run`, a fresh process) or restart the client first. A version-skewed server
31
+ has timed out an entire Stage-1 wave with literally zero output bytes on every leg; that
32
+ all-legs-zero-output shape is what distinguishes it from model/provider failures, which produce
33
+ partial output or errors.
25
34
  - **Read results from the JSON documents** (`--json`): a wave's `legs[].summary` / a run's
26
35
  `summary` is the model's output; `status`/`error`/`counts` are ground truth for failures. Never
27
36
  scrape stderr logs to judge success.
28
37
  - **Transient provider errors** (502s, connection drops): re-run the affected leg (solo
29
38
  `amicus start --json`, same briefing file) or the wave — see per-model notes for
30
39
  model-specific signals. Never present a half-finished run as an answer.
40
+ - **Seat losses are announced, retried once, and exit-coded — read the surfaces, don't diff
41
+ finding counts.** Every lost seat/leg is announced in one voice on stderr and recorded in
42
+ `run.json.degrades[]`, `verdict.json.degrades[]`, the report's **"What was lost"** section, and
43
+ `verdict.seatLoss`, and the run exits degraded (2). A Stage-1 wave or leg that dies is relaunched exactly once (serially, after the
44
+ surviving launches settle; skipped when the run is already over `--max-cost`): a heal announces
45
+ as a `Recovered:` line and the run stays exit 0; a seat still dead after its retry is recorded
46
+ with both attempts named in the why. A dead route still doesn't stop a council — it shrinks it —
47
+ but the shrinkage is no longer silent: confirm the bench you paid for from
48
+ `seatLoss`/`degrades[]` after every run, because a shrunken bench weakens the tiers (fewer
49
+ corroborators per finding).
31
50
  - **Credentials:** keys live in `~/.config/amicus/.env`. The legacy `~/.config/sidecar/.env`
32
- fallback was removed in v2.0.0 (see `docs/SHIMS.md`). Configure with `amicus setup`.
51
+ fallback was removed in v2.0.0 (see `docs/SHIMS.md`). Configure with `amicus setup`. If
52
+ `ANTHROPIC_BASE_URL` is set anywhere in the environment, it must carry its `/v1` suffix for
53
+ direct-Anthropic legs — see the haiku section for the full diagnosis.
33
54
  - **PowerShell `--models` quoting (Windows):** always quote comma-separated model lists —
34
55
  `--models "gemini,gpt,deepseek"`. Unquoted, PowerShell splits on commas and amicus receives one
35
56
  mangled alias → instant arg-parse failure. (Now baked into every SKILL.md example.)
@@ -40,18 +61,50 @@ debate defense/re-vote waves now exercised, haiku and glm notes; see changelog).
40
61
  gpt/deepseek/grok have handled 82k-word agentic reads; gemini(-flash) and kimi stalled (narrate-
41
62
  stall / 25-min timeout / poller "Incomplete"). Pre-select proven long-read models or inline the
42
63
  text for large-context models.
64
+ - **Long agentic reads can "narrate-then-never-deliver" even on capable models that DO finish
65
+ reading — bake the anti-narration hardening into the FIRST attempt, not the retry.** Models have
66
+ done a genuine full chunked read (confirmed by realistic input-token counts) and then ended the
67
+ turn with only process narration ("Continuing…"), never producing the review + JSON: the read
68
+ completed, but the turn budget ran out before the switch from reading mode to writing mode.
69
+ Whenever the task requires many sequential file-read tool calls before writing, the briefing
70
+ needs an explicit preamble: *"this is headless with no follow-up turn; you must produce the
71
+ complete deliverable by the end of THIS response; if running low on room, stop reading early and
72
+ write from what you have rather than deliver nothing."*
73
+ - **Multi-FILE agentic reads stub out like long single reads — even WITH the anti-narration
74
+ preamble baked in.** Weaker readers have returned narration stubs of roughly 60–100 chars on a
75
+ multi-file packet despite the standard preamble. Treat a many-file briefing as a long-read task:
76
+ expect stubs, retry solo once with the identical briefing, then substitute the model — or
77
+ pre-concatenate the packet into one file for weaker readers.
78
+ - **`--agent Plan` can trigger a literal, hard "planning mode — cannot execute" refusal on some
79
+ models** (terse, near-zero-token responses), not just the intended read-only tool restriction —
80
+ and prompt wording alone has not fixed it; switching that model to `--agent Build` has, immediately.
81
+ On the manual path, Stages 2-3 never touch the source file (the briefing is self-contained plus a
82
+ no-tools preamble), so `--agent Build` is safe there; reserve `--agent Plan` for Stage-1 legs
83
+ that read the source, and fall back to Build per-model on a hard refusal.
43
84
  - **Debate mode rarely fires on correctness questions — design for it or expect a no-op.**
44
85
  `--debate` only engages findings the tally marked Contested or Disputed, and judges agree far
45
86
  more than expected: **2 disputes in 123 adjudications across four councils** (gemini/gpt/qwen).
46
87
  Three of those four runs produced zero debatable findings, so the round never ran. Bug hunts
47
88
  converge — a race condition either exists or it does not. If you want a rebuttal round, brief a
48
89
  genuine judgement call (architecture, tradeoffs, "is this over-engineered"), not a defect hunt.
90
+ - **Peer consensus is not evidence on published, checkable numbers — verify against a primary
91
+ source before applying such a finding, especially from the Confirmed tier and findings that
92
+ survived debate.** In one paid council, 2 of 23 findings were factually wrong about published
93
+ specs and BOTH sat in the tiers that are supposed to signal reliability: one reached Confirmed
94
+ on two peer agreements, the other hardened after a debate round in which a correct dispute was
95
+ talked out of its objection. A rebuttal is a rhetorical performance — judges re-voting on a
96
+ persuasive defense have no more access to the spec sheet than they did the first time. Any
97
+ finding that asserts a published figure (spec, capacity, rating, dimension, date) gets a
98
+ verification pass before you act on it.
49
99
  - **Read spend from `run.json`, never from a provider credit balance.** `usage.cost.amount` is the
50
100
  run total; per-leg costs are in `runStats[].usage.cost`. Only OpenRouter-routed legs move the
51
101
  OpenRouter balance — gemini/gpt/anthropic bill directly against their own keys, so inferring cost
52
102
  from that balance under-reports it badly (observed: ~6x low). A 3-model bench + chair + debate is
53
103
  roughly **$0.60-0.80 per run**, not cents; budget `--max-cost` accordingly or the chair gets
54
- skipped mid-run (exit 2, degraded) when the debate legs push the total past the ceiling.
104
+ skipped mid-run (exit 2, degraded) when the debate legs push the total past the ceiling. As of
105
+ v4.6, `runStats` also carries Stage-2 judge rows (judge-tagged), so totals read higher than
106
+ pre-4.6 runs for the same bench; anything keying `runStats` by model should exclude
107
+ `role: 'judge'`.
55
108
  - **Expect agreement inflation in Stage-2 adjudication.** The judge contract defines `agree` by
56
109
  worked example ("an 'I missed this — it's valid' counts as agree") but gives no example for
57
110
  `dispute` and no positive definition of `neutral`, while requiring a verdict on EVERY finding —
@@ -105,7 +158,9 @@ debate defense/re-vote waves now exercised, haiku and glm notes; see changelog).
105
158
  **rescope** mechanism rather than a defense — raisers overwhelmingly AMEND (downgrade an
106
159
  overstated severity, narrow a scope) rather than DEFEND, and amendments are re-confirmed on the
107
160
  re-vote. Expect high-amend / low-defend; the idea usually survives, the severity claim often does
108
- not.
161
+ not. In ideation councils, **cross-lens convergence** (independent lenses proposing the same
162
+ idea) is the strongest priority signal — compute it yourself when clustering; it is
163
+ complementary to, not the same as, the tally's agree/dispute tiers.
109
164
 
110
165
  ## Per-model notes
111
166
 
@@ -118,61 +173,115 @@ debate defense/re-vote waves now exercised, haiku and glm notes; see changelog).
118
173
  - **Red-team:** takes an adversarial brief well — high variance by design; use when consensus risk is high.
119
174
  - **Blind self-votes are inconsistent** (self-#1 in some runs, self-last in others) → discount self-votes either way.
120
175
  - Alias has resolved to **flash** tiers: fast, shallowest coverage, yet a recurring sharp fact/consistency checker (it alone refuted a bench-wide date error). Cheap cross-check value.
176
+ - **Asserts quantitative "corrections" (timings, capacities) with unearned confidence** — one such
177
+ "severely wrong" correction was disputed 3-0, including its own blind self-dispute. Cross-check
178
+ its numeric claims before weighting; its honest blind self-disputes are a useful calibration tell.
121
179
  - **Unreliable on long agentic reads** (see global rule) — inline the text or swap models for book-length material.
122
180
 
123
181
  ### DeepSeek (`--model deepseek` → via OpenRouter)
124
182
  - **Strengths:** resilient; produces strong, well-structured, well-cited critical analysis. A good
125
183
  default reviewer and a proven chair.
126
184
  - **Quirk:** occasional transient 502 mid-run → re-run the leg.
127
- - Proven chair (5 clean chairings) — decisive, well-structured synthesis.
185
+ - Proven chair (many clean chairings) — decisive, well-structured synthesis.
128
186
  - As a Stage-1 reviewer of human-facing documents it **over-escalates severity** (typos/tenure → "blocker"); discount its blocker labels against peers. Prune its self-retractions when tallying.
129
187
  - **"Agree-with-the-adversary" lean:** it has been the lone endorser of a red-team's harshest claims, turning them Contested — cross-check before treating its lone agreements as consensus.
188
+ - **Stub-on-fanout / clean-on-solo-retry:** its Stage-1 fanout leg has returned a sub-100-char
189
+ narration stub despite the anti-narration preamble, then produced a full review on a plain solo
190
+ retry with the identical briefing — retry solo before substituting the model.
130
191
 
131
192
  ### GPT (`--model gpt` → via OpenRouter)
132
193
  - **Strengths:** reachable via the OpenRouter key; resilient; very thorough structured critique
133
194
  (25 findings on a 1-page framework). Cleanly separates the review criteria.
134
195
  - **Quirks:** verbose — peers dinged it for volume-over-judgment (good coverage, lower
135
196
  discrimination); **self-ranked its own review #1** in cross-review → discount self-votes.
136
- - Ranked genuine #1 by all judges (incl. non-self) in one run — thoroughness is real, not a self-vote artifact. Handled an 82k-word agentic read cleanly.
197
+ - Ranked genuine #1 by all judges (incl. non-self) in one run — thoroughness is real, not a self-vote artifact. Handled an 82k-word agentic read cleanly — but a later similar-size read delivered only narration until retried with the anti-narration preamble; apply the preamble to gpt by default, not just to weaker models.
137
198
  - **Asserts context-dependent facts (dates, "is this future?") without verifying** — and self-confirms them in adjudication. Cross-check any time-dependent claim it raises.
138
- - A good calibration anchor in cross-review: confirms observational findings, disputes interpretive overreach.
199
+ - A good calibration anchor in cross-review: confirms observational findings, disputes interpretive overreach. A repeatable role: the seat most likely to catch **confidence inflation** in an otherwise-correct argument (theoretical risk asserted as demonstrated mechanism, proxies used as pseudo-diagnostics).
200
+ - **When gpt disputes a specific numeric claim, weight that dispute heavily before a debate round erodes it** — it has been right and then talked out of the objection by a persuasive rebuttal (see the peer-consensus rule).
139
201
 
140
202
  ### Grok (`--model grok` → via OpenRouter)
141
203
  - Very fast legs; credible judge and chair (rejected its own weak findings as chair; honest blind self-rank).
142
204
  - Strong red-team fit; handled an 82k-word agentic read. Weight its **observational** catches heavily and its **interpretive** verdicts cautiously (bench pattern: the former confirmed, the latter disputed).
143
205
  - Stage-1 non-red-team reviews skew to scope-inflated "missing content" majors.
206
+ - Has **hard-refused under `--agent Plan`** ("Plan mode active… cannot execute"; near-zero tokens)
207
+ where `--agent Build` worked immediately — prefer Build for grok legs (see the global Plan-refusal rule).
208
+ - **Fabrication risk on long-document tasks:** one fast, suspiciously low-fresh-token response
209
+ contained an invented "major"-severity finding plus unsupported flavor details, verified false
210
+ against the source. A later run under Build (genuine read, no fabrications on spot-check)
211
+ partially rebuilt confidence — usable for red-team slots under Build **with the spot-check
212
+ discipline**: verify its most specific/surprising claims against the source before letting them
213
+ into a council.
144
214
 
145
215
  ### Kimi (`--model kimi` → via OpenRouter)
146
216
  - The bench's sharpest adjudicator (caught strawmen and misreads other judges waved through).
147
- - **Very slow legs (5-7 min)** it gates wave wall-clock; budget timeouts around it.
148
- - Stalls on long agentic reads (poller "Incomplete" with only a preamble). Reserve for short-artifact adjudication.
217
+ - Repeatedly the **top-ranked reviewer on consumer-practical, safety-heavy artifacts** (food
218
+ safety, mechanical advice) top-ranked in two such runs (unanimous in one), with the most
219
+ granular findings on the bench and genuine structural catches.
220
+ - Its specificity is also its risk: it reaches for **exhaustive quantitative claims ("every",
221
+ "all", "none") that are directionally right and literally wrong** — and it has successfully
222
+ DEFENDED one in a debate round against a correct dispute. Verify its universal quantifiers
223
+ against a primary source (see the peer-consensus rule).
224
+ - **Very slow legs (4-8× its peers)** — it gates wave wall-clock; budget timeouts around it.
225
+ - Stalls on long agentic reads (poller "Incomplete" with only a preamble). Reserve for short-artifact work.
149
226
 
150
227
  ### Mistral (`--model mistral` → via OpenRouter)
151
228
  - Fast, broad coverage, catches real issues.
152
229
  - **Hallucination risk is real:** has invented non-existent product models/specs, disputed independently by two judges. Cross-check every specific model number or product claim it introduces.
153
230
 
154
231
  ### Claude (in-council, when toggle on)
155
- - Consistently the most *calibrated* reviewer (no severity inflation; findings overwhelmingly Confirmed; bench-best street-cred in recent runs) but sometimes the least *original* — it can miss the boldest single catch. Treat as a reliability floor, not a discovery engine.
156
-
157
- ### haiku (`--model haiku`) — **verify before using; it has been hard-404ing**
158
- - The direct-Anthropic route (`anthropic/claude-haiku-4-5-20251001`) returned a hard `Not Found`
159
- on **every** invocation of a recent paid corpus 3 of 3 legs across two separate runs, both as
160
- **chair** (twice, incl. the fallback retry) and as a **bench seat**in ~2 s with zero tokens.
161
- - Both runs degraded *around* it silently rather than failing: one fell back to another chair, the
162
- other collapsed its bench from 3 seats to 2 and exited 2. **A dead alias does not stop a council;
163
- it shrinks it.** In the bench-seat case every finding came out `confidence: "thin"` with a single
164
- peer corroborator, purely because the bench had halved and nothing in `verdict.json` said so.
165
- - Resolve the alias against the catalog (`amicus models --check`) before putting it on a paid
166
- bench, and re-check the bench roster in `run.json` against what you asked for afterwards.
167
-
168
- ### GLM (`--model glm` z-ai via OpenRouter)
169
- - Cheap and fast, and ranked best-by-peers on a clean debut — but a later run produced **35 KB of
170
- prose with `conformance: unstructured` and 0 parsed findings**, twice. Its structured-output
171
- reliability is **not** established; treat the debut as low-N.
172
- - Useful behavioural note: when its structured output failed it **refused to fabricate** on the
173
- repair attempts. Honest but an honest refusal still costs you the seat, so a bench that leans
174
- on `glm` for quorum can silently adjudicate a seat short while still paying for its tokens.
175
- Watch `conformance` per seat, not just the finding count.
232
+ - Consistently the most *calibrated* reviewer (no severity inflation; findings overwhelmingly Confirmed; bench-best street-cred in recent runs) but sometimes the least *original* — it can miss the boldest single catch. Treat as a reliability floor, not a discovery engine — though on some benches its findings have anchored the entire Confirmed tier.
233
+ - Its own checkable, arithmetic-style claims deserve the same **mechanical verification** as everyone else's: one calendar-consistency claim in a Claude first-pass review was a genuine reasoning error, caught only by computing the dates programmatically before submission (see General).
234
+
235
+ ### haiku (`--model haiku`) **the "hard-404" was the environment, not the model**
236
+ - **The model was never the problem.** The direct-Anthropic route returned an instant `Not Found`
237
+ (~2 s, zero tokens) on every invocation of a paid corpus3 of 3 legs across two runs, as chair
238
+ and as bench seat and the standing diagnosis was a rotten alias. It is not: the cause is an
239
+ **`ANTHROPIC_BASE_URL` set in host form, without its `/v1` suffix**. Anthropic SDKs (including
240
+ Claude Code itself) treat the var as a HOST and append `/v1` themselves; OpenCode's provider
241
+ layer treats it as the full prefixso a value that is correct for the host app kills every
242
+ direct-Anthropic leg amicus launches. Proven by control pair: an identical `fanout --models
243
+ opus` call fails "Not Found" on the host form and completes with `/v1` appended.
244
+ - **Symptom signature:** instant "Not Found", zero tokens, direct-Anthropic routes only —
245
+ OpenRouter-routed legs in the same run are unaffected. It hits every direct-Anthropic alias
246
+ equally (haiku, opus, sonnet, claude; `fable` is OpenRouter-only and unaffected), so a "dead"
247
+ cheap seat and a "dead" frontier chair with this signature share one cause.
248
+ - **The check:** inspect `ANTHROPIC_BASE_URL` in the environment amicus actually runs in — the var
249
+ can live only in a parent process's env (e.g. the host app), absent from every shell profile and
250
+ settings file on disk. If it lacks `/v1`, that is the kill. A doctor check for this is filed on
251
+ the backlog; until it ships, check by hand before blaming a model or an alias.
252
+ - Seat-loss mechanics for a dead route are the same as any other loss — announced in one voice,
253
+ exit 2 if still dead. A dead Stage-1 seat gets the one retry; a dead CHAIR walks the chair's own
254
+ chain instead — same-chair retry, then promotion of the best non-bench model from the ledger —
255
+ with the actual chair checkpointed into `run.json` (see the seat-loss bullet in Global
256
+ operating rules).
257
+
258
+ ### GLM (`--model glm` → `glm-5.1` via OpenRouter; the recent observations below are from the explicit `openrouter/z-ai/glm-5.2` id)
259
+ - The v4.4.0-era "structured-output reliability not established" warning is **withdrawn as
260
+ wrong-cause**: its `unstructured`-conformance results and repair refusals traced to two
261
+ since-fixed engine defects (the unanchored fence extractor that truncated any JSON quoting a
262
+ code fence, and repair prompts that omitted the artifact under repair — both fixed in v4.4.1),
263
+ not to the model. Conformance has been `clean` in subsequent paid runs.
264
+ - Cheap and fast; **ranked best-by-peers on an ideation-bench debut** — a focused,
265
+ fewest-findings reviewer whose findings land. Promising budget-to-mid bench member.
266
+ - **First recorded confident fact error:** it asserted an engine spec that the primary source (the
267
+ owner's manual) contradicts outright — and the finding reached Confirmed on two peer agreements,
268
+ denied only by a post-council verification pass. Treat glm's confident factual assertions as
269
+ unverified until checked (the peer-consensus rule exists because of findings like this one).
270
+ - Honest under pressure both ways: it refused to fabricate on repair attempts, and it has
271
+ withdrawn a contested finding cleanly in debate. An honest refusal still costs the seat — watch
272
+ `conformance` per seat, not just the finding count.
273
+
274
+ ### Qwen (`--model qwen` → qwen3.7-max via OpenRouter; distinct from `qwen-coder`)
275
+ - Very large context (1M tokens per catalog). As a red-team substitute it has produced a thorough,
276
+ well-organized adversarial review with accurate, specific line citations and genuinely unique
277
+ catches that verified true against the source — weight its specific, cited claims heavily.
278
+ - Same observational-vs-interpretive split as grok: its cited observational findings get
279
+ confirmed; its "blocker"-severity interpretive claims get disputed as genre-normative. Weight
280
+ its severity labels on broad interpretive claims cautiously.
281
+ - **Do not assign qwen Stage-1 multi-file reads.** Its proven mode is single-file reads and
282
+ short-artifact work: a book-length single-file read succeeded on retry with the anti-narration
283
+ preamble, but multi-file packets have produced narration stubs twice in one run — with the
284
+ preamble present, under both Plan and Build. It narrates rather than refuses under Plan.
176
285
 
177
286
  ### minimax (`--model minimax` → via OpenRouter)
178
287
  - Fast (~2 min review legs), cheap, `clean` findings-JSON conformance on debut.
@@ -189,6 +298,21 @@ debate defense/re-vote waves now exercised, haiku and glm notes; see changelog).
189
298
  - Ranked last on its debut bench — misses the offline, interruption-handling, and
190
299
  test-methodology gap classes stronger seats catch. Fine budget-bench filler; do not chair it.
191
300
 
301
+ ### GPT-5.6-Terra (`openrouter/openai/gpt-5.6-terra`)
302
+ - Debut (one paid ideation run): `clean` conformance, thorough — the most findings on its bench.
303
+ Handled an inlined ~13K-token digest cleanly under `--agent Build`.
304
+ - The priciest leg on its bench (~5× the cheap seats) and it self-ranked #1 (discount self-votes,
305
+ as always) while peers ranked it lower — thoroughness did not convert to peer standing. Low-N;
306
+ re-confirm before leaning on it.
307
+
308
+ ### Gemini 3.1 Pro (`openrouter/google/gemini-3.1-pro-preview` — the PRO tier; the bare `gemini` alias resolves to flash)
309
+ - Debut (one paid ideation run): `clean` conformance, mid-bench peer standing.
310
+ No narration or stall — but the material was INLINED, not an agentic read (where gemini-flash
311
+ historically stalls); don't extend the result to agentic reads untested.
312
+ - Use the shipped `gemini-pro` alias when you want a real Pro reviewer — it live-resolves to the
313
+ current Pro tier, falling back to `openrouter/google/gemini-3.1-pro-preview`; the bare `gemini`
314
+ alias gives you flash.
315
+
192
316
  ### (others — add as used)
193
317
  - Opus / o-series etc. are reachable via amicus **if their API keys are configured**. Add notes
194
318
  here the first time each is used.
@@ -209,7 +333,7 @@ This section keeps only per-model **qualitative quirks** and **structural-confor
209
333
 
210
334
  - **deepseek** — strong synthesis, resilient; occasional transient 502 → re-run the leg. Proven chair. Conforms cleanly.
211
335
  - **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.
212
- - **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.
336
+ - **gemini** — fast, very large context; the bare alias resolves to flash tiers (shallow coverage, cheap fact-checks); asserts numeric "corrections" with unearned confidence — cross-check them. Conforms cleanly; watch for preamble narration — instruct it to emit the JSON block verbatim after the prose.
213
337
 
214
338
  ## Free-tier models (OpenRouter `:free`)
215
339
  - Heavily rate-limited (shared daily pool); a 3-leg parallel wave + cross-review can 429 mid-run.
@@ -229,6 +353,12 @@ This section keeps only per-model **qualitative quirks** and **structural-confor
229
353
  ## General
230
354
  - Model citations are usually real but **verify any load-bearing reference before publishing**;
231
355
  watch for loosely-attached attributions (e.g., a real paper cited for the wrong claim).
356
+ - **Checkable, arithmetic-style claims (calendar/date consistency, counts, named specific facts)
357
+ deserve a mechanical verification pass before publishing — including claims from Claude's own
358
+ review, not just other models'.** One calendar-consistency claim in a Claude first-pass review
359
+ was wrong; running the actual dates through a real calendar caught it before it reached the
360
+ council or the author. This class of claim is cheap to verify mechanically and expensive to get
361
+ wrong in a document going to a human — don't re-reason it a second time; compute it.
232
362
  - Prefer models from **different families** for genuinely independent opinions.
233
363
 
234
364
  ## Lessons changelog
@@ -237,7 +367,7 @@ This section keeps only per-model **qualitative quirks** and **structural-confor
237
367
  its occasional transient 502.
238
368
  - **2026-06-03** — v2 council upgrade: added cross-review (Stage-2 anonymized peer ranking +
239
369
  per-finding adjudication) and reviewer-reliability tracking.
240
- - **2026-06-04** — Trusst messaging-framework council (Gemini + GPT + DeepSeek + Claude-in-council;
370
+ - **2026-06-04** — B2B messaging-framework council (Gemini + GPT + DeepSeek + Claude-in-council;
241
371
  DeepSeek chair). First GPT use → per-model note. First scored reviewer-reliability rows
242
372
  (deepseek 2.33/100%, gpt 2.67/92%, gemini 3.67/89%; 1–4 scale).
243
373
  - **2026-06-10** — v3 migration: transport moved to `fanout --json` + `--prompt-file` (F4); pruned
@@ -254,11 +384,28 @@ This section keeps only per-model **qualitative quirks** and **structural-confor
254
384
  a bench seat; `council run` prints nothing until terminal, so pin `--run-id` when backgrounding.
255
385
  Debate's defense/re-vote waves exercised for the first time — they work, and behave as a rescope
256
386
  (amend-heavy) rather than a defense. New per-model notes: **haiku** hard-404ed 3/3 legs across two
257
- runs and both councils silently degraded around it; **glm** returned `unstructured` conformance
387
+ runs and both councils silently degraded around it *(both halves since superseded: the 404 was a
388
+ `/v1`-less `ANTHROPIC_BASE_URL`, not the alias — see the haiku section — and the silent-degrade
389
+ era ended with the v4.5.2→v4.6 announcement contract)*; **glm** returned `unstructured` conformance
258
390
  with 0 findings twice after a clean debut, and honestly refused to fabricate on repair — which
259
- still costs the seat.
391
+ still costs the seat *(since re-diagnosed: both results were v4.4.0 engine defects, fixed in
392
+ v4.4.1 — see the GLM section)*.
260
393
  - **2026-07-14 (v2.2.0)** — Optional council elements shipped and verified on a planted-flaw
261
394
  ground-truth council (critic seat, debate mode nothing-to-debate path, chair verdict scale;
262
395
  expert lenses defined but not yet field-run). New lessons: claim-class dedup glosses
263
396
  rationale-level errors in Stage-2; minimax debut (strong critic seat, CJK-intrusion quirk);
264
397
  qwen-coder debut (fast budget filler). Debate mode's defense/re-vote waves still unexercised.
398
+ - **2026-08-03 (fold-back, both directions — owner's ruling)** — Per-section reconciliation with
399
+ the machine-local field ledger after six release-cycle deferrals. Corrections: the haiku
400
+ "hard-404" re-diagnosed (a `/v1`-less `ANTHROPIC_BASE_URL` in host form — the model was never
401
+ the problem; control pair: identical `fanout --models opus` fails host-form, completes with
402
+ `/v1`); glm's `unstructured` era re-attributed to the since-fixed v4.4.1 fence-extractor and
403
+ repair-prompt defects; every pre-degrade-era "silent shrink" claim re-grounded in the current
404
+ contract (one-voice announcement on stderr + `degrades[]` + `seatLoss`, exit 2, one Stage-1
405
+ retry with `Recovered:` heals). Upstreamed from the field: the peer-consensus≠evidence rule
406
+ (2 of 23 findings factually wrong inside high-trust tiers, caught only by primary-source
407
+ verification), the anti-narration-preamble and multi-file-stub rules, the Plan-hard-refusal
408
+ fallback, the stale-MCP-server signature, three model sections (qwen, gpt-5.6-terra,
409
+ gemini-3.1-pro), and gemini/deepseek/gpt/grok/kimi/Claude enrichments. Standing practice: each
410
+ release cherry-picks generalizable lessons per-section (docs/publishing.md release checklist),
411
+ never a bulk copy.
@@ -0,0 +1,51 @@
1
+ // src/cli-council-run-render.js
2
+ 'use strict';
3
+
4
+ /**
5
+ * @module cli-council-run-render
6
+ * Human-readable render of a finished `amicus council run` (the non-`--json`
7
+ * output path, v4.0 spec §4). Split out of cli-handlers-council-run.js (v4.6
8
+ * Plan 4 Task 2): that file sat at 298/300 lines and Plan 4 Task 3 adds the
9
+ * #81 Workspace notice, which needed the room. `renderRunHuman` is
10
+ * self-contained — no dependency on the handler's flag-parsing or engine
11
+ * dispatch — so it moves verbatim to its own leaf; the old home
12
+ * (cli-handlers-council-run.js) requires it back and re-exports it, so every
13
+ * existing import path keeps working unchanged.
14
+ */
15
+
16
+ function renderRunHuman(run) {
17
+ const lines = [
18
+ `Council run ${run.runId}: ${run.status} (exit ${run.exitCode})`,
19
+ ` bench: ${(run.bench || []).join(', ')} chair: ${run.chair}`,
20
+ ` dir: ${run.options && run.options.outDir}`,
21
+ ];
22
+ // v4.4: a cost line that omits unpriced legs reads as the whole bill. The
23
+ // diagnosis measured council-wsgate02 printing $0.3720 for a run that really
24
+ // spent $0.9859. Say what we know, then say what we cannot know — and print
25
+ // the line even when NOTHING resolved (the old `typeof amount === 'number'`
26
+ // guard silently dropped it, so a fully unpriced run looked free).
27
+ const u = run.usage || null;
28
+ const unknownLegs = u && typeof u.unknownLegs === 'number'
29
+ ? u.unknownLegs
30
+ : (u && u.cost && u.cost.unpricedLegs) || 0;
31
+ // v4.4 Task 2: a fully-priced run can still be short. `council-wsgate01`
32
+ // printed an unqualified $0.2821 for a run that really spent $0.3036 — every
33
+ // leg `reported`, and 100% of the gap one unattributed `explore` child session.
34
+ const subtreeLegs = u && typeof u.subtreeUnknownLegs === 'number'
35
+ ? u.subtreeUnknownLegs
36
+ : (u && u.cost && u.cost.subtreeUnknownLegs) || 0;
37
+ if (u && u.cost && (typeof u.cost.amount === 'number' || unknownLegs > 0 || subtreeLegs > 0)) {
38
+ const known = typeof u.cost.amount === 'number' ? `$${u.cost.amount.toFixed(4)}` : '$0.0000';
39
+ const gaps = [];
40
+ if (unknownLegs > 0) { gaps.push(`${unknownLegs} leg(s) unknown`); }
41
+ if (subtreeLegs > 0) { gaps.push(`${subtreeLegs} leg(s) with unattributed subagent child-session spend`); }
42
+ const tail = gaps.length > 0
43
+ ? ` + ${gaps.join(' + ')} — real spend is at least this much`
44
+ : '';
45
+ lines.push(` cost: ${known} (${u.cost.source})${tail}`);
46
+ }
47
+ if (run.error) { lines.push(` error: ${run.error.code}: ${run.error.message}`); }
48
+ return lines.join('\n') + '\n';
49
+ }
50
+
51
+ module.exports = { renderRunHuman };
@@ -13,6 +13,10 @@ const path = require('path');
13
13
  const { failJson, buildErrorDoc, ERROR_CODES } = require('./utils/error-doc');
14
14
  const { validateTaskId } = require('./utils/validators');
15
15
  const { GATEWAY_MODES } = require('./utils/model-descriptor');
16
+ // v4.6 Plan 4 Task 2: renderRunHuman moved to its own leaf (size gate); this
17
+ // file re-exports it below so every existing require() of this path still
18
+ // resolves it unchanged.
19
+ const { renderRunHuman } = require('./cli-council-run-render');
16
20
 
17
21
  const CHAIR_DEFAULT = 'deepseek';
18
22
 
@@ -40,9 +44,9 @@ function sanitizeCouncilName(name) {
40
44
 
41
45
  /**
42
46
  * Resolve bench models from --models XOR --council (mirrors handleFanout).
43
- * Also returns `presetName` (v4.3 Task 3, spec §7.1): the trimmed --council
44
- * name when that branch was taken, else null threaded into runCouncil's
45
- * `councilName` option so council ledger rows can be attributed to a preset.
47
+ * Also returns `presetName` (v4.3 Task 3, spec §7.1: trimmed --council name,
48
+ * else null) and `droppedMembers`: a preset's own drops, orbare --models
49
+ * the parsed `--dropped-members` MCP→child passthrough (v4.6 Plan 4 Task 4b).
46
50
  */
47
51
  function resolveBench(args, useJson) {
48
52
  const hasModels = typeof args.models === 'string' && args.models.trim();
@@ -68,54 +72,40 @@ function resolveBench(args, useJson) {
68
72
  if (expanded.error) {
69
73
  return { fail: failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: `Error: ${expanded.error}` }) };
70
74
  }
71
- if (expanded.dropped && expanded.dropped.length && !useJson) {
72
- process.stderr.write(`Notice: dropped unavailable council member(s): ${expanded.dropped.join(', ')}\n`);
73
- }
74
- // v4.5 Wave 2: threaded into runCouncil's options — the ONLY prior signal
75
- // was the stderr-only Notice above, which --json mode never even prints.
75
+ // v4.5 Wave 2 Plan 4 Task 4: threaded into runCouncil's options — the
76
+ // sink now announces each dropped member, with reason, on every transport and surface.
76
77
  return { bench: expanded.models, presetName, droppedMembers: expanded.droppedMembers || [] };
77
78
  }
78
- return { bench: parseList(args.models), presetName: null, droppedMembers: [] };
79
+ if (args['dropped-members'] === undefined) {
80
+ return { bench: parseList(args.models), presetName: null, droppedMembers: [] };
81
+ }
82
+ let dm; try { dm = JSON.parse(args['dropped-members']); } catch { dm = null; }
83
+ if (!Array.isArray(dm) || !dm.every(d => d && typeof d.member === 'string' && typeof d.reason === 'string')) {
84
+ return { fail: failJson(useJson, { code: ERROR_CODES.BAD_ARGS,
85
+ message: 'Error: --dropped-members must be a JSON array of {member, reason} entries' }) };
86
+ }
87
+ return { bench: parseList(args.models), presetName: null, droppedMembers: dm };
79
88
  }
80
89
 
81
- function renderRunHuman(run) {
82
- const lines = [
83
- `Council run ${run.runId}: ${run.status} (exit ${run.exitCode})`,
84
- ` bench: ${(run.bench || []).join(', ')} chair: ${run.chair}`,
85
- ` dir: ${run.options && run.options.outDir}`,
86
- ];
87
- // v4.4: a cost line that omits unpriced legs reads as the whole bill. The
88
- // diagnosis measured council-wsgate02 printing $0.3720 for a run that really
89
- // spent $0.9859. Say what we know, then say what we cannot know — and print
90
- // the line even when NOTHING resolved (the old `typeof amount === 'number'`
91
- // guard silently dropped it, so a fully unpriced run looked free).
92
- const u = run.usage || null;
93
- const unknownLegs = u && typeof u.unknownLegs === 'number'
94
- ? u.unknownLegs
95
- : (u && u.cost && u.cost.unpricedLegs) || 0;
96
- // v4.4 Task 2: a fully-priced run can still be short. `council-wsgate01`
97
- // printed an unqualified $0.2821 for a run that really spent $0.3036 — every
98
- // leg `reported`, and 100% of the gap one unattributed `explore` child session.
99
- const subtreeLegs = u && typeof u.subtreeUnknownLegs === 'number'
100
- ? u.subtreeUnknownLegs
101
- : (u && u.cost && u.cost.subtreeUnknownLegs) || 0;
102
- if (u && u.cost && (typeof u.cost.amount === 'number' || unknownLegs > 0 || subtreeLegs > 0)) {
103
- const known = typeof u.cost.amount === 'number' ? `$${u.cost.amount.toFixed(4)}` : '$0.0000';
104
- const gaps = [];
105
- if (unknownLegs > 0) { gaps.push(`${unknownLegs} leg(s) unknown`); }
106
- if (subtreeLegs > 0) { gaps.push(`${subtreeLegs} leg(s) with unattributed subagent child-session spend`); }
107
- const tail = gaps.length > 0
108
- ? ` + ${gaps.join(' + ')} — real spend is at least this much`
109
- : '';
110
- lines.push(` cost: ${known} (${u.cost.source})${tail}`);
111
- }
112
- if (run.error) { lines.push(` error: ${run.error.code}: ${run.error.message}`); }
113
- return lines.join('\n') + '\n';
90
+ /**
91
+ * Default real helpers; tests override via depsOverride (mirrors
92
+ * cli-handlers-spend.js's realDeps()/depsOverride convention).
93
+ */
94
+ function realDeps() {
95
+ return {
96
+ // #81 (spec §2): same pure presence probe doctor's electron checks use (src/cli-handlers-doctor.js).
97
+ getElectronPath: () => require('./sidecar/interactive-process').getElectronPath(),
98
+ };
114
99
  }
115
100
 
116
- /** @param {object} args parsed CLI args @returns {Promise<number>} exit code */
117
- async function handleCouncilRun(args) {
101
+ /**
102
+ * @param {object} args parsed CLI args
103
+ * @param {object} [depsOverride] test seam (getElectronPath)
104
+ * @returns {Promise<number>} exit code
105
+ */
106
+ async function handleCouncilRun(args, depsOverride = {}) {
118
107
  const useJson = !!args.json;
108
+ const deps = { ...realDeps(), ...depsOverride };
119
109
 
120
110
  // v4.5 Task 12 (B7/F5): resolve --pack FIRST, above the Task-5 template
121
111
  // block, so a pack-filled args.template renders through that single
@@ -243,6 +233,17 @@ async function handleCouncilRun(args) {
243
233
  const { readCache } = require('./utils/model-catalog');
244
234
  const { runCouncil } = require('./council/run');
245
235
  const cfg = loadConfig() || {};
236
+
237
+ // #81 (spec §2): the GUI's existence was announced on NO surface from the
238
+ // CLI path — MCP launches auto-open, the CLI stayed silent. Auto-open
239
+ // parity is a product decision (deliberately not taken here); the SILENCE
240
+ // is the spec's to fix. Presence probe only — never launches. Placed here
241
+ // (runId/runDir already resolved, still before the engine await) so the
242
+ // notice is useful WHILE the run is live, not just after it finishes.
243
+ if (!useJson && deps.getElectronPath()) {
244
+ process.stderr.write(`Notice: the Council Workspace can render this run live — open it with: amicus watch ${runId} --ui\n`);
245
+ }
246
+
246
247
  const { exitCode, run } = await runCouncil({
247
248
  briefing: promptRes.prompt, models: bench, chair, critic, lenses,
248
249
  project, runId, runDir,
@@ -8,7 +8,7 @@ const { sumWaveUsage, formatCost } = require('./utils/pricing');
8
8
  const { failJson, ERROR_CODES } = require('./utils/error-doc');
9
9
  const { buildReport } = require('./council/report');
10
10
  const { validateFindings, buildValidateDoc } = require('./council/findings');
11
- const { buildVerdict, readOverallVerdict, writeVerdictAtomic } = require('./council/verdict');
11
+ const { buildVerdict, readOverallVerdict, readPriorVerdictSurfaces, writeVerdictAtomic } = require('./council/verdict');
12
12
  const {
13
13
  runSave: runCouncilSave,
14
14
  runList: runCouncilList,
@@ -169,8 +169,14 @@ function runVerdict(args, useJson) {
169
169
  // one of only two homes of the chair's synthesis (the other is
170
170
  // chair-output.md); tally.json carries no copy. Recover it from the RUN
171
171
  // folder — the tally's own directory, not `-o` — before rebuilding.
172
- const overallVerdict = readOverallVerdict(path.dirname(path.resolve(tallyPath)), record.meta.runId);
173
- verdict = buildVerdict(record, decisions, { overallVerdict });
172
+ const runDir = path.dirname(path.resolve(tallyPath));
173
+ const overallVerdict = readOverallVerdict(runDir, record.meta.runId);
174
+ // #87: tally.json carries neither seatLoss nor degrades — recover both from
175
+ // the run folder's verdict the same way the chair line is recovered.
176
+ const prior = readPriorVerdictSurfaces(runDir, record.meta.runId);
177
+ verdict = buildVerdict(record, decisions, { overallVerdict,
178
+ ...(prior.seatLoss ? { seatLoss: prior.seatLoss } : {}),
179
+ ...(prior.degrades ? { degrades: prior.degrades } : {}) });
174
180
  }
175
181
  catch (e) {
176
182
  return failJson(useJson, { code: ERROR_CODES.BAD_ARGS, message: `cannot build verdict: ${e.message}`,