nexus-agents 8.58.2 → 8.58.3
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/dist/{chunk-CZN5JK7D.js → chunk-7DD6X5D7.js} +2 -2
- package/dist/{chunk-IIZKJP44.js → chunk-EJVVXHMZ.js} +7 -7
- package/dist/{chunk-CSYESWQ2.js → chunk-MEXE444I.js} +3 -3
- package/dist/{chunk-JE2TRDX7.js → chunk-TDBR47OD.js} +2 -2
- package/dist/{chunk-YF3YIHPZ.js → chunk-XKP6IEHD.js} +664 -592
- package/dist/chunk-XKP6IEHD.js.map +1 -0
- package/dist/{chunk-Y6MYVL5H.js → chunk-YG6MQYRD.js} +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +28 -19
- package/dist/cli.js.map +1 -1
- package/dist/{consensus-vote-PLLP7IJE.js → consensus-vote-PZEJL6KZ.js} +4 -4
- package/dist/{consensus-vote-types-ClG_UKij.d.ts → consensus-vote-types-DTUSdB9g.d.ts} +38 -0
- package/dist/{improvement-review-NAITF6LS.js → improvement-review-WMOCCVVF.js} +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -6
- package/dist/{setup-data-dir-AHUNLSRM.js → setup-data-dir-34C3UR7A.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-YF3YIHPZ.js.map +0 -1
- /package/dist/{chunk-CZN5JK7D.js.map → chunk-7DD6X5D7.js.map} +0 -0
- /package/dist/{chunk-IIZKJP44.js.map → chunk-EJVVXHMZ.js.map} +0 -0
- /package/dist/{chunk-CSYESWQ2.js.map → chunk-MEXE444I.js.map} +0 -0
- /package/dist/{chunk-JE2TRDX7.js.map → chunk-TDBR47OD.js.map} +0 -0
- /package/dist/{chunk-Y6MYVL5H.js.map → chunk-YG6MQYRD.js.map} +0 -0
- /package/dist/{consensus-vote-PLLP7IJE.js.map → consensus-vote-PZEJL6KZ.js.map} +0 -0
- /package/dist/{improvement-review-NAITF6LS.js.map → improvement-review-WMOCCVVF.js.map} +0 -0
- /package/dist/{setup-data-dir-AHUNLSRM.js.map → setup-data-dir-34C3UR7A.js.map} +0 -0
|
@@ -25,10 +25,10 @@ import {
|
|
|
25
25
|
toolSuccessStructured,
|
|
26
26
|
withProgressHeartbeat,
|
|
27
27
|
wrapToolWithTimeout
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-TDBR47OD.js";
|
|
29
29
|
import {
|
|
30
30
|
VERSION
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7DD6X5D7.js";
|
|
32
32
|
import {
|
|
33
33
|
UNRESOLVED_MODEL_ID,
|
|
34
34
|
assessPanelIndependence,
|
|
@@ -280,6 +280,463 @@ function reportPanelModelIndependence(modelIds, roleCount, phase, logger11) {
|
|
|
280
280
|
);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
// src/audit/vote-record.ts
|
|
284
|
+
import * as crypto from "node:crypto";
|
|
285
|
+
import { z as z2 } from "zod";
|
|
286
|
+
var VoteRecordDecisionSchema = z2.enum(["approved", "rejected", "no_quorum"]);
|
|
287
|
+
var VoteRecordOptionCoverageSchema = z2.object({
|
|
288
|
+
/** Every approving voter, selecting or not — the tally's denominator. */
|
|
289
|
+
approverCount: z2.number().int().nonnegative(),
|
|
290
|
+
/** Approvers whose selection matched a declared option. */
|
|
291
|
+
selectedCount: z2.number().int().nonnegative(),
|
|
292
|
+
/** Approvers with no usable selection; their choice is unmeasured. */
|
|
293
|
+
unattributedApprovals: z2.number().int().nonnegative()
|
|
294
|
+
}).strict();
|
|
295
|
+
var VoteRecordPanelCoverageSchema = z2.object({
|
|
296
|
+
/** Voters the panel asked for. */
|
|
297
|
+
requested: z2.number().int().nonnegative(),
|
|
298
|
+
/** Voters that returned a usable vote — the denominator of `voteCounts`. */
|
|
299
|
+
responded: z2.number().int().nonnegative(),
|
|
300
|
+
/** Voters that errored or timed out. */
|
|
301
|
+
errored: z2.number().int().nonnegative(),
|
|
302
|
+
/** The roles behind `errored`, in panel order. */
|
|
303
|
+
erroredRoles: z2.array(z2.string().min(1).max(100))
|
|
304
|
+
}).strict();
|
|
305
|
+
var MAX_VOTER_REASONING_CHARS = 2e4;
|
|
306
|
+
function clipForRecord(text) {
|
|
307
|
+
if (text.length <= MAX_VOTER_REASONING_CHARS) return { text };
|
|
308
|
+
return { text: text.slice(0, MAX_VOTER_REASONING_CHARS), truncated: true };
|
|
309
|
+
}
|
|
310
|
+
var RetriedFromRecordSchema = z2.object({
|
|
311
|
+
/** The first pass's `source`: errored, or answered without reading. */
|
|
312
|
+
source: z2.enum(["error", "unverifiable"]),
|
|
313
|
+
/** The first pass's `error` string, when it had one; already clipped. */
|
|
314
|
+
error: z2.string().max(MAX_VOTER_REASONING_CHARS).optional(),
|
|
315
|
+
/** True when `error` was clipped to {@link MAX_VOTER_REASONING_CHARS}. */
|
|
316
|
+
errorTruncated: z2.literal(true).optional()
|
|
317
|
+
}).strict();
|
|
318
|
+
var SeatFallbackRecordSchema = z2.object({
|
|
319
|
+
/** The CLI the seat was assigned to, bare (`claude`, not `cli-claude`). */
|
|
320
|
+
fromCli: z2.string().min(1).max(100),
|
|
321
|
+
/** The model the assigned adapter had detected, when it had one. */
|
|
322
|
+
fromModel: z2.string().min(1).max(200).optional(),
|
|
323
|
+
/** The adapter-layer class of the error that moved the seat. */
|
|
324
|
+
reason: z2.enum(["rate-limit", "capacity", "auth", "timeout", "sandbox", "unknown"])
|
|
325
|
+
}).strict();
|
|
326
|
+
var VoterSummarySchema = z2.object({
|
|
327
|
+
role: z2.string().min(1).max(100),
|
|
328
|
+
decision: z2.enum(["approve", "reject", "abstain"]),
|
|
329
|
+
confidence: z2.number().min(0).max(1),
|
|
330
|
+
/**
|
|
331
|
+
* The voter's stated grounds (#5373, schema 1.6).
|
|
332
|
+
*
|
|
333
|
+
* `generateVoteHash` already hashed `{role, decision, reasoning}` and then
|
|
334
|
+
* discarded the text, so the chain attested to a value it did not store and
|
|
335
|
+
* nobody could re-verify the hash without re-obtaining the reasoning. On
|
|
336
|
+
* #5228 a contrarian rejection was clipped mid-sentence in the terminal, the
|
|
337
|
+
* grounds were unrecoverable, and the same defect resurfaced a round later —
|
|
338
|
+
* one round of a 7-voter panel is 7-13 minutes of model time, and the
|
|
339
|
+
* objection was right both times.
|
|
340
|
+
*
|
|
341
|
+
* Stored whole rather than sampled. Warn-mode near-misses are emitted per
|
|
342
|
+
* TOOL CALL, thousands a day; votes are emitted per PANEL, dozens a day at
|
|
343
|
+
* most, so ~17 KB per vote is not the same growth problem.
|
|
344
|
+
*
|
|
345
|
+
* Absence is not an empty argument: an errored voter has no entry in
|
|
346
|
+
* `voters` at all (see `panelCoverage`), so `reasoning: ''` means a live
|
|
347
|
+
* voter returned nothing, which is itself a signal.
|
|
348
|
+
*/
|
|
349
|
+
reasoning: z2.string().max(MAX_VOTER_REASONING_CHARS).optional(),
|
|
350
|
+
/** True when `reasoning` was clipped to {@link MAX_VOTER_REASONING_CHARS}. */
|
|
351
|
+
reasoningTruncated: z2.literal(true).optional(),
|
|
352
|
+
/**
|
|
353
|
+
* True when this seat was recovered by the per-role retry (#6050, schema 1.7).
|
|
354
|
+
*
|
|
355
|
+
* `voter-retry.ts` has set `retried: true` on recovered results since it was
|
|
356
|
+
* written, and `vote-types.ts` states the reason: the flag is "what makes the
|
|
357
|
+
* recovery visible instead of indistinguishable from a clean first attempt".
|
|
358
|
+
* It had one producer and ZERO consumers — both summarizers and the record
|
|
359
|
+
* dropped it — so the field existed, was documented, and never reached
|
|
360
|
+
* anything that could act on it.
|
|
361
|
+
*
|
|
362
|
+
* A retried seat is weaker evidence than a first-pass one: the model was
|
|
363
|
+
* unavailable or timed out, and the recovery ran under different conditions.
|
|
364
|
+
* For a ratification vote on a governor-path change, "7 of 7 answered" and
|
|
365
|
+
* "6 answered, 1 recovered on retry" are different facts about the scrutiny
|
|
366
|
+
* the change received, and the record stated the first for both.
|
|
367
|
+
*
|
|
368
|
+
* `literal(true)` and optional, on the `reasoningTruncated` rule: a clean
|
|
369
|
+
* seat carries no key, so its entry re-hashes byte-identical and every
|
|
370
|
+
* historical record still verifies.
|
|
371
|
+
*/
|
|
372
|
+
retried: z2.literal(true).optional(),
|
|
373
|
+
/**
|
|
374
|
+
* Registry model id the seat ran on (#6091, schema 1.8).
|
|
375
|
+
*
|
|
376
|
+
* The seat→model mapping is round-robin over the available CLIs plus
|
|
377
|
+
* `NEXUS_VOTER_MODEL_<ROLE>` pins, so it is not recoverable from the
|
|
378
|
+
* ledger after the fact: 1844 voter entries across 278 records carried no
|
|
379
|
+
* model. "When an agent's output becomes evidence, its provenance travels
|
|
380
|
+
* with it" — a tally that says `scope_steward: approve` without saying
|
|
381
|
+
* which model answered cannot be audited for the #6068 failure class.
|
|
382
|
+
* Present-only, on the `retried` rule; absent when the result carried no
|
|
383
|
+
* model or only the pending-detection placeholder.
|
|
384
|
+
*/
|
|
385
|
+
model: z2.string().min(1).max(200).optional(),
|
|
386
|
+
/**
|
|
387
|
+
* True when the seat could not read the artifact (#6094, schema 1.8).
|
|
388
|
+
*
|
|
389
|
+
* `decision` is `abstain` for such a seat, so without this flag the record
|
|
390
|
+
* could not tell "read it and abstained" from "never saw it" — and one
|
|
391
|
+
* ledger entry (`vote-1789058788915-6lfrbuq`) shows a seat that APPROVED
|
|
392
|
+
* on the proposal text after failing to read. `literal(true)` and
|
|
393
|
+
* present-only, so every pre-1.8 entry re-hashes byte-identical.
|
|
394
|
+
*/
|
|
395
|
+
unverifiable: z2.literal(true).optional(),
|
|
396
|
+
/**
|
|
397
|
+
* The CLI the round-robin or `NEXUS_VOTER_MODEL_<ROLE>` pin chose for the
|
|
398
|
+
* seat, as a bare name (#6115, schema 1.9). `model` says where a seat
|
|
399
|
+
* answered; this says where it was MEANT to. Three consecutive 7-seat
|
|
400
|
+
* ratification panels on 2026-09-13 answered every seat on one gemini
|
|
401
|
+
* model while the assignment was three claude, two codex, two gemini,
|
|
402
|
+
* and the 1.8 record showed seven identical `model` values with nothing
|
|
403
|
+
* that said five of them were substitutes. Present-only, on the `model`
|
|
404
|
+
* rule; absent on a result built outside the panel launcher.
|
|
405
|
+
*/
|
|
406
|
+
assignedCli: z2.string().min(1).max(100).optional(),
|
|
407
|
+
/**
|
|
408
|
+
* Present only when the seat answered on a different CLI or model than
|
|
409
|
+
* assigned (#6115, schema 1.9): the exact `SeatFallback` shape the live
|
|
410
|
+
* result carries, rebuilt field-by-field. `fromCli` is the assigned CLI,
|
|
411
|
+
* `fromModel` the model it had detected (absent when it never did — the
|
|
412
|
+
* pending-detection placeholder is not a model), `reason` the adapter
|
|
413
|
+
* layer's error class. The vocabulary is compile-checked against the live
|
|
414
|
+
* `FallbackReason` in vote-record.test.ts; a class one side learns alone
|
|
415
|
+
* would either refuse every record that carries it (#6054) or accept one
|
|
416
|
+
* no producer can write.
|
|
417
|
+
*/
|
|
418
|
+
fallback: SeatFallbackRecordSchema.optional(),
|
|
419
|
+
/**
|
|
420
|
+
* Present only when the per-role retry REPLACED this seat (#6246, schema
|
|
421
|
+
* 1.12): the first pass's source and, when it had one, its clipped error
|
|
422
|
+
* string. `retried` (1.7) says a recovery happened; this says what it
|
|
423
|
+
* recovered from. On the #6241 panel a seat recorded `retried: true,
|
|
424
|
+
* unverifiable: true` after a first pass that errored on two response-parse
|
|
425
|
+
* failures, and nothing in the record joined the two — a reader of the
|
|
426
|
+
* ledger and the log together concluded the parse errors had been
|
|
427
|
+
* misclassified (#6244). Rebuilt field-by-field on the `fallback` rule; the
|
|
428
|
+
* `source` vocabulary is compile-checked against the live `RetriedFrom`.
|
|
429
|
+
*/
|
|
430
|
+
retriedFrom: RetriedFromRecordSchema.optional()
|
|
431
|
+
}).strict();
|
|
432
|
+
function defineVoterKeys(keys) {
|
|
433
|
+
return keys;
|
|
434
|
+
}
|
|
435
|
+
var VOTER_SUMMARY_KEYS = defineVoterKeys([
|
|
436
|
+
"role",
|
|
437
|
+
"decision",
|
|
438
|
+
"confidence",
|
|
439
|
+
"reasoning",
|
|
440
|
+
"reasoningTruncated",
|
|
441
|
+
"retried",
|
|
442
|
+
// 1.8 (#6091, #6094): appended after `retried`, present-only, so a 1.7
|
|
443
|
+
// entry projects byte-identically.
|
|
444
|
+
"model",
|
|
445
|
+
"unverifiable",
|
|
446
|
+
// 1.9 (#6115): appended after `unverifiable`, present-only, so a 1.8 entry
|
|
447
|
+
// projects byte-identically. `fallback` is the first nested voter field;
|
|
448
|
+
// `projectSeatFallback` rebuilds it in its own canonical order.
|
|
449
|
+
"assignedCli",
|
|
450
|
+
"fallback",
|
|
451
|
+
// 1.12 (#6246): appended after `fallback`, present-only, so a 1.11 entry
|
|
452
|
+
// projects byte-identically. Nested; `projectRetriedFrom` rebuilds it in
|
|
453
|
+
// its own canonical order.
|
|
454
|
+
"retriedFrom"
|
|
455
|
+
]);
|
|
456
|
+
function noUnprojectedKeys(rest) {
|
|
457
|
+
return rest;
|
|
458
|
+
}
|
|
459
|
+
function projectSeatFallback(f) {
|
|
460
|
+
const { fromCli, fromModel, reason, ...rest } = f;
|
|
461
|
+
noUnprojectedKeys(rest);
|
|
462
|
+
return {
|
|
463
|
+
fromCli,
|
|
464
|
+
...fromModel !== void 0 ? { fromModel } : {},
|
|
465
|
+
reason
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function projectRetriedFrom(r) {
|
|
469
|
+
const { source, error, errorTruncated, ...rest } = r;
|
|
470
|
+
noUnprojectedKeys(rest);
|
|
471
|
+
return {
|
|
472
|
+
source,
|
|
473
|
+
...error !== void 0 ? { error } : {},
|
|
474
|
+
...errorTruncated !== void 0 ? { errorTruncated } : {}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function projectVoterField(v, key) {
|
|
478
|
+
if (key === "fallback") {
|
|
479
|
+
return v.fallback === void 0 ? void 0 : projectSeatFallback(v.fallback);
|
|
480
|
+
}
|
|
481
|
+
if (key === "retriedFrom") {
|
|
482
|
+
return v.retriedFrom === void 0 ? void 0 : projectRetriedFrom(v.retriedFrom);
|
|
483
|
+
}
|
|
484
|
+
return v[key];
|
|
485
|
+
}
|
|
486
|
+
function projectVoterSummary(v) {
|
|
487
|
+
const out = {};
|
|
488
|
+
for (const key of VOTER_SUMMARY_KEYS) {
|
|
489
|
+
const value = projectVoterField(v, key);
|
|
490
|
+
if (value !== void 0) out[key] = value;
|
|
491
|
+
}
|
|
492
|
+
return out;
|
|
493
|
+
}
|
|
494
|
+
var VoteRecordCountsSchema = z2.object({
|
|
495
|
+
approve: z2.number().int().nonnegative(),
|
|
496
|
+
reject: z2.number().int().nonnegative(),
|
|
497
|
+
abstain: z2.number().int().nonnegative(),
|
|
498
|
+
total: z2.number().int().nonnegative()
|
|
499
|
+
}).strict();
|
|
500
|
+
var VoteRecordOptionCountSchema = z2.object({
|
|
501
|
+
/** The option label as declared in the proposal (e.g. 'A'). */
|
|
502
|
+
option: z2.string().min(1),
|
|
503
|
+
/** How many voters selected it. */
|
|
504
|
+
count: z2.number().int().nonnegative()
|
|
505
|
+
}).strict();
|
|
506
|
+
var GIT_SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
507
|
+
var VoteRecordPrBindingSchema = z2.object({
|
|
508
|
+
pr: z2.number().int().positive(),
|
|
509
|
+
headSha: z2.string().regex(GIT_SHA_PATTERN, "expected a full 40-hex lowercase commit sha")
|
|
510
|
+
}).strict();
|
|
511
|
+
function projectPrBinding(b) {
|
|
512
|
+
return { pr: b.pr, headSha: b.headSha };
|
|
513
|
+
}
|
|
514
|
+
var VoteRecordErrorPolicySchema = z2.enum([
|
|
515
|
+
"reduce_denominator",
|
|
516
|
+
"count_as_abstain",
|
|
517
|
+
"fail_closed",
|
|
518
|
+
"absolute_quorum"
|
|
519
|
+
]);
|
|
520
|
+
var VoteRecordSchema = z2.object({
|
|
521
|
+
/**
|
|
522
|
+
* Schema version. '1.1' marked the chain→record-set+sequence model (#3927);
|
|
523
|
+
* '1.2' adds the optional `ratifies` subject-binding field (#3927 item 1);
|
|
524
|
+
* '1.10' adds the optional `ratifiesPr` PR-binding (#5130); '1.11' the
|
|
525
|
+
* optional `errorPolicy` (#6211); '1.12' the per-voter `retriedFrom`
|
|
526
|
+
* (#6246). Every tier is accepted — a 1.1 record
|
|
527
|
+
* (no `ratifies`) verifies unchanged because each optional is folded into
|
|
528
|
+
* the self-hash ONLY when present (see {@link computeVoteRecordHash}).
|
|
529
|
+
* Tiers are labels, not ordered numbers: '1.10' follows '1.9' by
|
|
530
|
+
* convention only and nothing compares them.
|
|
531
|
+
*/
|
|
532
|
+
version: z2.enum([
|
|
533
|
+
"1.1",
|
|
534
|
+
"1.2",
|
|
535
|
+
"1.3",
|
|
536
|
+
"1.4",
|
|
537
|
+
"1.5",
|
|
538
|
+
"1.6",
|
|
539
|
+
"1.7",
|
|
540
|
+
"1.8",
|
|
541
|
+
"1.9",
|
|
542
|
+
"1.10",
|
|
543
|
+
"1.11",
|
|
544
|
+
"1.12"
|
|
545
|
+
]),
|
|
546
|
+
/** Unique record id (also usable as a `ratificationVoteRef`). */
|
|
547
|
+
id: z2.string().min(1),
|
|
548
|
+
/**
|
|
549
|
+
* Monotonic sequence number (integer ≥ 0). Assigned as (max existing
|
|
550
|
+
* sequence)+1 at write time. Sorted, the set of sequences must cover
|
|
551
|
+
* 0..maxSeq with no gap (omission detection); DUPLICATE sequences are a
|
|
552
|
+
* benign concurrent-fork signal, not tampering.
|
|
553
|
+
*/
|
|
554
|
+
sequence: z2.number().int().nonnegative(),
|
|
555
|
+
/** ISO-8601 timestamp the vote was recorded. */
|
|
556
|
+
recordedAt: z2.string().min(1),
|
|
557
|
+
/**
|
|
558
|
+
* SHA-256 of the FULL proposal text. The proposal itself is truncated for
|
|
559
|
+
* the human record (`proposal`), but the hash binds the record to the exact
|
|
560
|
+
* proposal voted on — a later edit of `proposal` that changes meaning is
|
|
561
|
+
* detectable by recomputing this hash from the original.
|
|
562
|
+
*/
|
|
563
|
+
proposalHash: z2.string().length(64),
|
|
564
|
+
/** Truncated proposal text for the human/reviewer record. */
|
|
565
|
+
proposal: z2.string(),
|
|
566
|
+
/** The voting strategy used. */
|
|
567
|
+
strategy: z2.enum([
|
|
568
|
+
"simple_majority",
|
|
569
|
+
"supermajority",
|
|
570
|
+
"unanimous",
|
|
571
|
+
"higher_order",
|
|
572
|
+
"opinion_wise",
|
|
573
|
+
"proof_of_learning"
|
|
574
|
+
]),
|
|
575
|
+
/** The resolved decision. */
|
|
576
|
+
decision: VoteRecordDecisionSchema,
|
|
577
|
+
/** Approval fraction as a percentage (0-100). */
|
|
578
|
+
approvalPercentage: z2.number().min(0).max(100),
|
|
579
|
+
/** Vote-count breakdown. */
|
|
580
|
+
voteCounts: VoteRecordCountsSchema,
|
|
581
|
+
/** Per-voter {role, decision, confidence} summary. */
|
|
582
|
+
voters: z2.array(VoterSummarySchema),
|
|
583
|
+
/** Optional correlation/decision id linking to the cost rollup / trace. */
|
|
584
|
+
correlationId: z2.string().min(1).optional(),
|
|
585
|
+
/**
|
|
586
|
+
* Per-option distribution for a multi-option proposal (#4452, schema 1.3).
|
|
587
|
+
*
|
|
588
|
+
* Present only when the vote declared `options`. Absent on an ordinary
|
|
589
|
+
* yes/no vote, and absent on every 1.1/1.2 record — which is why it is
|
|
590
|
+
* folded into the self-hash ONLY when present (see
|
|
591
|
+
* {@link computeVoteRecordHash}), exactly as `ratifies` was.
|
|
592
|
+
*
|
|
593
|
+
* Without this, a 6-1 or 5-2 option split is recorded as `approve: 7` and
|
|
594
|
+
* reads as unanimous. Threshold semantics invert too: `unanimous` becomes
|
|
595
|
+
* the EASIEST bar to clear, because every engaged voter approves while
|
|
596
|
+
* choosing different things.
|
|
597
|
+
*/
|
|
598
|
+
// No `.min(1)` (#6049). The rule this field's own docstring states is
|
|
599
|
+
// "present only when the vote declared `options`" -- keyed on DECLARATION,
|
|
600
|
+
// not on whether anyone picked one. `.min(1)` encoded the older, narrower
|
|
601
|
+
// rule, so once the builder started emitting `[]` for "declared, nothing
|
|
602
|
+
// attributable" the record appended fine and then failed to parse on read:
|
|
603
|
+
// `too_small, minimum 1`. The write path does not validate, so the line
|
|
604
|
+
// landed in the ledger and `parseVoteRecordsText` silently dropped it --
|
|
605
|
+
// the case most worth auditing became the one no reader could see.
|
|
606
|
+
//
|
|
607
|
+
// Relaxing is a pure widening: no previously-valid record changes meaning
|
|
608
|
+
// or hash, and no persisted record can currently hold an empty tally.
|
|
609
|
+
optionTally: z2.array(VoteRecordOptionCountSchema).optional(),
|
|
610
|
+
/**
|
|
611
|
+
* Selection coverage for a multi-option vote (#4472, schema 1.4).
|
|
612
|
+
*
|
|
613
|
+
* `optionTally` alone cannot distinguish dissent from absence: `4 pick X
|
|
614
|
+
* + 3 unreadable` and a genuine 4/3 split both leave a leading share of
|
|
615
|
+
* 57%. Recording how many approvers produced no usable selection is what
|
|
616
|
+
* makes a partial measurement legible AS partial — the condition every
|
|
617
|
+
* voter attached to adopting the "credit no option" rule.
|
|
618
|
+
*/
|
|
619
|
+
optionCoverage: VoteRecordOptionCoverageSchema.optional(),
|
|
620
|
+
/**
|
|
621
|
+
* Panel coverage (#5738, schema 1.5). Present only when at least one
|
|
622
|
+
* requested voter failed to return a vote; absent keeps a clean panel on
|
|
623
|
+
* the pre-1.5 projection, so every historical record still verifies.
|
|
624
|
+
*/
|
|
625
|
+
panelCoverage: VoteRecordPanelCoverageSchema.optional(),
|
|
626
|
+
/**
|
|
627
|
+
* The loop/strategy subject this vote RATIFIES (#3927 item 1). Present only on
|
|
628
|
+
* a ratification vote; set at vote time and bound into the self-hash (so it is
|
|
629
|
+
* tamper-evident). The authority-tier promotion gate
|
|
630
|
+
* (`scripts/check-authority-tier-drift.ts`) resolves a transition's
|
|
631
|
+
* `ratificationVoteRef` to a record and requires `ratifies === transition.subject`
|
|
632
|
+
* (with `decision === 'approved'` and `strategy === 'higher_order'`). Absent on
|
|
633
|
+
* an ordinary (non-ratification) vote; a 1.1 record never carries it.
|
|
634
|
+
*/
|
|
635
|
+
ratifies: z2.string().min(1).optional(),
|
|
636
|
+
/**
|
|
637
|
+
* The PR this vote RATIFIES, bound to the head the panel saw (#5130 step 1,
|
|
638
|
+
* schema 1.10; panel decision Q1 option A, 5 of 6). Present only on a
|
|
639
|
+
* governor-path ratification vote; hash-covered like `ratifies`, so a
|
|
640
|
+
* record cannot be repointed at a different PR or a later push without
|
|
641
|
+
* breaking its hash. The governor gate (step 2, #5779/#5131) resolves the
|
|
642
|
+
* record from the committed ledger and requires `pr` to be the PR under
|
|
643
|
+
* review and `headSha` to be its head (or `head^` when `head` touches only
|
|
644
|
+
* the ledger — the caller-commits tip). Absent on every pre-1.10 record.
|
|
645
|
+
*
|
|
646
|
+
* A SEPARATE key rather than a structured encoding of `ratifies`: that
|
|
647
|
+
* field is the authority-ladder SUBJECT, compared by string equality to
|
|
648
|
+
* `transition.subject` and grouped by `conflictingRatifiedSubjects`; no
|
|
649
|
+
* resolver parses it. Overloading it would give one field two consumers
|
|
650
|
+
* with two equality rules. The two may coexist on one record.
|
|
651
|
+
*/
|
|
652
|
+
ratifiesPr: VoteRecordPrBindingSchema.optional(),
|
|
653
|
+
/**
|
|
654
|
+
* The error policy the panel actually ran under (#6211, schema 1.11): the
|
|
655
|
+
* EFFECTIVE policy after `consensus_vote` applied the per-strategy default
|
|
656
|
+
* (`input.errorPolicy ?? getDefaultErrorPolicy(strategy)`), not the raw
|
|
657
|
+
* input the caller may have omitted. Hash-covered, present-only.
|
|
658
|
+
*
|
|
659
|
+
* Before this tier the policy reached the RESPONSE only, so the governor
|
|
660
|
+
* ledger gate (`scripts/governor-ledger-evidence.ts`) could not say which
|
|
661
|
+
* policy a whole-panel ratification was configured with: under
|
|
662
|
+
* `absolute_quorum` an errored seat voids the vote, so the one
|
|
663
|
+
* ledger-observable trace of a different policy was an approved record
|
|
664
|
+
* with `panelCoverage.errored > 0` — and a whole panel under
|
|
665
|
+
* `reduce_denominator` looked exactly like one under `absolute_quorum`.
|
|
666
|
+
* The gate's `wrong-error-policy` verdict reads this field; a record
|
|
667
|
+
* without it (every pre-1.11 record) keeps the panel-coverage inference
|
|
668
|
+
* and is reported as `errorPolicy: unrecorded`.
|
|
669
|
+
*/
|
|
670
|
+
errorPolicy: VoteRecordErrorPolicySchema.optional(),
|
|
671
|
+
/**
|
|
672
|
+
* ADVISORY hash of the tip record at write time (absent for the first).
|
|
673
|
+
* Retained for audit texture but NOT covered by `hash` and NOT verified —
|
|
674
|
+
* the record-set model is position-independent (#3927).
|
|
675
|
+
*/
|
|
676
|
+
previousHash: z2.string().length(64).optional(),
|
|
677
|
+
/** SHA-256 over every field above EXCEPT `previousHash` (and except `hash`). */
|
|
678
|
+
hash: z2.string().length(64)
|
|
679
|
+
}).strict();
|
|
680
|
+
function foldOptionalFields(base, payload) {
|
|
681
|
+
const withTally = payload.optionTally !== void 0 ? {
|
|
682
|
+
...base,
|
|
683
|
+
optionTally: payload.optionTally.map((o) => ({ option: o.option, count: o.count }))
|
|
684
|
+
} : base;
|
|
685
|
+
const withCoverage = payload.optionCoverage !== void 0 ? {
|
|
686
|
+
...withTally,
|
|
687
|
+
optionCoverage: {
|
|
688
|
+
approverCount: payload.optionCoverage.approverCount,
|
|
689
|
+
selectedCount: payload.optionCoverage.selectedCount,
|
|
690
|
+
unattributedApprovals: payload.optionCoverage.unattributedApprovals
|
|
691
|
+
}
|
|
692
|
+
} : withTally;
|
|
693
|
+
const withPanel = payload.panelCoverage !== void 0 ? {
|
|
694
|
+
...withCoverage,
|
|
695
|
+
panelCoverage: {
|
|
696
|
+
requested: payload.panelCoverage.requested,
|
|
697
|
+
responded: payload.panelCoverage.responded,
|
|
698
|
+
errored: payload.panelCoverage.errored,
|
|
699
|
+
erroredRoles: [...payload.panelCoverage.erroredRoles]
|
|
700
|
+
}
|
|
701
|
+
} : withCoverage;
|
|
702
|
+
const withRatifies = payload.ratifies !== void 0 ? { ...withPanel, ratifies: payload.ratifies } : withPanel;
|
|
703
|
+
const withPrBinding = payload.ratifiesPr !== void 0 ? { ...withRatifies, ratifiesPr: projectPrBinding(payload.ratifiesPr) } : withRatifies;
|
|
704
|
+
return payload.errorPolicy !== void 0 ? { ...withPrBinding, errorPolicy: payload.errorPolicy } : withPrBinding;
|
|
705
|
+
}
|
|
706
|
+
function computeVoteRecordHash(payload) {
|
|
707
|
+
const base = {
|
|
708
|
+
version: payload.version,
|
|
709
|
+
id: payload.id,
|
|
710
|
+
sequence: payload.sequence,
|
|
711
|
+
recordedAt: payload.recordedAt,
|
|
712
|
+
proposalHash: payload.proposalHash,
|
|
713
|
+
proposal: payload.proposal,
|
|
714
|
+
strategy: payload.strategy,
|
|
715
|
+
decision: payload.decision,
|
|
716
|
+
approvalPercentage: payload.approvalPercentage,
|
|
717
|
+
// Rebuild voteCounts in schema order (approve, reject, abstain, total) so the
|
|
718
|
+
// hash does not depend on how the nested object's keys were ordered (#3962).
|
|
719
|
+
voteCounts: {
|
|
720
|
+
approve: payload.voteCounts.approve,
|
|
721
|
+
reject: payload.voteCounts.reject,
|
|
722
|
+
abstain: payload.voteCounts.abstain,
|
|
723
|
+
total: payload.voteCounts.total
|
|
724
|
+
},
|
|
725
|
+
// `reasoning` / `reasoningTruncated` (#5373, schema 1.6) are appended per
|
|
726
|
+
// entry ONLY when present, on the same rule as the record-level optional
|
|
727
|
+
// fields: a pre-1.6 voter entry re-hashes byte-identical, so every
|
|
728
|
+
// historical record still verifies, while editing or removing a stored
|
|
729
|
+
// reasoning flips the hash.
|
|
730
|
+
voters: payload.voters.map(projectVoterSummary),
|
|
731
|
+
correlationId: payload.correlationId ?? null
|
|
732
|
+
};
|
|
733
|
+
const canonical = JSON.stringify(foldOptionalFields(base, payload));
|
|
734
|
+
return crypto.createHash("sha256").update(canonical).digest("hex");
|
|
735
|
+
}
|
|
736
|
+
function hashProposal(proposal) {
|
|
737
|
+
return crypto.createHash("sha256").update(proposal).digest("hex");
|
|
738
|
+
}
|
|
739
|
+
|
|
283
740
|
// src/cli/voter-unverifiable.ts
|
|
284
741
|
var UNVERIFIABLE_STDERR_RE = /\bbwrap: |RTM_NEWADDR|\bsandbox\b[^\n]*\b(?:denied|failed|not permitted)\b/i;
|
|
285
742
|
var UNVERIFIABLE_REASONING_RE = /\bbwrap: |RTM_NEWADDR|\b(?:repository|source|shell) (?:reads?|access|execution|inspection) failed\b|\bcould not (?:read|inspect) the artifact\b/i;
|
|
@@ -317,6 +774,13 @@ function isAbsentSeat(v) {
|
|
|
317
774
|
}
|
|
318
775
|
|
|
319
776
|
// src/cli/voter-retry.ts
|
|
777
|
+
var CONTROL_CHARS_RE = /[\x00-\x1f\x7f-\x9f]/g;
|
|
778
|
+
function retriedFromOf(first) {
|
|
779
|
+
const source = first.source === "unverifiable" ? "unverifiable" : "error";
|
|
780
|
+
if (first.error === void 0) return { source };
|
|
781
|
+
const { text, truncated } = clipForRecord(first.error.replace(CONTROL_CHARS_RE, " "));
|
|
782
|
+
return { source, error: text, ...truncated === true ? { errorTruncated: true } : {} };
|
|
783
|
+
}
|
|
320
784
|
var DEFAULT_ERRORED_ROLE_BACKOFF_MS = 3e3;
|
|
321
785
|
async function retryErroredRoles(first, relaunch, logger11, backoffMs) {
|
|
322
786
|
const erroredRoles = first.filter(isAbsentSeat).map((v) => v.role);
|
|
@@ -328,10 +792,17 @@ async function retryErroredRoles(first, relaunch, logger11, backoffMs) {
|
|
|
328
792
|
});
|
|
329
793
|
if (backoffMs > 0) await sleep(backoffMs);
|
|
330
794
|
const retriedResults = await relaunch(erroredRoles);
|
|
795
|
+
const firstByRole = new Map(first.map((v) => [v.role, v]));
|
|
331
796
|
const recovered = /* @__PURE__ */ new Map();
|
|
332
797
|
for (const r of retriedResults) {
|
|
333
798
|
if (r.source === "error") continue;
|
|
334
|
-
|
|
799
|
+
const prior = firstByRole.get(r.role);
|
|
800
|
+
const from = prior !== void 0 && isAbsentSeat(prior) ? retriedFromOf(prior) : void 0;
|
|
801
|
+
recovered.set(r.role, {
|
|
802
|
+
...r,
|
|
803
|
+
retried: true,
|
|
804
|
+
...from !== void 0 ? { retriedFrom: from } : {}
|
|
805
|
+
});
|
|
335
806
|
}
|
|
336
807
|
if (recovered.size === 0) {
|
|
337
808
|
logger11.warn("Per-role retry recovered no voter \u2014 the panel stays degraded", { erroredRoles });
|
|
@@ -666,7 +1137,7 @@ var SIMULATED_VOTE_REASONING = {
|
|
|
666
1137
|
};
|
|
667
1138
|
|
|
668
1139
|
// src/cli/voter-response.ts
|
|
669
|
-
import { z as
|
|
1140
|
+
import { z as z3 } from "zod";
|
|
670
1141
|
|
|
671
1142
|
// src/consensus/option-tally.ts
|
|
672
1143
|
var SUPERMAJORITY_SHARE = 2 / 3;
|
|
@@ -726,26 +1197,26 @@ var SyntheticVoteError = class extends Error {
|
|
|
726
1197
|
}
|
|
727
1198
|
rawOutput;
|
|
728
1199
|
};
|
|
729
|
-
var RawFindingSchema =
|
|
730
|
-
summary:
|
|
731
|
-
location:
|
|
732
|
-
severity:
|
|
733
|
-
gate:
|
|
734
|
-
reread_cited_line:
|
|
735
|
-
traced_call_path:
|
|
736
|
-
named_assertion:
|
|
737
|
-
ruled_out_language_non_issue:
|
|
1200
|
+
var RawFindingSchema = z3.object({
|
|
1201
|
+
summary: z3.string().min(1).max(500).describe("One-line summary of the issue"),
|
|
1202
|
+
location: z3.string().min(1).max(200).describe("path/file.ext:line"),
|
|
1203
|
+
severity: z3.enum(["critical", "high", "medium", "low"]).default("medium"),
|
|
1204
|
+
gate: z3.object({
|
|
1205
|
+
reread_cited_line: z3.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
1206
|
+
traced_call_path: z3.enum(["passed", "failed", "skipped"]).default("skipped"),
|
|
1207
|
+
named_assertion: z3.string().default("").describe("Concrete failing assertion \u2014 substantive, not a rubber-stamp word"),
|
|
1208
|
+
ruled_out_language_non_issue: z3.enum(["passed", "failed", "skipped"]).default("skipped")
|
|
738
1209
|
}),
|
|
739
|
-
claim:
|
|
1210
|
+
claim: z3.string().min(1).max(2e3).describe("What is wrong and why it justifies blocking")
|
|
740
1211
|
});
|
|
741
|
-
var VoteResponseSchema =
|
|
742
|
-
decision:
|
|
743
|
-
reasoning:
|
|
744
|
-
confidence:
|
|
745
|
-
conditions:
|
|
1212
|
+
var VoteResponseSchema = z3.object({
|
|
1213
|
+
decision: z3.enum(["approve", "reject", "abstain"]).describe("Your vote decision"),
|
|
1214
|
+
reasoning: z3.string().min(10).max(4e3).describe("Explanation for your vote (10-4000 chars)"),
|
|
1215
|
+
confidence: z3.number().min(0).max(1).describe("Confidence level 0-1"),
|
|
1216
|
+
conditions: z3.array(z3.string()).optional().describe("Optional conditions for approval"),
|
|
746
1217
|
/** Structured rejection categories for reject→refine→re-vote loops (Issue #1213). */
|
|
747
|
-
rejectionCategories:
|
|
748
|
-
|
|
1218
|
+
rejectionCategories: z3.array(
|
|
1219
|
+
z3.enum([
|
|
749
1220
|
"YAGNI",
|
|
750
1221
|
"DRY_VIOLATION",
|
|
751
1222
|
"OVER_ENGINEERING",
|
|
@@ -757,11 +1228,11 @@ var VoteResponseSchema = z2.object({
|
|
|
757
1228
|
).optional().describe("Rejection reason categories when decision is reject"),
|
|
758
1229
|
/** Top-level structured findings for PR-review mode (#2245 v4 follow-up).
|
|
759
1230
|
* Replaces the YAML-in-reasoning encoding that proved lossy. */
|
|
760
|
-
findings:
|
|
1231
|
+
findings: z3.array(RawFindingSchema).optional().describe("Structured findings (PR review only)"),
|
|
761
1232
|
/** Chosen option when the proposal declared `options` (#4472). Validated
|
|
762
1233
|
* against the declared list by the caller; an unmatched value is dropped
|
|
763
1234
|
* rather than recorded, so a parse miss surfaces as absent. */
|
|
764
|
-
selectedOption:
|
|
1235
|
+
selectedOption: z3.string().optional().describe("Which declared option you choose")
|
|
765
1236
|
});
|
|
766
1237
|
var VOTE_JSON_SCHEMA = {
|
|
767
1238
|
type: "object",
|
|
@@ -1653,9 +2124,9 @@ async function collectRealVotes(options) {
|
|
|
1653
2124
|
// src/cli/voter-project.ts
|
|
1654
2125
|
import { readFileSync, statSync } from "node:fs";
|
|
1655
2126
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
1656
|
-
import { z as
|
|
2127
|
+
import { z as z4 } from "zod";
|
|
1657
2128
|
var VOTER_PROJECT_PATTERN = /^[A-Za-z0-9._/@-]{1,200}$/;
|
|
1658
|
-
var VoterProjectInputSchema =
|
|
2129
|
+
var VoterProjectInputSchema = z4.string().regex(VOTER_PROJECT_PATTERN).optional().describe(
|
|
1659
2130
|
"The project the panel is judging (#6110), e.g. `acme/widgets` \u2014 it replaces `nexus-agents` in every voter's system prompt, so a consuming repository is not judged against this one's mission and governance files. When omitted the name is DERIVED from the server's working directory (the `origin` remote as `owner/repo`, else the nearest `package.json` name) and falls back to `nexus-agents`; the response discloses which on `project.source`. Letters, digits and `._/@-` only, at most 200 characters."
|
|
1660
2131
|
);
|
|
1661
2132
|
var MAX_ANCESTOR_DEPTH = 64;
|
|
@@ -1821,40 +2292,40 @@ function describeVeto(verdict) {
|
|
|
1821
2292
|
}
|
|
1822
2293
|
|
|
1823
2294
|
// src/consensus/types-voting-protocol.ts
|
|
1824
|
-
import { z as
|
|
2295
|
+
import { z as z5 } from "zod";
|
|
1825
2296
|
var VOTING_ROUND_DEFAULT_TIMEOUT_MS = OPERATION_CLASSES["multi-llm-panel"].guardMs;
|
|
1826
|
-
var VotingRoundPhaseSchema =
|
|
1827
|
-
var VotingRoundStatusSchema =
|
|
2297
|
+
var VotingRoundPhaseSchema = z5.enum(["analysis", "deliberation", "consensus"]);
|
|
2298
|
+
var VotingRoundStatusSchema = z5.enum([
|
|
1828
2299
|
"pending",
|
|
1829
2300
|
"in_progress",
|
|
1830
2301
|
"awaiting_votes",
|
|
1831
2302
|
"completed",
|
|
1832
2303
|
"aborted"
|
|
1833
2304
|
]);
|
|
1834
|
-
var AgentFindingSchema =
|
|
1835
|
-
agentId:
|
|
1836
|
-
category:
|
|
1837
|
-
severity:
|
|
1838
|
-
description:
|
|
1839
|
-
location:
|
|
1840
|
-
suggestion:
|
|
1841
|
-
confidence:
|
|
1842
|
-
timestamp:
|
|
2305
|
+
var AgentFindingSchema = z5.object({
|
|
2306
|
+
agentId: z5.string(),
|
|
2307
|
+
category: z5.enum(["bug", "security", "performance", "style", "design", "documentation", "other"]),
|
|
2308
|
+
severity: z5.enum(["critical", "major", "minor", "suggestion"]),
|
|
2309
|
+
description: z5.string().min(1),
|
|
2310
|
+
location: z5.string().optional().describe("File path and line range if applicable"),
|
|
2311
|
+
suggestion: z5.string().optional().describe("Recommended fix"),
|
|
2312
|
+
confidence: z5.number().min(0).max(1),
|
|
2313
|
+
timestamp: z5.iso.datetime().optional()
|
|
1843
2314
|
});
|
|
1844
|
-
var FindingVoteSchema =
|
|
1845
|
-
agentId:
|
|
1846
|
-
findingId:
|
|
1847
|
-
agree:
|
|
1848
|
-
reasoning:
|
|
1849
|
-
amendedSeverity:
|
|
2315
|
+
var FindingVoteSchema = z5.object({
|
|
2316
|
+
agentId: z5.string(),
|
|
2317
|
+
findingId: z5.string(),
|
|
2318
|
+
agree: z5.boolean(),
|
|
2319
|
+
reasoning: z5.string().optional(),
|
|
2320
|
+
amendedSeverity: z5.enum(["critical", "major", "minor", "suggestion"]).optional()
|
|
1850
2321
|
});
|
|
1851
|
-
var VotingProtocolConfigSchema =
|
|
1852
|
-
committeeSize:
|
|
1853
|
-
maxRounds:
|
|
1854
|
-
roundTimeoutMs:
|
|
1855
|
-
agreementThreshold:
|
|
1856
|
-
enableAntiSycophancy:
|
|
1857
|
-
sycophancyThreshold:
|
|
2322
|
+
var VotingProtocolConfigSchema = z5.object({
|
|
2323
|
+
committeeSize: z5.number().int().min(2).max(7).default(3),
|
|
2324
|
+
maxRounds: z5.number().int().min(1).max(5).default(3),
|
|
2325
|
+
roundTimeoutMs: z5.number().int().positive().default(VOTING_ROUND_DEFAULT_TIMEOUT_MS),
|
|
2326
|
+
agreementThreshold: z5.number().min(0.5).max(1).default(SUPERMAJORITY_THRESHOLD),
|
|
2327
|
+
enableAntiSycophancy: z5.boolean().default(true),
|
|
2328
|
+
sycophancyThreshold: z5.number().min(0).max(1).default(0.8)
|
|
1858
2329
|
});
|
|
1859
2330
|
var DEFAULT_VOTING_PROTOCOL_CONFIG = {
|
|
1860
2331
|
committeeSize: 3,
|
|
@@ -1867,30 +2338,30 @@ var DEFAULT_VOTING_PROTOCOL_CONFIG = {
|
|
|
1867
2338
|
};
|
|
1868
2339
|
|
|
1869
2340
|
// src/consensus/types-weighted-voting.ts
|
|
1870
|
-
import { z as
|
|
1871
|
-
var TaskOutcomeStatusSchema =
|
|
1872
|
-
var WeightedAgentRecordSchema =
|
|
1873
|
-
agentId:
|
|
1874
|
-
totalTasks:
|
|
1875
|
-
successfulTasks:
|
|
1876
|
-
failedTasks:
|
|
1877
|
-
partialTasks:
|
|
1878
|
-
successRate:
|
|
1879
|
-
weight:
|
|
1880
|
-
trustScore:
|
|
1881
|
-
byzantineFlags:
|
|
1882
|
-
lastActive:
|
|
1883
|
-
createdAt:
|
|
2341
|
+
import { z as z6 } from "zod";
|
|
2342
|
+
var TaskOutcomeStatusSchema = z6.enum(["success", "failure", "partial", "unknown"]);
|
|
2343
|
+
var WeightedAgentRecordSchema = z6.object({
|
|
2344
|
+
agentId: z6.string().min(1),
|
|
2345
|
+
totalTasks: z6.number().int().nonnegative(),
|
|
2346
|
+
successfulTasks: z6.number().int().nonnegative(),
|
|
2347
|
+
failedTasks: z6.number().int().nonnegative(),
|
|
2348
|
+
partialTasks: z6.number().int().nonnegative(),
|
|
2349
|
+
successRate: z6.number().min(0).max(1),
|
|
2350
|
+
weight: z6.number().min(0).max(1),
|
|
2351
|
+
trustScore: z6.number().min(0).max(1),
|
|
2352
|
+
byzantineFlags: z6.number().int().nonnegative(),
|
|
2353
|
+
lastActive: z6.date(),
|
|
2354
|
+
createdAt: z6.date()
|
|
1884
2355
|
});
|
|
1885
|
-
var WeightedVotingConfigSchema =
|
|
1886
|
-
minWeight:
|
|
1887
|
-
maxByzantineFraction:
|
|
1888
|
-
weightDecayFactor:
|
|
1889
|
-
weightRecoveryFactor:
|
|
1890
|
-
minTrustScore:
|
|
1891
|
-
byzantineFlagThreshold:
|
|
1892
|
-
initialWeight:
|
|
1893
|
-
quorumThreshold:
|
|
2356
|
+
var WeightedVotingConfigSchema = z6.object({
|
|
2357
|
+
minWeight: z6.number().min(0).max(1).default(0.1),
|
|
2358
|
+
maxByzantineFraction: z6.number().min(0).max(0.5).default(0.33),
|
|
2359
|
+
weightDecayFactor: z6.number().min(0.5).max(1).default(0.9),
|
|
2360
|
+
weightRecoveryFactor: z6.number().min(1).max(2).default(1.05),
|
|
2361
|
+
minTrustScore: z6.number().min(0).max(1).default(0.3),
|
|
2362
|
+
byzantineFlagThreshold: z6.number().int().positive().default(3),
|
|
2363
|
+
initialWeight: z6.number().min(0).max(1).default(0.5),
|
|
2364
|
+
quorumThreshold: z6.number().min(0.5).max(1).default(SUPERMAJORITY_THRESHOLD)
|
|
1894
2365
|
});
|
|
1895
2366
|
var DEFAULT_WEIGHTED_VOTING_CONFIG = {
|
|
1896
2367
|
minWeight: 0.1,
|
|
@@ -1905,7 +2376,7 @@ var DEFAULT_WEIGHTED_VOTING_CONFIG = {
|
|
|
1905
2376
|
};
|
|
1906
2377
|
|
|
1907
2378
|
// src/consensus/higher-order-types.ts
|
|
1908
|
-
import { z as
|
|
2379
|
+
import { z as z7 } from "zod";
|
|
1909
2380
|
function createAgentPairKey(agentA, agentB) {
|
|
1910
2381
|
return agentA < agentB ? `${agentA}:${agentB}` : `${agentB}:${agentA}`;
|
|
1911
2382
|
}
|
|
@@ -1916,52 +2387,52 @@ function parseAgentPairKey(key) {
|
|
|
1916
2387
|
}
|
|
1917
2388
|
return [parts[0], parts[1]];
|
|
1918
2389
|
}
|
|
1919
|
-
var CorrelationCoefficientSchema =
|
|
1920
|
-
var IndependentSubsetSchema =
|
|
1921
|
-
id:
|
|
1922
|
-
agentIds:
|
|
1923
|
-
independenceScore:
|
|
1924
|
-
pairCoverage:
|
|
1925
|
-
observed:
|
|
1926
|
-
total:
|
|
2390
|
+
var CorrelationCoefficientSchema = z7.number().min(-1).max(1);
|
|
2391
|
+
var IndependentSubsetSchema = z7.object({
|
|
2392
|
+
id: z7.string(),
|
|
2393
|
+
agentIds: z7.array(z7.string()),
|
|
2394
|
+
independenceScore: z7.number().min(0).max(1),
|
|
2395
|
+
pairCoverage: z7.object({
|
|
2396
|
+
observed: z7.number().int().nonnegative(),
|
|
2397
|
+
total: z7.number().int().nonnegative()
|
|
1927
2398
|
}),
|
|
1928
|
-
observationCount:
|
|
2399
|
+
observationCount: z7.number().int().nonnegative()
|
|
1929
2400
|
});
|
|
1930
|
-
var VotingObservationSchema =
|
|
1931
|
-
proposalId:
|
|
1932
|
-
agentId:
|
|
1933
|
-
decision:
|
|
1934
|
-
confidence:
|
|
1935
|
-
alignedWithOutcome:
|
|
1936
|
-
timestamp:
|
|
1937
|
-
modelKey:
|
|
1938
|
-
observedModel:
|
|
2401
|
+
var VotingObservationSchema = z7.object({
|
|
2402
|
+
proposalId: z7.string(),
|
|
2403
|
+
agentId: z7.string(),
|
|
2404
|
+
decision: z7.enum(["approve", "reject", "abstain"]),
|
|
2405
|
+
confidence: z7.number().min(0).max(1),
|
|
2406
|
+
alignedWithOutcome: z7.boolean(),
|
|
2407
|
+
timestamp: z7.date(),
|
|
2408
|
+
modelKey: z7.string().optional(),
|
|
2409
|
+
observedModel: z7.string().optional()
|
|
1939
2410
|
});
|
|
1940
|
-
var PairwiseVotingHistorySchema =
|
|
1941
|
-
pairKey:
|
|
1942
|
-
jointObservations:
|
|
1943
|
-
agreements:
|
|
1944
|
-
disagreements:
|
|
2411
|
+
var PairwiseVotingHistorySchema = z7.object({
|
|
2412
|
+
pairKey: z7.string(),
|
|
2413
|
+
jointObservations: z7.number().int().nonnegative(),
|
|
2414
|
+
agreements: z7.number().int().nonnegative(),
|
|
2415
|
+
disagreements: z7.number().int().nonnegative(),
|
|
1945
2416
|
correlation: CorrelationCoefficientSchema,
|
|
1946
|
-
lastUpdated:
|
|
2417
|
+
lastUpdated: z7.date()
|
|
1947
2418
|
});
|
|
1948
|
-
var HigherOrderVotingConfigSchema =
|
|
1949
|
-
minObservationsForCorrelation:
|
|
1950
|
-
correlationThreshold:
|
|
2419
|
+
var HigherOrderVotingConfigSchema = z7.object({
|
|
2420
|
+
minObservationsForCorrelation: z7.number().int().positive().default(10),
|
|
2421
|
+
correlationThreshold: z7.number().min(0).max(1).default(0.3),
|
|
1951
2422
|
/**
|
|
1952
2423
|
* @deprecated Ignored since 8.x: correlation evidence is lifetime and partitioned by the role's pinned model (#5555); nothing reads this value. Removed in the next major (#5564).
|
|
1953
2424
|
*/
|
|
1954
|
-
correlationMaxAgeMs:
|
|
2425
|
+
correlationMaxAgeMs: z7.number().int().positive().default(864e5),
|
|
1955
2426
|
// 24 hours
|
|
1956
|
-
independenceThreshold:
|
|
1957
|
-
fallbackToSimpleVoting:
|
|
2427
|
+
independenceThreshold: z7.number().min(0).max(1).default(0.2),
|
|
2428
|
+
fallbackToSimpleVoting: z7.boolean().default(true),
|
|
1958
2429
|
/**
|
|
1959
2430
|
* @deprecated Ignored since 8.x: correlation evidence is lifetime and partitioned by the role's pinned model (#5555); nothing reads this value. Removed in the next major (#5564).
|
|
1960
2431
|
*/
|
|
1961
|
-
observationDecayFactor:
|
|
1962
|
-
maxObservationsPerAgent:
|
|
1963
|
-
maxProposals:
|
|
1964
|
-
maxTrackedPairs:
|
|
2432
|
+
observationDecayFactor: z7.number().min(0).max(1).default(0.95),
|
|
2433
|
+
maxObservationsPerAgent: z7.number().int().positive().default(1e3),
|
|
2434
|
+
maxProposals: z7.number().int().positive().default(5e3),
|
|
2435
|
+
maxTrackedPairs: z7.number().int().positive().default(100)
|
|
1965
2436
|
});
|
|
1966
2437
|
var DEFAULT_HIGHER_ORDER_CONFIG = {
|
|
1967
2438
|
minObservationsForCorrelation: 10,
|
|
@@ -1975,25 +2446,25 @@ var DEFAULT_HIGHER_ORDER_CONFIG = {
|
|
|
1975
2446
|
maxProposals: 5e3,
|
|
1976
2447
|
maxTrackedPairs: 100
|
|
1977
2448
|
};
|
|
1978
|
-
var HigherOrderVotingResultSchema =
|
|
1979
|
-
decision:
|
|
1980
|
-
posteriorApproval:
|
|
1981
|
-
posteriorRejection:
|
|
1982
|
-
effectiveVoteCount:
|
|
1983
|
-
usedCorrelationData:
|
|
1984
|
-
method:
|
|
1985
|
-
improvementOverBaseline:
|
|
1986
|
-
independentSubsets:
|
|
1987
|
-
downweightedAgents:
|
|
1988
|
-
reasoning:
|
|
2449
|
+
var HigherOrderVotingResultSchema = z7.object({
|
|
2450
|
+
decision: z7.enum(["approve", "reject", "no_consensus"]),
|
|
2451
|
+
posteriorApproval: z7.number().min(0).max(1),
|
|
2452
|
+
posteriorRejection: z7.number().min(0).max(1),
|
|
2453
|
+
effectiveVoteCount: z7.number().nonnegative(),
|
|
2454
|
+
usedCorrelationData: z7.boolean(),
|
|
2455
|
+
method: z7.enum(["ow", "isp", "simple"]),
|
|
2456
|
+
improvementOverBaseline: z7.number(),
|
|
2457
|
+
independentSubsets: z7.array(IndependentSubsetSchema).optional(),
|
|
2458
|
+
downweightedAgents: z7.array(z7.string()),
|
|
2459
|
+
reasoning: z7.string()
|
|
1989
2460
|
});
|
|
1990
|
-
var CorrelationTrackerStatsSchema =
|
|
1991
|
-
totalAgents:
|
|
1992
|
-
trackedPairs:
|
|
1993
|
-
totalObservations:
|
|
1994
|
-
averageCorrelation:
|
|
1995
|
-
independentSubsetCount:
|
|
1996
|
-
pairsWithSufficientData:
|
|
2461
|
+
var CorrelationTrackerStatsSchema = z7.object({
|
|
2462
|
+
totalAgents: z7.number().int().nonnegative(),
|
|
2463
|
+
trackedPairs: z7.number().int().nonnegative(),
|
|
2464
|
+
totalObservations: z7.number().int().nonnegative(),
|
|
2465
|
+
averageCorrelation: z7.number(),
|
|
2466
|
+
independentSubsetCount: z7.number().int().nonnegative(),
|
|
2467
|
+
pairsWithSufficientData: z7.number().int().nonnegative()
|
|
1997
2468
|
});
|
|
1998
2469
|
|
|
1999
2470
|
// src/consensus/higher-order-helpers.ts
|
|
@@ -5005,24 +5476,24 @@ function createCorrelationTracker(config) {
|
|
|
5005
5476
|
import * as fs from "node:fs";
|
|
5006
5477
|
|
|
5007
5478
|
// src/consensus/correlation-persistence-records.ts
|
|
5008
|
-
import { z as
|
|
5009
|
-
var PersistedVoteSchema =
|
|
5010
|
-
agentId:
|
|
5011
|
-
decision:
|
|
5012
|
-
confidence:
|
|
5013
|
-
modelKey:
|
|
5014
|
-
observedModel:
|
|
5479
|
+
import { z as z8 } from "zod";
|
|
5480
|
+
var PersistedVoteSchema = z8.object({
|
|
5481
|
+
agentId: z8.string(),
|
|
5482
|
+
decision: z8.enum(["approve", "reject", "abstain"]),
|
|
5483
|
+
confidence: z8.number().min(0).max(1),
|
|
5484
|
+
modelKey: z8.string().optional(),
|
|
5485
|
+
observedModel: z8.string().optional()
|
|
5015
5486
|
});
|
|
5016
|
-
var PersistedProposalSchema =
|
|
5017
|
-
proposalId:
|
|
5018
|
-
votes:
|
|
5019
|
-
outcome:
|
|
5020
|
-
timestamp:
|
|
5487
|
+
var PersistedProposalSchema = z8.object({
|
|
5488
|
+
proposalId: z8.string(),
|
|
5489
|
+
votes: z8.array(PersistedVoteSchema),
|
|
5490
|
+
outcome: z8.enum(["approved", "rejected"]),
|
|
5491
|
+
timestamp: z8.iso.datetime()
|
|
5021
5492
|
});
|
|
5022
|
-
var PersistedCorrelationDataSchema =
|
|
5023
|
-
version:
|
|
5024
|
-
proposals:
|
|
5025
|
-
savedAt:
|
|
5493
|
+
var PersistedCorrelationDataSchema = z8.object({
|
|
5494
|
+
version: z8.number().int().positive(),
|
|
5495
|
+
proposals: z8.array(PersistedProposalSchema),
|
|
5496
|
+
savedAt: z8.iso.datetime()
|
|
5026
5497
|
});
|
|
5027
5498
|
function createPersistedProposal(proposalId, votes, outcome, context) {
|
|
5028
5499
|
const persistedVotes = [];
|
|
@@ -5270,449 +5741,40 @@ import { z as z9 } from "zod";
|
|
|
5270
5741
|
function answeringSeats(votes) {
|
|
5271
5742
|
return votes.filter((v) => v.source === "llm");
|
|
5272
5743
|
}
|
|
5273
|
-
function resolvedModel(v) {
|
|
5274
|
-
return v.model === void 0 || v.model === "" || v.model === UNRESOLVED_MODEL_ID ? void 0 : v.model;
|
|
5275
|
-
}
|
|
5276
|
-
function seatFallbacks(votes) {
|
|
5277
|
-
const out = [];
|
|
5278
|
-
for (const v of answeringSeats(votes)) {
|
|
5279
|
-
if (v.fallback === void 0) continue;
|
|
5280
|
-
out.push({
|
|
5281
|
-
role: v.role,
|
|
5282
|
-
fallback: v.fallback,
|
|
5283
|
-
toCli: v.cli === void 0 ? "?" : bareCliName(v.cli),
|
|
5284
|
-
toModel: resolvedModel(v)
|
|
5285
|
-
});
|
|
5286
|
-
}
|
|
5287
|
-
return out;
|
|
5288
|
-
}
|
|
5289
|
-
function panelDiversityOf(votes) {
|
|
5290
|
-
const models = answeringSeats(votes).map(resolvedModel).filter((m) => m !== void 0);
|
|
5291
|
-
return {
|
|
5292
|
-
distinctModels: countDistinctModels(models),
|
|
5293
|
-
fallbacks: seatFallbacks(votes).length
|
|
5294
|
-
};
|
|
5295
|
-
}
|
|
5296
|
-
var MIN_PANEL_FOR_DIVERSITY_WARNING = 3;
|
|
5297
|
-
function singleModelPanelWarning(votes) {
|
|
5298
|
-
if (votes.length < MIN_PANEL_FOR_DIVERSITY_WARNING) return void 0;
|
|
5299
|
-
const answered = answeringSeats(votes);
|
|
5300
|
-
const models = answered.map(resolvedModel);
|
|
5301
|
-
const model = models[0];
|
|
5302
|
-
if (model === void 0 || models.includes(void 0)) return void 0;
|
|
5303
|
-
if (panelDiversityOf(votes).distinctModels !== 1) return void 0;
|
|
5304
|
-
const silent = votes.length - answered.length;
|
|
5305
|
-
const missing = silent > 0 ? ` (${String(silent)} did not answer)` : "";
|
|
5306
|
-
return `All ${String(answered.length)} seats answered on ${model}${missing}; independence is weaker than assigned.`;
|
|
5307
|
-
}
|
|
5308
|
-
|
|
5309
|
-
// src/audit/vote-record.ts
|
|
5310
|
-
import * as crypto from "node:crypto";
|
|
5311
|
-
import { z as z8 } from "zod";
|
|
5312
|
-
var VoteRecordDecisionSchema = z8.enum(["approved", "rejected", "no_quorum"]);
|
|
5313
|
-
var VoteRecordOptionCoverageSchema = z8.object({
|
|
5314
|
-
/** Every approving voter, selecting or not — the tally's denominator. */
|
|
5315
|
-
approverCount: z8.number().int().nonnegative(),
|
|
5316
|
-
/** Approvers whose selection matched a declared option. */
|
|
5317
|
-
selectedCount: z8.number().int().nonnegative(),
|
|
5318
|
-
/** Approvers with no usable selection; their choice is unmeasured. */
|
|
5319
|
-
unattributedApprovals: z8.number().int().nonnegative()
|
|
5320
|
-
}).strict();
|
|
5321
|
-
var VoteRecordPanelCoverageSchema = z8.object({
|
|
5322
|
-
/** Voters the panel asked for. */
|
|
5323
|
-
requested: z8.number().int().nonnegative(),
|
|
5324
|
-
/** Voters that returned a usable vote — the denominator of `voteCounts`. */
|
|
5325
|
-
responded: z8.number().int().nonnegative(),
|
|
5326
|
-
/** Voters that errored or timed out. */
|
|
5327
|
-
errored: z8.number().int().nonnegative(),
|
|
5328
|
-
/** The roles behind `errored`, in panel order. */
|
|
5329
|
-
erroredRoles: z8.array(z8.string().min(1).max(100))
|
|
5330
|
-
}).strict();
|
|
5331
|
-
var MAX_VOTER_REASONING_CHARS = 2e4;
|
|
5332
|
-
var SeatFallbackRecordSchema = z8.object({
|
|
5333
|
-
/** The CLI the seat was assigned to, bare (`claude`, not `cli-claude`). */
|
|
5334
|
-
fromCli: z8.string().min(1).max(100),
|
|
5335
|
-
/** The model the assigned adapter had detected, when it had one. */
|
|
5336
|
-
fromModel: z8.string().min(1).max(200).optional(),
|
|
5337
|
-
/** The adapter-layer class of the error that moved the seat. */
|
|
5338
|
-
reason: z8.enum(["rate-limit", "capacity", "auth", "timeout", "sandbox", "unknown"])
|
|
5339
|
-
}).strict();
|
|
5340
|
-
var VoterSummarySchema = z8.object({
|
|
5341
|
-
role: z8.string().min(1).max(100),
|
|
5342
|
-
decision: z8.enum(["approve", "reject", "abstain"]),
|
|
5343
|
-
confidence: z8.number().min(0).max(1),
|
|
5344
|
-
/**
|
|
5345
|
-
* The voter's stated grounds (#5373, schema 1.6).
|
|
5346
|
-
*
|
|
5347
|
-
* `generateVoteHash` already hashed `{role, decision, reasoning}` and then
|
|
5348
|
-
* discarded the text, so the chain attested to a value it did not store and
|
|
5349
|
-
* nobody could re-verify the hash without re-obtaining the reasoning. On
|
|
5350
|
-
* #5228 a contrarian rejection was clipped mid-sentence in the terminal, the
|
|
5351
|
-
* grounds were unrecoverable, and the same defect resurfaced a round later —
|
|
5352
|
-
* one round of a 7-voter panel is 7-13 minutes of model time, and the
|
|
5353
|
-
* objection was right both times.
|
|
5354
|
-
*
|
|
5355
|
-
* Stored whole rather than sampled. Warn-mode near-misses are emitted per
|
|
5356
|
-
* TOOL CALL, thousands a day; votes are emitted per PANEL, dozens a day at
|
|
5357
|
-
* most, so ~17 KB per vote is not the same growth problem.
|
|
5358
|
-
*
|
|
5359
|
-
* Absence is not an empty argument: an errored voter has no entry in
|
|
5360
|
-
* `voters` at all (see `panelCoverage`), so `reasoning: ''` means a live
|
|
5361
|
-
* voter returned nothing, which is itself a signal.
|
|
5362
|
-
*/
|
|
5363
|
-
reasoning: z8.string().max(MAX_VOTER_REASONING_CHARS).optional(),
|
|
5364
|
-
/** True when `reasoning` was clipped to {@link MAX_VOTER_REASONING_CHARS}. */
|
|
5365
|
-
reasoningTruncated: z8.literal(true).optional(),
|
|
5366
|
-
/**
|
|
5367
|
-
* True when this seat was recovered by the per-role retry (#6050, schema 1.7).
|
|
5368
|
-
*
|
|
5369
|
-
* `voter-retry.ts` has set `retried: true` on recovered results since it was
|
|
5370
|
-
* written, and `vote-types.ts` states the reason: the flag is "what makes the
|
|
5371
|
-
* recovery visible instead of indistinguishable from a clean first attempt".
|
|
5372
|
-
* It had one producer and ZERO consumers — both summarizers and the record
|
|
5373
|
-
* dropped it — so the field existed, was documented, and never reached
|
|
5374
|
-
* anything that could act on it.
|
|
5375
|
-
*
|
|
5376
|
-
* A retried seat is weaker evidence than a first-pass one: the model was
|
|
5377
|
-
* unavailable or timed out, and the recovery ran under different conditions.
|
|
5378
|
-
* For a ratification vote on a governor-path change, "7 of 7 answered" and
|
|
5379
|
-
* "6 answered, 1 recovered on retry" are different facts about the scrutiny
|
|
5380
|
-
* the change received, and the record stated the first for both.
|
|
5381
|
-
*
|
|
5382
|
-
* `literal(true)` and optional, on the `reasoningTruncated` rule: a clean
|
|
5383
|
-
* seat carries no key, so its entry re-hashes byte-identical and every
|
|
5384
|
-
* historical record still verifies.
|
|
5385
|
-
*/
|
|
5386
|
-
retried: z8.literal(true).optional(),
|
|
5387
|
-
/**
|
|
5388
|
-
* Registry model id the seat ran on (#6091, schema 1.8).
|
|
5389
|
-
*
|
|
5390
|
-
* The seat→model mapping is round-robin over the available CLIs plus
|
|
5391
|
-
* `NEXUS_VOTER_MODEL_<ROLE>` pins, so it is not recoverable from the
|
|
5392
|
-
* ledger after the fact: 1844 voter entries across 278 records carried no
|
|
5393
|
-
* model. "When an agent's output becomes evidence, its provenance travels
|
|
5394
|
-
* with it" — a tally that says `scope_steward: approve` without saying
|
|
5395
|
-
* which model answered cannot be audited for the #6068 failure class.
|
|
5396
|
-
* Present-only, on the `retried` rule; absent when the result carried no
|
|
5397
|
-
* model or only the pending-detection placeholder.
|
|
5398
|
-
*/
|
|
5399
|
-
model: z8.string().min(1).max(200).optional(),
|
|
5400
|
-
/**
|
|
5401
|
-
* True when the seat could not read the artifact (#6094, schema 1.8).
|
|
5402
|
-
*
|
|
5403
|
-
* `decision` is `abstain` for such a seat, so without this flag the record
|
|
5404
|
-
* could not tell "read it and abstained" from "never saw it" — and one
|
|
5405
|
-
* ledger entry (`vote-1789058788915-6lfrbuq`) shows a seat that APPROVED
|
|
5406
|
-
* on the proposal text after failing to read. `literal(true)` and
|
|
5407
|
-
* present-only, so every pre-1.8 entry re-hashes byte-identical.
|
|
5408
|
-
*/
|
|
5409
|
-
unverifiable: z8.literal(true).optional(),
|
|
5410
|
-
/**
|
|
5411
|
-
* The CLI the round-robin or `NEXUS_VOTER_MODEL_<ROLE>` pin chose for the
|
|
5412
|
-
* seat, as a bare name (#6115, schema 1.9). `model` says where a seat
|
|
5413
|
-
* answered; this says where it was MEANT to. Three consecutive 7-seat
|
|
5414
|
-
* ratification panels on 2026-09-13 answered every seat on one gemini
|
|
5415
|
-
* model while the assignment was three claude, two codex, two gemini,
|
|
5416
|
-
* and the 1.8 record showed seven identical `model` values with nothing
|
|
5417
|
-
* that said five of them were substitutes. Present-only, on the `model`
|
|
5418
|
-
* rule; absent on a result built outside the panel launcher.
|
|
5419
|
-
*/
|
|
5420
|
-
assignedCli: z8.string().min(1).max(100).optional(),
|
|
5421
|
-
/**
|
|
5422
|
-
* Present only when the seat answered on a different CLI or model than
|
|
5423
|
-
* assigned (#6115, schema 1.9): the exact `SeatFallback` shape the live
|
|
5424
|
-
* result carries, rebuilt field-by-field. `fromCli` is the assigned CLI,
|
|
5425
|
-
* `fromModel` the model it had detected (absent when it never did — the
|
|
5426
|
-
* pending-detection placeholder is not a model), `reason` the adapter
|
|
5427
|
-
* layer's error class. The vocabulary is compile-checked against the live
|
|
5428
|
-
* `FallbackReason` in vote-record.test.ts; a class one side learns alone
|
|
5429
|
-
* would either refuse every record that carries it (#6054) or accept one
|
|
5430
|
-
* no producer can write.
|
|
5431
|
-
*/
|
|
5432
|
-
fallback: SeatFallbackRecordSchema.optional()
|
|
5433
|
-
}).strict();
|
|
5434
|
-
function defineVoterKeys(keys) {
|
|
5435
|
-
return keys;
|
|
5436
|
-
}
|
|
5437
|
-
var VOTER_SUMMARY_KEYS = defineVoterKeys([
|
|
5438
|
-
"role",
|
|
5439
|
-
"decision",
|
|
5440
|
-
"confidence",
|
|
5441
|
-
"reasoning",
|
|
5442
|
-
"reasoningTruncated",
|
|
5443
|
-
"retried",
|
|
5444
|
-
// 1.8 (#6091, #6094): appended after `retried`, present-only, so a 1.7
|
|
5445
|
-
// entry projects byte-identically.
|
|
5446
|
-
"model",
|
|
5447
|
-
"unverifiable",
|
|
5448
|
-
// 1.9 (#6115): appended after `unverifiable`, present-only, so a 1.8 entry
|
|
5449
|
-
// projects byte-identically. `fallback` is the first nested voter field;
|
|
5450
|
-
// `projectSeatFallback` rebuilds it in its own canonical order.
|
|
5451
|
-
"assignedCli",
|
|
5452
|
-
"fallback"
|
|
5453
|
-
]);
|
|
5454
|
-
function noUnprojectedKeys(rest) {
|
|
5455
|
-
return rest;
|
|
5456
|
-
}
|
|
5457
|
-
function projectSeatFallback(f) {
|
|
5458
|
-
const { fromCli, fromModel, reason, ...rest } = f;
|
|
5459
|
-
noUnprojectedKeys(rest);
|
|
5460
|
-
return {
|
|
5461
|
-
fromCli,
|
|
5462
|
-
...fromModel !== void 0 ? { fromModel } : {},
|
|
5463
|
-
reason
|
|
5464
|
-
};
|
|
5465
|
-
}
|
|
5466
|
-
function projectVoterSummary(v) {
|
|
5467
|
-
const out = {};
|
|
5468
|
-
for (const key of VOTER_SUMMARY_KEYS) {
|
|
5469
|
-
const value = key === "fallback" ? v.fallback === void 0 ? void 0 : projectSeatFallback(v.fallback) : v[key];
|
|
5470
|
-
if (value !== void 0) out[key] = value;
|
|
5744
|
+
function resolvedModel(v) {
|
|
5745
|
+
return v.model === void 0 || v.model === "" || v.model === UNRESOLVED_MODEL_ID ? void 0 : v.model;
|
|
5746
|
+
}
|
|
5747
|
+
function seatFallbacks(votes) {
|
|
5748
|
+
const out = [];
|
|
5749
|
+
for (const v of answeringSeats(votes)) {
|
|
5750
|
+
if (v.fallback === void 0) continue;
|
|
5751
|
+
out.push({
|
|
5752
|
+
role: v.role,
|
|
5753
|
+
fallback: v.fallback,
|
|
5754
|
+
toCli: v.cli === void 0 ? "?" : bareCliName(v.cli),
|
|
5755
|
+
toModel: resolvedModel(v)
|
|
5756
|
+
});
|
|
5471
5757
|
}
|
|
5472
5758
|
return out;
|
|
5473
5759
|
}
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
}).strict();
|
|
5480
|
-
var VoteRecordOptionCountSchema = z8.object({
|
|
5481
|
-
/** The option label as declared in the proposal (e.g. 'A'). */
|
|
5482
|
-
option: z8.string().min(1),
|
|
5483
|
-
/** How many voters selected it. */
|
|
5484
|
-
count: z8.number().int().nonnegative()
|
|
5485
|
-
}).strict();
|
|
5486
|
-
var GIT_SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
5487
|
-
var VoteRecordPrBindingSchema = z8.object({
|
|
5488
|
-
pr: z8.number().int().positive(),
|
|
5489
|
-
headSha: z8.string().regex(GIT_SHA_PATTERN, "expected a full 40-hex lowercase commit sha")
|
|
5490
|
-
}).strict();
|
|
5491
|
-
function projectPrBinding(b) {
|
|
5492
|
-
return { pr: b.pr, headSha: b.headSha };
|
|
5493
|
-
}
|
|
5494
|
-
var VoteRecordErrorPolicySchema = z8.enum([
|
|
5495
|
-
"reduce_denominator",
|
|
5496
|
-
"count_as_abstain",
|
|
5497
|
-
"fail_closed",
|
|
5498
|
-
"absolute_quorum"
|
|
5499
|
-
]);
|
|
5500
|
-
var VoteRecordSchema = z8.object({
|
|
5501
|
-
/**
|
|
5502
|
-
* Schema version. '1.1' marked the chain→record-set+sequence model (#3927);
|
|
5503
|
-
* '1.2' adds the optional `ratifies` subject-binding field (#3927 item 1);
|
|
5504
|
-
* '1.10' adds the optional `ratifiesPr` PR-binding (#5130); '1.11' the
|
|
5505
|
-
* optional `errorPolicy` (#6211). Every tier is accepted — a 1.1 record
|
|
5506
|
-
* (no `ratifies`) verifies unchanged because each optional is folded into
|
|
5507
|
-
* the self-hash ONLY when present (see {@link computeVoteRecordHash}).
|
|
5508
|
-
* Tiers are labels, not ordered numbers: '1.10' follows '1.9' by
|
|
5509
|
-
* convention only and nothing compares them.
|
|
5510
|
-
*/
|
|
5511
|
-
version: z8.enum([
|
|
5512
|
-
"1.1",
|
|
5513
|
-
"1.2",
|
|
5514
|
-
"1.3",
|
|
5515
|
-
"1.4",
|
|
5516
|
-
"1.5",
|
|
5517
|
-
"1.6",
|
|
5518
|
-
"1.7",
|
|
5519
|
-
"1.8",
|
|
5520
|
-
"1.9",
|
|
5521
|
-
"1.10",
|
|
5522
|
-
"1.11"
|
|
5523
|
-
]),
|
|
5524
|
-
/** Unique record id (also usable as a `ratificationVoteRef`). */
|
|
5525
|
-
id: z8.string().min(1),
|
|
5526
|
-
/**
|
|
5527
|
-
* Monotonic sequence number (integer ≥ 0). Assigned as (max existing
|
|
5528
|
-
* sequence)+1 at write time. Sorted, the set of sequences must cover
|
|
5529
|
-
* 0..maxSeq with no gap (omission detection); DUPLICATE sequences are a
|
|
5530
|
-
* benign concurrent-fork signal, not tampering.
|
|
5531
|
-
*/
|
|
5532
|
-
sequence: z8.number().int().nonnegative(),
|
|
5533
|
-
/** ISO-8601 timestamp the vote was recorded. */
|
|
5534
|
-
recordedAt: z8.string().min(1),
|
|
5535
|
-
/**
|
|
5536
|
-
* SHA-256 of the FULL proposal text. The proposal itself is truncated for
|
|
5537
|
-
* the human record (`proposal`), but the hash binds the record to the exact
|
|
5538
|
-
* proposal voted on — a later edit of `proposal` that changes meaning is
|
|
5539
|
-
* detectable by recomputing this hash from the original.
|
|
5540
|
-
*/
|
|
5541
|
-
proposalHash: z8.string().length(64),
|
|
5542
|
-
/** Truncated proposal text for the human/reviewer record. */
|
|
5543
|
-
proposal: z8.string(),
|
|
5544
|
-
/** The voting strategy used. */
|
|
5545
|
-
strategy: z8.enum([
|
|
5546
|
-
"simple_majority",
|
|
5547
|
-
"supermajority",
|
|
5548
|
-
"unanimous",
|
|
5549
|
-
"higher_order",
|
|
5550
|
-
"opinion_wise",
|
|
5551
|
-
"proof_of_learning"
|
|
5552
|
-
]),
|
|
5553
|
-
/** The resolved decision. */
|
|
5554
|
-
decision: VoteRecordDecisionSchema,
|
|
5555
|
-
/** Approval fraction as a percentage (0-100). */
|
|
5556
|
-
approvalPercentage: z8.number().min(0).max(100),
|
|
5557
|
-
/** Vote-count breakdown. */
|
|
5558
|
-
voteCounts: VoteRecordCountsSchema,
|
|
5559
|
-
/** Per-voter {role, decision, confidence} summary. */
|
|
5560
|
-
voters: z8.array(VoterSummarySchema),
|
|
5561
|
-
/** Optional correlation/decision id linking to the cost rollup / trace. */
|
|
5562
|
-
correlationId: z8.string().min(1).optional(),
|
|
5563
|
-
/**
|
|
5564
|
-
* Per-option distribution for a multi-option proposal (#4452, schema 1.3).
|
|
5565
|
-
*
|
|
5566
|
-
* Present only when the vote declared `options`. Absent on an ordinary
|
|
5567
|
-
* yes/no vote, and absent on every 1.1/1.2 record — which is why it is
|
|
5568
|
-
* folded into the self-hash ONLY when present (see
|
|
5569
|
-
* {@link computeVoteRecordHash}), exactly as `ratifies` was.
|
|
5570
|
-
*
|
|
5571
|
-
* Without this, a 6-1 or 5-2 option split is recorded as `approve: 7` and
|
|
5572
|
-
* reads as unanimous. Threshold semantics invert too: `unanimous` becomes
|
|
5573
|
-
* the EASIEST bar to clear, because every engaged voter approves while
|
|
5574
|
-
* choosing different things.
|
|
5575
|
-
*/
|
|
5576
|
-
// No `.min(1)` (#6049). The rule this field's own docstring states is
|
|
5577
|
-
// "present only when the vote declared `options`" -- keyed on DECLARATION,
|
|
5578
|
-
// not on whether anyone picked one. `.min(1)` encoded the older, narrower
|
|
5579
|
-
// rule, so once the builder started emitting `[]` for "declared, nothing
|
|
5580
|
-
// attributable" the record appended fine and then failed to parse on read:
|
|
5581
|
-
// `too_small, minimum 1`. The write path does not validate, so the line
|
|
5582
|
-
// landed in the ledger and `parseVoteRecordsText` silently dropped it --
|
|
5583
|
-
// the case most worth auditing became the one no reader could see.
|
|
5584
|
-
//
|
|
5585
|
-
// Relaxing is a pure widening: no previously-valid record changes meaning
|
|
5586
|
-
// or hash, and no persisted record can currently hold an empty tally.
|
|
5587
|
-
optionTally: z8.array(VoteRecordOptionCountSchema).optional(),
|
|
5588
|
-
/**
|
|
5589
|
-
* Selection coverage for a multi-option vote (#4472, schema 1.4).
|
|
5590
|
-
*
|
|
5591
|
-
* `optionTally` alone cannot distinguish dissent from absence: `4 pick X
|
|
5592
|
-
* + 3 unreadable` and a genuine 4/3 split both leave a leading share of
|
|
5593
|
-
* 57%. Recording how many approvers produced no usable selection is what
|
|
5594
|
-
* makes a partial measurement legible AS partial — the condition every
|
|
5595
|
-
* voter attached to adopting the "credit no option" rule.
|
|
5596
|
-
*/
|
|
5597
|
-
optionCoverage: VoteRecordOptionCoverageSchema.optional(),
|
|
5598
|
-
/**
|
|
5599
|
-
* Panel coverage (#5738, schema 1.5). Present only when at least one
|
|
5600
|
-
* requested voter failed to return a vote; absent keeps a clean panel on
|
|
5601
|
-
* the pre-1.5 projection, so every historical record still verifies.
|
|
5602
|
-
*/
|
|
5603
|
-
panelCoverage: VoteRecordPanelCoverageSchema.optional(),
|
|
5604
|
-
/**
|
|
5605
|
-
* The loop/strategy subject this vote RATIFIES (#3927 item 1). Present only on
|
|
5606
|
-
* a ratification vote; set at vote time and bound into the self-hash (so it is
|
|
5607
|
-
* tamper-evident). The authority-tier promotion gate
|
|
5608
|
-
* (`scripts/check-authority-tier-drift.ts`) resolves a transition's
|
|
5609
|
-
* `ratificationVoteRef` to a record and requires `ratifies === transition.subject`
|
|
5610
|
-
* (with `decision === 'approved'` and `strategy === 'higher_order'`). Absent on
|
|
5611
|
-
* an ordinary (non-ratification) vote; a 1.1 record never carries it.
|
|
5612
|
-
*/
|
|
5613
|
-
ratifies: z8.string().min(1).optional(),
|
|
5614
|
-
/**
|
|
5615
|
-
* The PR this vote RATIFIES, bound to the head the panel saw (#5130 step 1,
|
|
5616
|
-
* schema 1.10; panel decision Q1 option A, 5 of 6). Present only on a
|
|
5617
|
-
* governor-path ratification vote; hash-covered like `ratifies`, so a
|
|
5618
|
-
* record cannot be repointed at a different PR or a later push without
|
|
5619
|
-
* breaking its hash. The governor gate (step 2, #5779/#5131) resolves the
|
|
5620
|
-
* record from the committed ledger and requires `pr` to be the PR under
|
|
5621
|
-
* review and `headSha` to be its head (or `head^` when `head` touches only
|
|
5622
|
-
* the ledger — the caller-commits tip). Absent on every pre-1.10 record.
|
|
5623
|
-
*
|
|
5624
|
-
* A SEPARATE key rather than a structured encoding of `ratifies`: that
|
|
5625
|
-
* field is the authority-ladder SUBJECT, compared by string equality to
|
|
5626
|
-
* `transition.subject` and grouped by `conflictingRatifiedSubjects`; no
|
|
5627
|
-
* resolver parses it. Overloading it would give one field two consumers
|
|
5628
|
-
* with two equality rules. The two may coexist on one record.
|
|
5629
|
-
*/
|
|
5630
|
-
ratifiesPr: VoteRecordPrBindingSchema.optional(),
|
|
5631
|
-
/**
|
|
5632
|
-
* The error policy the panel actually ran under (#6211, schema 1.11): the
|
|
5633
|
-
* EFFECTIVE policy after `consensus_vote` applied the per-strategy default
|
|
5634
|
-
* (`input.errorPolicy ?? getDefaultErrorPolicy(strategy)`), not the raw
|
|
5635
|
-
* input the caller may have omitted. Hash-covered, present-only.
|
|
5636
|
-
*
|
|
5637
|
-
* Before this tier the policy reached the RESPONSE only, so the governor
|
|
5638
|
-
* ledger gate (`scripts/governor-ledger-evidence.ts`) could not say which
|
|
5639
|
-
* policy a whole-panel ratification was configured with: under
|
|
5640
|
-
* `absolute_quorum` an errored seat voids the vote, so the one
|
|
5641
|
-
* ledger-observable trace of a different policy was an approved record
|
|
5642
|
-
* with `panelCoverage.errored > 0` — and a whole panel under
|
|
5643
|
-
* `reduce_denominator` looked exactly like one under `absolute_quorum`.
|
|
5644
|
-
* The gate's `wrong-error-policy` verdict reads this field; a record
|
|
5645
|
-
* without it (every pre-1.11 record) keeps the panel-coverage inference
|
|
5646
|
-
* and is reported as `errorPolicy: unrecorded`.
|
|
5647
|
-
*/
|
|
5648
|
-
errorPolicy: VoteRecordErrorPolicySchema.optional(),
|
|
5649
|
-
/**
|
|
5650
|
-
* ADVISORY hash of the tip record at write time (absent for the first).
|
|
5651
|
-
* Retained for audit texture but NOT covered by `hash` and NOT verified —
|
|
5652
|
-
* the record-set model is position-independent (#3927).
|
|
5653
|
-
*/
|
|
5654
|
-
previousHash: z8.string().length(64).optional(),
|
|
5655
|
-
/** SHA-256 over every field above EXCEPT `previousHash` (and except `hash`). */
|
|
5656
|
-
hash: z8.string().length(64)
|
|
5657
|
-
}).strict();
|
|
5658
|
-
function foldOptionalFields(base, payload) {
|
|
5659
|
-
const withTally = payload.optionTally !== void 0 ? {
|
|
5660
|
-
...base,
|
|
5661
|
-
optionTally: payload.optionTally.map((o) => ({ option: o.option, count: o.count }))
|
|
5662
|
-
} : base;
|
|
5663
|
-
const withCoverage = payload.optionCoverage !== void 0 ? {
|
|
5664
|
-
...withTally,
|
|
5665
|
-
optionCoverage: {
|
|
5666
|
-
approverCount: payload.optionCoverage.approverCount,
|
|
5667
|
-
selectedCount: payload.optionCoverage.selectedCount,
|
|
5668
|
-
unattributedApprovals: payload.optionCoverage.unattributedApprovals
|
|
5669
|
-
}
|
|
5670
|
-
} : withTally;
|
|
5671
|
-
const withPanel = payload.panelCoverage !== void 0 ? {
|
|
5672
|
-
...withCoverage,
|
|
5673
|
-
panelCoverage: {
|
|
5674
|
-
requested: payload.panelCoverage.requested,
|
|
5675
|
-
responded: payload.panelCoverage.responded,
|
|
5676
|
-
errored: payload.panelCoverage.errored,
|
|
5677
|
-
erroredRoles: [...payload.panelCoverage.erroredRoles]
|
|
5678
|
-
}
|
|
5679
|
-
} : withCoverage;
|
|
5680
|
-
const withRatifies = payload.ratifies !== void 0 ? { ...withPanel, ratifies: payload.ratifies } : withPanel;
|
|
5681
|
-
const withPrBinding = payload.ratifiesPr !== void 0 ? { ...withRatifies, ratifiesPr: projectPrBinding(payload.ratifiesPr) } : withRatifies;
|
|
5682
|
-
return payload.errorPolicy !== void 0 ? { ...withPrBinding, errorPolicy: payload.errorPolicy } : withPrBinding;
|
|
5683
|
-
}
|
|
5684
|
-
function computeVoteRecordHash(payload) {
|
|
5685
|
-
const base = {
|
|
5686
|
-
version: payload.version,
|
|
5687
|
-
id: payload.id,
|
|
5688
|
-
sequence: payload.sequence,
|
|
5689
|
-
recordedAt: payload.recordedAt,
|
|
5690
|
-
proposalHash: payload.proposalHash,
|
|
5691
|
-
proposal: payload.proposal,
|
|
5692
|
-
strategy: payload.strategy,
|
|
5693
|
-
decision: payload.decision,
|
|
5694
|
-
approvalPercentage: payload.approvalPercentage,
|
|
5695
|
-
// Rebuild voteCounts in schema order (approve, reject, abstain, total) so the
|
|
5696
|
-
// hash does not depend on how the nested object's keys were ordered (#3962).
|
|
5697
|
-
voteCounts: {
|
|
5698
|
-
approve: payload.voteCounts.approve,
|
|
5699
|
-
reject: payload.voteCounts.reject,
|
|
5700
|
-
abstain: payload.voteCounts.abstain,
|
|
5701
|
-
total: payload.voteCounts.total
|
|
5702
|
-
},
|
|
5703
|
-
// `reasoning` / `reasoningTruncated` (#5373, schema 1.6) are appended per
|
|
5704
|
-
// entry ONLY when present, on the same rule as the record-level optional
|
|
5705
|
-
// fields: a pre-1.6 voter entry re-hashes byte-identical, so every
|
|
5706
|
-
// historical record still verifies, while editing or removing a stored
|
|
5707
|
-
// reasoning flips the hash.
|
|
5708
|
-
voters: payload.voters.map(projectVoterSummary),
|
|
5709
|
-
correlationId: payload.correlationId ?? null
|
|
5760
|
+
function panelDiversityOf(votes) {
|
|
5761
|
+
const models = answeringSeats(votes).map(resolvedModel).filter((m) => m !== void 0);
|
|
5762
|
+
return {
|
|
5763
|
+
distinctModels: countDistinctModels(models),
|
|
5764
|
+
fallbacks: seatFallbacks(votes).length
|
|
5710
5765
|
};
|
|
5711
|
-
const canonical = JSON.stringify(foldOptionalFields(base, payload));
|
|
5712
|
-
return crypto.createHash("sha256").update(canonical).digest("hex");
|
|
5713
5766
|
}
|
|
5714
|
-
|
|
5715
|
-
|
|
5767
|
+
var MIN_PANEL_FOR_DIVERSITY_WARNING = 3;
|
|
5768
|
+
function singleModelPanelWarning(votes) {
|
|
5769
|
+
if (votes.length < MIN_PANEL_FOR_DIVERSITY_WARNING) return void 0;
|
|
5770
|
+
const answered = answeringSeats(votes);
|
|
5771
|
+
const models = answered.map(resolvedModel);
|
|
5772
|
+
const model = models[0];
|
|
5773
|
+
if (model === void 0 || models.includes(void 0)) return void 0;
|
|
5774
|
+
if (panelDiversityOf(votes).distinctModels !== 1) return void 0;
|
|
5775
|
+
const silent = votes.length - answered.length;
|
|
5776
|
+
const missing = silent > 0 ? ` (${String(silent)} did not answer)` : "";
|
|
5777
|
+
return `All ${String(answered.length)} seats answered on ${model}${missing}; independence is weaker than assigned.`;
|
|
5716
5778
|
}
|
|
5717
5779
|
|
|
5718
5780
|
// src/mcp/tools/consensus-vote-option-detection.ts
|
|
@@ -5876,7 +5938,9 @@ function toAgentVoteSummary(result) {
|
|
|
5876
5938
|
// #6094: same rule for a seat that could not read the artifact.
|
|
5877
5939
|
...result.source === "unverifiable" ? { unverifiable: true } : {},
|
|
5878
5940
|
// #6115: and for a seat that answered elsewhere.
|
|
5879
|
-
...result.fallback !== void 0 ? { fallback: result.fallback } : {}
|
|
5941
|
+
...result.fallback !== void 0 ? { fallback: result.fallback } : {},
|
|
5942
|
+
// #6246: and for what a recovered seat was retried from.
|
|
5943
|
+
...result.retriedFrom !== void 0 ? { retriedFrom: result.retriedFrom } : {}
|
|
5880
5944
|
};
|
|
5881
5945
|
}
|
|
5882
5946
|
function panelDegradationWarning(errorCount, total) {
|
|
@@ -6236,17 +6300,18 @@ function toVoterSummaries(votes) {
|
|
|
6236
6300
|
// the fallback the live result stated. `model` alone showed seven
|
|
6237
6301
|
// identical values on a panel assigned three ways and could not say so.
|
|
6238
6302
|
...v.assignedCli !== void 0 ? { assignedCli: v.assignedCli } : {},
|
|
6239
|
-
...v.fallback !== void 0 ? { fallback: projectSeatFallback(v.fallback) } : {}
|
|
6303
|
+
...v.fallback !== void 0 ? { fallback: projectSeatFallback(v.fallback) } : {},
|
|
6304
|
+
// #6246: what the per-role retry replaced, so the record names both the
|
|
6305
|
+
// seat's final state and the state it recovered from. The live retry
|
|
6306
|
+
// already clipped and de-controlled the cause; this is projection only.
|
|
6307
|
+
...v.retriedFrom !== void 0 ? { retriedFrom: projectRetriedFrom(v.retriedFrom) } : {}
|
|
6240
6308
|
});
|
|
6241
6309
|
}
|
|
6242
6310
|
return summaries;
|
|
6243
6311
|
}
|
|
6244
6312
|
function reasoningFields(reasoning) {
|
|
6245
|
-
|
|
6246
|
-
return {
|
|
6247
|
-
reasoning: reasoning.slice(0, MAX_VOTER_REASONING_CHARS),
|
|
6248
|
-
reasoningTruncated: true
|
|
6249
|
-
};
|
|
6313
|
+
const { text, truncated } = clipForRecord(reasoning);
|
|
6314
|
+
return { reasoning: text, ...truncated === true ? { reasoningTruncated: true } : {} };
|
|
6250
6315
|
}
|
|
6251
6316
|
function recordDecisionFor(input) {
|
|
6252
6317
|
return input.resolvedDecision ?? outcomeToDecision(input.result, input.votes, input.errorVoided ?? false);
|
|
@@ -6260,15 +6325,22 @@ function deriveOptionFields(votes, declaredOptions) {
|
|
|
6260
6325
|
return { optionTally: tallied ?? [], optionCoverage: coverageOf(votes) };
|
|
6261
6326
|
}
|
|
6262
6327
|
function recordVersion(optionTally, optionCoverage, panelCoverage, voters, recordLevel) {
|
|
6328
|
+
const voterTier = voterTierOf(voters);
|
|
6329
|
+
if (voterTier === "1.12") return "1.12";
|
|
6263
6330
|
if (recordLevel.errorPolicy !== void 0) return "1.11";
|
|
6264
6331
|
if (recordLevel.ratifiesPr !== void 0) return "1.10";
|
|
6332
|
+
if (voterTier !== void 0) return voterTier;
|
|
6333
|
+
if (panelCoverage !== void 0) return "1.5";
|
|
6334
|
+
if (optionCoverage !== void 0) return "1.4";
|
|
6335
|
+
return optionTally !== void 0 ? "1.3" : "1.2";
|
|
6336
|
+
}
|
|
6337
|
+
function voterTierOf(voters) {
|
|
6338
|
+
if (voters.some((v) => v.retriedFrom !== void 0)) return "1.12";
|
|
6265
6339
|
if (voters.some((v) => v.assignedCli !== void 0 || v.fallback !== void 0)) return "1.9";
|
|
6266
6340
|
if (voters.some((v) => v.model !== void 0 || v.unverifiable === true)) return "1.8";
|
|
6267
6341
|
if (voters.some((v) => v.retried === true)) return "1.7";
|
|
6268
6342
|
if (voters.some((v) => v.reasoning !== void 0)) return "1.6";
|
|
6269
|
-
|
|
6270
|
-
if (optionCoverage !== void 0) return "1.4";
|
|
6271
|
-
return optionTally !== void 0 ? "1.3" : "1.2";
|
|
6343
|
+
return void 0;
|
|
6272
6344
|
}
|
|
6273
6345
|
function panelCoverageOf(votes, always) {
|
|
6274
6346
|
const erroredRoles = votes.filter((v) => v.source === "error").map((v) => v.role);
|
|
@@ -8753,6 +8825,7 @@ export {
|
|
|
8753
8825
|
DEFAULT_VOTE_TIMEOUT_MS,
|
|
8754
8826
|
isRateLimitError,
|
|
8755
8827
|
PersistentOutcomeStore,
|
|
8828
|
+
VoteRecordPrBindingSchema,
|
|
8756
8829
|
isAbsentSeat,
|
|
8757
8830
|
NoAdapterError,
|
|
8758
8831
|
resolveAdapter,
|
|
@@ -8805,7 +8878,6 @@ export {
|
|
|
8805
8878
|
createCorrelationTracker,
|
|
8806
8879
|
seatFallbacks,
|
|
8807
8880
|
panelDiversityOf,
|
|
8808
|
-
VoteRecordPrBindingSchema,
|
|
8809
8881
|
VotingStrategySchema,
|
|
8810
8882
|
ErrorPolicySchema,
|
|
8811
8883
|
VoteThresholdSchema,
|
|
@@ -8827,4 +8899,4 @@ export {
|
|
|
8827
8899
|
CONSENSUS_VOTE_TOOL_SCHEMA,
|
|
8828
8900
|
registerConsensusVoteTool
|
|
8829
8901
|
};
|
|
8830
|
-
//# sourceMappingURL=chunk-
|
|
8902
|
+
//# sourceMappingURL=chunk-XKP6IEHD.js.map
|