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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/commands/council.md +1 -1
- package/docs/DISTRIBUTION.md +38 -11
- package/docs/ROADMAP.md +40 -8
- package/docs/publishing.md +1 -1
- package/docs/usage.md +1 -1
- package/package.json +3 -2
- package/schemas/council-run.schema.json +20 -0
- package/schemas/council-verdict.schema.json +20 -0
- package/schemas/doctor.schema.json +23 -1
- package/skills/second-opinion/MODEL-NOTES.md +182 -35
- package/src/cli-council-run-render.js +51 -0
- package/src/cli-handlers-council-run.js +45 -44
- package/src/cli-handlers-council.js +9 -3
- package/src/cli-handlers-doctor.js +16 -37
- package/src/cli-handlers-watch.js +1 -1
- package/src/cli.js +1 -1
- package/src/council/ledger.js +5 -1
- package/src/council/report-html.js +16 -1
- package/src/council/report.js +25 -1
- package/src/council/run-assemble.js +25 -7
- package/src/council/run-budget.js +14 -8
- package/src/council/run-chair.js +21 -4
- package/src/council/run-debate-stage.js +115 -0
- package/src/council/run-degrade.js +44 -0
- package/src/council/run-finalize.js +18 -3
- package/src/council/run-launch.js +4 -0
- package/src/council/run-retry-notes.js +74 -0
- package/src/council/run-retry.js +280 -0
- package/src/council/run-server.js +24 -7
- package/src/council/run-stage2.js +10 -2
- package/src/council/run-stages.js +59 -27
- package/src/council/run.js +39 -67
- package/src/council/verdict.js +81 -8
- package/src/mcp-council-bench.js +45 -0
- package/src/mcp-council-run.js +11 -28
- package/src/mcp-server.js +22 -3
- package/src/mcp-tools.js +13 -1
- package/src/utils/degrade.js +69 -0
- package/src/utils/doctor-degrade.js +51 -0
- package/src/utils/doctor-electron-mcp-check.js +64 -5
- package/src/utils/doctor-engine-check.js +14 -3
- package/src/utils/doctor-mcp-checks.js +10 -3
- package/src/utils/known-flags.js +2 -1
- package/src/utils/remediation-hints.js +20 -14
- package/src/utils/result-schema.js +6 -2
- package/src/utils/session-index-tmp-sweep.js +2 -1
- package/src/utils/update-notice.js +171 -0
- 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-
|
|
8
|
-
|
|
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 (
|
|
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
|
-
-
|
|
148
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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 corpus — 3 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 prefix — so 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;
|
|
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** —
|
|
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
|
|
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
|
|
44
|
-
*
|
|
45
|
-
*
|
|
47
|
+
* Also returns `presetName` (v4.3 Task 3, spec §7.1: trimmed --council name,
|
|
48
|
+
* else null) and `droppedMembers`: a preset's own drops, or — bare --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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
/**
|
|
117
|
-
|
|
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
|
|
173
|
-
|
|
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}`,
|