the-high-council 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.env.example +19 -0
  2. package/CONTRIBUTING.md +41 -0
  3. package/LICENSE +21 -0
  4. package/PERSONAS.md +51 -0
  5. package/README.md +499 -0
  6. package/TROUBLESHOOTING.md +45 -0
  7. package/chains/cheap.json +35 -0
  8. package/chains/gp-judge-v1.json +80 -0
  9. package/chains/idea-open-c2.json +106 -0
  10. package/chains/mock-budget.json +15 -0
  11. package/chains/mock-debate.json +59 -0
  12. package/chains/mock-external.json +59 -0
  13. package/chains/mock-open.json +60 -0
  14. package/chains/mock-proposals.json +57 -0
  15. package/chains/mock-questions-wait.json +63 -0
  16. package/chains/mock-questions.json +63 -0
  17. package/chains/mock-relay.json +42 -0
  18. package/chains/mock-unanimous.json +42 -0
  19. package/chains/mock.json +17 -0
  20. package/chains/plan-auto-mistral.json +101 -0
  21. package/chains/plan-auto.json +131 -0
  22. package/chains/plan-cheap.json +82 -0
  23. package/chains/plan-debate-c2.json +86 -0
  24. package/chains/plan-debate-open-c2.json +98 -0
  25. package/chains/plan-debate-roles-c1.json +149 -0
  26. package/chains/plan-debate.json +116 -0
  27. package/chains/plan-fast.json +66 -0
  28. package/chains/plan-proposals-best-of-5.json +98 -0
  29. package/chains/plan-proposals.json +96 -0
  30. package/chains/plan-relay.json +83 -0
  31. package/chains/plan-thorough.json +82 -0
  32. package/chains/plan-unanimous.json +82 -0
  33. package/chains/seven-cheap.json +67 -0
  34. package/chains/seven.json +60 -0
  35. package/chains/smo-v2-cheap.json +67 -0
  36. package/chains/smo.json +50 -0
  37. package/chains/verify.json +40 -0
  38. package/package.json +55 -0
  39. package/src/board-export.js +77 -0
  40. package/src/budget.js +126 -0
  41. package/src/buys.js +44 -0
  42. package/src/cache-integrity.js +38 -0
  43. package/src/chain-lint.js +107 -0
  44. package/src/chain.js +880 -0
  45. package/src/cli.js +1031 -0
  46. package/src/cost-forecast.js +70 -0
  47. package/src/cost.js +139 -0
  48. package/src/errors.js +82 -0
  49. package/src/http-server.js +349 -0
  50. package/src/integrity.js +48 -0
  51. package/src/key-redaction.js +97 -0
  52. package/src/mcp/server.js +319 -0
  53. package/src/name-lint.js +186 -0
  54. package/src/partial-deliverable.js +38 -0
  55. package/src/peer-claim.js +98 -0
  56. package/src/personas.js +54 -0
  57. package/src/preflight.js +84 -0
  58. package/src/pricing.json +120 -0
  59. package/src/providers.js +268 -0
  60. package/src/replay.js +40 -0
  61. package/src/resume-brief.js +114 -0
  62. package/src/role-diagnostics.js +181 -0
  63. package/src/roles.js +520 -0
  64. package/src/schema-version.js +39 -0
  65. package/src/scope-freeze.js +35 -0
  66. package/src/seat-role.js +96 -0
  67. package/src/shape-rounds.js +58 -0
  68. package/src/spend.js +193 -0
  69. package/src/stage-contract.js +223 -0
  70. package/src/stage-submission.js +53 -0
  71. package/src/tie-break.js +94 -0
  72. package/src/ui/app.js +139 -0
  73. package/src/ui/index.html +43 -0
  74. package/src/ui/parse.js +101 -0
  75. package/src/ui/server.js +117 -0
  76. package/src/ui/style.css +37 -0
  77. package/src/verdict-stats.js +267 -0
  78. package/src/withdrawal-ledger.js +47 -0
