amicus 4.8.0 → 4.9.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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +355 -0
- package/README.md +10 -5
- package/docs/CITATIONS.md +13 -5
- package/docs/ROADMAP.md +101 -10
- package/docs/configuration.md +55 -5
- package/docs/council.md +102 -14
- package/docs/troubleshooting.md +9 -2
- package/docs/usage.md +128 -12
- package/electron/ipc-setup.js +39 -2
- package/electron/main.js +46 -3
- package/electron/offer-session.js +51 -0
- package/electron/setup-ui-model.js +99 -9
- package/electron/setup-ui-styles.js +22 -0
- package/electron/setup-ui.js +244 -32
- package/electron/workspace-ui/live-dead-seats.js +163 -91
- package/electron/workspace-ui/live-seats.js +4 -4
- package/electron/workspace-ui/workspace-banners.js +30 -7
- package/electron/workspace-ui/workspace-matrix.js +23 -3
- package/electron/workspace-ui/workspace-seats.js +95 -79
- package/package.json +2 -1
- package/schemas/council-run.schema.json +2 -2
- package/schemas/council-tally.schema.json +17 -1
- package/schemas/council-verdict.schema.json +12 -4
- package/schemas/run.schema.json +6 -1
- package/skills/second-opinion/COUNCIL-DESIGN.md +1 -1
- package/skills/second-opinion/MANUAL-ORCHESTRATION.md +1 -1
- package/skills/second-opinion/MODEL-NOTES.md +88 -9
- package/skills/second-opinion/SEAT-BRIEFS.md +36 -4
- package/skills/second-opinion/SKILL.md +151 -36
- package/src/cli-council-run-bench.js +98 -6
- package/src/cli-handlers-council-run.js +18 -6
- package/src/cli-handlers-council.js +57 -7
- package/src/cli-handlers-doctor.js +12 -15
- package/src/cli.js +3 -1
- package/src/council/anonymize.js +2 -1
- package/src/council/briefings-chair-task.js +161 -0
- package/src/council/briefings-chair.js +33 -8
- package/src/council/briefings-debate.js +79 -13
- package/src/council/briefings-stage2-task.js +236 -0
- package/src/council/briefings-stage2.js +103 -26
- package/src/council/briefings-task.js +167 -0
- package/src/council/briefings.js +41 -4
- package/src/council/chair-fallback.js +95 -0
- package/src/council/debate.js +38 -21
- package/src/council/findings.js +3 -2
- package/src/council/ledger.js +2 -2
- package/src/council/parse-stage2.js +64 -16
- package/src/council/report-cost.js +61 -0
- package/src/council/report-html.js +26 -4
- package/src/council/report-md.js +30 -2
- package/src/council/report.js +40 -37
- package/src/council/run-assemble.js +21 -6
- package/src/council/run-chair.js +44 -95
- package/src/council/run-debate-revote.js +81 -49
- package/src/council/run-debate.js +51 -34
- package/src/council/run-finish.js +5 -3
- package/src/council/run-retry-keys.js +4 -4
- package/src/council/run-retry-launch.js +4 -4
- package/src/council/run-retry-notes.js +72 -15
- package/src/council/run-stage1-launch.js +4 -4
- package/src/council/run-stage1-rows.js +9 -6
- package/src/council/run-stage2.js +81 -47
- package/src/council/run-stages.js +9 -21
- package/src/council/run-stats-entry.js +46 -1
- package/src/council/run.js +28 -13
- package/src/council/seats.js +2 -2
- package/src/council/stage1-bind.js +3 -2
- package/src/council/verdict-seat-loss.js +124 -0
- package/src/council/verdict.js +108 -99
- package/src/headless.js +256 -49
- package/src/mcp-council-bench.js +64 -3
- package/src/mcp-council-run.js +10 -3
- package/src/mcp-server.js +52 -12
- package/src/mcp-tools.js +41 -5
- package/src/observe/council-legs.js +2 -2
- package/src/opencode-client.js +19 -1
- package/src/pack/pack-forward.js +15 -12
- package/src/pack/pack-resolve.js +1 -1
- package/src/prompt-builder.js +17 -1
- package/src/sidecar/fanout-leg.js +26 -0
- package/src/sidecar/fanout.js +1 -1
- package/src/sidecar/list-council.js +178 -0
- package/src/sidecar/list-limit.js +3 -1
- package/src/sidecar/list-search.js +2 -1
- package/src/sidecar/models.js +8 -1
- package/src/sidecar/read.js +34 -10
- package/src/sidecar/setup.js +124 -0
- package/src/template/render.js +16 -7
- package/src/utils/alias-audit.js +81 -3
- package/src/utils/alias-shadow-writer.js +220 -0
- package/src/utils/alias-shadow.js +294 -0
- package/src/utils/config.js +1 -1
- package/src/utils/curated-models.js +16 -8
- package/src/utils/degrade.js +12 -5
- package/src/utils/doctor-alias-check.js +149 -0
- package/src/utils/engine-log-parse.js +289 -0
- package/src/utils/engine-log-tail.js +114 -0
- package/src/utils/engine-log.js +250 -0
- package/src/utils/engine-skew-records.js +146 -0
- package/src/utils/engine-skew.js +300 -0
- package/src/utils/gateway-router.js +10 -2
- package/src/utils/model-canonicalization.js +64 -0
- package/src/utils/model-catalog.js +1 -1
- package/src/utils/model-shortlist.js +100 -0
- package/src/utils/provider-default-picker.js +93 -45
- package/src/utils/provider-default-prompt.js +1 -1
- package/src/utils/quick-picks.js +2 -2
- package/src/utils/remediation-hints.js +24 -0
- package/src/utils/result-schema.js +10 -0
- package/src/utils/text-sanitize.js +81 -0
- package/src/utils/ttft.js +57 -0
- package/src/utils/untrusted-fence.js +111 -1
- package/src/workspace/fold-format.js +28 -7
- package/src/workspace/live-normalize.js +2 -1
- package/src/workspace/matrix-model.js +6 -2
- package/src/workspace/run-detail.js +35 -9
- package/src/workspace/seat-space.js +10 -6
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Council Workspace — seats panel painter (v4.4 §5). D8 extraction (Task 1,
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* discipline as every sibling renderer file (workspace-app.js boots last and
|
|
8
|
-
* owns `state`).
|
|
2
|
+
* Council Workspace — seats panel painter (v4.4 §5). D8 extraction (Task 1, v4.6.2 PR4): moved
|
|
3
|
+
* verbatim out of workspace-panels.js, which was pressed up against the 300-line size gate —
|
|
4
|
+
* this file is where Task 2 adds dead-seat rows. Loads immediately before workspace-panels.js
|
|
5
|
+
* (index.html), which keeps a thin delegate; reads `window.AmicusApp` at CALL time, same
|
|
6
|
+
* discipline as every sibling renderer file (workspace-app.js boots last and owns `state`).
|
|
9
7
|
*
|
|
10
|
-
* Task 2 ("dead-seat rows"): `state.detail.run` and `state.detail.verdict`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
14
|
-
*
|
|
15
|
-
* live-seats.js) and this file's painting are new.
|
|
8
|
+
* Task 2 ("dead-seat rows"): `state.detail.run` and `state.detail.verdict` are the raw
|
|
9
|
+
* run.json/verdict.json docs (src/workspace/run-detail.js — `getRunDetail` returns them
|
|
10
|
+
* wholesale, unfiltered), so `run.degrades` and `verdict.seatLoss` are already on
|
|
11
|
+
* `state.detail` today; no data-layer threading was needed. Only the derivation
|
|
12
|
+
* (window.AmicusLive.deadSeats, live-seats.js) and this file's painting are new.
|
|
16
13
|
*
|
|
17
14
|
* NOTE (scope, matches the plan's file list): renderSeatsPanel() (below) is reached from
|
|
18
15
|
* renderDetail() — called both from openRun() (a fresh run open) and from the blind toggle
|
|
@@ -50,41 +47,57 @@
|
|
|
50
47
|
* seat-keyed and deadSeats still dedup'd on the alias; PR5c seat-keyed the other side too,
|
|
51
48
|
* so both now key on "seat id where the record supplies one, alias otherwise". The
|
|
52
49
|
* kind/channel FILTER still mirrors window.AmicusLive.deadSeats — now at
|
|
53
|
-
* live-dead-seats.js,
|
|
54
|
-
* must keep doing so. Two spellings of one rule
|
|
50
|
+
* live-dead-seats.js, where PR5c's size-gate split put it and v4.9 W9 named it `isSeatLoss` —
|
|
51
|
+
* and must keep doing so; the two moved together in W9's one commit. Two spellings of one rule
|
|
52
|
+
* is PR5a council finding B1; only the behavioural drift pin in workspace-seats.test.js holds it.
|
|
55
53
|
*
|
|
56
54
|
* ⚠️ The two sides are NOT identical, and the difference is deliberate: deadSeats decides
|
|
57
55
|
* whether a seat is RENDERED, so it fails toward showing a row; this decides whether a
|
|
58
56
|
* badge is PAINTED, so an unidentified record falls back to the alias and marks every seat
|
|
59
57
|
* sharing it. Over-badging is visible and self-correcting; a missing badge is silent.
|
|
60
58
|
*
|
|
61
|
-
* The kind/channel filter is load-bearing: run.degrades[] also carries
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* The kind/channel filter is load-bearing: run.degrades[] also carries kind:'heal' /
|
|
60
|
+
* channel:'stage1-retry' records with the SAME retryWaveId/firstFailure fields for seats that
|
|
61
|
+
* RECOVERED, and a field-only scan would tag a recovered seat "retried once".
|
|
64
62
|
*
|
|
65
|
-
* ⚠️ firstFailure is
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
63
|
+
* ⚠️ `firstFailure` is no longer a retried MARKER here — the W9 fix round narrowed the gate to
|
|
64
|
+
* `retryWaveId` (see below) — and the only thing left read off it is `.seatId`. THREE shapes
|
|
65
|
+
* carry that key today: `run-retry-group.js :: groupStage1Losses` builds two, its deadLegs
|
|
66
|
+
* loop's {seat, class:'leg', status, reason} and its deadWaves loop's {seat, class:
|
|
67
|
+
* lossClass(w), waveId, reason} at three sites (lens/critic/bench), both stamped with `seatId`
|
|
68
|
+
* by `recordFailure`; `run-retry-notes.js :: skippedWaveNote` builds a third, {seat, class:
|
|
69
|
+
* 'missing', waveId, reason}, which never passes through recordFailure and so carries NO
|
|
70
|
+
* seatId — it puts the seat id on `data.seatId` instead, which the key read below falls
|
|
71
|
+
* through to. ⚠️ T-A8 DROPPED five line numbers here: re-opened 2026-08-17 they had all rotted
|
|
72
|
+
* a uniform +31 (T-A3 +15, T-A6 +16), and "now 235" is 266.
|
|
72
73
|
*/
|
|
73
74
|
function retriedSeats(degrades) {
|
|
74
75
|
var out = Object.create(null);
|
|
75
76
|
(degrades || []).forEach(function (d) {
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
// A kind-LESS record IS a degrade (W9 fix round, C4) — this panel falls back to
|
|
78
|
+
// `verdict.degrades` below, documents that can predate kinds. Mirrors isSeatLoss.
|
|
79
|
+
if (!d || (d.kind !== undefined && d.kind !== 'degrade')) { return; }
|
|
78
80
|
var data = d.data || {};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
// Hoisted ABOVE the channel test (behaviour-preserving — the two loss channels already
|
|
82
|
+
// required it) because it is ALSO the `seat-unbound` gate v4.9 W9 admits that shared channel
|
|
83
|
+
// through: orphan-leg, re-vote and Stage-2 judge notes ride it, are not retried seats, and
|
|
84
|
+
// none carries a retry-family field. ⚠️ NARROWED to `retryWaveId` alone in the W9 fix round
|
|
85
|
+
// (mutant SKIPRETRIED): this is a RETRIED set, and `run-retry-notes.js :: skippedWaveNote`
|
|
86
|
+
// carries a `firstFailure` for a seat the retry never ATTEMPTED, so badging it would be
|
|
87
|
+
// false. `isSeatLoss` still admits that record as a LOSS — its gate keeps both fields; only
|
|
88
|
+
// the retried READ differs (the drift pin compares exactly that read). Every RETRIED seat's
|
|
89
|
+
// builder emits `retryWaveId` — never-attempted skippedWaveNote alone lacks it — so no badge is lost.
|
|
90
|
+
if (!data.retryWaveId) { return; }
|
|
91
|
+
if (d.channel !== 'dead-leg' && d.channel !== 'dead-wave'
|
|
92
|
+
&& !(d.channel === 'seat-unbound' && (data.seatId || data.seat))) { return; }
|
|
93
|
+
if (d.channel !== 'dead-wave') {
|
|
94
|
+
// ⚠️ Prefer the SEAT ID when the record names one. TWO mechanisms supply it, not one:
|
|
95
|
+
// retryLegStillDeadNote and missingLegStillDeadNote via `firstFailure.seatId` (pinned by
|
|
96
|
+
// tests/council/run-retry.test.js:628 on a twin bench), and — since v4.8 PR5c —
|
|
97
|
+
// srcLegStillDeadNote via its own `data.seatId`, joined on that key by waveStillDeadNote's
|
|
98
|
+
// partial `seat-unbound` arm in v4.9 W9. Reading only the first meant a srcLeg record
|
|
99
|
+
// keyed by ALIAS and badged the live twin "retried once" while the seat that was actually
|
|
100
|
+
// retried showed nothing. `data.seat` is the last fallback, for pre-PR5c records (R6).
|
|
88
101
|
var key = (data.firstFailure && data.firstFailure.seatId) || data.seatId || data.seat;
|
|
89
102
|
if (key) { out[key] = true; }
|
|
90
103
|
} else {
|
|
@@ -102,9 +115,8 @@
|
|
|
102
115
|
return out;
|
|
103
116
|
}
|
|
104
117
|
|
|
105
|
-
// Mirrors isReviewing at live-dead-seats.js (moved there by PR5c's size-gate split) —
|
|
106
|
-
//
|
|
107
|
-
// carry a reviewer's retry marker.
|
|
118
|
+
// Mirrors isReviewing at live-dead-seats.js (moved there by PR5c's size-gate split) — a
|
|
119
|
+
// chair/judge/rebuttal/revote row must not carry a reviewer's retry marker.
|
|
108
120
|
function isReviewingRole(role) {
|
|
109
121
|
return role === 'seat' || role === 'critic' ||
|
|
110
122
|
(typeof role === 'string' && role.indexOf('lens:') === 0);
|
|
@@ -117,11 +129,16 @@
|
|
|
117
129
|
var tbody = A.$('seats-body');
|
|
118
130
|
window.AmicusRender.renderSeats(tbody, seats, A.state.blind, A.labelOf);
|
|
119
131
|
var seatLoss = d.verdict && d.verdict.seatLoss;
|
|
120
|
-
|
|
132
|
+
// `criticSeat` (v4.9 W9 / R4): run.json's resolved critic SEAT id (run-state.js ::
|
|
133
|
+
// initCouncilRun seeds it, seats.js :: preflightSeats supplies it) — what lets deadSeats
|
|
134
|
+
// tag the critic by seat identity. Null with no critic, and on pre-field run.json.
|
|
135
|
+
var runMeta = { critic: (d.run && d.run.critic) || null,
|
|
136
|
+
criticSeat: (d.run && d.run.criticSeat) || null };
|
|
121
137
|
// Source-selection (v4.6.3 PR2, spec D4): run-degrade.js swallows checkpoint failures, so
|
|
122
|
-
// verdict.json can carry degrade records run.json's own checkpoint lost — fall back to it
|
|
123
|
-
//
|
|
124
|
-
//
|
|
138
|
+
// verdict.json can carry degrade records run.json's own checkpoint lost — fall back to it ONLY
|
|
139
|
+
// when run.degrades is empty/absent. A fallback, never a union: both docs can carry records for
|
|
140
|
+
// the SAME run, and the persisted run.json copy is authoritative when present. (This fallback
|
|
141
|
+
// is why the kind test above must admit a kind-LESS record: verdict.json can predate kinds.)
|
|
125
142
|
var deg = (d.run && d.run.degrades && d.run.degrades.length) ? d.run.degrades
|
|
126
143
|
: ((d.verdict && d.verdict.degrades) || []);
|
|
127
144
|
var retried = retriedSeats(deg);
|
|
@@ -130,16 +147,14 @@
|
|
|
130
147
|
// composed doc's leg order — so tbody.children[i] is not seats[i]. Build the key exactly the
|
|
131
148
|
// way renderSeats does or the lookup silently misses.
|
|
132
149
|
var rowsByKey = Object.create(null);
|
|
133
|
-
Array.prototype.slice.call(tbody.children).forEach(function (row) {
|
|
134
|
-
rowsByKey[row.dataset.key] = row;
|
|
135
|
-
});
|
|
150
|
+
Array.prototype.slice.call(tbody.children).forEach(function (row) { rowsByKey[row.dataset.key] = row; });
|
|
136
151
|
seats.forEach(function (s) {
|
|
137
152
|
var row = rowsByKey[String(s.id || s.model)];
|
|
138
153
|
if (!row || !row.children[8]) { return; }
|
|
139
|
-
// Column 8 is the table's unlabeled trailing flag cell (index.html:51's final <th></th>).
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
154
|
+
// Column 8 is the table's unlabeled trailing flag cell (index.html:51's final <th></th>). It
|
|
155
|
+
// carries '⏳ stalled' on the LIVE path; on this terminal path seatsFromRunStats hardcodes
|
|
156
|
+
// stalled:false, so it is always empty here and free to use — the collision site if that
|
|
157
|
+
// ever changes.
|
|
143
158
|
// Fix wave (whole-branch review, finding 2): this pass must be SYMMETRIC. renderSeats
|
|
144
159
|
// reuses rows keyed on `model:role` across calls — including across two different
|
|
145
160
|
// terminal runs opened in sequence that happen to share an alias+role — and never resets
|
|
@@ -158,34 +173,35 @@
|
|
|
158
173
|
//
|
|
159
174
|
// ⚠️ NAMING HAZARD (council A1): `seat` means TWO different things three lines apart.
|
|
160
175
|
// `s.seat` is a SEAT ID (`alias#N`, from src/council/run-stats-entry.js :: buildRunStatsEntry
|
|
161
|
-
// via the cost row). The degrade records keyed into `retried` above use `data.seat`,
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
// Reading one as the other is precisely how an earlier revision of
|
|
165
|
-
// alias-keyed map with a seat-id lookup and dropped every badge. When
|
|
166
|
-
// side, say which space you are in.
|
|
176
|
+
// via the cost row). The degrade records keyed into `retried` above use `data.seat`, which is
|
|
177
|
+
// an ALIAS and stays one deliberately (run-retry-notes.js :: waveStillDeadNote's `data`
|
|
178
|
+
// comment; `verdict-seat-loss.js :: deriveSeatLoss`'s `criticLeg` compares it to `o.critic` —
|
|
179
|
+
// both by SYMBOL since W9). Reading one as the other is precisely how an earlier revision of
|
|
180
|
+
// this fix paired an alias-keyed map with a seat-id lookup and dropped every badge. When
|
|
181
|
+
// touching either side, say which space you are in.
|
|
167
182
|
//
|
|
168
183
|
// ⚠️ The pre-PR expression was `retried[s.modelInput || s.model]` (council B1). The
|
|
169
184
|
// `modelInput` arm is dropped on purpose: this loop only ever iterates
|
|
170
185
|
// `seatsFromRunStats(...)` output (assigned in workspace-seats.js :: renderSeatsPanel), and
|
|
171
186
|
// that projection emits no `modelInput` at all — live payload seats, which DO carry it,
|
|
172
187
|
// reach `deadSeats` in workspace-seats.js :: appendDeadRows and never reach here. The
|
|
173
|
-
// invariant is pinned by test (12) in workspace-seats.test.js;
|
|
174
|
-
//
|
|
175
|
-
// ⚠️ `s.seat &&` is LOAD-BEARING (council A1). `s.seat` is null on a unique bench, and a
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
// `m = Object.create(null); m['null'] = true`, `m[null]` is `true`. Contrived, but this
|
|
179
|
-
//
|
|
180
|
-
// named `toString` crashing the seats repaint, which is why
|
|
181
|
-
// throughout. The guard costs nothing and closes it.
|
|
188
|
+
// invariant is pinned by test (12) in workspace-seats.test.js; if that test ever fails,
|
|
189
|
+
// restore the `s.modelInput` arm rather than deleting the test.
|
|
190
|
+
// ⚠️ `s.seat &&` is LOAD-BEARING (council A1). `s.seat` is null on a unique bench, and a bare
|
|
191
|
+
// `retried[s.seat]` coerces null to the STRING key 'null' — so a seat with no seat id would
|
|
192
|
+
// match a degrade record whose alias is literally `null`. Measured: with
|
|
193
|
+
// `m = Object.create(null); m['null'] = true`, `m[null]` is `true`. Contrived, but this module
|
|
194
|
+
// already guards the same class elsewhere — live-dead-seats.js's Object.create(null) note in
|
|
195
|
+
// deadSeats records a model named `toString` crashing the seats repaint, which is why
|
|
196
|
+
// Object.create(null) is used throughout. The guard costs nothing and closes it.
|
|
182
197
|
//
|
|
183
|
-
// ⚠️ KEYSPACE (council B1): `retried` deliberately mixes seat ids (`alias#N`) and bare
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
198
|
+
// ⚠️ KEYSPACE (council B1): `retried` deliberately mixes seat ids (`alias#N`) and bare aliases
|
|
199
|
+
// as keys — every emitter arm supplies a seat id since v4.9 W9 P1, but a slot the producer
|
|
200
|
+
// could NOT identify still emits null and falls back to the alias. That is a real collision
|
|
201
|
+
// surface and a KNOWN one — src/council/seats.js:236 already records that a literal alias
|
|
202
|
+
// containing '#' collides with a minted #N id, and preflightSeats refuses exactly that shape.
|
|
203
|
+
// This map inherits that guarantee rather than re-deriving it; if preflightSeats ever stops
|
|
204
|
+
// refusing it, this lookup becomes ambiguous.
|
|
189
205
|
var isRetried = isReviewingRole(s.role)
|
|
190
206
|
&& !!((s.seat && retried[s.seat]) || retried[s.model]);
|
|
191
207
|
if (isRetried) {
|
|
@@ -201,13 +217,12 @@
|
|
|
201
217
|
}
|
|
202
218
|
|
|
203
219
|
/**
|
|
204
|
-
* Paints the dead-seat rows appended after live rows. Deliberately NOT
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* just given), so nothing here needs to track dead rows across calls.
|
|
220
|
+
* Paints the dead-seat rows appended after live rows. Deliberately NOT folded into
|
|
221
|
+
* workspace-render.js's renderSeats (293/300 — must not grow) and NOT run through its keyed
|
|
222
|
+
* diff: dead rows carry no per-tick-changing field, so a full rebuild every call is correct
|
|
223
|
+
* and cheap, and renderSeats just above already self-cleans any PRIOR dead row as an
|
|
224
|
+
* unrecognized `data-key` (its own seen-set only knows about the live `seats` it was just
|
|
225
|
+
* given), so nothing here needs to track dead rows across calls.
|
|
211
226
|
*
|
|
212
227
|
* Cells route through window.AmicusLive.seatCells(...) — the SAME function
|
|
213
228
|
* live rows use — so name masking (and every other column's blank/em-dash
|
|
@@ -264,7 +279,8 @@
|
|
|
264
279
|
var A = window.AmicusApp;
|
|
265
280
|
var d = A.state.detail;
|
|
266
281
|
var seatLoss = d && d.verdict ? d.verdict.seatLoss : null;
|
|
267
|
-
var runMeta = { critic: (d && d.run && d.run.critic) || null
|
|
282
|
+
var runMeta = { critic: (d && d.run && d.run.critic) || null,
|
|
283
|
+
criticSeat: (d && d.run && d.run.criticSeat) || null };
|
|
268
284
|
// Source-selection (v4.6.3 PR2, spec D4), live-path twin of renderSeatsPanel's fallback
|
|
269
285
|
// above: the tick's own live.degrades wins when non-empty; state.detail.verdict.degrades is
|
|
270
286
|
// usually absent mid-run (verdict.json doesn't exist until the run finishes) — fine, this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
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": [
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"models:info": "node bin/amicus.js models",
|
|
64
64
|
"models:check": "node bin/amicus.js models --check",
|
|
65
65
|
"models:check:strict": "node bin/amicus.js models --check --strict",
|
|
66
|
+
"check:ci-alias-pins": "node scripts/check-ci-alias-pins.js",
|
|
66
67
|
"generate-icon": "node scripts/generate-icon.js",
|
|
67
68
|
"generate-docs": "node scripts/generate-docs.js",
|
|
68
69
|
"generate-docs:check": "node scripts/generate-docs.js --check",
|
|
@@ -103,13 +103,13 @@
|
|
|
103
103
|
}
|
|
104
104
|
},
|
|
105
105
|
"degrades": {
|
|
106
|
-
"description": "v4.6 Plan 1: every degrade/heal the sink announced — additive, written by run-degrade.js.",
|
|
106
|
+
"description": "v4.6 Plan 1: every degrade/heal the sink announced — additive, written by run-degrade.js. v4.9 widened kind with 'info': an announcement that is neither a loss nor a recovery (e.g. a task run's ledger-skipped note) — recorded here but never flipping the run degraded.",
|
|
107
107
|
"type": "array",
|
|
108
108
|
"items": {
|
|
109
109
|
"type": "object",
|
|
110
110
|
"required": ["kind", "channel", "what", "why", "effect"],
|
|
111
111
|
"properties": {
|
|
112
|
-
"kind": { "enum": ["degrade", "heal"] },
|
|
112
|
+
"kind": { "enum": ["degrade", "heal", "info"] },
|
|
113
113
|
"channel": { "type": "string" },
|
|
114
114
|
"what": { "type": "string" },
|
|
115
115
|
"why": { "type": "string" },
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"seats": {
|
|
22
22
|
"type": "array",
|
|
23
23
|
"description": "v4.8, optional. The run's seat table, one {id, alias, role, lens, position} entry per BENCH seat in bench order, echoed verbatim from the tally input. The engine emits it only when the bench repeats an alias — the one case where the `alias#N` ids on findings[].raiserSeat, adjudications[].seat and runStats[].seat resolve to nothing else in the document, since meta.models is the ALIAS list. Bench-only: 'claude' is never a seat, so meta.models.length need not equal seats.length and the two must never be joined positionally. ABSENCE DOES NOT MEAN the bench had no repeated alias — hand-assembled and MCP-assembled input reaches tally() with no seat machinery behind it; it means only that no seat table is available. `items` is deliberately unconstrained: consumers must tolerate a table that is not an array of seat objects (the report and Workspace renderers fall back to alias space whole rather than failing), and the MCP input schema accepts the same latitude."
|
|
24
|
+
},
|
|
25
|
+
"intent": {
|
|
26
|
+
"enum": ["review", "task"],
|
|
27
|
+
"description": "v4.9, optional. The run's intent. The engine emits it only when 'task' — a review run's meta never materializes the key, keeping every review artifact byte-identical — but an explicit 'review' from hand-assembled input is accepted."
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
},
|
|
@@ -96,7 +100,19 @@
|
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
},
|
|
99
|
-
"runStats": {
|
|
103
|
+
"runStats": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"items": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"properties": {
|
|
108
|
+
"ttftMs": {
|
|
109
|
+
"type": "integer",
|
|
110
|
+
"minimum": 0,
|
|
111
|
+
"description": "v4.9 W13, optional. Time-to-first-token for this row's leg: milliseconds from the leg asking OpenCode for output to the first poll carrying substantive activity (output, reasoning, a tool call, a tool result, or a tool call reaching a terminal status) — the very same predicate the no-output backstop disarms on, read at the one site in src/headless.js's poll loop, so the two can never drift. EMIT-WHEN-SET: absent means no substantive tick was ever OBSERVED, which is NOT the same as `0` (a first token inside the first poll, a real value that must survive) and NOT the same as `null`. KNOWN BIAS, stated so the C2 derivation reads the sample correctly: (a) the stamp is taken at poll time, not token time, so every value is an UPPER BOUND carrying up to one poll interval of slack; (b) absence is still not proof the model produced nothing — the activity block now runs ABOVE all four completion gates (so a leg that first produced output and finished inside one poll IS measured), but a leg ended by an external abort, by a consecutive-poll-failure bail, by --timeout expiry during the final sleep, or by the pre-send backstop ends without any poll observing what it may already have served; (c) PR #207 round 3 adds one more cause of absence — a reading taken across a BACKWARD wall-clock jump is negative (the probe is a Date.now() delta), and every producer DROPS such a reading rather than clamping it to 0, because a clamp would publish 'first token inside the first poll' — the single most consequential value in this distribution — for a leg that measured nothing of the kind. PROBE ONLY at v4.9 W13 (ruling R12, probe first / derive later): nothing in the engine derives a backstop, threshold, or routing decision from it. No `additionalProperties: false` on this row means additive fields were always accepted here — this documents the shape rather than changing what is accepted, exactly as streetCred[].seat above does."
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
100
116
|
"tierCounts": {
|
|
101
117
|
"type": "object",
|
|
102
118
|
"required": ["Confirmed", "Contested", "Singleton", "Disputed"],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://raw.githubusercontent.com/BourbonDog/amicus/main/schemas/council-verdict.schema.json",
|
|
4
4
|
"title": "amicus council-verdict document",
|
|
5
|
-
"description": "Verdict record (`council verdict --json`, amicus_verdict, verdict.json). overallVerdict is the chair's parsed VERDICT line — null in every Stage-4 manual path, populated by the headless engine.",
|
|
5
|
+
"description": "Verdict record (`council verdict --json`, amicus_verdict, verdict.json). overallVerdict is the chair's parsed terminal line — the VERDICT line on a review run, the ANSWER line on a task run (v4.9 W7) — null in every Stage-4 manual path, populated by the headless engine.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"schemaVersion",
|
|
@@ -56,13 +56,21 @@
|
|
|
56
56
|
"claudeInCouncil": {
|
|
57
57
|
"type": "boolean"
|
|
58
58
|
},
|
|
59
|
+
"intent": {
|
|
60
|
+
"enum": ["review", "task"],
|
|
61
|
+
"description": "v4.9, optional. The run's intent, forwarded from the tally record's meta.intent. The engine emits it only when 'task' — a review verdict never materializes the key, keeping review-run verdict.json byte-identical — but an explicit 'review' from hand-assembled input is accepted."
|
|
62
|
+
},
|
|
59
63
|
"overallVerdict": {
|
|
64
|
+
"description": "v4.9 W7: the chair's parsed terminal line on EITHER scale — a review run's VERDICT phrase ('Ship it' | 'Fix these first' | 'Fundamental rethink') or a task run's ANSWER phrase ('Converged' | 'Split' | 'Insufficient'). The two scales are disjoint by construction, so the value alone identifies the intent; `intent` above states it explicitly on task runs.",
|
|
60
65
|
"oneOf": [
|
|
61
66
|
{
|
|
62
67
|
"enum": [
|
|
63
68
|
"Ship it",
|
|
64
69
|
"Fix these first",
|
|
65
|
-
"Fundamental rethink"
|
|
70
|
+
"Fundamental rethink",
|
|
71
|
+
"Converged",
|
|
72
|
+
"Split",
|
|
73
|
+
"Insufficient"
|
|
66
74
|
]
|
|
67
75
|
},
|
|
68
76
|
{
|
|
@@ -222,13 +230,13 @@
|
|
|
222
230
|
]
|
|
223
231
|
},
|
|
224
232
|
"degrades": {
|
|
225
|
-
"description": "v4.6 Plan 2: what this run lost — copied verbatim from the sink at verdict assembly. Additive; absent
|
|
233
|
+
"description": "v4.6 Plan 2: what this run lost — copied verbatim from the sink at verdict assembly. Additive; absent when the sink recorded nothing. v4.9 widened kind with 'info': an announcement that is neither a loss nor a recovery (e.g. a task run's ledger-skipped note) — carried here without degrading the run.",
|
|
226
234
|
"type": "array",
|
|
227
235
|
"items": {
|
|
228
236
|
"type": "object",
|
|
229
237
|
"required": ["kind", "channel", "what", "why", "effect"],
|
|
230
238
|
"properties": {
|
|
231
|
-
"kind": { "enum": ["degrade", "heal"] },
|
|
239
|
+
"kind": { "enum": ["degrade", "heal", "info"] },
|
|
232
240
|
"channel": { "type": "string" },
|
|
233
241
|
"what": { "type": "string" },
|
|
234
242
|
"why": { "type": "string" },
|
package/schemas/run.schema.json
CHANGED
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
"opencodeSessionId": { "type": ["string", "null"] },
|
|
24
24
|
"usage": { "type": ["object", "null"] },
|
|
25
25
|
"pack": { "type": "object" },
|
|
26
|
-
"tag": { "type": "string" }
|
|
26
|
+
"tag": { "type": "string" },
|
|
27
|
+
"ttftMs": {
|
|
28
|
+
"type": "integer",
|
|
29
|
+
"minimum": 0,
|
|
30
|
+
"description": "v4.9 W13, optional. Time-to-first-token for this leg: milliseconds from the leg asking OpenCode for output to the first poll carrying substantive activity — the same predicate the no-output backstop disarms on. EMIT-WHEN-SET, like `pack` and `tag` above and unlike `opencodeSessionId`/`durationMs`: absent means no substantive tick was OBSERVED, which is neither `0` (a real first-token-inside-the-first-poll measurement) nor `null`. KNOWN BIAS, for whoever derives from this later: the value is stamped at poll time rather than token time, so it is an UPPER BOUND carrying up to one poll interval of slack; and absence is not proof the model served nothing — a leg ended by an external abort, by a consecutive-poll-failure bail, by --timeout expiry during the final sleep, or by the pre-send backstop can have served output no poll ever observed, and (PR #207 round 3) so can a leg whose only reading was taken across a backward wall-clock jump: the probe is a Date.now() delta, so a skewed reading is negative, and every producer DROPS such a reading rather than clamping it to 0 — a clamp would publish 'first token inside the first poll' for a leg that measured nothing of the kind. PROBE ONLY at v4.9 W13 — nothing derives from it yet."
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
}
|
|
@@ -110,7 +110,7 @@ Run as ordered phases; track as todos. **Three sequential waves of model calls**
|
|
|
110
110
|
- Chair selection & fallback: §5.3.
|
|
111
111
|
|
|
112
112
|
### Stage 4 — Tiered decisions (peer-validated)
|
|
113
|
-
- **Consensus tier** = **Confirmed** findings (≥ 2 peer agreements
|
|
113
|
+
- **Consensus tier** = **Confirmed** findings (≥ 2 peer agreements with agrees dominating — or a lone corroborating peer with zero disputes) → offer one **bulk accept/deny** (user may name exceptions).
|
|
114
114
|
- **Judgment tier** = **Disputed** (strong peer pushback), **Contested** (live dispute), or **Singleton** (only the raiser) → present **each individually**, showing the adjudication data and which model raised/disputed it.
|
|
115
115
|
- Record every decision (accepted / denied / modified / deferred).
|
|
116
116
|
|
|
@@ -228,7 +228,7 @@ Instruct the chair to write a **synthesized verdict** that:
|
|
|
228
228
|
- Distinguishes findings the bench broadly endorsed from contested or singleton claims
|
|
229
229
|
- Arrives at an overall assessment of the artifact
|
|
230
230
|
|
|
231
|
-
**Chair
|
|
231
|
+
**Chair closing scale (standard since v4.9 — not an opt-in element):** append the addendum from `SEAT-BRIEFS.md § Chair closing-scale addendum` to the chair packet — the chair must close with 3–5 **hard questions** the artifact's author probably hasn't asked themselves, then a final parseable line: `VERDICT: Ship it | Fix these first | Fundamental rethink`. (On a task-intent run, use that section's ANSWER twin instead — `ANSWER: Converged | Split | Insufficient` — and never translate one scale into the other.) Surface that line verbatim at the top of `report.md` and in the inline chat presentation. When debate mode is also ON, the chair packet is built from the *final* (post-rebuttal) tally and includes the defense/re-vote outcomes.
|
|
232
232
|
|
|
233
233
|
Save the chair's output to the run folder as `verdict.md`.
|
|
234
234
|
|
|
@@ -4,10 +4,13 @@ This file is the `second-opinion` skill's evolving memory of **how to actually d
|
|
|
4
4
|
well**. Read it before Stage 0 (council selection and launch); update it, with the user's
|
|
5
5
|
approval, at the end of each run (Stage 6). Keep it tight — merge and prune rather than append.
|
|
6
6
|
|
|
7
|
-
_Last updated: 2026-08-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
the
|
|
7
|
+
_Last updated: 2026-08-25 (per-section fold-back, both directions — the `NO_OUTPUT_BACKSTOP`
|
|
8
|
+
mechanism, conformance-vs-content plus the credit-exhaustion attribution correction, the
|
|
9
|
+
inlined-source-pack technique, and per-model calibration nuances upstreamed from the field ledger;
|
|
10
|
+
the `runStats` allowlist correction adopted back into the local ledger; see changelog). Prior:
|
|
11
|
+
2026-08-03 (haiku "hard-404" re-diagnosed as a `/v1`-less `ANTHROPIC_BASE_URL`, pre-degrade-era
|
|
12
|
+
claims re-grounded in the announcement contract, three model sections and the peer-consensus≠
|
|
13
|
+
evidence rule upstreamed)._
|
|
11
14
|
|
|
12
15
|
## Global operating rules (all models)
|
|
13
16
|
- **Fast path:** `council run` applies `--agent Plan` / `--no-context` / `--summary-length
|
|
@@ -34,6 +37,28 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
34
37
|
- **Read results from the JSON documents** (`--json`): a wave's `legs[].summary` / a run's
|
|
35
38
|
`summary` is the model's output; `status`/`error`/`counts` are ground truth for failures. Never
|
|
36
39
|
scrape stderr logs to judge success.
|
|
40
|
+
- **`conformance` measures FORMAT, not content — read an `unstructured` leg before writing it
|
|
41
|
+
off.** A leg that fails to emit a parseable findings block scores zero findings in the tally even
|
|
42
|
+
when its prose review is the bench's strongest — hand-extracted, one such "zero-finding" leg has
|
|
43
|
+
tied for the best peers-only street-cred on its bench and carried the round's deepest structural
|
|
44
|
+
finding no other seat found. Open every non-`clean` `review-<seat>.md` and judge it by substance
|
|
45
|
+
before discarding it. Before blaming a model's format for a run of `unstructured`/zero-finding
|
|
46
|
+
legs, **check account credit health** — a mid-run credit exhaustion produces the same signature
|
|
47
|
+
(repair and judge legs failing together) and has been traced to billing, not model behavior. Tell:
|
|
48
|
+
a cluster of legs failing in seconds with zero tokens, especially if OpenRouter-routed seats die
|
|
49
|
+
while direct-routed seats on the same run keep working.
|
|
50
|
+
- **`NO_OUTPUT_BACKSTOP` is a fixed no-first-token deadline (120s by default) — it kills slow
|
|
51
|
+
models, not just dead ones, and the once-only auto-retry cannot save them.** A model-agnostic
|
|
52
|
+
dead-man's switch arms at leg launch and disarms only on substantive activity (output growth,
|
|
53
|
+
reasoning-token growth, or a tool call); it is independent of `--timeout`, and the only override
|
|
54
|
+
is the env var `AMICUS_NO_OUTPUT_BACKSTOP_MS` — there is no CLI flag or per-model setting. The
|
|
55
|
+
retry reuses the same threshold, so a slow-but-alive model can burn its one retry for nothing and
|
|
56
|
+
still exit degraded. It fires identically on a genuinely dead route and on a live model that
|
|
57
|
+
simply thinks past 120s before its first token — **distinguish them with a solo re-run before
|
|
58
|
+
blaming the model.** Size an override from the *slowest observed leg × 2* with real headroom; a
|
|
59
|
+
merely-doubled setting has still been killed by the slowest legs seen in practice. It applies to
|
|
60
|
+
every leg in the process, so raising it loses the genuine dead-endpoint fast-fail for the whole
|
|
61
|
+
bench while it's set.
|
|
37
62
|
- **Transient provider errors** (502s, connection drops): re-run the affected leg (solo
|
|
38
63
|
`amicus start --json`, same briefing file) or the wave — see per-model notes for
|
|
39
64
|
model-specific signals. Never present a half-finished run as an answer.
|
|
@@ -75,6 +100,15 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
75
100
|
multi-file packet despite the standard preamble. Treat a many-file briefing as a long-read task:
|
|
76
101
|
expect stubs, retry solo once with the identical briefing, then substitute the model — or
|
|
77
102
|
pre-concatenate the packet into one file for weaker readers.
|
|
103
|
+
- **For code/plan review, inline a curated source pack and forbid tools — it eliminates the
|
|
104
|
+
narrate-then-stub class outright.** Instead of pointing seats at a repo to read live, put the
|
|
105
|
+
artifact plus a line-numbered extract of exactly the cited source regions directly in the
|
|
106
|
+
briefing, each headed with its real file path, under an explicit "everything is inline; do NOT
|
|
107
|
+
use any tools or read any files" preamble. This has produced first-attempt findings from every
|
|
108
|
+
seat on a bench — including models whose documented failure mode is multi-file agentic reads —
|
|
109
|
+
and it makes citation-checking possible, since reviewers can verify line numbers against the
|
|
110
|
+
pack. Trade is a bigger briefing per seat; worth it whenever the question is "are these claims
|
|
111
|
+
true of this code?"
|
|
78
112
|
- **`--agent Plan` can trigger a literal, hard "planning mode — cannot execute" refusal on some
|
|
79
113
|
models** (terse, near-zero-token responses), not just the intended read-only tool restriction —
|
|
80
114
|
and prompt wording alone has not fixed it; switching that model to `--agent Build` has, immediately.
|
|
@@ -106,6 +140,11 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
106
140
|
own `runStats` rows too — so anything keying `runStats` by model must use an **allowlist**, not a
|
|
107
141
|
judge exclusion; see `docs/council.md`'s `runStats[].role` roster (under `amicus council tally`)
|
|
108
142
|
for the exact set.
|
|
143
|
+
- **Budget Stage 2 at rough parity with Stage 1 on a multi-seat bench, not as a rounding error.**
|
|
144
|
+
Every judge reads the *whole* anonymized bundle, so judge input scales with `seats ×
|
|
145
|
+
review-length` while Stage-1 input is just the briefing — on a 4-seat bench the bundle is often
|
|
146
|
+
larger than the briefing itself. Rule of thumb: estimate Stage 2 ≈ Stage 1, then add the chair.
|
|
147
|
+
The old habit of treating cross-review as cheap comes from small benches with short reviews.
|
|
109
148
|
- **Expect agreement inflation in Stage-2 adjudication.** The judge contract defines `agree` by
|
|
110
149
|
worked example ("an 'I missed this — it's valid' counts as agree") but gives no example for
|
|
111
150
|
`dispute` and no positive definition of `neutral`, while requiring a verdict on EVERY finding —
|
|
@@ -124,6 +163,11 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
124
163
|
`~/.config/amicus/config.json` take precedence over the shipped routes, so a bench you picked for
|
|
125
164
|
price can resolve to a Pro/preview tier and trip a low `--max-cost` (exit 1) — or quietly cost
|
|
126
165
|
5-10× what you budgeted. Confirm what each seat actually resolves to before a budget bench.
|
|
166
|
+
- **`amicus models` shows what THIS machine resolves an alias to, not the shipped catalog.** It
|
|
167
|
+
merges local `config.json` overrides silently, so a CI runner or a teammate's machine — which
|
|
168
|
+
carries no user config — can seat a materially different model under the identical alias and
|
|
169
|
+
price. To know what actually ships, read the alias table in `src/utils/curated-models.js`
|
|
170
|
+
directly rather than trusting a local `amicus models` listing.
|
|
127
171
|
- **The chair cannot also hold a bench seat**, so a bench built from budget aliases cannot chair
|
|
128
172
|
itself with one of them — pick the chair from *outside* the bench list.
|
|
129
173
|
- **Stage-6 approvals:** write the proposed MODEL-NOTES diff to a run-folder file and put that path
|
|
@@ -199,6 +243,12 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
199
243
|
- **Asserts context-dependent facts (dates, "is this future?") without verifying** — and self-confirms them in adjudication. Cross-check any time-dependent claim it raises.
|
|
200
244
|
- A good calibration anchor in cross-review: confirms observational findings, disputes interpretive overreach. A repeatable role: the seat most likely to catch **confidence inflation** in an otherwise-correct argument (theoretical risk asserted as demonstrated mechanism, proxies used as pseudo-diagnostics).
|
|
201
245
|
- **When gpt disputes a specific numeric claim, weight that dispute heavily before a debate round erodes it** — it has been right and then talked out of the objection by a persuasive rebuttal (see the peer-consensus rule).
|
|
246
|
+
- **On long, dense, code-heavy artifacts it returns fast and skims — seat it for calibration, not
|
|
247
|
+
coverage, there.** Where it returns 25-32 findings on prose/resumes/business docs, on a dense
|
|
248
|
+
technical/code plan it has come in the fastest leg on the bench by a wide margin with only a
|
|
249
|
+
couple of findings against peers' 8 — both real, both confirmed, so this is shallow rather than
|
|
250
|
+
wrong; its lifetime confirm-rate held. Put a slower, deeper model on coverage and let gpt anchor
|
|
251
|
+
calibration on artifacts like this.
|
|
202
252
|
|
|
203
253
|
### Grok (`--model grok` → via OpenRouter)
|
|
204
254
|
- Very fast legs; credible judge and chair (rejected its own weak findings as chair; honest blind self-rank).
|
|
@@ -221,8 +271,15 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
221
271
|
- Its specificity is also its risk: it reaches for **exhaustive quantitative claims ("every",
|
|
222
272
|
"all", "none") that are directionally right and literally wrong** — and it has successfully
|
|
223
273
|
DEFENDED one in a debate round against a correct dispute. Verify its universal quantifiers
|
|
224
|
-
against a primary source (see the peer-consensus rule).
|
|
225
|
-
|
|
274
|
+
against a primary source (see the peer-consensus rule). It has also been observed doing the
|
|
275
|
+
opposite well: correctly scoping its own blocker-severity finding as *contingent on an unprobed
|
|
276
|
+
precondition* rather than asserting it outright — a genuine calibration improvement worth
|
|
277
|
+
crediting when it shows up.
|
|
278
|
+
- **Its time-to-first-token now sits directly on the `NO_OUTPUT_BACKSTOP` cliff, not comfortably
|
|
279
|
+
under it** — observed legs have run well past the 120s default, and even a doubled 180-240s
|
|
280
|
+
override has been killed by the slowest of them. Size the override from the *slowest observed
|
|
281
|
+
leg × 2* with real headroom (see the Global `NO_OUTPUT_BACKSTOP` rule); it still gates wave
|
|
282
|
+
wall-clock either way.
|
|
226
283
|
- Stalls on long agentic reads (poller "Incomplete" with only a preamble). Reserve for short-artifact work.
|
|
227
284
|
|
|
228
285
|
### Mistral (`--model mistral` → via OpenRouter)
|
|
@@ -271,8 +328,16 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
271
328
|
- Honest under pressure both ways: it refused to fabricate on repair attempts, and it has
|
|
272
329
|
withdrawn a contested finding cleanly in debate. An honest refusal still costs the seat — watch
|
|
273
330
|
`conformance` per seat, not just the finding count.
|
|
274
|
-
|
|
275
|
-
|
|
331
|
+
- **Cheap-seat pattern across repeat use: low finding count, high calibration value.** It has
|
|
332
|
+
repeatedly been the cheapest seat on its bench while contributing fewer findings by volume than
|
|
333
|
+
peers, but its findings have skewed toward internal-consistency catches (a claim contradicted by
|
|
334
|
+
an earlier section of the same artifact) rather than raw coverage — read them for that, not for
|
|
335
|
+
volume, before discounting a low count.
|
|
336
|
+
- **First use as chair was clean and notably good:** recognized when two surviving reviews had
|
|
337
|
+
split the labor rather than duplicated it and synthesized across that seam instead of flattening
|
|
338
|
+
it. A viable chair candidate alongside deepseek.
|
|
339
|
+
|
|
340
|
+
### Qwen (`--model qwen` → qwen3.8-max via OpenRouter; distinct from `qwen-coder`)
|
|
276
341
|
- Very large context (1M tokens per catalog). As a red-team substitute it has produced a thorough,
|
|
277
342
|
well-organized adversarial review with accurate, specific line citations and genuinely unique
|
|
278
343
|
catches that verified true against the source — weight its specific, cited claims heavily.
|
|
@@ -283,8 +348,12 @@ the peer-consensus≠evidence rule upstreamed from the field ledger; see changel
|
|
|
283
348
|
short-artifact work: a book-length single-file read succeeded on retry with the anti-narration
|
|
284
349
|
preamble, but multi-file packets have produced narration stubs twice in one run — with the
|
|
285
350
|
preamble present, under both Plan and Build. It narrates rather than refuses under Plan.
|
|
351
|
+
- **Inlining removes its multi-file-read failure mode entirely.** On a fully inlined briefing (no
|
|
352
|
+
agentic file reads) it has tied for most findings on the bench and ranked #2 by every judge —
|
|
353
|
+
the counterpart to the multi-file-read stalls above. Seat qwen for inlined verification-
|
|
354
|
+
scaffolding review; keep it off agentic multi-file reads.
|
|
286
355
|
|
|
287
|
-
### minimax (`--model minimax` → via OpenRouter)
|
|
356
|
+
### minimax (`--model minimax` → minimax-m2.7 via OpenRouter)
|
|
288
357
|
- Fast (~2 min review legs), cheap, `clean` findings-JSON conformance on debut.
|
|
289
358
|
- Took the **critic seat** brief exceptionally well: unanimously ranked #1 by its bench, full
|
|
290
359
|
coverage on a ground-truth test, zero padded findings under the anti-padding rule. A strong
|
|
@@ -410,3 +479,13 @@ This section keeps only per-model **qualitative quirks** and **structural-confor
|
|
|
410
479
|
gemini-3.1-pro), and gemini/deepseek/gpt/grok/kimi/Claude enrichments. Standing practice: each
|
|
411
480
|
release cherry-picks generalizable lessons per-section (docs/publishing.md release checklist),
|
|
412
481
|
never a bulk copy.
|
|
482
|
+
- **2026-08-25 (fold-back, both directions)** — Folded in from the field ledger: the
|
|
483
|
+
`NO_OUTPUT_BACKSTOP` no-first-token-deadline mechanism and sizing rule; `conformance` measures
|
|
484
|
+
format not content, with the credit-exhaustion attribution correction; the `amicus models`
|
|
485
|
+
local-vs-shipped alias-resolution caveat; the inlined-source-pack technique; the Stage-2
|
|
486
|
+
cost-parity rule; kimi's backstop-cliff and contingent-precondition calibration nuance; gpt's
|
|
487
|
+
technical-artifact shallowness and qwen's inlining rehabilitation; glm's calibration-over-volume
|
|
488
|
+
and chair-debut notes; minimax's pinned route id. Adopted back into the local ledger: the v4.7
|
|
489
|
+
`runStats` allowlist correction to the spend-tracking rule (the local copy's v4.6
|
|
490
|
+
judge-exclusion description was superseded). No client/engagement material, run identifiers, or
|
|
491
|
+
dollar figures carried over.
|