amicus 4.0.1 → 4.1.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 (39) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +169 -0
  3. package/README.md +4 -4
  4. package/commands/council.md +6 -6
  5. package/package.json +3 -2
  6. package/schemas/council-run.schema.json +15 -1
  7. package/schemas/council-tally.schema.json +10 -1
  8. package/schemas/council-verdict.schema.json +10 -1
  9. package/schemas/error.schema.json +1 -1
  10. package/scripts/postinstall.js +6 -3
  11. package/skills/second-opinion/COUNCIL-DESIGN.md +40 -0
  12. package/skills/second-opinion/MANUAL-ORCHESTRATION.md +266 -0
  13. package/skills/second-opinion/MODEL-NOTES.md +21 -0
  14. package/skills/second-opinion/SEAT-BRIEFS.md +4 -0
  15. package/skills/second-opinion/SKILL.md +319 -333
  16. package/skills/sidecar/SKILL.md +5 -5
  17. package/src/cli-handlers-council-run.js +9 -0
  18. package/src/cli-handlers-council.js +20 -2
  19. package/src/cli.js +8 -0
  20. package/src/council/briefings-debate.js +158 -0
  21. package/src/council/briefings-stage2.js +16 -9
  22. package/src/council/debate.js +98 -0
  23. package/src/council/ledger.js +2 -1
  24. package/src/council/parse-stage2.js +83 -1
  25. package/src/council/report-html.js +28 -1
  26. package/src/council/report.js +64 -4
  27. package/src/council/run-assemble.js +91 -9
  28. package/src/council/run-chair.js +145 -0
  29. package/src/council/run-debate.js +293 -0
  30. package/src/council/run-launch.js +27 -1
  31. package/src/council/run-stages.js +19 -7
  32. package/src/council/run.js +104 -110
  33. package/src/council/verdict.js +43 -2
  34. package/src/mcp-council-run.js +7 -0
  35. package/src/mcp-server.js +28 -3
  36. package/src/mcp-tools.js +24 -4
  37. package/src/utils/curated-models.js +22 -20
  38. package/src/utils/error-doc.js +2 -0
  39. package/src/utils/model-fetcher.js +6 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amicus",
3
- "version": "4.0.1",
3
+ "version": "4.1.1",
4
4
  "description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