package/.env.example ADDED
@@ -0,0 +1,19 @@
1
+ # Copy to .env and fill in only the labs the chosen chain actually uses.
2
+ # The CLI refuses to start if any seat in the chosen chain is missing its key.
3
+ ANTHROPIC_API_KEY=
4
+ OPENAI_API_KEY=
5
+ GOOGLE_API_KEY=
6
+ XAI_API_KEY=
7
+ MISTRAL_API_KEY=
8
+ DEEPSEEK_API_KEY=
9
+ GROQ_API_KEY=
10
+ TOGETHER_API_KEY=
11
+ COHERE_API_KEY=
12
+ OPENROUTER_API_KEY=
13
+ ZAI_API_KEY=
14
+
15
+ # Default per-run spend ceiling in USD. Overridden per run by --max-usd.
16
+ # A run stops cleanly before any stage that could take it past this, and
17
+ # resumes with a higher ceiling. Unset means $5. Use --max-usd none to run
18
+ # without a ceiling.
19
+ MAX_USD_PER_RUN=5
@@ -0,0 +1,41 @@
1
+ # Contributing
2
+
3
+ This project has one maintainer. The bottleneck is reviewing pull requests, not writing code -
4
+ an unscoped PR costs more of that time than it saves. The three shapes below are pre-scoped and
5
+ easy to land; a PR outside them is welcome but will take longer to review, since it needs a scope
6
+ discussion first.
7
+
8
+ ## The three smallest landable contributions
9
+
10
+ 1. **A new provider adapter** (`src/providers.js`). Every provider except `mock`/`external` speaks
11
+ either the Anthropic Messages API or the OpenAI chat-completions shape via `OPENAI_COMPAT`.
12
+ Adding a new OpenAI-compatible provider is usually one entry in `OPENAI_COMPAT` plus a pricing
13
+ row in `src/pricing.json`. Test it the way the existing providers are tested: no network calls
14
+ in `node --test`, real calls are opt-in and cost real money.
15
+
16
+ 2. **A new chain config** (`chains/*.json`). A chain is data, not code - it names which seats run
17
+ which stages. `council doctor` validates any chain you add against every shipped-provider's key
18
+ presence and prices it worst-case with zero network calls; run that against your new file
19
+ before opening a PR. Look at `chains/plan-debate.json` or `chains/verify.json` as a starting
20
+ shape depending on whether you want a debate-and-panel chain or a single-critic chain.
21
+
22
+ 3. **A new mock fixture** (`src/providers.js`'s `callMock`, exercised from `test/*.test.js`). If
23
+ you're adding test coverage for a new code path in the chain itself, the offline `mock`
24
+ provider needs a new scripted branch (keyed on a `mock-<something>` model name) rather than a
25
+ real API call. See the existing branches in `callMock` - `mock-unreadable`,
26
+ `mock-provider-error` - for the shape: a model name that triggers one deterministic scripted
27
+ reply, documented with which real-world failure it stands in for.
28
+
29
+ ## Everything else
30
+
31
+ Bug fixes, docs fixes, and typo fixes are always welcome with no scoping step. Anything larger -
32
+ a new CLI command, a new stage, a change to what a chain's JSON schema can express - should start
33
+ as an issue describing what it's for, before the PR, so review time isn't spent re-deriving scope
34
+ from a diff.
35
+
36
+ ## Running the tests
37
+
38
+ npm test
39
+
40
+ Offline, no API key, no network call, no cost. `node --test` matches the project's own test
41
+ runner - no additional test framework to install.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sower Industries
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PERSONAS.md ADDED
@@ -0,0 +1,51 @@
1
+ # Personas
2
+
3
+ A seat's optional `role.persona` (see `src/seat-role.js`) is a name and voice directive layered
4
+ onto a debate-stage prompt - it changes how a seat *talks*, never what it's graded on (see
5
+ `src/personas.js`, `src/name-lint.js`). The default set, defined in `src/personas.js`:
6
+
7
+ - **Moses**
8
+ - **Noah**
9
+ - **Matthew**
10
+ - **Van Gogh**
11
+ - **Parzival**
12
+
13
+ These are historical and religious figures, chosen deliberately because none of them is anyone's
14
+ trademark. That said, using any recognizable name as a software judge's voice is itself an
15
+ editorial choice: a public audience may read it as reverence, satire, or an implied endorsement by
16
+ association, in either direction. That reading is not intended, but it's real, and this file says
17
+ so plainly rather than leaving it for someone to notice on their own.
18
+
19
+ One flag, recorded once, on a decision that already stands: "Parzival" is the medieval literary
20
+ figure (Wolfram von Eschenbach), unambiguously public domain - the name is not anyone's IP. It is
21
+ also the alias the protagonist chooses in Ernest Cline's *Ready Player One* (novel and Warner Bros.
22
+ film), a work still under active copyright. The character name "Parzival" in that context is drawn
23
+ from the same public-domain legend and is not itself claimed as anyone's trademark, so the exposure
24
+ here is a secondary association in a reader's mind, not a legal one - worth having on the record,
25
+ not worth revisiting a decision already made.
26
+
27
+ **You can replace the whole set.** `loadPersonas()` in `src/personas.js` reads an operator-supplied
28
+ JSON file (path via the `COUNCIL_PERSONAS_FILE` environment variable, or passed directly) and uses
29
+ it in place of the default five entirely - nothing in this repo needs editing to do that. A
30
+ persona file is a flat object keyed by the same short key your chain's `role.persona` field
31
+ references:
32
+
33
+ ```json
34
+ {
35
+ "your-key": { "name": "Public-facing name", "voice": "One or two sentences of voice directive." }
36
+ }
37
+ ```
38
+
39
+ ## The naming lint
40
+
41
+ `src/name-lint.js` scans every text file in this repository for name patterns matched against a
42
+ forbidden-name hash list, and fails loudly (not a warning) if any shipped file, doc, chain config,
43
+ task fixture, or prompt template contains one. This repository ships **no** forbidden names of its
44
+ own - the default list is empty. An operator (this author included) can add their own forbidden
45
+ names - their own trademark risks to guard against, not only this project's - via a local file
46
+ (`COUNCIL_FORBIDDEN_NAMES_FILE`) that is hashed at load time and never committed; see
47
+ `src/name-lint.js`'s own comments for why a hash, not a plain list, is what a public repo can
48
+ safely carry. `.gitignore` covers a `.council/` directory as the suggested place to put this file
49
+ and your own `COUNCIL_PERSONAS_FILE` - that's a convention, not an enforced one: the environment
50
+ variable accepts any path, and only the `.council/` directory specifically is protected from an
51
+ accidental `git add`.
package/README.md ADDED
@@ -0,0 +1,499 @@
1
+ # The High Council
2
+
3
+ [![Test](https://github.com/muad-yasin/the-high-council-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/muad-yasin/the-high-council-mcp/actions/workflows/test.yml)
4
+
5
+ See it work right now - no keys, no setup, no cost:
6
+
7
+ npx github:muad-yasin/the-high-council-mcp council demo
8
+
9
+ Add it to Claude Code as an MCP server the same way:
10
+
11
+ claude mcp add council -- npx -y github:muad-yasin/the-high-council-mcp council --mcp
12
+
13
+ *(An npm package is coming - not yet published. Both lines above work today, straight from this
14
+ GitHub repo, no npm account needed.)*
15
+
16
+ A planning harness that runs one idea past several AI models from different labs, makes them
17
+ argue about it on the record, and stops at a checkable result.
18
+
19
+ Runs as an **MCP server** (so an agent like Claude Code can drive it) or as a **CLI**. Bring your
20
+ own API keys. Nothing is resold, nothing is hosted for you, and no key ever leaves your machine.
21
+ Run `npx github:muad-yasin/the-high-council-mcp council doctor` to see which of your own keys are
22
+ set, which shipped chains you can already run with them, and what each would cost - before
23
+ spending anything.
24
+
25
+ > **On what this does and doesn't claim.** This repo publishes the mechanism: the chains, the seat
26
+ > rosters, the stage order. It does not claim to produce better plans than a single good model
27
+ > would. That is an open question and we have not measured it. What it does, concretely, is make
28
+ > disagreement between models *visible and recorded* instead of averaged away - you can read who
29
+ > objected to what, who withdrew a proposal under argument, and who held their position.
30
+
31
+ **A visual walkthrough of all of this - the stages, one real run's debate board, and what that run
32
+ cost - is at [muad-yasin.github.io/the-high-council-mcp](https://muad-yasin.github.io/the-high-council-mcp/).**
33
+ A different write-up, built around one run's actual debate board, is at
34
+ [sower-industries.de/en/MCP/](https://sower-industries.de/en/MCP/).
35
+
36
+ ## How it works
37
+
38
+ A run moves through fixed stages. Which stages fire depends on the chain you pick.
39
+
40
+ 1. **Questions** - a seat reads the request and asks only the questions whose answers would change
41
+ the output. You answer them (or take its stated defaults).
42
+ 2. **Criteria** - the request becomes a short list of acceptance criteria, each one a yes/no check
43
+ rather than a matter of taste.
44
+ 3. **Skeleton** - an outline the other models will propose against. Names the parts, decides
45
+ nothing.
46
+ 4. **Proposals** - every lab proposes buildable parts, blind to each other.
47
+ 5. **Debate** - the labs read each other's proposals, anonymised, and post support / object /
48
+ merge. Then each author replies: keep, amend, or withdraw.
49
+ 6. **Build** - one seat integrates the surviving proposals into a single document.
50
+ 7. **Panel review** - every critic independently grades the draft against the acceptance criteria,
51
+ blind. Not unanimous? It revises against the union of every objection and the panel reviews
52
+ again, up to the chain's round cap.
53
+ 8. **Handoff** - a `HANDOFF.md` written for whoever executes the result.
54
+
55
+ Every run writes a folder: the deliverable, `BOARD.md` (the full debate - every post, every
56
+ withdrawal), `HANDOFF.md`, a per-lab scoreboard, and the real token/cost accounting.
57
+
58
+ ### Reading a run with a program
59
+
60
+ `BOARD.md` is for people. **`report.json` is the same board, structured** - if you are building
61
+ anything on top of a run, read that instead. Do not parse the markdown.
62
+
63
+ ```jsonc
64
+ {
65
+ "runId": "...", "chain": "...", "passed": false, "maxUsd": 5,
66
+ "criteria": [ "each acceptance criterion, as written before the debate" ],
67
+ "questions": [ { "question": "...", "why": "...", "default": "..." } ], // null if the chain skipped them
68
+ "proposals": [ { "id": "DEEPSEEK-1", "lab": "deepseek", "model": "...",
69
+ "title": "...", "serves": "...", "what": "...", "why": "...",
70
+ "how": "...", "acceptance_test": "...", "attempt": 1,
71
+ "withdrawn": true, "amended": true, "replaced_by": "GLM-1" } ],
72
+ "dropouts": [ { "lab": "...", "model": "...", "stage": "proposals", "reason": "..." } ],
73
+ "debate": {
74
+ "posts": [ { "by": "deepseek", "on": "QWEN-1", "stance": "object|support|merge",
75
+ "text": "...", "merge_with": "GLM-2" } ],
76
+ "replies": [ { "id": "DEEPSEEK-1", "action": "keep|amend|withdraw",
77
+ "text": "...", "replaced_by": "GLM-1" } ]
78
+ },
79
+ "signoff": [ { "provider": "qwen", "model": "...", "signedOff": false,
80
+ "objections": [ { "criterion": "...", "problem": "...", "fix": "..." } ] } ],
81
+ "lastCritique": { "meets": false, "failures": [ { "criterion": "...", "lab": "qwen" } ] },
82
+ "scoreboard": {
83
+ "rows": [ /* one per proposal, with its outcome */ ],
84
+ "labs": [ { "lab": "deepseek", "model": "...", "proposed": 3, "accepted": 0,
85
+ "cut": 0, "withdrawn": 3, "unaccounted": 0, "built": null } ]
86
+ },
87
+ "totals": { "input": 0, "output": 0, "total": 0, "usd": 0, "unpriced": [] }
88
+ }
89
+ ```
90
+
91
+ `proposals` plus `debate.posts` is a directed graph with verifiable edges: one node per proposal,
92
+ one edge per posture, and `replies[].action` says what each author did with their own proposal
93
+ once they had read the argument against it. That is the whole artifact, already machine-readable.
94
+ `scoreboard.labs` is the per-lab tally already aggregated for you — start there for a scoreboard
95
+ rather than counting `proposals` yourself.
96
+
97
+ **`dropouts` is the field you will regret ignoring.** A lab whose proposals came back unreadable
98
+ after a retry is dropped from the run: it is not in `proposals`, not in the debate, and not in the
99
+ scoreboard. It is only in `dropouts`. A consumer that counts labs from `proposals` and reports
100
+ "four labs debated this" will be wrong — the roster shrank, and the run log says so loudly at the
101
+ time. Report a seated lab that produced nothing as what it was, or you are publishing a panel that
102
+ was smaller than you claim. Treat `null` and `[]` alike here; runs written by older versions have
103
+ no such field at all.
104
+
105
+ **Two of these are not closed schemas.** `debate.replies[]` is built by spreading the model's own
106
+ reply object and normalising `id`, `action` and `replaced_by` over it, so an entry carries at least
107
+ those plus `text` and may carry more (an `amend` often brings a `how`). `replaced_by` is only set
108
+ when the action is `withdraw`. `proposals[]` likewise carries `withdrawn` / `amended` /
109
+ `replaced_by` only when they apply. Read defensively: check for a field, do not assume it.
110
+
111
+ `signoff[].objections` is why a seat declined, on the seat's own record. `signedOff: null` means
112
+ the seat gave no usable reply and abstained - neither a pass nor an objection, and `objections` is
113
+ `null` rather than empty so the two stay apart. A seat that agreed has an empty list.
114
+ The same objections also appear flattened in `lastCritique.failures[]`, tagged with `lab`, because
115
+ the reviser wants the union across the whole panel. Both are built in one pass; they cannot drift.
116
+
117
+ **Before you publish a run,** note that `report.json` records `task` as the path to your task file
118
+ on your own disk. Nothing transmits it anywhere - but if you are rendering a board onto a public
119
+ page, drop that field. The debate itself is the part worth showing.
120
+
121
+ ## Requirements
122
+
123
+ - Node 20+
124
+ - API keys for whichever labs the chain you choose actually uses
125
+
126
+ ## Setup
127
+
128
+ ```bash
129
+ git clone https://github.com/muad-yasin/the-high-council-mcp.git
130
+ cd the-high-council-mcp
131
+ npm install
132
+ cp .env.example .env
133
+ ```
134
+
135
+ Fill in `.env` with only the keys your chosen chain needs. Supported providers:
136
+
137
+ `ANTHROPIC_API_KEY` · `OPENAI_API_KEY` · `GOOGLE_API_KEY` · `MISTRAL_API_KEY` ·
138
+ `DEEPSEEK_API_KEY` · `GROQ_API_KEY` · `TOGETHER_API_KEY` · `COHERE_API_KEY` ·
139
+ `OPENROUTER_API_KEY` · `ZAI_API_KEY` · `XAI_API_KEY`
140
+
141
+ The CLI refuses to start if any seat in the chosen chain is missing its key, rather than failing
142
+ halfway through a paid run.
143
+
144
+ ## Quick start (CLI)
145
+
146
+ A task file is the request the council plans against: plain prose, written by you. There is no
147
+ template and no format - write what you actually want, including the constraints that matter.
148
+
149
+ ```bash
150
+ mkdir -p tasks
151
+ echo "Plan the data model for a bookmarking app. Single user, offline-first." > tasks/your-idea.md
152
+
153
+ # see what's available and what it would cost
154
+ node src/cli.js --help
155
+ npm run dry -- --task tasks/your-idea.md --chain verify
156
+
157
+ # a real run
158
+ node src/cli.js --task tasks/your-idea.md --chain verify
159
+ ```
160
+
161
+ `tasks/` and `runs/` are both gitignored. Your requests and everything the council writes about
162
+ them stay on your machine - if you fork this repo, you will not accidentally publish them.
163
+
164
+ `verify` is the cheap default: two labs, a hard two-round cap. Start there.
165
+
166
+ ### Telling the council what your build session can actually use
167
+
168
+ One optional convention, worth the four lines it costs. If your task file ends with a section
169
+ headed **`## Available tools`**, the handoff seat will name the right one in the acceptance test
170
+ for each item of work, instead of inventing a check from nothing:
171
+
172
+ ```markdown
173
+ ## Available tools
174
+
175
+ - `scope-gate` - decides GO / NEEDS-SPEC / KILL before anything is built
176
+ - `bug-audit` - reads code line by line, reports verified defects, edits nothing
177
+ - `npm test` - the project's own suite
178
+ ```
179
+
180
+ The council is told what exists, not how it is invoked, and is instructed never to name a tool
181
+ that is not on your list. Leave the section out and the handoff assumes nothing beyond your test
182
+ suite - which is what it did before this existed, and what it will keep doing.
183
+
184
+ This costs no tokens you were not already spending: the task file is already in the handoff seat's
185
+ prompt. It is the difference between *"write a test for this"* and *"run `scope-gate` on this
186
+ before building it."*
187
+
188
+ ### The spend cap
189
+
190
+ Every run has a per-run ceiling in USD. It defaults to **$5**, and is checked *before* each paid
191
+ stage - a stage that could take the run past the ceiling is never called, so the cap holds rather
192
+ than reporting the overspend afterwards.
193
+
194
+ ```bash
195
+ node src/cli.js --task tasks/x.md --max-usd 2 # this run stops at $2
196
+ node src/cli.js --task tasks/x.md --max-usd none # no ceiling
197
+ export MAX_USD_PER_RUN=20 # change the default
198
+ ```
199
+
200
+ A run that hits the ceiling stops cleanly and writes `STOPPED-budget.md` saying what it spent, which
201
+ stage it stopped at, and what that stage would have cost. Nothing is half-written: it produces no
202
+ `deliverable.md` and no `report.json`, so a stopped run never reads as a finished one. Continue it
203
+ with a higher ceiling - completed stages replay from disk and cost nothing the second time:
204
+
205
+ ```bash
206
+ node src/cli.js --resume runs/<id> --max-usd 10
207
+ ```
208
+
209
+ The cap governs one run. To see what you have spent across all of them:
210
+
211
+ ```bash
212
+ node src/cli.js --spend # today
213
+ node src/cli.js --spend --days 7 # the last week
214
+ node src/cli.js --cost-today # today by calendar day, with a per-model breakdown
215
+ node src/cli.js --cost-today --date 2026-09-01
216
+ node src/cli.js --stats --days 30 # how the debate mechanism itself is doing
217
+ ```
218
+
219
+ That is read back off the run folders on disk - there is no ledger file, nothing is recorded
220
+ anywhere else, and nothing leaves your machine. Runs still going, or stopped by the cap, are
221
+ counted from the stages they already paid for. The same figures are available to an MCP client as
222
+ `spend_report`.
223
+
224
+ To watch this work without spending anything, the `mock-budget` chain calls no API but carries
225
+ fixture prices:
226
+
227
+ ```bash
228
+ node src/cli.js --task tasks/your-idea.md --chain mock-budget --max-usd 1
229
+ ```
230
+
231
+ If a chain pauses for your input, it writes `NEEDS-<stage>.md` into the run folder and tells you
232
+ how to resume:
233
+
234
+ ```bash
235
+ # write your answer to runs/<id>/answers.md, then:
236
+ node src/cli.js --resume runs/<id>
237
+ ```
238
+
239
+ ## Quick start (MCP)
240
+
241
+ Register the server with your MCP client. No clone needed - straight from GitHub:
242
+
243
+ ```bash
244
+ claude mcp add council -- npx -y github:muad-yasin/the-high-council-mcp council --mcp
245
+ ```
246
+
247
+ Or from a clone of this repo:
248
+
249
+ ```bash
250
+ claude mcp add high-council -- node /absolute/path/to/the-high-council-mcp/src/mcp/server.js
251
+ ```
252
+
253
+ **The path must be absolute.** The client starts the server from its own working directory, not
254
+ from this one, so a relative path resolves somewhere unexpected and the server never starts.
255
+
256
+ An npm package is coming - not yet published, no date. Once it lands:
257
+
258
+ ```bash
259
+ claude mcp add high-council -- npx -y the-high-council --mcp
260
+ ```
261
+
262
+ Either way the server reads and writes in **your** working directory, not inside the package:
263
+ `.env` for keys, `tasks/` for requests, `runs/` for output, and a `chains/` of your own takes
264
+ precedence over the bundled ones.
265
+
266
+ To run it by hand, from a clone:
267
+
268
+ ```bash
269
+ npm run mcp # or: node src/cli.js --mcp
270
+ ```
271
+
272
+ Then drive it with these tools:
273
+
274
+ | Tool | What it does |
275
+ |---|---|
276
+ | `list_chains` | Available chains with descriptions and worst-case price |
277
+ | `write_task` | Write the request the harness plans against |
278
+ | `dry_run` | Price a run before spending anything |
279
+ | `start_run` | Start a run in the background (`max_usd` sets its ceiling) |
280
+ | `run_status` | Stage reached, panel verdicts, scoreboard, cost, budget remaining |
281
+ | `spend_report` | What every run has cost across a window of days, plus `session_cost_today` (calendar-day, per-model breakdown), derived from disk |
282
+ | `verdict_stats` | How the debate mechanism itself is doing, per chain and per lab: sign-off rate, rounds, objections, dropouts, cost/wall time, largest prompt files - derived from disk |
283
+ | `external_prompt` | The prompt a paused external stage is waiting on - **read it with this, not by opening the file**; the prompts are routinely tens of thousands of tokens and most clients silently truncate a file read |
284
+ | `prepare_stage_prompt` | Write a self-contained bundle for a paused stage, so a driving session doing other work at the same time can dispatch it to a fresh subagent instead of authoring it inline - see [`docs/dispatch-pattern.md`](docs/dispatch-pattern.md) |
285
+ | `submit_stage` | Answer a paused stage and resume. Optional `claimed_by` records who's answering; a second submission for an already-answered stage no longer errors - it's kept as `<stage>.late.md` with a warning instead of being rejected |
286
+ | `resume_run` | Resume a paused run, or raise `max_usd` on one the cap stopped |
287
+ | `read_run_file` | Read any file from a run folder |
288
+ | `list_runs` | Past runs |
289
+ | `plan_outline` | Outline pass |
290
+
291
+ **External vs. API-backed seats.** `{ "provider": "external" }` pauses a run at that seat so a
292
+ Claude Code session on a flat-rate subscription plays it - free at the point of use, since the
293
+ subagent runs on the same subscription. If you don't hold a flat-rate subscription and pay
294
+ metered rates for everything anyway, `{ "provider": "anthropic" }` (or another API provider) may
295
+ be simpler for those seats: no dispatch pattern to learn, at roughly 5x the metered cost of the
296
+ same debate on external, based on one measured comparison, not a guarantee. Not the default
297
+ anywhere in this repo's config, README ordering, or example chains - external stays recommended
298
+ for anyone with a subscription, since the project's cost story depends on it.
299
+
300
+ ## Chains
301
+
302
+ 31 chain configs live in `chains/`. Each one is plain JSON - the seat roster, which models fill
303
+ which seat, the round cap, and whether proposals/debate/handoff stages run. They are meant to be
304
+ copied and edited.
305
+
306
+ A few worth knowing:
307
+
308
+ - **`verify`** - two labs, two rounds. The recommended starting point.
309
+ - **`cheap`** - small models throughout. For testing the harness itself, not for real work.
310
+ - **`plan-debate`** - five labs propose blind, debate each other's proposals anonymised, then a
311
+ blind panel grades the integrated draft.
312
+ - **`plan-auto`** - the full open-scope chain, every seat on a real API, runs unattended end to end.
313
+ - **`plan-unanimous`** - every critic must independently sign off on the *same* draft. Three rounds
314
+ here means up to fifteen critic calls, not five.
315
+
316
+ Read a chain's `description` field before running it; they say what they cost you in calls.
317
+
318
+ ### Optional chain-config fields
319
+
320
+ - **`schemaVersion`** - an integer, optional. `council doctor` warns (never fails) when a chain
321
+ omits it or names an older/newer one than this build knows about. A chain file without it keeps
322
+ working exactly as it always did - this is a warning, not a requirement.
323
+ - **`max_proposals_per_seat`** - an integer, **opt-in, no default**. Debate size stays unlimited
324
+ unless a chain file explicitly sets this. When it is set and one seat's own proposals still
325
+ exceed it, that seat gets one merge prompt to fold its own list down before the debate board
326
+ ever sees the extras.
327
+ - **`role`** - optional, on any `seats.proposers` entry only (set anywhere else and `council
328
+ doctor` rejects it fail-loud, since it would silently do nothing there): `{ lens?, persona? }`.
329
+ `lens` is one of `adversary | integrator | long-horizon | user-advocate | security-and-legal` -
330
+ a critique function. `persona` is a name - a voice, resolved against the default set in
331
+ `PERSONAS.md` when it matches, otherwise used as free text. Applies only to that seat's
332
+ debate-stage prompt; the panel/critique stage that sets `passed` never sees it, enforced by a
333
+ source-level guard, not just an intention. A chain with no `role` set is byte-identical to
334
+ before. **No efficacy claim**: this ships the mechanism, not evidence that it helps - see
335
+ CHANGELOG.md's 0.6.0 entry.
336
+
337
+ ## New in 0.6.0
338
+
339
+ - **Role-assigned debate seats** - see the `role` field above. Ships the mechanism only; whether
340
+ it changes anything about a real debate is untested by this release.
341
+ - **`report.json`'s `debate.diagnostics`** - four failure-mode signals computed from a run's own
342
+ real debate output (never from an offline probe): a seat quoting no evidence despite objecting
343
+ or merging, textual overlap between differently-lensed seats' posts on the same proposal, a
344
+ same-run gap between role-bearing and role-less seats' evidence quoting, and the share of a
345
+ seat's own words that fall inside quoted evidence versus voice. Present (with nulls/empty
346
+ flags where there's nothing to compute) on any run that had a debate stage; absent otherwise.
347
+ - **`debate.tie_break`** - a `report.json` field recording a weighted-tiebreak event, when one
348
+ fires. Not yet wired into a live vote-counting decision in this release.
349
+ - **`PERSONAS.md`** - the five default public persona names and their voice directives, and how
350
+ an operator replaces the whole set with their own via `COUNCIL_PERSONAS_FILE`.
351
+
352
+ ## New in 0.5.0
353
+
354
+ - **`council init [--yes]`** - the first thing to run in a fresh clone with no keys yet. Prints a
355
+ key check, writes a starter chain and task file you own (`chains/my-first-chain.json`,
356
+ `tasks/my-first-task.md` - a rerun never overwrites an edit you made to either), prices the
357
+ starter chain with no network call, then runs a canned, all-mock, $0 task end to end so the
358
+ first thing you inspect is a real run folder, not just terminal output.
359
+ - **`council doctor --chain <file>`** - lints one chain config before you ever run it: a seat
360
+ under a misspelled key (never wired to any stage), a missing or empty `seats.critics` (the
361
+ review round can't run), or a seat naming a provider this codebase doesn't know. The same check
362
+ also runs automatically, fail-loud, the moment you try to actually run a broken chain - before
363
+ any paid call.
364
+ - **`council doctor --scan-artifacts`** - scans your `tasks/` and `chains/` for API-key-shaped
365
+ strings before you commit or share them. Reports the file and line, never the matched text.
366
+ - **`council --forecast-cost --chain <name> [--days N]`** - a realistic-case USD range for a
367
+ chain, built from its own historical runs on your machine and repriced at today's rates -
368
+ distinct from `--dry-run`'s worst-case estimate from a chain's declared token assumptions.
369
+ - **`council export-board --run <folder> --out <file>`** - a run's proposals, debate, replies and
370
+ verdict as one self-contained HTML file. No external assets, no server, `<details>` sections.
371
+ - **`council replay --run <folder> [--json]`** - a numbered, step-by-step transcript of a run's
372
+ reasoning in your terminal, with a `--json` mode for scripting.
373
+ - **A structured error catalog** (`COUNCIL-E001`-`COUNCIL-E004`, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md))
374
+ for the hard-fail paths this project actually has - a missing key, an unpriced model, a
375
+ malformed chain file, an unreadable stage reply - each with a plain-words cause, a concrete fix
376
+ naming the real file, and a doc pointer. Degradable conditions exit 5; fatal ones exit 6.
377
+ - **Structured per-stage JSON logs** - every run now also writes `stage-log.jsonl` (one line per
378
+ stage: seat, lab, token counts, cost, timing - no prompt content) alongside the existing
379
+ markdown/`report.json` artifacts.
380
+ - **[CONTRIBUTING.md](CONTRIBUTING.md)** - the three smallest landable contribution shapes, for
381
+ anyone who wants to send a PR rather than only file an issue.
382
+
383
+ None of the above change any existing chain's behaviour. `schemaVersion` and
384
+ `max_proposals_per_seat` are both opt-in; a chain file that predates 0.5.0 runs exactly as it did
385
+ before.
386
+
387
+ ## How this was built
388
+
389
+ Vibecoded, with **Claude Code** doing the writing, by one person at
390
+ [Sower Industries](https://sower-industries.de). Parts of it were planned by the harness itself —
391
+ the build order for its own v2 went through a four-lab council, and the plan came back with one of
392
+ its findings built on evidence that turned out to be wrong, which is recorded rather than quietly
393
+ dropped.
394
+
395
+ The parts worth knowing, because they explain why the code looks the way it does:
396
+
397
+ **Almost every defensive branch here is a headstone.** `parseJson` in `chain.js` repairs two
398
+ specific malformations — an unescaped `"` inside a markdown-quoted span, and raw newlines inside
399
+ string values — because three consecutive real runs died on the first one and silently logged a
400
+ pass that never happened. The comments name the run id and the date. The retry that disables
401
+ thinking exists because an Anthropic seat burned its entire `maxTokens` budget thinking and
402
+ returned nothing. The `ROSTER SHRANK` warning exists because a lab can return nothing and vanish
403
+ from the scoreboard without saying so. None of these were designed in advance; each one is a day
404
+ that went wrong.
405
+
406
+ **The failure modes are testable offline.** The `mock` provider has named models
407
+ (`mock-unreadable`, `mock-provider-error`, `mock-proposer-empty`, `mock-critic-holdout`) that each
408
+ drive one of those branches for $0, so the regression tests run without keys and without spend.
409
+
410
+ **`docs/` ships no JavaScript on purpose.** The landing page arrived from a design tool depending
411
+ on an unlicensed runtime that also fetched React and Babel from a CDN at page load. Shipping it
412
+ would have relicensed someone else's build artifact as MIT and put a third-party request on a page
413
+ shared with a German site. Its loops were pre-rendered to static HTML and the fonts self-hosted
414
+ instead. `test/landing-page.test.js` fails if a script tag or a third-party URL ever comes back —
415
+ and if the page's typed-in numbers stop matching the repo, which has already caught the page
416
+ claiming 29 chains and 12 tools when there were 30 and 11.
417
+
418
+ No claim is made here that any of this produces better plans. It has not been measured.
419
+
420
+ ## Known limits, stated plainly
421
+
422
+ - The spend cap is enforced against a **worst case**, not a prediction: the whole prompt billed as
423
+ input plus the seat's entire `maxTokens` budget billed as output (doubled for Anthropic seats,
424
+ which may retry once). Real stages almost never cost that much, so a run can stop with headroom
425
+ left. That is the intended trade - resume it with a higher ceiling.
426
+ - A seat whose model has no entry in `src/pricing.json` is **unpriced, and therefore uncapped**.
427
+ It contributes $0 to the running total no matter what it really costs. Check `dry_run` output
428
+ for `unpriced` before trusting a ceiling.
429
+ - Prices in `src/pricing.json` are hand-maintained list prices, last verified 2026-09-06. They are
430
+ estimates, not invoices. Your provider's bill is the real number.
431
+ - Chains with many labs and high round caps get expensive quickly. `plan-unanimous` at three rounds
432
+ is fifteen critic calls; `plan-auto` runs several frontier models over multiple rounds. Price
433
+ before you run.
434
+ - A panel that signs off is not a guarantee the output is correct. It means every critic seat
435
+ checked it against the stated acceptance criteria and found nothing. Criteria that are vague
436
+ produce sign-offs that mean nothing - the quality of the run depends heavily on the quality of
437
+ the criteria stage.
438
+ - Some chains pause for human input by design. They are not stuck.
439
+
440
+ ## Privacy
441
+
442
+ This tool and the [demo page](https://muad-yasin.github.io/the-high-council-mcp/demo.html)
443
+ collect nothing. Stated plainly, not as a claim about quality:
444
+
445
+ - No telemetry, no analytics, no tracking of any kind, anywhere in this repo.
446
+ - The only network calls this tool ever makes are to the AI provider APIs you configure with
447
+ your own keys (`ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, and so on) - and only when you start
448
+ a real run with a non-mock, non-external seat. `council doctor` and `council demo` make zero
449
+ network calls; both are implemented to read only local files and environment variable names.
450
+ - The GitHub Pages site (`docs/`) ships no JavaScript and loads no third-party resource - see
451
+ `test/landing-page.test.js`, which fails the build if either ever changes.
452
+ - Everything a run produces - the task, the debate, the deliverable, the cost - is written to a
453
+ folder on your own disk (`runs/<id>/`) and nowhere else. Deleting that folder deletes the
454
+ record. See `src/spend.js`'s own documentation for why spend accounting is derived from those
455
+ folders rather than kept in a separate log.
456
+
457
+ ## Accessibility
458
+
459
+ The GitHub Pages site (`docs/`) uses only native HTML elements for interaction - `<a href>`
460
+ links and `<details>/<summary>` for the expandable demo sections - specifically because those
461
+ are keyboard-operable by the browser itself, with no custom JavaScript to get wrong. Tested by
462
+ hand, 2026-09-13, on the live site:
463
+
464
+ - **`index.html`**: tabbed from the page's first link; focus reached the "Clone the repo",
465
+ "How it works" and "Try it (demo)" buttons in order, each showing a visible focus ring;
466
+ pressing Enter on "Try it (demo)" navigated to `demo.html`, the same as a click.
467
+ - **`demo.html`**: tabbed to the first proposal's `<summary>` ("A-1 Synchronous invalidation...");
468
+ pressing Enter expanded it in place (the ▶ marker flipped to ▼ and the body text appeared),
469
+ identical to clicking it.
470
+ - No custom widget on any page (`index.html`, `board.html`, `demo.html`) uses a `<div>` or
471
+ `<span>` with a click handler in place of a real interactive element - checked directly in the
472
+ page source, not inferred.
473
+ - No image ships anywhere in `docs/` (checked directly - zero `<img>` tags across all three
474
+ pages), so there is no informative image that could be missing alt text.
475
+ - Primary body text (`#8d8a84`/`#b9b5ad`) against the page background (`#0a0a0b`) measures
476
+ roughly 5.7:1 contrast by the WCAG relative-luminance formula, above the 4.5:1 AA threshold
477
+ for normal text.
478
+
479
+ ## Reporting a bug
480
+
481
+ Open an issue: [github.com/muad-yasin/the-high-council-mcp/issues](https://github.com/muad-yasin/the-high-council-mcp/issues).
482
+ Or write to [contact@sower-industries.de](mailto:contact@sower-industries.de).
483
+
484
+ ## If this makes you money
485
+
486
+ The licence is MIT. You owe nothing, and that is the whole licence - nothing below changes it.
487
+
488
+ If The High Council ends up helping you build something that earns, I'd ask for **0.7%** of what it
489
+ earns. Not a fee, not a clause, not a subscription. A request between people who build things.
490
+
491
+ There is no reporting, no audit, no tracking, and nothing checks. **No is a complete answer** - use
492
+ it, fork it, sell what you build with it, and never think about this section again. That is a fine
493
+ outcome and it's why the licence says what it says.
494
+
495
+ If you'd rather say yes: [pay what you think it's worth](https://buy.stripe.com/cNi14neoycSF0sseYrfjG01).
496
+
497
+ ## License
498
+
499
+ MIT. See [LICENSE](LICENSE). The section above is a request, not a term of it.