the-high-council 0.7.7 → 0.7.8
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/.env.example +5 -0
- package/CONTRIBUTING.md +3 -3
- package/PERSONAS.md +4 -0
- package/README.md +152 -25
- package/TROUBLESHOOTING.md +22 -2
- package/chains/mock-tiered.json +43 -0
- package/chains/plan-highest-7.json +45 -0
- package/chains/verify.json +1 -1
- package/package.json +4 -3
- package/schemas/report-v1.json +32 -1
- package/src/api.js +2 -2
- package/src/chain-lint.js +167 -6
- package/src/chain-name.js +18 -0
- package/src/chain.js +265 -76
- package/src/cli.js +98 -40
- package/src/context-files.js +47 -0
- package/src/cost.js +124 -8
- package/src/deep-dive.js +143 -0
- package/src/denied-models.js +9 -5
- package/src/fence.js +28 -1
- package/src/mcp/server.js +100 -24
- package/src/outcome.js +2 -1
- package/src/policy.js +6 -1
- package/src/pricing.json +42 -13
- package/src/providers.js +72 -1
- package/src/quote-check.js +27 -4
- package/src/rematch.js +45 -20
- package/src/report-shape.js +29 -2
- package/src/roles.js +126 -6
- package/src/run-state.js +4 -1
- package/src/run-status.js +5 -0
- package/src/stage-contract.js +6 -1
- package/src/terminal-safe.js +14 -0
- package/src/tools.js +8 -0
- package/src/ui/app.js +26 -22
- package/src/ui/server.js +40 -8
- package/src/http-server.js +0 -349
package/.env.example
CHANGED
|
@@ -16,3 +16,8 @@ ZAI_API_KEY=
|
|
|
16
16
|
# resumes with a higher ceiling. Unset means $7. Use --max-usd none to run
|
|
17
17
|
# without a ceiling.
|
|
18
18
|
MAX_USD_PER_RUN=7
|
|
19
|
+
|
|
20
|
+
# Optional settings (README "Setup" lists them all): COUNCIL_WORKDIR and
|
|
21
|
+
# COUNCIL_MAX_USD_LIMIT for the MCP server, COUNCIL_ALLOW_LOOPBACK_KEY_HOST=1
|
|
22
|
+
# for a local proxy in front of a keyed provider, OLLAMA_API_KEY,
|
|
23
|
+
# COUNCIL_PERSONAS_FILE, AUDIT_HMAC_KEY_FILE.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -14,9 +14,9 @@ discussion first.
|
|
|
14
14
|
in `node --test`, real calls are opt-in and cost real money.
|
|
15
15
|
|
|
16
16
|
2. **A new chain config** (`chains/*.json`). A chain is data, not code - it names which seats run
|
|
17
|
-
which stages. `council doctor`
|
|
18
|
-
|
|
19
|
-
before opening a PR. Look at `chains/plan-debate.json` or `chains/verify.json` as a starting
|
|
17
|
+
which stages. `council doctor --chain chains/<yours>.json` lints it (the same rules every run
|
|
18
|
+
applies, including the denied-model and `key-host` rules), and `council doctor` prices it
|
|
19
|
+
worst-case, both with zero network calls; run both against your new file before opening a PR. Look at `chains/plan-debate.json` or `chains/verify.json` as a starting
|
|
20
20
|
shape depending on whether you want a debate-and-panel chain or a single-critic chain.
|
|
21
21
|
|
|
22
22
|
3. **A new mock fixture** (`src/providers.js`'s `callMock`, exercised from `test/*.test.js`). If
|
package/PERSONAS.md
CHANGED
|
@@ -24,6 +24,10 @@ from the same public-domain legend and is not itself claimed as anyone's tradema
|
|
|
24
24
|
here is a secondary association in a reader's mind, not a legal one - worth having on the record,
|
|
25
25
|
not worth revisiting a decision already made.
|
|
26
26
|
|
|
27
|
+
A `role.persona` key that is not in the set in use is sent as a bare name, with no voice
|
|
28
|
+
directive. `plan-premium-7` seats two such keys, `hypatia` and `avicenna`, next to the five
|
|
29
|
+
defaults: without a persona file of your own they reach the debate prompt as those names alone.
|
|
30
|
+
|
|
27
31
|
**You can replace the whole set.** `loadPersonas()` in `src/personas.js` reads an operator-supplied
|
|
28
32
|
JSON file (path via the `COUNCIL_PERSONAS_FILE` environment variable, or passed directly) and uses
|
|
29
33
|
it in place of the default five entirely - nothing in this repo needs editing to do that. A
|
package/README.md
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# The High Council
|
|
2
2
|
|
|
3
|
-
A planning harness that runs one idea past several AI models from different labs
|
|
4
|
-
argue about software engineering, architecture, specific features, roadmaps and
|
|
5
|
-
stops at a checkable result at
|
|
6
|
-
seats and labs (except xAI) and
|
|
3
|
+
A planning harness that runs one idea past several AI models from different labs and makes
|
|
4
|
+
them argue about software engineering, architecture, specific features, roadmaps and
|
|
5
|
+
philosophy. It stops at a checkable result, at a price estimated before you spend anything.
|
|
6
|
+
You choose the number of debate rounds, the seats and labs (any lab except xAI), and the token
|
|
7
|
+
limit for each model's replies.
|
|
8
|
+
|
|
9
|
+
We recommend our own chains: `cheap-7-v2`, `plan-premium-7` and `plan-highest-7` (the top
|
|
10
|
+
models, GPT-6 Astra, Claude Fable 5.1, DeepSeek V4 Pro and GLM-5.3, write the alternative
|
|
11
|
+
architectures and are the only seats that vote; cheaper seats write the proposals and the debate). You can also edit a chain to suit yourself, or run every seat on
|
|
12
|
+
your own machine with `local-ollama`.
|
|
7
13
|
|
|
8
14
|
See it work right now - no keys, no setup, no cost:
|
|
9
15
|
|
|
@@ -15,17 +21,48 @@ amended, a panel that splits in round 1 and signs off in round 2, and the handof
|
|
|
15
21
|
model reply in it is written in advance; what is real is the code that runs the stages around
|
|
16
22
|
them.
|
|
17
23
|
|
|
24
|
+
**What it costs.** You pay the model providers directly, with your own API keys. The demo,
|
|
25
|
+
`doctor`, `init` and the `mock` chains cost $0 and make no network call. `--dry-run` prints a
|
|
26
|
+
run's worst-case price before anything is called, and every run stops before any stage that could
|
|
27
|
+
take it past its spend cap ($7 unless you set `--max-usd`). Most runs cost less than the worst
|
|
28
|
+
case: a panel that signs off early skips the remaining rounds.
|
|
29
|
+
|
|
30
|
+
Three commands from nothing to a priced run of your own idea (no key needed until you drop
|
|
31
|
+
`--dry-run`; [Your first ten minutes](#your-first-ten-minutes) walks through them):
|
|
32
|
+
|
|
33
|
+
npx the-high-council demo # $0: every stage, scripted replies, no network
|
|
34
|
+
npx the-high-council init # writes tasks/my-first-task.md for your idea, plus a $0 mock run
|
|
35
|
+
npx the-high-council --task tasks/my-first-task.md --chain cheap-7-v2 --dry-run
|
|
36
|
+
|
|
37
|
+
**Which chain.** Worst-case prices as `council doctor` prints them, from the shipped price table
|
|
38
|
+
(prices as of 2026-09-06) and the default task-size estimate; a long task costs more.
|
|
39
|
+
|
|
40
|
+
| Chain | Who does what | Needs | Worst case |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `cheap-7-v2` | Claude Sonnet 5 writes the plan; low-cost models from seven other labs review it, and all seven must sign off. Up to 7 rounds. | one OpenRouter key | $5.38 |
|
|
43
|
+
| `plan-premium-7` | A Claude Code session writes the plan; a seven-lab panel of larger models (GPT-6 Astra, Claude Fable 5.1, Gemini 3.8 Flash, Muse Spark 1.2, Qwen 3.8 Max, DeepSeek V4 Pro, GLM-5.3) proposes, debates and reviews. | OpenRouter key + a Claude Code session | $21.59 |
|
|
44
|
+
| `plan-highest-7` | Seven low-cost models propose and debate; four top models write whole architectures and are the only reviewers; one deep-dive seat checks the first draft against the task. **Untested with real models.** | OpenRouter key + a Claude Code session | $14.89 |
|
|
45
|
+
| `local-ollama` | Every seat on your own machine, through Ollama. | Ollama and the models pulled | $0 |
|
|
46
|
+
|
|
47
|
+
`plan-premium-7` and `plan-highest-7` cost more than the default cap, so give them a `--max-usd` at
|
|
48
|
+
or above their dry-run price. Their writer seats are [external](#quick-start-mcp): the run pauses
|
|
49
|
+
and a Claude Code session (or you) writes that stage, at no API cost. Nothing about any chain's
|
|
50
|
+
output quality has been measured.
|
|
51
|
+
|
|
18
52
|
Add it to Claude Code as an MCP server:
|
|
19
53
|
|
|
20
54
|
claude mcp add council -- npx -y the-high-council council --mcp
|
|
21
55
|
|
|
56
|
+
It is also listed in the official MCP Registry as `io.github.muad-yasin/the-high-council`, and
|
|
57
|
+
there is a one-file Claude Desktop bundle ([Quick start (MCP)](#quick-start-mcp)).
|
|
58
|
+
|
|
22
59
|
Runs as an **MCP server** (so an agent like Claude Code can drive it) or as a **CLI**. Bring your
|
|
23
60
|
own API keys. Nothing is resold, nothing is hosted for you, and your keys go only to the
|
|
24
61
|
providers you choose, never to us.
|
|
25
62
|
|
|
26
63
|
Run `npx the-high-council council doctor` to see which of your own keys are
|
|
27
64
|
set, which shipped chains you can already run with them, and what each would cost - before
|
|
28
|
-
spending anything.
|
|
65
|
+
spending anything. Where it falls short: [Known limits](#known-limits-stated-plainly).
|
|
29
66
|
|
|
30
67
|
> **On what this does and doesn't claim.** This repo publishes the mechanism: the chains, the seat
|
|
31
68
|
> rosters, the stage order. It does not claim to produce better plans than a single good model
|
|
@@ -112,6 +149,10 @@ A run moves through fixed stages. Which stages fire depends on the chain you pic
|
|
|
112
149
|
again, up to the chain's round cap.
|
|
113
150
|
8. **Handoff** - a `HANDOFF.md` written for whoever executes the result.
|
|
114
151
|
|
|
152
|
+
Some chains add optional stages to this: whole alternative architectures written blind before
|
|
153
|
+
the skeleton, a deep-dive check of the first draft, a dispute stage that records unresolved
|
|
154
|
+
objections at the top of the plan, and a final security review.
|
|
155
|
+
|
|
115
156
|
Every run writes a folder: the deliverable, `BOARD.md` (the full debate - every post, every
|
|
116
157
|
withdrawal), `HANDOFF.md`, a per-lab scoreboard, and the real token/cost accounting.
|
|
117
158
|
|
|
@@ -124,7 +165,7 @@ which are experimental, and how versions change: [docs/report-format.md](docs/re
|
|
|
124
165
|
|
|
125
166
|
```jsonc
|
|
126
167
|
{
|
|
127
|
-
"schemaVersion": 1, "runId": "...", "chain": "...", "passed": false, "maxUsd":
|
|
168
|
+
"schemaVersion": 1, "runId": "...", "chain": "...", "passed": false, "maxUsd": 7,
|
|
128
169
|
"criteria": [ "each acceptance criterion, as written before the debate" ],
|
|
129
170
|
"questions": [ { "question": "...", "why": "...", "default": "..." } ], // null if the chain skipped them
|
|
130
171
|
"proposals": [ { "id": "DEEPSEEK-1", "lab": "deepseek", "model": "...",
|
|
@@ -136,7 +177,11 @@ which are experimental, and how versions change: [docs/report-format.md](docs/re
|
|
|
136
177
|
"posts": [ { "by": "deepseek", "on": "QWEN-1", "stance": "object|support|merge",
|
|
137
178
|
"text": "...", "merge_with": "GLM-2" } ],
|
|
138
179
|
"replies": [ { "id": "DEEPSEEK-1", "action": "keep|amend|withdraw",
|
|
139
|
-
"text": "...", "replaced_by": "GLM-1" } ]
|
|
180
|
+
"text": "...", "replaced_by": "GLM-1" } ],
|
|
181
|
+
// Since 0.7.8 (experimental): posts and replies the harness rejected, counted per lab and
|
|
182
|
+
// reason (unknown_target, own_proposal, bad_stance, not_own_proposal, not_posted_on,
|
|
183
|
+
// bad_action, unreadable). [] when nothing was dropped.
|
|
184
|
+
"dropped": [ { "stage": "debate|replies", "by": "qwen", "reason": "unknown_target", "count": 1 } ]
|
|
140
185
|
},
|
|
141
186
|
// Only on chains with `alternatives: { enabled: true }`: one whole architecture per lab, written
|
|
142
187
|
// blind before the skeleton, and the same post/reply board the proposals get.
|
|
@@ -189,6 +234,11 @@ those plus `text` and may carry more (an `amend` often brings a `how`). `replace
|
|
|
189
234
|
when the action is `withdraw`. `proposals[]` likewise carries `withdrawn` / `amended` /
|
|
190
235
|
`replaced_by` only when they apply. Read defensively: check for a field, do not assume it.
|
|
191
236
|
|
|
237
|
+
Chains with newer, opt-in stages add their own fields (`deep_dive`, `notQuorate`, `dispute`,
|
|
238
|
+
`security_review`, ...); [docs/report-format.md](docs/report-format.md) lists which are stable and
|
|
239
|
+
which experimental. With the majority guard on, a withdrawal that does not quote the argument it
|
|
240
|
+
concedes to carries `unargued: true` in `debate.replies[]` and its proposal stays for the builder.
|
|
241
|
+
|
|
192
242
|
`signoff[].lab` is the seat's lab; `signoff[].provider` holds the same value under an older,
|
|
193
243
|
misleading name and is deprecated (kept until the report format's version 2).
|
|
194
244
|
`signoff[].objections` is why a seat declined, on the seat's own record. `signedOff: null` means
|
|
@@ -225,13 +275,30 @@ ones. Put only the keys your chosen chain needs in that `.env`, one `NAME=value`
|
|
|
225
275
|
`DEEPSEEK_API_KEY` · `GROQ_API_KEY` · `TOGETHER_API_KEY` · `COHERE_API_KEY` ·
|
|
226
276
|
`OPENROUTER_API_KEY` · `ZAI_API_KEY`
|
|
227
277
|
|
|
228
|
-
xAI/Grok
|
|
229
|
-
|
|
230
|
-
|
|
278
|
+
xAI/Grok is never seated, and neither is a router id (`openrouter/auto`, the Pareto Code router)
|
|
279
|
+
that could route to it: chain-lint refuses such a chain and the run itself refuses such a seat,
|
|
280
|
+
with no override. There is no `xai` provider. Kimi/Moonshot models are not in any chain this
|
|
281
|
+
package ships, but you may seat one in a chain of your own.
|
|
231
282
|
|
|
232
283
|
The CLI refuses to start if any seat in the chosen chain is missing its key, rather than failing
|
|
233
284
|
halfway through a paid run. `ollama` (below) is the one exception - it needs no key at all.
|
|
234
285
|
|
|
286
|
+
A keyed seat sends its key only to its own provider's https API. A chain that points a keyed
|
|
287
|
+
seat's `baseUrl` anywhere else is refused before any call (see
|
|
288
|
+
[Local models](#local-models-ollama-lm-studio-)).
|
|
289
|
+
|
|
290
|
+
Other settings, all optional, read from the environment or `.env`:
|
|
291
|
+
|
|
292
|
+
| Variable | What it does |
|
|
293
|
+
|---|---|
|
|
294
|
+
| `MAX_USD_PER_RUN` | The default per-run spend cap (`7` when unset). `--max-usd` overrides it for one run. |
|
|
295
|
+
| `COUNCIL_WORKDIR` | MCP server only: the folder for `tasks/` and `runs/`, when the client starts the server somewhere else. |
|
|
296
|
+
| `COUNCIL_MAX_USD_LIMIT` | MCP server only: a cap the MCP client cannot raise or remove through `max_usd`. |
|
|
297
|
+
| `COUNCIL_ALLOW_LOOPBACK_KEY_HOST` | `1` lets a keyed seat use a loopback `baseUrl` (a local proxy in front of the provider). Nothing else. |
|
|
298
|
+
| `OLLAMA_API_KEY` | Sent to an `ollama` seat's server only if you set it. |
|
|
299
|
+
| `COUNCIL_PERSONAS_FILE` | Your own persona set for debate seats ([PERSONAS.md](PERSONAS.md)). |
|
|
300
|
+
| `AUDIT_HMAC_KEY` / `AUDIT_HMAC_KEY_FILE` | Signs the opt-in audit log (see [Privacy](#privacy)). |
|
|
301
|
+
|
|
235
302
|
### From source
|
|
236
303
|
|
|
237
304
|
```bash
|
|
@@ -279,6 +346,12 @@ If your local runner listens somewhere else (LM Studio, a different port, a remo
|
|
|
279
346
|
{ "provider": "ollama", "model": "mistral", "baseUrl": "http://localhost:1234/v1" }
|
|
280
347
|
```
|
|
281
348
|
|
|
349
|
+
Put a local or self-hosted server on an `ollama` seat, as above. A seat with a keyed provider
|
|
350
|
+
(`openrouter`, `openai`, ...) sends that provider's key to its `baseUrl`, so chain lint refuses
|
|
351
|
+
one whose `baseUrl` is anything but that provider's own https API address (`key-host`), and the
|
|
352
|
+
CLI lints before every run and every resume. For a local proxy in front of the provider, set
|
|
353
|
+
`COUNCIL_ALLOW_LOOPBACK_KEY_HOST=1` in your environment: it allows a loopback address, nothing else.
|
|
354
|
+
|
|
282
355
|
A local seat prices at $0 in every estimate and run report - `dry_run` shows it as priced, not
|
|
283
356
|
"unpriced," and it can never trip the spend cap. See `chains/local-ollama.json` for a full
|
|
284
357
|
example chain with every seat local. As with every other seat in this project: BYOK is really
|
|
@@ -318,6 +391,16 @@ will not accidentally publish them.
|
|
|
318
391
|
`verify` is the default when you name no chain: a panel of two labs, a hard two-round cap. It
|
|
319
392
|
needs three keys (Anthropic, OpenAI and Google, one account each). With one OpenRouter key, start
|
|
320
393
|
with `cheap-7-v2` instead; `council doctor` lists every chain that runs on the keys you have.
|
|
394
|
+
`--chain` takes a chain's name (`cheap-7-v2`), never a file path: a `chains/<name>.json` of your
|
|
395
|
+
own in the working directory is found by its name.
|
|
396
|
+
|
|
397
|
+
**If your task names files.** A task that names a file (`src/app.js`) without including its
|
|
398
|
+
content is stopped before any call (exit 9, `BLOCKED-ARTIFACTS.md`), because the seats would
|
|
399
|
+
otherwise plan against code they never saw. Put the real content in with
|
|
400
|
+
`council fence --task tasks/x.md --repo <path to your repo> src/app.js` (it appends each file,
|
|
401
|
+
fenced and labelled, and refuses to write if it finds a key), read the task file, then run.
|
|
402
|
+
If a name is only a location, pass `--allow-unfenced` for the whole task or
|
|
403
|
+
`--allow-unfenced app.js` for named files. Everything in the task file goes to every seat.
|
|
321
404
|
|
|
322
405
|
### Exit codes
|
|
323
406
|
|
|
@@ -327,7 +410,7 @@ Each outcome has its own code, so a script or CI job can branch on it:
|
|
|
327
410
|
|---|---|
|
|
328
411
|
| 0 | Finished: a deliverable and `report.json` were written. |
|
|
329
412
|
| 1 | The chain failed lint, or an input is missing (no task file, a resume from the wrong directory). |
|
|
330
|
-
| 2 | Usage error: a flag is missing its value or has an invalid one
|
|
413
|
+
| 2 | Usage error: a flag is missing its value or has an invalid one, `--chain` is not a chain name, a `--context` file is not a regular file or is over 2 MB, or `--rematch`/`--replay` of a run given `--criteria`. |
|
|
331
414
|
| 3 | Paused at an external seat: answer `NEEDS-<stage>.md`, then `--resume`. |
|
|
332
415
|
| 4 | Stopped at the per-run spend cap before the next stage was paid for; resume with a higher `--max-usd`. |
|
|
333
416
|
| 5 | A provider key the chain needs is missing (degradable: set it and run again). |
|
|
@@ -383,8 +466,10 @@ export MAX_USD_PER_RUN=20 # change the default
|
|
|
383
466
|
|
|
384
467
|
A run that hits the ceiling stops cleanly and writes `STOPPED-budget.md` saying what it spent, which
|
|
385
468
|
stage it stopped at, and what that stage would have cost. Nothing is half-written: it produces no
|
|
386
|
-
`deliverable.md` and no `report.json`, so a stopped run never reads as a finished one.
|
|
387
|
-
|
|
469
|
+
`deliverable.md` and no `report.json`, so a stopped run never reads as a finished one. What it did
|
|
470
|
+
get through is in `report-partial.json` and `BOARD-partial.md`
|
|
471
|
+
([docs/report-format.md](docs/report-format.md)). Continue it with a higher ceiling - completed
|
|
472
|
+
stages replay from disk and cost nothing the second time:
|
|
388
473
|
|
|
389
474
|
```bash
|
|
390
475
|
council --resume runs/<id> --max-usd 10
|
|
@@ -463,6 +548,17 @@ up in `claude mcp list` as `plugin:the-high-council:high-council`. Remove it wit
|
|
|
463
548
|
local clone instead (`claude plugin marketplace add /path/to/clone`), the server runs from the
|
|
464
549
|
clone itself, so run `npm install` in it first.
|
|
465
550
|
|
|
551
|
+
**Claude Desktop:** download `the-high-council-<version>.mcpb` from the
|
|
552
|
+
[GitHub Releases](https://github.com/muad-yasin/the-high-council-mcp/releases) page (its SHA-256
|
|
553
|
+
is next to it) and open it. Claude Desktop shows a settings form: a council folder for tasks and
|
|
554
|
+
runs, a per-run spend ceiling that Claude cannot raise or remove from a chat, and one optional
|
|
555
|
+
field per provider key (marked sensitive, so the app keeps them in the OS keychain). The bundle is unsigned. How it is built:
|
|
556
|
+
[mcpb/README.md](mcpb/README.md).
|
|
557
|
+
|
|
558
|
+
**MCP Registry:** the server is listed in the official registry as
|
|
559
|
+
`io.github.muad-yasin/the-high-council` (npm package, stdio). A client that installs from the
|
|
560
|
+
registry runs the same `npx` command as above.
|
|
561
|
+
|
|
466
562
|
Registering with a client other than Claude Code (DeepSeek Harness, OpenHands, Cline, goose,
|
|
467
563
|
Continue): [docs/mcp-clients.md](docs/mcp-clients.md) has the real config for each.
|
|
468
564
|
|
|
@@ -472,6 +568,13 @@ own takes precedence over the bundled ones. In a git project, add `.env` (and `r
|
|
|
472
568
|
ideas are private) to `.gitignore` before an agent commits everything; `council doctor` warns
|
|
473
569
|
when `.env` is not ignored.
|
|
474
570
|
|
|
571
|
+
The server only works inside that directory. `dry_run` and `start_run` take a chain by name.
|
|
572
|
+
Every path `start_run` is given (`task`, `draft`, `context`, `from_run`, and each file inside a
|
|
573
|
+
`context` folder or `from_run` run folder) must resolve inside the working directory, after
|
|
574
|
+
symlinks, and must not be on the secret/credential denylist; outside `tasks/`, `runs/` and
|
|
575
|
+
`context/` a gitignored file is refused too. Set `COUNCIL_MAX_USD_LIMIT` in the server's
|
|
576
|
+
environment to cap what the client's `max_usd` can ask for.
|
|
577
|
+
|
|
475
578
|
To run it by hand:
|
|
476
579
|
|
|
477
580
|
```bash
|
|
@@ -485,18 +588,18 @@ Then drive it with these tools:
|
|
|
485
588
|
| `list_chains` | Available chains with descriptions and worst-case price |
|
|
486
589
|
| `write_task` | Write the request the harness plans against |
|
|
487
590
|
| `dry_run` | Price a run before spending anything |
|
|
488
|
-
| `start_run` | Start a run in the background (`max_usd` sets its ceiling) |
|
|
591
|
+
| `start_run` | Start a run in the background (`max_usd` sets its ceiling). `chain` is a chain name; `task`, `draft`, `context` and `from_run` must be inside the working directory |
|
|
489
592
|
| `run_status` | Stage reached, panel verdicts, scoreboard, cost, budget remaining |
|
|
490
593
|
| `spend_report` | What every run has cost across a window of days, plus `session_cost_today` (calendar-day, per-model breakdown), derived from disk |
|
|
491
594
|
| `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 |
|
|
492
595
|
| `metrics_report` | Descriptive telemetry only (never an evaluation or baseline): amendment rate, withdrawal rate, objection-follow-through rate, tool-call usage, consensus-induced-regression count - derived from disk. See [`docs/paid-comparison-run.md`](docs/paid-comparison-run.md) for what a *real* comparison against a single model would need - not built, not this metric |
|
|
493
|
-
| `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 |
|
|
596
|
+
| `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. `waiting` lists every stage the run waits on (a panel of external critics pauses together); pass `stage` for another one's prompt |
|
|
494
597
|
| `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) |
|
|
495
|
-
| `submit_stage` | Answer a paused stage
|
|
598
|
+
| `submit_stage` | Answer a paused stage; the run resumes once every stage it waits on has an answer. 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 |
|
|
496
599
|
| `resume_run` | Resume a paused run, or raise `max_usd` on one the cap stopped |
|
|
497
|
-
| `read_run_file` | Read any file from a run folder |
|
|
600
|
+
| `read_run_file` | Read any file from a run folder (`report-partial.json` for a run the cap stopped) |
|
|
498
601
|
| `list_runs` | Past runs |
|
|
499
|
-
| `plan_outline` |
|
|
602
|
+
| `plan_outline` | Section tree of a run's deliverable (or a `.md` file in the working directory) with word counts |
|
|
500
603
|
|
|
501
604
|
**External vs. API-backed seats.** `{ "provider": "external" }` pauses a run at that seat so a
|
|
502
605
|
Claude Code session on a flat-rate subscription plays it - free at the point of use, since the
|
|
@@ -509,13 +612,17 @@ for anyone with a subscription, since the project's cost story depends on it.
|
|
|
509
612
|
|
|
510
613
|
## Chains
|
|
511
614
|
|
|
512
|
-
|
|
615
|
+
51 chain configs live in `chains/`. Each one is plain JSON - the seat roster, which models fill
|
|
513
616
|
which seat, the round cap, and whether proposals/debate/handoff stages run. They are meant to be
|
|
514
617
|
copied and edited.
|
|
515
618
|
|
|
516
|
-
|
|
619
|
+
The four recommended ones (`cheap-7-v2`, `plan-premium-7`, `plan-highest-7`, `local-ollama`) are
|
|
620
|
+
in the table at the top. A few more worth knowing:
|
|
517
621
|
|
|
518
|
-
- **`verify`** - two labs, two rounds. The
|
|
622
|
+
- **`verify`** - two labs, two rounds. The default when you name no chain; needs Anthropic, OpenAI
|
|
623
|
+
and Google keys.
|
|
624
|
+
- **`plan-open-7`** - the `cheap-7-v2` panel, with a Claude Code session writing the plan instead
|
|
625
|
+
of a paid seat.
|
|
519
626
|
- **`cheap`** - small models throughout. For testing the harness itself, not for real work.
|
|
520
627
|
- **`plan-debate`** - five labs propose blind, debate each other's proposals anonymised, then a
|
|
521
628
|
blind panel grades the integrated draft.
|
|
@@ -544,6 +651,17 @@ Read a chain's `description` field before running it; they say what they cost yo
|
|
|
544
651
|
source-level guard, not just an intention. A chain with no `role` set is byte-identical to
|
|
545
652
|
before. **No efficacy claim**: this ships the mechanism, not evidence that it helps - see
|
|
546
653
|
CHANGELOG.md's 0.6.0 entry.
|
|
654
|
+
- **`quorum: { "minHeard": N }`** - opt-in, unanimous chains only, set in no shipped chain. A
|
|
655
|
+
round's sign-off counts only when at least N reviewers gave a verdict (signed off or objected);
|
|
656
|
+
a round short of it is recorded as `notQuorate`, the run does not pass, and the loop stops.
|
|
657
|
+
- **Tiered councils (experimental, used by `plan-highest-7` and `mock-tiered`).**
|
|
658
|
+
`seats.alternatives` names who writes the whole alternative architectures, separately from the
|
|
659
|
+
proposers. `deep_dive` + `seats.deep_dive` add one non-voting seat with its own dollar cap
|
|
660
|
+
(`usd`, required) inside the run's cap. `majority_guard.enabled` shows each author every
|
|
661
|
+
argument once, with no lab name or count, and keeps a proposal whose withdrawal does not quote
|
|
662
|
+
the argument it gives in to. chain-lint refuses a tiered chain where a lab or model sits
|
|
663
|
+
both among the proposers and among the reviewers or architecture authors, or where the deep-dive seat shares a lab or model with a reviewer. See
|
|
664
|
+
CHANGELOG.md's 0.7.8 entry.
|
|
547
665
|
|
|
548
666
|
## How this was built
|
|
549
667
|
|
|
@@ -618,8 +736,16 @@ what each rule guards against and why. Copy the folders you want into your proje
|
|
|
618
736
|
such a reply is recorded as `REASONING_EXHAUSTED` and is not retried: the seat abstains, which
|
|
619
737
|
blocks unanimity and never counts as consent. Whether to change a chain's reasoning settings for it
|
|
620
738
|
is a roster decision, not something the harness does for you.
|
|
621
|
-
- Prices in `src/pricing.json` are hand-maintained list prices
|
|
622
|
-
|
|
739
|
+
- Prices in `src/pricing.json` are hand-maintained list prices. Its `asOf` date (2026-09-06: the
|
|
740
|
+
oldest date every entry was last checked) is printed by `council doctor` and `--dry-run`, with a
|
|
741
|
+
warning once it is more than 60 days old. The spend cap and every estimate use these static
|
|
742
|
+
prices. They are estimates, not invoices. Your provider's bill is the real number.
|
|
743
|
+
- `plan-highest-7`, the tiered council, has not been run with real models, and none of its
|
|
744
|
+
mechanisms (tiers, the deep-dive seat, the majority guard) has been measured. Its dry-run price
|
|
745
|
+
is a projection.
|
|
746
|
+
- Seats marked `external` (the writer seats in `plan-premium-7`, `plan-highest-7` and
|
|
747
|
+
`plan-open-7`) pause the run until someone answers them, and are $0 only because a Claude Code
|
|
748
|
+
session on a subscription writes them. Nothing checks who wrote the answer.
|
|
623
749
|
- Chains with many labs and high round caps get expensive quickly. `plan-unanimous` at three rounds
|
|
624
750
|
is fifteen critic calls; `plan-auto` runs several frontier models over multiple rounds. Price
|
|
625
751
|
before you run.
|
|
@@ -669,8 +795,9 @@ hand, 2026-09-13, on the live site:
|
|
|
669
795
|
- No custom widget on any page (`index.html`, `board.html`, `demo.html`) uses a `<div>` or
|
|
670
796
|
`<span>` with a click handler in place of a real interactive element - checked directly in the
|
|
671
797
|
page source, not inferred.
|
|
672
|
-
-
|
|
673
|
-
|
|
798
|
+
- The only image in `docs/` is the local logo (`logo-diamond.png`): the header copy carries alt
|
|
799
|
+
text, the faint background copy on `index.html` has an empty `alt=""` as decoration.
|
|
800
|
+
`test/landing-page.test.js` checks both on every page.
|
|
674
801
|
- Primary body text (`#8d8a84`/`#b9b5ad`) against the page background (`#0a0a0b`) measures
|
|
675
802
|
roughly 5.7:1 contrast by the WCAG relative-luminance formula, above the 4.5:1 AA threshold
|
|
676
803
|
for normal text.
|
package/TROUBLESHOOTING.md
CHANGED
|
@@ -96,8 +96,28 @@ local chains as runnable because they need no key, not because the server is up.
|
|
|
96
96
|
### A run stopped with `STOPPED: per-run spend cap reached` (exit 4)
|
|
97
97
|
|
|
98
98
|
The next stage's worst case would have crossed the cap. Nothing past the cap was spent, and no
|
|
99
|
-
plan was written yet
|
|
100
|
-
|
|
99
|
+
plan was written yet; what the run got through is in `report-partial.json` and `BOARD-partial.md`.
|
|
100
|
+
`council --resume runs/<id> --max-usd <higher>` continues from where it stopped. The run tells you
|
|
101
|
+
at the start when a chain's worst case is above your cap. `plan-premium-7` and `plan-highest-7`
|
|
102
|
+
are above the default $7 cap before they start: price them with `--dry-run` and pass a
|
|
103
|
+
`--max-usd` at or above that.
|
|
104
|
+
|
|
105
|
+
### `BLOCKED: ... file(s) named but never fenced` (exit 9)
|
|
106
|
+
|
|
107
|
+
The task names a file (`src/app.js`, `package.json`) but does not include its content, so the
|
|
108
|
+
seats would plan against code they never saw. Nothing was called. Either append the real files
|
|
109
|
+
with `council fence --task tasks/x.md --repo <path> <file> ...` and read the task file before
|
|
110
|
+
running, or, if the names are only locations, run with `--allow-unfenced` (whole task) or
|
|
111
|
+
`--allow-unfenced a.js,b.ts` (those files). `BLOCKED-ARTIFACTS.md` in the run folder lists the
|
|
112
|
+
names it found.
|
|
113
|
+
|
|
114
|
+
### `--chain` refused, or a chain lint error naming `key-host`
|
|
115
|
+
|
|
116
|
+
`--chain` takes a chain's name (`cheap-7-v2`, or `my-chain` for your own `chains/my-chain.json`),
|
|
117
|
+
never a path; anything else is a usage error (exit 2). `key-host` means a seat with a keyed
|
|
118
|
+
provider has a `baseUrl` that is not that provider's own https API, and its key would go there.
|
|
119
|
+
Move a local or self-hosted server to an `ollama` seat, or, for a local proxy in front of the
|
|
120
|
+
provider, set `COUNCIL_ALLOW_LOOPBACK_KEY_HOST=1` (loopback addresses only).
|
|
101
121
|
|
|
102
122
|
### `.env holds your API keys and git would commit it`
|
|
103
123
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mock-tiered",
|
|
3
|
+
"description": "Offline. A tiered council at $0 (tiered councils, 2026-09-26): mass seats (seats.proposers) propose parts, debate them and post on the architectures; anchor seats (seats.alternatives) write the whole architectures and reply; the anchor panel (seats.critics) alone votes. The majority guard is on: authors see arguments without lab letters or counts, and a withdrawal that quotes no argument (mock-yield-unargued, mock-alt-withdraw) stays for the builder while one that does (mock-yield-argued) is honoured. A deep-dive seat reads the task in 1000-character chunks, once per focus entry, under its own cap and maxCalls, and the reviser answers its findings before the panel's first review.",
|
|
4
|
+
"maxRounds": 2,
|
|
5
|
+
"signoff": "unanimous",
|
|
6
|
+
"dispute": { "enabled": true, "stall_rounds": 2 },
|
|
7
|
+
"proposals": { "parts": 2, "maxTokens": 400 },
|
|
8
|
+
"debate": true,
|
|
9
|
+
"decisions": { "enabled": true },
|
|
10
|
+
"alternatives": { "enabled": true, "debaters": "all" },
|
|
11
|
+
"majority_guard": { "enabled": true },
|
|
12
|
+
"canary": { "enabled": true, "sampleRate": 1 },
|
|
13
|
+
"deep_dive": {
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"job": "sources",
|
|
16
|
+
"focus": ["Every requirement in the excerpt.", "Every number in the excerpt."],
|
|
17
|
+
"usd": 1,
|
|
18
|
+
"maxCalls": 5,
|
|
19
|
+
"chunkChars": 1000
|
|
20
|
+
},
|
|
21
|
+
"handoff": true,
|
|
22
|
+
"estimate": { "promptTokens": 1000, "draftTokens": 1000, "critiqueTokens": 300 },
|
|
23
|
+
"seats": {
|
|
24
|
+
"criteria": { "provider": "mock", "model": "mock-criteria" },
|
|
25
|
+
"skeleton": { "provider": "mock", "model": "mock-skeleton" },
|
|
26
|
+
"builder": { "provider": "mock", "model": "mock-builder" },
|
|
27
|
+
"reviser": { "provider": "mock", "model": "mock-builder" },
|
|
28
|
+
"proposers": [
|
|
29
|
+
{ "provider": "mock", "model": "mock-yield-argued", "lab": "mass-a" },
|
|
30
|
+
{ "provider": "mock", "model": "mock-yield-unargued", "lab": "mass-b" },
|
|
31
|
+
{ "provider": "mock", "model": "mock-proposer-a", "lab": "mass-c" }
|
|
32
|
+
],
|
|
33
|
+
"alternatives": [
|
|
34
|
+
{ "provider": "mock", "model": "mock-alt-a", "lab": "anchor-a" },
|
|
35
|
+
{ "provider": "mock", "model": "mock-alt-withdraw", "lab": "anchor-b" }
|
|
36
|
+
],
|
|
37
|
+
"critics": [
|
|
38
|
+
{ "provider": "mock", "model": "mock-critic-a", "lab": "anchor-a" },
|
|
39
|
+
{ "provider": "mock", "model": "mock-critic-b", "lab": "anchor-b" }
|
|
40
|
+
],
|
|
41
|
+
"deep_dive": { "provider": "mock", "model": "mock-deep", "lab": "deep-a", "maxTokens": 128000 }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plan-highest-7",
|
|
3
|
+
"description": "Tiered council, Muad's design (2026-09-26): the highest models for the highest tasks, the cheapest models for the mass work. MASS SEATS (seats.proposers, the cheap-7-v2 roster: GPT-5.6 Luna, Gemini 3.8 Flash, DeepSeek V4.1 Flash, GLM 5.3 Flash, Qwen 3.7 Plus, MiniMax M3, Mistral Medium 3.5) write the blind proposals and all the proposal debate posts and replies, and post on the anchors' architectures (alternatives.debaters \"all\"). ANCHOR SEATS: GPT-6 Astra and Claude Fable 5.1 (his deliberate exception: an Anthropic model reviews a draft a Claude Code session writes), DeepSeek V4 Pro and GLM-5.3 write the whole alternative architectures (seats.alternatives), answer the posts on them, and form the review panel that must sign off unanimously (seats.critics). The mass seats argue; only the anchors vote, so a cheap majority can never out-vote them. DEEP-DIVE SEAT (seats.deep_dive, DeepSeek V4.1 Flash, a mass-tier model: it must not share a lab with a voting anchor, chain-lint deep-dive-votes): one job, checking the first draft against every part of the task, 40,000 characters per call at a 36k output cap, up to 24 calls under its OWN $4 cap inside the run's; it does not vote, and the reviser answers its findings before the anchors' first review. The MAJORITY GUARD is ON (majority_guard, Muad's go 2026-09-26; off in every other shipped chain but mock-tiered): authors see each argument once with no lab letter or count, the reply prompts carry an evidence bar, and a withdrawal that does not quote the argument it concedes to is kept for the builder. Criteria move to Llama 4 Maverick, which holds no other seat. Same Claude Code writer seats and stages as plan-premium-7, including decision records and the dispute stage. No Kimi, no xAI. Untested with real models: price it with `npm run dry` and get an explicit go before any paid run.",
|
|
4
|
+
"maxRounds": 7,
|
|
5
|
+
"signoff": "unanimous",
|
|
6
|
+
"dispute": { "enabled": true, "stall_rounds": 2 },
|
|
7
|
+
"proposals": { "parts": 3, "maxTokens": 4000 },
|
|
8
|
+
"debate": true,
|
|
9
|
+
"decisions": { "enabled": true },
|
|
10
|
+
"alternatives": { "enabled": true, "debaters": "all" },
|
|
11
|
+
"deep_dive": { "enabled": true, "job": "sources", "usd": 4, "maxCalls": 24, "chunkChars": 40000 },
|
|
12
|
+
"majority_guard": { "enabled": true },
|
|
13
|
+
"canary": { "enabled": true, "sampleRate": 0.1 },
|
|
14
|
+
"handoff": true,
|
|
15
|
+
"estimate": { "promptTokens": 10000, "draftTokens": 10000, "critiqueTokens": 5000 },
|
|
16
|
+
"seats": {
|
|
17
|
+
"criteria": { "provider": "openrouter", "model": "meta-llama/llama-4-maverick", "maxTokens": 36000, "lab": "llama4-maverick" },
|
|
18
|
+
"skeleton": { "provider": "external", "model": "claude-code-session" },
|
|
19
|
+
"builder": { "provider": "external", "model": "claude-code-session" },
|
|
20
|
+
"reviser": { "provider": "external", "model": "claude-code-session" },
|
|
21
|
+
"handoff": { "provider": "external", "model": "claude-code-session" },
|
|
22
|
+
"proposers": [
|
|
23
|
+
{ "provider": "openrouter", "model": "openai/gpt-5.6-luna", "maxTokens": 36000, "lab": "gpt5.6-luna" },
|
|
24
|
+
{ "provider": "openrouter", "model": "google/gemini-3.8-flash", "maxTokens": 36000, "lab": "gemini3.8-flash" },
|
|
25
|
+
{ "provider": "openrouter", "model": "deepseek/deepseek-v4.1-flash", "maxTokens": 36000, "lab": "deepseek-v4.1-flash" },
|
|
26
|
+
{ "provider": "openrouter", "model": "z-ai/glm-5.3-flash", "maxTokens": 36000, "lab": "glm5.3-flash" },
|
|
27
|
+
{ "provider": "openrouter", "model": "qwen/qwen3.7-plus", "maxTokens": 36000, "lab": "qwen3.7-plus" },
|
|
28
|
+
{ "provider": "openrouter", "model": "minimax/minimax-m3", "maxTokens": 36000, "lab": "minimax-m3" },
|
|
29
|
+
{ "provider": "openrouter", "model": "mistralai/mistral-medium-3-5", "maxTokens": 36000, "lab": "mistral-medium3.5" }
|
|
30
|
+
],
|
|
31
|
+
"alternatives": [
|
|
32
|
+
{ "provider": "openrouter", "model": "openai/gpt-6-astra", "maxTokens": 36000, "lab": "gpt6-astra" },
|
|
33
|
+
{ "provider": "openrouter", "model": "anthropic/claude-fable-5.1", "maxTokens": 36000, "lab": "fable5.1" },
|
|
34
|
+
{ "provider": "openrouter", "model": "deepseek/deepseek-v4-pro", "maxTokens": 64000, "lab": "deepseek-v4-pro" },
|
|
35
|
+
{ "provider": "openrouter", "model": "z-ai/glm-5.3", "maxTokens": 64000, "lab": "glm5.3" }
|
|
36
|
+
],
|
|
37
|
+
"critics": [
|
|
38
|
+
{ "provider": "openrouter", "model": "openai/gpt-6-astra", "maxTokens": 36000, "lab": "gpt6-astra" },
|
|
39
|
+
{ "provider": "openrouter", "model": "anthropic/claude-fable-5.1", "maxTokens": 36000, "lab": "fable5.1" },
|
|
40
|
+
{ "provider": "openrouter", "model": "deepseek/deepseek-v4-pro", "maxTokens": 64000, "lab": "deepseek-v4-pro" },
|
|
41
|
+
{ "provider": "openrouter", "model": "z-ai/glm-5.3", "maxTokens": 64000, "lab": "glm5.3" }
|
|
42
|
+
],
|
|
43
|
+
"deep_dive": { "provider": "openrouter", "model": "deepseek/deepseek-v4.1-flash", "maxTokens": 36000, "lab": "deepseek-v4.1-flash" }
|
|
44
|
+
}
|
|
45
|
+
}
|
package/chains/verify.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verify",
|
|
3
|
-
"description": "The
|
|
3
|
+
"description": "The CLI default when --chain is not given. Two labs, a hard 2-round cap: the smallest cross-lab review. For a first real council, start with cheap-7-v2 (one OpenRouter key); see the README's chain table.",
|
|
4
4
|
"maxRounds": 2,
|
|
5
5
|
"stopOnPass": true,
|
|
6
6
|
"estimate": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "the-high-council",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"mcpName": "io.github.muad-yasin/the-high-council",
|
|
5
5
|
"description": "The High Council: a multi-model planning harness. One builder, independent critics from other labs, checkable acceptance criteria, a debate on the record, and a hard stop.",
|
|
6
6
|
"type": "module",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
52
|
-
"express": "^4.22.2",
|
|
53
52
|
"zod": "^4.6.4"
|
|
54
53
|
},
|
|
55
54
|
"files": [
|
|
56
55
|
"src/",
|
|
56
|
+
"!src/http-server.js",
|
|
57
57
|
"chains/",
|
|
58
58
|
"schemas/",
|
|
59
59
|
"types/",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
],
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@yao-pkg/pkg": "6.22.0",
|
|
87
|
-
"ajv": "^8.20.0"
|
|
87
|
+
"ajv": "^8.20.0",
|
|
88
|
+
"express": "^4.22.2"
|
|
88
89
|
}
|
|
89
90
|
}
|
package/schemas/report-v1.json
CHANGED
|
@@ -140,6 +140,11 @@
|
|
|
140
140
|
"$ref": "#/$defs/alternatives",
|
|
141
141
|
"description": "Present only on chains with alternatives.enabled: one whole architecture per lab, written blind before the skeleton, and the same post/reply board the proposals get."
|
|
142
142
|
},
|
|
143
|
+
"deep_dive": {
|
|
144
|
+
"x-stability": "experimental",
|
|
145
|
+
"type": "object",
|
|
146
|
+
"description": "Present only on chains with deep_dive.enabled (tiered councils): the one deep-dive seat's calls, its own dollar cap, what it spent, why it stopped (null, own_cap or max_calls) and its findings, each with a quote_status. The seat does not vote."
|
|
147
|
+
},
|
|
143
148
|
|
|
144
149
|
"challenge": {
|
|
145
150
|
"x-stability": "experimental",
|
|
@@ -254,7 +259,7 @@
|
|
|
254
259
|
"required": ["ran", "reason"],
|
|
255
260
|
"properties": {
|
|
256
261
|
"ran": { "type": "boolean" },
|
|
257
|
-
"reason": { "enum": ["stalled", "round_cap", "no_heard_reviewer", "panel_signed_off", "no_open_objections"] },
|
|
262
|
+
"reason": { "enum": ["stalled", "round_cap", "no_heard_reviewer", "not_quorate", "panel_signed_off", "no_open_objections"] },
|
|
258
263
|
"stall_rounds": { "type": ["integer", "null"] },
|
|
259
264
|
"stopped_at_round": { "type": ["integer", "null"] },
|
|
260
265
|
"open_objections": {
|
|
@@ -458,6 +463,18 @@
|
|
|
458
463
|
},
|
|
459
464
|
"description": "Present only when a review round heard no reviewer at all and the loop stopped there. Makes outcome 'degraded'."
|
|
460
465
|
},
|
|
466
|
+
"notQuorate": {
|
|
467
|
+
"x-stability": "experimental",
|
|
468
|
+
"type": "object",
|
|
469
|
+
"required": ["round", "heard", "minHeard"],
|
|
470
|
+
"properties": {
|
|
471
|
+
"round": { "type": "integer" },
|
|
472
|
+
"heard": { "type": "integer", "description": "Reviewers that gave a verdict (signed off or objected) that round." },
|
|
473
|
+
"minHeard": { "type": "integer", "description": "The chain's quorum.minHeard." },
|
|
474
|
+
"passedOnly": { "type": "integer", "description": "Reviewers that only stated a pass." }
|
|
475
|
+
},
|
|
476
|
+
"description": "Since 0.7.8. Present only when the chain sets quorum.minHeard and a review round in which every verdict was a sign-off had fewer verdicts than that; the loop stopped there. Not agreement: passed is false and outcome is 'degraded'."
|
|
477
|
+
},
|
|
461
478
|
"unverifiedReplays": {
|
|
462
479
|
"x-stability": "experimental",
|
|
463
480
|
"type": "array",
|
|
@@ -664,6 +681,20 @@
|
|
|
664
681
|
"diagnostics": {
|
|
665
682
|
"type": "object",
|
|
666
683
|
"description": "Experimental. Role failure-mode diagnostics computed from this run's own posts (src/role-diagnostics.js): perSeat, pairwiseAgreement, roleVsPlainSubstanceGap, flags."
|
|
684
|
+
},
|
|
685
|
+
"dropped": {
|
|
686
|
+
"type": "array",
|
|
687
|
+
"description": "Experimental (0.7.8). Debate posts and author replies the stage's filters rejected, one row per stage, lab and reason; [] when none were. Absent in runs made before 0.7.8. Reasons: for stage 'debate' unknown_target, own_proposal, bad_stance, unreadable; for stage 'replies' unknown_target, not_own_proposal, not_posted_on, bad_action, unreadable. An 'unreadable' row counts whole replies that did not parse, not posts.",
|
|
688
|
+
"items": {
|
|
689
|
+
"type": "object",
|
|
690
|
+
"required": ["stage", "by", "reason", "count"],
|
|
691
|
+
"properties": {
|
|
692
|
+
"stage": { "enum": ["debate", "replies"] },
|
|
693
|
+
"by": { "type": "string", "description": "The lab whose post or reply was dropped." },
|
|
694
|
+
"reason": { "enum": ["unknown_target", "own_proposal", "bad_stance", "not_own_proposal", "not_posted_on", "bad_action", "unreadable"] },
|
|
695
|
+
"count": { "type": "integer", "minimum": 1 }
|
|
696
|
+
}
|
|
697
|
+
}
|
|
667
698
|
}
|
|
668
699
|
}
|
|
669
700
|
},
|
package/src/api.js
CHANGED
|
@@ -16,6 +16,7 @@ import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
|
|
16
16
|
import { join, dirname, resolve, basename } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
import { resolveChainSeats } from './chain.js';
|
|
19
|
+
import { isChainName } from './chain-name.js';
|
|
19
20
|
import { estimateChainRows } from './cost.js';
|
|
20
21
|
import { deriveRunStatus } from './run-status.js';
|
|
21
22
|
import { harnessVersion } from './version.js';
|
|
@@ -23,7 +24,6 @@ import { harnessVersion } from './version.js';
|
|
|
23
24
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
const pkg = resolve(here, '..');
|
|
25
26
|
const cli = join(pkg, 'src', 'cli.js');
|
|
26
|
-
const CHAIN_NAME = /^[A-Za-z0-9._-]+$/;
|
|
27
27
|
const readJson = p => { try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return null; } };
|
|
28
28
|
|
|
29
29
|
/** The installed package version, e.g. "0.7.7". */
|
|
@@ -33,7 +33,7 @@ export function version() {
|
|
|
33
33
|
|
|
34
34
|
// Same lookup order as the CLI: the user's chains/ in `cwd` first, then the shipped ones.
|
|
35
35
|
function chainPath(name, cwd) {
|
|
36
|
-
if (
|
|
36
|
+
if (!isChainName(name)) throw new TypeError(`chain: a chain name such as "mock" or "plan-debate", got ${JSON.stringify(name)}`);
|
|
37
37
|
const found = [join(cwd, 'chains', `${name}.json`), join(pkg, 'chains', `${name}.json`)].find(existsSync);
|
|
38
38
|
if (!found) throw new Error(`no such chain: ${name} (looked in ${join(cwd, 'chains')} and the package's chains/)`);
|
|
39
39
|
return found;
|