5
5
  "author": {
6
6
  "name": "Christian Wagner"
package/CHANGELOG.md CHANGED
@@ -5,6 +5,175 @@ All notable changes to Amicus are documented here. Format follows
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.1.1] - 2026-07-21
9
+
10
+ ### Fixed
11
+
12
+ - **The integration tier is tested again, and now something actually watches it.** All 14
13
+ `tests/**/*.integration.test.js` files were unreachable from every gate — `jest.config.js` excludes
14
+ them from `npm test`, no workflow ran `npm run test:integration`, and the pre-push hook ran only the
15
+ unit suite. Six tests had been failing unnoticed behind a single dead model alias: three E2E suites
16
+ passed `--model gemini-flash`, which is not a live alias (`tryResolveModel('gemini-flash')` returns
17
+ `Unknown model alias`). They now pass `gemini`.
18
+ - **`--claude-review` reports no longer grow a blank `claude` judge column, or a false self-vote `*`.**
19
+ `report.js`'s `toModel()` reused `verdict.council` (the street-cred universe, which legitimately
20
+ includes `claude` on a `--claude-review` run) as the adjudication-matrix judge roster too. Claude is
21
+ judged but never judges, so the matrix grew an extra column that always rendered blank, or a bare
22
+ `*` on a row Claude itself raised — asserting Claude voted for its own finding, the opposite of the
23
+ documented guarantee. `judges` is now filtered out of `council` independently (gated on the
24
+ `claudeInCouncil` flag plus the `claude` name, never on whether a model cast any adjudications, so a
25
+ genuinely dead/unstructured bench judge with zero votes still keeps its blank column).
26
+ - **`debate-revote` now checkpoints `skipped`, not a false `complete`, when the re-vote wave never
27
+ launches** (nothing was defended/amended, or the cost ceiling skipped it). **This is a
28
+ consumer-visible `run.json` value change**: anything parsing `stages[].status` for this stage — the
29
+ Council Review Action's stage-ladder footer, `amicus status`, or a user script — now sees `skipped`
30
+ instead of `complete` on a run that hits this path. Neither the Action's footer nor `amicus
31
+ status`'s human/JSON renderers special-case `complete`, so both already print whatever value is
32
+ there correctly; a script that hard-coded an expectation of `complete` for this stage should treat
33
+ `skipped` as the equivalent no-op, the same way `run-chair.js`'s existing chair-skipped-over-budget
34
+ convention already works.
35
+ - **`amicus council verdict --render` now writes `report.html` at `0o600`**, matching every sibling
36
+ writer (`run-assemble.js`, `mcp-server.js`'s `amicus_verdict` `render:true` path, `run-launch.js`).
37
+ It previously wrote with no explicit mode, which falls back to the process umask (typically `0o644`
38
+ on POSIX — group/world-readable) whenever the target directory had no pre-existing `report.html`,
39
+ i.e. exactly the fresh-`--out-dir` case, for a file that holds model output.
40
+ - **Dropped the false `amicus wait` CLI claim.** `docs/usage.md` and `docs/council.md` both documented
41
+ `amicus status|wait|abort <councilRunId>` as working CLI commands. There is no CLI `wait` (verified
42
+ against `bin/amicus.js`'s command dispatch) — only `status` and `abort` genuinely resolve council
43
+ runs via the sessions-dir pointer file. Both docs now point readers at the MCP `amicus_wait` tool
44
+ instead.
45
+
46
+ ### Added
47
+
48
+ - **Keyless integration job in CI.** `.github/workflows/ci.yml` gained an `integration` job that runs
49
+ `npm run test:integration` with no secrets on every push and PR, so the tier is permanently watched
50
+ for free (~51 assertions, ~10s). `npm run test:integration` now goes through
51
+ `scripts/run-integration-keyless.js`, which strips every provider credential and sandboxes
52
+ `HOME`/`USERPROFILE` before spawning jest — the money-spending suites self-skip and the script cannot
53
+ bill even on a machine with keys on disk. The scrub derives its key names from the engine's own
54
+ `PROVIDER_ENV_MAP`, so it covers providers and paid suites added later without maintenance.
55
+ - **`npm run test:integration:live`** — the paid rail, split out so the CI job cannot silently start
56
+ billing if secrets are ever added to it. Run by the new `.github/workflows/integration-live.yml`
57
+ (`workflow_dispatch` only, carries `secrets.OPENROUTER_API_KEY`) and by the release checklist in
58
+ `docs/publishing.md`.
59
+
60
+ ### Fixed (docs)
61
+
62
+ - `docs/testing.md` and `CLAUDE.md` both claimed a pre-push integration gate that has never existed;
63
+ the pre-push hook runs the unit suite only, deliberately, so a local push never spends money. Both
64
+ now describe the real rails, and `.husky/pre-push`'s stale "until the 'Fix integration tests' task
65
+ lands" comment is replaced with the reason the hook stays unit-only.
66
+ - **`amicus doctor` no longer warns about Amicus's own shipped defaults — on a fresh install, and in
67
+ the keyless `model-drift.yml` CI check.** Both previously reported `⚠ Model aliases: 2 stale: opus,
68
+ haiku` (and the scheduled Model Drift Check ran red) with no user config involved.
69
+ `toDefaultAliases()` built each alias's pinned id by string-stripping the `openrouter/` prefix
70
+ instead of routing through the module's own `directFormFor()`, so it emitted OpenRouter's dot ids
71
+ for Anthropic — `anthropic/claude-opus-4.8`, `anthropic/claude-haiku-4.5` — which the direct API
72
+ rejects, and invented a bare `anthropic/claude-fable-5` for a model OpenRouter serves exclusively.
73
+ Defaults now come from `toGatewayRoutes()`, so an alias resolves to its authored direct form when
74
+ one exists (`anthropic/claude-opus-4-8`, `anthropic/claude-haiku-4-5-20251001`) and to its
75
+ OpenRouter route when none does (`openrouter/anthropic/claude-fable-5`). `fable`'s *recorded* form
76
+ changes, but this is **not a routing change** — OpenRouter was already the only gateway that serves
77
+ it, so a `fable` run resolves and routes identically before and after; only the id `doctor`/`amicus
78
+ models` display and compare against is different. Council artifacts are unaffected either way — they
79
+ record alias strings, not resolved ids.
80
+ - **This fix does not reach everyone who already ran `amicus setup`.** `createDefaultConfig()`
81
+ (`src/sidecar/setup.js`) persists the *entire* default alias map into `config.json` at setup time,
82
+ and `getEffectiveAliases()` lets that persisted user config win over the shipped defaults — so
83
+ anyone who ran `amicus setup` on Amicus ≤4.1.0 has the old, broken ids frozen on disk
84
+ (`source: user-config`), which `findStaleAliases()` never suppresses. Measured: such a user sees
85
+ **4 stale** aliases after upgrading to 4.1.1 — `opus`, `haiku`, `fable` (the pre-fix ids) plus
86
+ `gemini` (this release's own pin move, see Changed below) — not zero, and `amicus doctor` /
87
+ the Model Drift Check will keep warning for them. **There is no code fix for this in 4.1.1**; a
88
+ self-healing config migration is a 4.2 candidate. If `amicus doctor` still warns after upgrading,
89
+ re-run `amicus setup`, or fix individual aliases by hand, e.g.
90
+ `amicus setup --add-alias opus=anthropic/claude-opus-4-8`.
91
+ - The offline Anthropic model floor now also lists `anthropic/claude-haiku-4-5-20251001`, the dated id
92
+ Anthropic's own listing returns. Without it, keyless and OpenRouter-only users saw the shipped
93
+ `haiku` default reported stale against the shipped floor.
94
+
95
+ ### Changed
96
+
97
+ - **Curated `gemini` pin moves from Gemini 3.5 Flash to Gemini 3.6 Flash on both gateways.** This is a
98
+ **silent model change, not merely a drift-notice cleanup**: a user with no `gemini` alias override of
99
+ their own starts talking to a different underlying model the next time they use the `gemini` alias
100
+ after upgrading. It also clears the pinned-fallback drift notice from `amicus models --check`.
101
+
102
+ ## [4.1.0] - 2026-07-21
103
+
104
+ The `second-opinion` skill stops hand-driving councils. Stages 1–3 and the Stage-5 artifacts
105
+ collapse into a single `amicus council run`, leaving Claude only the stages that genuinely need
106
+ judgement — intake, decisions, and lessons. The engine gains an optional rebuttal round so a
107
+ finding's author can answer the reviewers who disputed it, and Claude can now enter its own review
108
+ into the bundle without ever being launched as a model leg.
109
+
110
+ ### Added
111
+
112
+ - **Skill fast path.** `skills/second-opinion/SKILL.md` now delegates Stages 1–3 (and the Stage-5
113
+ artifact materialization) to one `amicus council run` invocation; the human stages (0 intake,
114
+ 4 decisions, 6 lessons) stay Claude-orchestrated. The manual mechanics are preserved verbatim in
115
+ the new `skills/second-opinion/MANUAL-ORCHESTRATION.md` as the documented fallback for when the
116
+ engine is unavailable or a case the fast path cannot express.
117
+ - **Headless debate mode** — `amicus council run --debate` / `amicus_council_run {debate:true}`.
118
+ A Stage-2.5 rebuttal round runs between cross-review and the final tally: findings that came out
119
+ Contested or Disputed go back to the model that raised them, which defends, amends, or withdraws
120
+ each; the judges who disputed them then re-vote; a final tally folds the outcome in. Exactly one
121
+ round, structurally — there is no edge back into a debate stage. `run.json` gains an additive
122
+ `debate` summary, tally/verdict findings gain a `debate` decoration, and both report renderers
123
+ gain a "Debate round" section. The Council Review Action gains a `debate` input (default off),
124
+ and withdrawn findings are excluded from its PR annotations.
125
+ - **`--claude-review <file>` / `claudeReviewFile`.** Enters Claude's own review as judged review
126
+ N+1 from a file. No model leg is ever launched for it and it may never chair — `claude` is a
127
+ reserved seat name that is rejected pre-flight in `--models`, `--chair` and `--critic` on such a
128
+ run.
129
+ - **`--render` on `council verdict`, `render:true` on `amicus_verdict`.** Refreshes `report.html`
130
+ from the decided verdict. The MCP tool also returns the markdown rendering; it writes only when
131
+ an `outDir` inside the project is supplied, and its `readOnlyHint` is now correctly `false`.
132
+ - **`--no-cost-gate` on `council run`.** Disables the per-leg price gate for the whole run —
133
+ repairs, chair chain and debate legs included — in one place instead of per invocation.
134
+
135
+ ### Fixed
136
+
137
+ - **The Council Review Action no longer defaults to a bench that cannot lose a leg.** Its default
138
+ `models` were `deepseek,gemini,glm` with `deepseek` as chair; because the chair is excluded from
139
+ the bench, that left **two** seats against a quorum minimum of two, so one stalled leg failed the
140
+ entire review. The default is now four seats (`glm,qwen,minimax,qwen-coder`), leaving real slack.
141
+ - **`postinstall` now installs `SEAT-BRIEFS.md`.** It shipped in the tarball but was never copied
142
+ into `~/.claude/skills/second-opinion/`, so the seat briefing reference has been missing from
143
+ every installation to date.
144
+ - **`amicus council verdict` no longer discards the chair's verdict.** Writing the decided verdict
145
+ over the engine's one dropped `overallVerdict` to `null`, because the tally record it is built
146
+ from does not carry it. `runVerdict` now recovers it from the run folder — the engine's
147
+ `verdict.json` when present (guarded on `runId`, so a foreign file cannot inject another run's
148
+ verdict), otherwise by re-parsing `chair-output.md` with the engine's own chair parser. A run
149
+ whose chair was skipped still yields `null`; nothing is ever invented. `amicus_verdict` had the
150
+ same loss on the Cowork path and gains an explicit optional `overallVerdict` input, since the MCP
151
+ tool receives a record inline with no run folder to recover from.
152
+
153
+ ### Documentation
154
+
155
+ - **The skill's Stage 4 and Stage 5 now name where finding claims actually live.** Both stages
156
+ instructed the reader to show each finding's claim while pointing only at `tally.json`, whose
157
+ findings carry tiers and adjudications but no `claim`. The claim and location live in
158
+ `tally-input.json`; both stages now state the join (on finding `id`) explicitly.
159
+
160
+ ### Changed
161
+
162
+ - **`claude` can no longer be promoted as fallback chair on any run.** When a configured chair
163
+ dies, the engine promotes another model by reliability; `claude` is now excluded unconditionally.
164
+ This affects runs that never use `--claude-review`, because the reliability ledger has no way to
165
+ distinguish a file-sourced `claude` row from a real leg — and promoting it would select a chair
166
+ the engine cannot launch.
167
+ - **`npm i -g amicus@4.1` rewrites the installed `SKILL.md`** to the fast path (existing
168
+ product-code overwrite policy). `MODEL-NOTES.md` remains machine-local and is never overwritten.
169
+ Rollback is a reinstall of 4.0.x.
170
+
171
+ ### Notes
172
+
173
+ - All changes are additive: the council document family stays `schemaVersion: 2`, the MCP tool
174
+ count stays 15 (new inputs only), and a run using none of the new flags produces byte-identical
175
+ artifacts to 4.0.1. No migration is required.
176
+
8
177
  ## [4.0.1] - 2026-07-20
9
178
 
10
179
  Follow-up fixes to the v4.0.0 council engine: `amicus abort` and `amicus status` now see every
package/README.md CHANGED
@@ -301,7 +301,7 @@ amicus update
301
301
  | `amicus spend` | Cross-run cost rollup from the spend ledger — total + per-model spend, tokens, and source mix, most-expensive first (`--since 7d` windows it; `--json` for a versioned doc; shows remaining OpenRouter credit when a key is configured). |
302
302
  | `amicus key` | Manage API keys non-interactively: `amicus key <provider> <key>` saves after live validation; `--remove`; bare `amicus key` lists providers. |
303
303
  | `amicus council` | Council math: `tally <input.json>` (deterministic tiers + ledger append), `stats` (reviewer reliability), `report <verdict.json> [--md\|--html]`, `validate <file>` (findings-block check, exit 0/2/1), `verdict <tally.json> [--decisions <d.json>] [-o <out.json>]` (build + write verdict.json). Presets: `save <name> --models a,b,c`, `list [--json]`, `show <name> [--json]` — see [The Council](#the-council) for the built-in `free`/`budget`/`frontier` benches. |
304
- | `amicus council run` | The headless council engine (v4.0): Stage-1 reviews → anonymized cross-review → deterministic tally → non-Claude chair verdict, in one command with no Claude runtime. Writes a run directory with `verdict.json` (including `overallVerdict`) and `report.html` — see [docs/council.md](./docs/council.md#amicus-council-run). |
304
+ | `amicus council run` | The headless council engine: Stage-1 reviews → anonymized cross-review → deterministic tally → non-Claude chair verdict, in one command with no Claude runtime. Add `--debate` for a Stage-2.5 rebuttal round (raisers defend/amend/withdraw, disputing judges re-vote) and `--claude-review <file>` to enter Claude's own review as judged review N+1. Writes a run directory with `verdict.json` (including `overallVerdict`) and `report.html` — see [docs/council.md](./docs/council.md#amicus-council-run). |
305
305
  | `amicus abort` | Abort a running session (or `--all`). |
306
306
  | `amicus setup` | Configure default model, API keys, and aliases. |
307
307
  | `amicus update` | Update to the latest version. |
@@ -329,7 +329,7 @@ $ amicus status demo123 --json
329
329
  "taskId": "demo123",
330
330
  "status": "complete",
331
331
  "elapsed": "5m 0s",
332
- "version": "4.0.1",
332
+ "version": "4.1.1",
333
333
  "model": "google/gemini-2.5-flash",
334
334
  "phase": "terminal"
335
335
  }
@@ -352,7 +352,7 @@ amicus models --search gemini # filter by substring
352
352
 
353
353
  ### Routing
354
354
 
355
- - **Bare `provider/model`** (e.g. `openai/gpt-5.5`, `anthropic/claude-opus-4.8`, `google/gemini-3.5-flash`) is the canonical, policy-routed form — Amicus routes it **direct-first**: your direct provider key when one is configured, falling back to OpenRouter automatically when only an OpenRouter key exists.
355
+ - **Bare `provider/model`** (e.g. `openai/gpt-5.5`, `anthropic/claude-opus-4-8`, `google/gemini-3.6-flash`) is the canonical, policy-routed form — Amicus routes it **direct-first**: your direct provider key when one is configured, falling back to OpenRouter automatically when only an OpenRouter key exists.
356
356
  - **`openrouter/provider/model`** is an explicit override that always forces OpenRouter, even when a direct key is present — reach for it deliberately, or for gateway-only vendors with no direct integration (Qwen, Grok, Mistral, GLM, …).
357
357
  - **`--gateway auto|direct|openrouter`** (CLI, also on the MCP tools) overrides routing for one call; `auto` is the direct-first default. `routing.prefer` in `config.json` (`"direct"` by default, or `"openrouter"`) sets the global default.
358
358
  - **One-time migration notice:** if you hold both an OpenRouter key and a direct key for a vendor, the first launch that resolves to that vendor under **`auto`** routing (the default) prints a one-time notice that routing moved to direct API; set `routing.prefer: "openrouter"` to restore the old all-OpenRouter behavior. The notice never fires when you explicitly pass `--gateway direct` — that's your own choice, not a migration.
@@ -380,7 +380,7 @@ The MCP server is auto-registered on install (Claude Code and Claude Desktop / C
380
380
  | `amicus_fanout` | Launch a same-prompt wave; returns `{ waveId, taskIds[] }`. |
381
381
  | `amicus_council_tally` | Aggregate a council wave's reviews into a scored tally. |
382
382
  | `amicus_council_stats` | Reviewer-reliability stats from past council runs. |
383
- | `amicus_verdict` | Build the final council verdict from a tally + decisions. |
383
+ | `amicus_verdict` | Build the final council verdict from a tally + decisions; writes `<outDir>/report.html` when `render:true` and `outDir` are given. |
384
384
  | `amicus_council_run` | Run the full headless council engine (Stage-1 reviews → anonymized cross-review → tally → chair verdict); returns `{ runId, runDir }`, async. |
385
385
 
386
386
  The async pattern is **start → status → read** — `amicus_start`/`amicus_fanout` return immediately, then you poll `amicus_status` and call `amicus_read`; `amicus_wait` collapses that poll loop into one blocking call.
@@ -17,13 +17,13 @@ the **analysis request**, and the **criteria**. If any of the three is missing o
17
17
  ambiguous, ask for it before launching any model (the skill's Stage 0 covers this —
18
18
  don't re-ask for what is already present).
19
19
 
20
- Then follow the second-opinion skill end to end, in pipeline order: Stage 0
20
+ Then follow the second-opinion skill's engine fast path, in pipeline order: Stage 0
21
21
  intake/prep and run-folder setup, then council selection with a cost estimate
22
- and explicit user confirmation; Stage 1 independent reviews, running
23
- `amicus council validate` on each leg's findings block as it lands; Stage 2
24
- cross-review, followed by `amicus council tally` once cross-review settles;
25
- Stage 3 council-chair synthesis; Stage 4 the accept/deny decision pass; and
26
- Stage 5, which runs `amicus council verdict` to write the final `verdict.json`.
22
+ and explicit user confirmation; one `amicus council run` call — the engine runs
23
+ validate, cross-review, tally, and chair internally, covering the Stage-1
24
+ independent reviews through the Stage-3 chair synthesis with no Claude runtime
25
+ in between; Stage 4, the accept/deny decision pass, once the run returns; and
26
+ Stage 5, which runs `amicus council verdict` to write the decided `verdict.json`.
27
27
 
28
28
  The user may also name **optional council elements** in the arguments (critic seat,
29
29
  expert lenses, debate mode, verdict scale, Claude in the council). All elements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amicus",
3
- "version": "4.0.1",
3
+ "version": "4.1.1",
4
4
  "mcpName": "io.github.BourbonDog/amicus",
5
5
  "description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
6
6
  "keywords": [
@@ -49,7 +49,8 @@
49
49
  "scripts": {
50
50
  "start": "node --experimental-top-level-await --experimental-vm-modules bin/amicus.js",
51
51
  "test": "jest",
52
- "test:integration": "jest --testPathIgnorePatterns='worktrees' --testMatch='**/tests/**/*.integration.test.js'",
52
+ "test:integration": "node scripts/run-integration-keyless.js",
53
+ "test:integration:live": "jest --testPathIgnorePatterns='worktrees' --testMatch='**/tests/**/*.integration.test.js'",
53
54
  "test:all": "jest --testPathIgnorePatterns='/node_modules/' --testPathIgnorePatterns='worktrees' && node scripts/mark-test-passed.js",
54
55
  "test:e2e:mcp": "jest tests/mcp-repomix-e2e.integration.test.js --testTimeout=180000 --forceExit",
55
56
  "posttest": "node scripts/mark-test-passed.js",
@@ -33,6 +33,20 @@
33
33
  "labelMap": { "type": ["object", "null"], "additionalProperties": { "type": "string" } },
34
34
  "options": { "type": "object" },
35
35
  "usage": { "type": "object" },
36
- "exitCode": { "type": ["number", "null"] }
36
+ "exitCode": { "type": ["number", "null"] },
37
+ "debate": {
38
+ "type": "object",
39
+ "properties": {
40
+ "enabled": { "type": "boolean" },
41
+ "outcome": { "enum": ["ran", "nothing-to-debate", "skipped-cost-ceiling"] },
42
+ "contested": { "type": "integer" }, "disputed": { "type": "integer" },
43
+ "defended": { "type": "integer" }, "amended": { "type": "integer" },
44
+ "withdrawn": { "type": "integer" }, "noResponse": { "type": "integer" },
45
+ "revoteJudges": { "type": "integer" }, "revoteApplied": { "type": "integer" },
46
+ "verdictChanges": { "type": "integer" }
47
+ },
48
+ "required": ["enabled"],
49
+ "additionalProperties": false
50
+ }
37
51
  }
38
52
  }
@@ -51,7 +51,16 @@
51
51
  },
52
52
  "confidence": { "enum": ["thin", "solid"] },
53
53
  "tierOverride": { "type": ["object", "null"] },
54
- "adjudications": { "type": "array", "items": { "type": "object" } }
54
+ "adjudications": { "type": "array", "items": { "type": "object" } },
55
+ "debate": {
56
+ "type": "object",
57
+ "properties": {
58
+ "action": { "enum": ["defended", "amended", "withdrawn", "no-response"] },
59
+ "previousTier": { "type": ["string", "null"] }
60
+ },
61
+ "required": ["action"],
62
+ "additionalProperties": false
63
+ }
55
64
  }
56
65
  }
57
66
  },
@@ -36,7 +36,16 @@
36
36
  "duplicateOf": { "type": ["string", "null"] },
37
37
  "adjudications": { "type": "array" },
38
38
  "decision": { "type": ["string", "null"] },
39
- "applied": { "type": "boolean" }
39
+ "applied": { "type": "boolean" },
40
+ "debate": {
41
+ "type": "object",
42
+ "properties": {
43
+ "action": { "enum": ["defended", "amended", "withdrawn", "no-response"] },
44
+ "previousTier": { "type": ["string", "null"] }
45
+ },
46
+ "required": ["action"],
47
+ "additionalProperties": false
48
+ }
40
49
  }
41
50
  }
42
51
  },
@@ -13,7 +13,7 @@
13
13
  "type": "object",
14
14
  "required": ["code", "message"],
15
15
  "properties": {
16
- "code": { "enum": ["BAD_ARGS", "MISSING_PROMPT", "BAD_MODEL", "MISSING_KEY", "BAD_SESSION", "BUDGET_EXCEEDED", "INTERNAL"] },
16
+ "code": { "enum": ["BAD_ARGS", "MISSING_PROMPT", "BAD_MODEL", "MISSING_KEY", "BAD_SESSION", "BUDGET_EXCEEDED", "INTERNAL", "COUNCIL_QUORUM", "COST_EXCEEDED", "COUNCIL_CLAUDE_REVIEW_INVALID"] },
17
17
  "message": { "type": "string" },
18
18
  "hint": { "type": ["string", "null"] },
19
19
  "command": { "type": ["string", "null"] }
@@ -25,12 +25,15 @@ const PROVISION_TIMEOUT_MS = 15000;
25
25
  const SKILL_SOURCE = path.join(__dirname, '..', 'skills', 'sidecar', 'SKILL.md');
26
26
  const COUNCIL_SOURCE_DIR = path.join(__dirname, '..', 'skills', 'second-opinion');
27
27
 
28
- /** Council files + per-file install semantics: SKILL/COUNCIL-DESIGN are product code
29
- * (overwrite on update); MODEL-NOTES is user data its reviewer-reliability table evolves
30
- * per-run, so it is seeded once and never clobbered. */
28
+ /** Council files + per-file install semantics: SKILL/COUNCIL-DESIGN/SEAT-BRIEFS are
29
+ * product code (overwrite on update, so upgrades keep them in sync with the package);
30
+ * MODEL-NOTES is user data — its reviewer-reliability table evolves per-run, so it is
31
+ * seeded once and never clobbered. */
31
32
  const COUNCIL_FILES = [
32
33
  { file: 'SKILL.md', mode: 'overwrite' },
33
34
  { file: 'COUNCIL-DESIGN.md', mode: 'overwrite' },
35
+ { file: 'SEAT-BRIEFS.md', mode: 'overwrite' },
36
+ { file: 'MANUAL-ORCHESTRATION.md', mode: 'overwrite' },
34
37
  { file: 'MODEL-NOTES.md', mode: 'if-missing' },
35
38
  ];
36
39
 
@@ -36,6 +36,10 @@ non-Claude chairman + per-model inspectable artifacts.
36
36
  is now engine-native — each review wave is ONE `amicus fanout --json` call returning structured
37
37
  run documents — but scoring, tallying, anonymization, and synthesis remain Claude's manual work.
38
38
  No backend, no parsing code beyond reading JSON fields. Deterministic arithmetic/formatting/schema helpers under `amicus council` (findings validation, tier tally, street-cred, ledger) are sanctioned; judgment, synthesis, anonymization, and de-anonymization remain Claude's inline work.
39
+ _(v4.1 annotation — superseded by the engine in the fast path: `amicus council run` now performs
40
+ the anonymization, scoring, tallying, and chair-dispatch steps above headlessly, with no Claude
41
+ runtime in the loop. This v3 note remains an accurate historical record of the design this repo
42
+ shipped from v3 through v4.0's manual path; see §13.)_
39
43
 
40
44
  ## 3. What changes vs. v1
41
45
 
@@ -156,6 +160,9 @@ The cross-review matrix shows both; the ledger and Stage-0 bench recommendations
156
160
  - Default: Claude **recommends a non-Claude chair** from the council each run (often the
157
161
  strongest reasoner / best reviewer-reliability) and the user confirms at launch.
158
162
  - The chair **may** also be a Stage-1 council member (it sees the anonymized bundle + scores).
163
+ _(v4.1 annotation: the engine fast path's `amicus council run` rejects this — the chair must
164
+ NOT be a bench seat, checked pre-flight, exit 1 before any spend. This flexibility survives only
165
+ in the manual path; see `MANUAL-ORCHESTRATION.md` and §13.)_
159
166
  - **Fallback order if the chair fails:** re-run → promote next-best council model →
160
167
  **Claude chairs only as last resort, with explicit disclosure** that the verdict is no longer
161
168
  fully independent.
@@ -291,3 +298,36 @@ The debate agent's parallel-panels + cross-panel synthesis maps to two full coun
291
298
  different benches or lens-sets plus a super-chair synthesis of where the panels agree,
292
299
  disagree, and what each missed. Deferred: cost doubles and the run-folder conventions need
293
300
  namespacing. Revisit if lens runs prove valuable.
301
+
302
+ ## 13. v4.1 — the engine fast path
303
+
304
+ `amicus council run` (v4.0; extended in v4.1 with `--debate`, `--claude-review`, and `council
305
+ verdict --render`) is the **headless realization of Stages 1–3 plus the Stage-5 deterministic
306
+ artifacts** described above: the Stage-1 review wave, per-leg findings validation and bounded
307
+ repair, anonymization and run-global finding-id rewriting, the identical judge bundle, the
308
+ Stage-2 cross-review wave, the optional Stage-2.5 debate round (§12.3), the tally, the chair
309
+ synthesis, and the deterministic `verdict.json` (undecided) + `report.html` — all as **one
310
+ command with no Claude runtime** in the loop. `SKILL.md`'s engine fast path drives this directly.
311
+ The hand-driven Stage 1 → 2 → 3 waves this document describes above remain available as
312
+ `MANUAL-ORCHESTRATION.md` — the fallback for the engine being unavailable/misbehaving, a fully
313
+ custom per-seat brief beyond `--critic`/`--lenses`, or deliberate mid-stage inspection.
314
+
315
+ **Stage 4 stays human, always.** The engine is report-only: `amicus council run` never
316
+ fabricates an accept/deny decision. Claude still drives Stage 4 (§4) and finalizes the verdict via
317
+ `amicus council verdict --decisions <decisions.json> --render` (§4, Stage 5) — the fast path
318
+ changes the *transport* for Stages 1–3 plus the Stage-5 artifacts, not the decision authority of
319
+ Stage 4.
320
+
321
+ **§12.4 (chair verdict scale) is standard-on in the fast path.** What was an opt-in menu item in
322
+ the manual path is now baked into `amicus council run`'s chair packet unconditionally — every
323
+ headless chair closes with the hard questions and a parseable `VERDICT:` line, surfaced as
324
+ `overallVerdict` in `verdict.json`. There is no toggle for it on the engine path; it is always on.
325
+
326
+ **§12.3 (debate mode) headless realization: `--debate` on `council run`.** The Stage-2.5 rebuttal
327
+ round described in §12.3 is implemented headlessly in `src/council/run-debate.js`
328
+ (orchestration) + `src/council/debate.js` (pure tally-input reassembly) +
329
+ `src/council/briefings-debate.js` (rebuttal/re-vote prompt templates) — same mechanics, same
330
+ exactly-one-round rule, same withdrawn-findings-auto-denied outcome (§12.3), now driven by the
331
+ engine instead of Claude's hand-launched solos/waves. The round's summary lands in `run.json`'s
332
+ `debate` object and each affected finding's `findings[].debate` decoration — see
333
+ [docs/council.md](../../docs/council.md) for the field-by-field reference.