zelari-code 1.48.0 → 1.49.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/dist/cli/desktopConfig.js +51 -4
- package/dist/cli/desktopConfig.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +80 -0
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/kraken/candidateRegistry.js +195 -0
- package/dist/cli/kraken/candidateRegistry.js.map +1 -0
- package/dist/cli/kraken/candidateRegistry.test.js +211 -0
- package/dist/cli/kraken/candidateRegistry.test.js.map +1 -0
- package/dist/cli/kraken/completionGate.js +116 -0
- package/dist/cli/kraken/completionGate.js.map +1 -0
- package/dist/cli/kraken/completionGate.test.js +172 -0
- package/dist/cli/kraken/completionGate.test.js.map +1 -0
- package/dist/cli/kraken/metrics.js +96 -0
- package/dist/cli/kraken/metrics.js.map +1 -0
- package/dist/cli/kraken/metrics.test.js +145 -0
- package/dist/cli/kraken/metrics.test.js.map +1 -0
- package/dist/cli/kraken/selectionPlaybook.js +23 -0
- package/dist/cli/kraken/selectionPlaybook.js.map +1 -0
- package/dist/cli/kraken/selectionPlaybook.test.js +55 -0
- package/dist/cli/kraken/selectionPlaybook.test.js.map +1 -0
- package/dist/cli/kraken/turnRuntime.js +207 -0
- package/dist/cli/kraken/turnRuntime.js.map +1 -0
- package/dist/cli/kraken/turnRuntime.test.js +377 -0
- package/dist/cli/kraken/turnRuntime.test.js.map +1 -0
- package/dist/cli/kraken/verifier.js +233 -0
- package/dist/cli/kraken/verifier.js.map +1 -0
- package/dist/cli/kraken/verifier.test.js +232 -0
- package/dist/cli/kraken/verifier.test.js.map +1 -0
- package/dist/cli/kraken/verifierSettings.test.js +237 -0
- package/dist/cli/kraken/verifierSettings.test.js.map +1 -0
- package/dist/cli/kraken/verifyReport.js +115 -0
- package/dist/cli/kraken/verifyReport.js.map +1 -0
- package/dist/cli/kraken/verifyReport.test.js +111 -0
- package/dist/cli/kraken/verifyReport.test.js.map +1 -0
- package/dist/cli/main.bundled.js +1369 -111
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/provider/openai-compatible.js +40 -6
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/provider/openai-compatible.timeout.test.js +72 -0
- package/dist/cli/provider/openai-compatible.timeout.test.js.map +1 -0
- package/dist/cli/providerConfig.js +46 -0
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/runHeadless.js +85 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/toolRegistry.js +50 -6
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/krakenSelectTool.js +165 -0
- package/dist/cli/tools/krakenSelectTool.js.map +1 -0
- package/dist/cli/tools/krakenSelectTool.test.js +246 -0
- package/dist/cli/tools/krakenSelectTool.test.js.map +1 -0
- package/dist/cli/tools/taskTool.dynamicChecks.test.js +156 -0
- package/dist/cli/tools/taskTool.dynamicChecks.test.js.map +1 -0
- package/dist/cli/tools/taskTool.js +174 -6
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/dist/cli/tools/taskTool.planSafety.test.js +280 -0
- package/dist/cli/tools/taskTool.planSafety.test.js.map +1 -0
- package/dist/cli/tools/taskTool.verifyReport.test.js +140 -0
- package/dist/cli/tools/taskTool.verifyReport.test.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verifier — Kraken selection verifier (Fase 4, ADR-0020).
|
|
3
|
+
*
|
|
4
|
+
* Compares the candidate reports registered this turn (Fase 3) and produces
|
|
5
|
+
* ONE structured {@link KrakenSelectionVerdict}. Design rules:
|
|
6
|
+
*
|
|
7
|
+
* - Verifier identity DEFAULTS to the exact PARENT model (no cheap-model
|
|
8
|
+
* auto-pick — judging quality is the point). Overrides: explicit param
|
|
9
|
+
* (Fase 9 settings) > ZELARI_KRAKEN_SELECT_PROVIDER/MODEL env > parent.
|
|
10
|
+
* - Structured judging: the model must answer with a
|
|
11
|
+
* `<selection-verdict>` JSON block, parsed LENIENTLY.
|
|
12
|
+
* - `needs_more_evidence` is a first-class outcome (ties, weak evidence).
|
|
13
|
+
* - Fallback NEVER fails the parent turn: a malformed/failed verifier
|
|
14
|
+
* response degrades to `needs_more_evidence` with `degraded: true` and
|
|
15
|
+
* the parent proceeds with its own judgment.
|
|
16
|
+
* - Deterministic short-circuits (no LLM call): zero usable candidates,
|
|
17
|
+
* single usable candidate.
|
|
18
|
+
*
|
|
19
|
+
* Pure module: the LLM call is injected (`callModel`), so tests never hit
|
|
20
|
+
* the network.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the verifier identity.
|
|
24
|
+
*
|
|
25
|
+
* 1. explicit override (both provider+model required — partial overrides
|
|
26
|
+
* are treated as invalid and fall through)
|
|
27
|
+
* 2. env: ZELARI_KRAKEN_SELECT_PROVIDER + ZELARI_KRAKEN_SELECT_MODEL
|
|
28
|
+
* (model-only keeps the parent provider; provider-only is invalid)
|
|
29
|
+
* 3. parent identity — the DEFAULT (ADR-0020: verifier = parent model)
|
|
30
|
+
*/
|
|
31
|
+
export function resolveKrakenVerifier(parent, env = process.env, explicit) {
|
|
32
|
+
if (explicit?.provider?.trim() && explicit?.model?.trim()) {
|
|
33
|
+
return { provider: explicit.provider.trim(), model: explicit.model.trim() };
|
|
34
|
+
}
|
|
35
|
+
const envProvider = env.ZELARI_KRAKEN_SELECT_PROVIDER?.trim();
|
|
36
|
+
const envModel = env.ZELARI_KRAKEN_SELECT_MODEL?.trim();
|
|
37
|
+
if (envProvider && envModel)
|
|
38
|
+
return { provider: envProvider, model: envModel };
|
|
39
|
+
if (envModel)
|
|
40
|
+
return { provider: parent.provider, model: envModel };
|
|
41
|
+
// Provider-only (env or explicit) is not actionable without a model →
|
|
42
|
+
// documented fallback: inherit the parent exactly.
|
|
43
|
+
return { provider: parent.provider, model: parent.model };
|
|
44
|
+
}
|
|
45
|
+
// ── Prompt ─────────────────────────────────────────────────────────────────
|
|
46
|
+
export const VERIFIER_SYSTEM_PROMPT = [
|
|
47
|
+
'You are the Kraken selection verifier. You compare independent research',
|
|
48
|
+
'reports (candidates) about ONE task and decide which hypothesis is best',
|
|
49
|
+
'supported by OBSERVED EVIDENCE. You do not explore anything yourself —',
|
|
50
|
+
'you judge what the candidates actually observed.',
|
|
51
|
+
'',
|
|
52
|
+
'RULES:',
|
|
53
|
+
'- Evidence decides. A specific observation (file:line, command output,',
|
|
54
|
+
' test result) outweighs any amount of confident wording.',
|
|
55
|
+
'- An eloquent candidate with no observations must NOT beat a plainly',
|
|
56
|
+
' worded candidate whose claims are grounded in evidence.',
|
|
57
|
+
'- Degraded observations (timeouts, empty searches, unavailable backends)',
|
|
58
|
+
' are NOT proof of absence — never treat them as confirmation that',
|
|
59
|
+
' something does not exist or cannot happen.',
|
|
60
|
+
'- A candidate CONTRADICTED by a concrete observation loses to one',
|
|
61
|
+
' consistent with it. Weigh listed risks.',
|
|
62
|
+
'- If the leading candidates are equally (un)supported, answer',
|
|
63
|
+
' needs_more_evidence — do not guess.',
|
|
64
|
+
'- requiredChecks: 0-5 concrete, runnable checks the implementation must',
|
|
65
|
+
' pass to prove the winning hypothesis (test names, commands, behaviors).',
|
|
66
|
+
'',
|
|
67
|
+
'Answer with EXACTLY this block (valid JSON, no prose before/after):',
|
|
68
|
+
'<selection-verdict>',
|
|
69
|
+
'{',
|
|
70
|
+
' "status": "selected" | "needs_more_evidence",',
|
|
71
|
+
' "winnerIndex": <1-based index of the winning candidate, or null>,',
|
|
72
|
+
' "rationale": "2-4 sentences grounded in the evidence",',
|
|
73
|
+
' "requiredChecks": ["concrete check", "..."]',
|
|
74
|
+
'}',
|
|
75
|
+
'</selection-verdict>',
|
|
76
|
+
].join('\n');
|
|
77
|
+
function renderCandidate(entry) {
|
|
78
|
+
if (entry.status === 'malformed') {
|
|
79
|
+
return [
|
|
80
|
+
`## Candidate #${entry.index} — UNUSABLE (malformed report: ${entry.error})`,
|
|
81
|
+
'This candidate cannot win; it is listed for completeness.',
|
|
82
|
+
].join('\n');
|
|
83
|
+
}
|
|
84
|
+
const lines = [`## Candidate #${entry.index} — ${entry.report.hypothesis || '(no hypothesis stated)'}`];
|
|
85
|
+
if (entry.report.evidence.length === 0) {
|
|
86
|
+
lines.push('Evidence: NONE (unsupported hypothesis)');
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lines.push('Evidence:');
|
|
90
|
+
for (const e of entry.report.evidence) {
|
|
91
|
+
const tag = e.degraded ? 'DEGRADED — inconclusive, NOT proof of absence' : 'OK';
|
|
92
|
+
lines.push(`- [${tag}] ${e.claim}${e.basis ? ` (basis: ${e.basis})` : ''}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (entry.report.risks.length > 0) {
|
|
96
|
+
lines.push('Risks:');
|
|
97
|
+
for (const r of entry.report.risks)
|
|
98
|
+
lines.push(`- ${r}`);
|
|
99
|
+
}
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
/** Build the user prompt for the judging call. */
|
|
103
|
+
export function buildSelectionPrompt(task, candidates) {
|
|
104
|
+
return [
|
|
105
|
+
'TASK',
|
|
106
|
+
task || '(the current user task — judge against the candidates below)',
|
|
107
|
+
'',
|
|
108
|
+
'CANDIDATES (read-only explorer reports):',
|
|
109
|
+
...candidates.map(renderCandidate),
|
|
110
|
+
'',
|
|
111
|
+
'Decide which candidate is best supported by the evidence above.',
|
|
112
|
+
].join('\n');
|
|
113
|
+
}
|
|
114
|
+
// ── Verdict parsing ────────────────────────────────────────────────────────
|
|
115
|
+
const VERDICT_OPEN = '<selection-verdict>';
|
|
116
|
+
const VERDICT_CLOSE = '</selection-verdict>';
|
|
117
|
+
function needsMoreEvidence(rationale, fallbackReason) {
|
|
118
|
+
return {
|
|
119
|
+
status: 'needs_more_evidence',
|
|
120
|
+
winnerIndex: null,
|
|
121
|
+
rationale,
|
|
122
|
+
requiredChecks: [],
|
|
123
|
+
degraded: true,
|
|
124
|
+
...(fallbackReason ? { fallbackReason } : {}),
|
|
125
|
+
verifier: null,
|
|
126
|
+
judgedBy: 'deterministic',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse + validate the verifier response against the candidate set.
|
|
131
|
+
* Lenient on shapes, STRICT on semantics: `selected` requires a valid
|
|
132
|
+
* winnerIndex pointing at an OK candidate.
|
|
133
|
+
*/
|
|
134
|
+
export function parseSelectionVerdict(raw, candidates) {
|
|
135
|
+
const open = raw.lastIndexOf(VERDICT_OPEN);
|
|
136
|
+
const close = raw.lastIndexOf(VERDICT_CLOSE);
|
|
137
|
+
if (open === -1 || close === -1 || close < open) {
|
|
138
|
+
return { ok: false, error: 'missing verdict block' };
|
|
139
|
+
}
|
|
140
|
+
const body = raw.slice(open + VERDICT_OPEN.length, close).trim();
|
|
141
|
+
let parsed;
|
|
142
|
+
try {
|
|
143
|
+
parsed = JSON.parse(body);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return { ok: false, error: `invalid JSON (${err instanceof Error ? err.message : String(err)})` };
|
|
147
|
+
}
|
|
148
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
149
|
+
return { ok: false, error: 'verdict is not a JSON object' };
|
|
150
|
+
}
|
|
151
|
+
const obj = parsed;
|
|
152
|
+
const status = obj.status;
|
|
153
|
+
if (status !== 'selected' && status !== 'needs_more_evidence') {
|
|
154
|
+
return { ok: false, error: `invalid status (${String(status)})` };
|
|
155
|
+
}
|
|
156
|
+
const rationale = typeof obj.rationale === 'string' ? obj.rationale.trim() : '';
|
|
157
|
+
const requiredChecks = (Array.isArray(obj.requiredChecks) ? obj.requiredChecks : [])
|
|
158
|
+
.filter((c) => typeof c === 'string' && c.trim().length > 0)
|
|
159
|
+
.map((c) => c.trim())
|
|
160
|
+
.slice(0, 5);
|
|
161
|
+
if (status === 'needs_more_evidence') {
|
|
162
|
+
return {
|
|
163
|
+
ok: true,
|
|
164
|
+
verdict: {
|
|
165
|
+
status,
|
|
166
|
+
winnerIndex: null,
|
|
167
|
+
rationale: rationale || 'Candidates are not sufficiently differentiated by evidence.',
|
|
168
|
+
requiredChecks,
|
|
169
|
+
degraded: false,
|
|
170
|
+
verifier: null,
|
|
171
|
+
judgedBy: 'llm',
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const winner = obj.winnerIndex;
|
|
176
|
+
if (typeof winner !== 'number' || !Number.isInteger(winner)) {
|
|
177
|
+
return { ok: false, error: `selected without integer winnerIndex (${String(winner)})` };
|
|
178
|
+
}
|
|
179
|
+
const entry = candidates.find((c) => c.index === winner);
|
|
180
|
+
if (!entry || entry.status !== 'ok') {
|
|
181
|
+
return { ok: false, error: `winnerIndex ${winner} does not point at a usable candidate` };
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
verdict: {
|
|
186
|
+
status: 'selected',
|
|
187
|
+
winnerIndex: winner,
|
|
188
|
+
rationale: rationale || 'Selected by evidence comparison.',
|
|
189
|
+
requiredChecks,
|
|
190
|
+
degraded: false,
|
|
191
|
+
verifier: null,
|
|
192
|
+
judgedBy: 'llm',
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Run one selection. NEVER throws: every failure path degrades to a
|
|
198
|
+
* `needs_more_evidence` verdict so the parent turn always continues.
|
|
199
|
+
*/
|
|
200
|
+
export async function runKrakenSelection(opts) {
|
|
201
|
+
const okEntries = opts.candidates.filter((c) => c.status === 'ok');
|
|
202
|
+
// Deterministic short-circuit: nothing usable to compare.
|
|
203
|
+
if (okEntries.length === 0) {
|
|
204
|
+
return needsMoreEvidence('No usable candidate reports this turn (all malformed). Proceed with your own judgment.', 'no usable candidates');
|
|
205
|
+
}
|
|
206
|
+
// Deterministic short-circuit: single candidate — nothing to compare.
|
|
207
|
+
if (okEntries.length === 1) {
|
|
208
|
+
return {
|
|
209
|
+
status: 'selected',
|
|
210
|
+
winnerIndex: okEntries[0].index,
|
|
211
|
+
rationale: 'Single usable candidate this turn — selected without a comparison call.',
|
|
212
|
+
requiredChecks: [],
|
|
213
|
+
degraded: false,
|
|
214
|
+
verifier: null,
|
|
215
|
+
judgedBy: 'deterministic',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const system = VERIFIER_SYSTEM_PROMPT;
|
|
219
|
+
const user = buildSelectionPrompt(opts.task, opts.candidates);
|
|
220
|
+
let raw;
|
|
221
|
+
try {
|
|
222
|
+
raw = await opts.callModel({ system, user, identity: opts.identity });
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
return needsMoreEvidence(`Verifier call failed (${err instanceof Error ? err.message : String(err)}). Proceed with your own judgment.`, 'verifier call failed');
|
|
226
|
+
}
|
|
227
|
+
const parsed = parseSelectionVerdict(raw, opts.candidates);
|
|
228
|
+
if (!parsed.ok) {
|
|
229
|
+
return needsMoreEvidence(`Verifier response unusable (${parsed.error}). Proceed with your own judgment.`, `malformed verifier response: ${parsed.error}`);
|
|
230
|
+
}
|
|
231
|
+
return { ...parsed.verdict, verifier: opts.identity };
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../../src/cli/kraken/verifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verifier.test — Fase 4 (ADR-0020) unit tests for the Kraken selection
|
|
3
|
+
* verifier: identity resolution (parent-exact default), judging prompt
|
|
4
|
+
* rules, lenient verdict parsing, and the never-throw fallback contract.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
import { buildSelectionPrompt, parseSelectionVerdict, resolveKrakenVerifier, runKrakenSelection, VERIFIER_SYSTEM_PROMPT, } from './verifier.js';
|
|
8
|
+
const PARENT = { provider: 'grok', model: 'grok-4-fast' };
|
|
9
|
+
function okCandidate(index, hypothesis, opts = {}) {
|
|
10
|
+
return {
|
|
11
|
+
status: 'ok',
|
|
12
|
+
index,
|
|
13
|
+
description: `candidate ${index}`,
|
|
14
|
+
report: {
|
|
15
|
+
hypothesis,
|
|
16
|
+
evidence: [
|
|
17
|
+
{ claim: `claim-${index}`, basis: `file:${index}`, degraded: opts.degraded === true },
|
|
18
|
+
],
|
|
19
|
+
risks: [],
|
|
20
|
+
hasDegradedEvidence: opts.degraded === true,
|
|
21
|
+
},
|
|
22
|
+
raw: '',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function malformedCandidate(index) {
|
|
26
|
+
return {
|
|
27
|
+
status: 'malformed',
|
|
28
|
+
index,
|
|
29
|
+
description: `candidate ${index}`,
|
|
30
|
+
error: 'missing report block',
|
|
31
|
+
raw: 'no block',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const verdictJson = (o) => `<selection-verdict>\n${JSON.stringify(o, null, 2)}\n</selection-verdict>`;
|
|
35
|
+
// ── resolveKrakenVerifier ──────────────────────────────────────────────────
|
|
36
|
+
describe('resolveKrakenVerifier', () => {
|
|
37
|
+
it('defaults to the EXACT parent identity (no cheap-model routing)', () => {
|
|
38
|
+
expect(resolveKrakenVerifier(PARENT, {})).toEqual(PARENT);
|
|
39
|
+
});
|
|
40
|
+
it('model-only env keeps parent provider, overrides model', () => {
|
|
41
|
+
expect(resolveKrakenVerifier(PARENT, { ZELARI_KRAKEN_SELECT_MODEL: 'glm-4.6' })).toEqual({
|
|
42
|
+
provider: 'grok',
|
|
43
|
+
model: 'glm-4.6',
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
it('provider+model env fully overrides', () => {
|
|
47
|
+
expect(resolveKrakenVerifier(PARENT, {
|
|
48
|
+
ZELARI_KRAKEN_SELECT_PROVIDER: 'openai-compatible',
|
|
49
|
+
ZELARI_KRAKEN_SELECT_MODEL: 'gpt-5',
|
|
50
|
+
})).toEqual({ provider: 'openai-compatible', model: 'gpt-5' });
|
|
51
|
+
});
|
|
52
|
+
it('provider-only env is invalid → falls back to parent (§64)', () => {
|
|
53
|
+
expect(resolveKrakenVerifier(PARENT, { ZELARI_KRAKEN_SELECT_PROVIDER: 'glm' })).toEqual(PARENT);
|
|
54
|
+
});
|
|
55
|
+
it('explicit settings override (Fase 9) wins over env', () => {
|
|
56
|
+
expect(resolveKrakenVerifier(PARENT, { ZELARI_KRAKEN_SELECT_MODEL: 'env-model' }, { provider: 'anthropic', model: 'claude-x' })).toEqual({ provider: 'anthropic', model: 'claude-x' });
|
|
57
|
+
});
|
|
58
|
+
it('partial explicit override is ignored → env applies', () => {
|
|
59
|
+
expect(resolveKrakenVerifier(PARENT, { ZELARI_KRAKEN_SELECT_MODEL: 'env-model' }, { model: 'x' })).toEqual({ provider: 'grok', model: 'env-model' });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
// ── Prompt ─────────────────────────────────────────────────────────────────
|
|
63
|
+
describe('buildSelectionPrompt / system rules', () => {
|
|
64
|
+
it('system prompt carries the evidence-integrity rules', () => {
|
|
65
|
+
expect(VERIFIER_SYSTEM_PROMPT).toContain('NOT proof of absence');
|
|
66
|
+
expect(VERIFIER_SYSTEM_PROMPT).toContain('Evidence decides');
|
|
67
|
+
expect(VERIFIER_SYSTEM_PROMPT).toContain('needs_more_evidence');
|
|
68
|
+
});
|
|
69
|
+
it('renders hypotheses, degraded tags and malformed candidates', () => {
|
|
70
|
+
const p = buildSelectionPrompt('fix session loss', [
|
|
71
|
+
okCandidate(1, 'token rotation race'),
|
|
72
|
+
okCandidate(2, 'cookie expiry bug', { degraded: true }),
|
|
73
|
+
malformedCandidate(3),
|
|
74
|
+
]);
|
|
75
|
+
expect(p).toContain('TASK');
|
|
76
|
+
expect(p).toContain('Candidate #1 — token rotation race');
|
|
77
|
+
expect(p).toContain('DEGRADED — inconclusive, NOT proof of absence');
|
|
78
|
+
expect(p).toContain('Candidate #3 — UNUSABLE');
|
|
79
|
+
expect(p).toContain('claim-1 (basis: file:1)');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
// ── parseSelectionVerdict ──────────────────────────────────────────────────
|
|
83
|
+
describe('parseSelectionVerdict', () => {
|
|
84
|
+
const two = [okCandidate(1, 'h1'), okCandidate(2, 'h2')];
|
|
85
|
+
it('accepts a valid selected verdict', () => {
|
|
86
|
+
const r = parseSelectionVerdict(verdictJson({ status: 'selected', winnerIndex: 2, rationale: 'grounded', requiredChecks: ['test:session'] }), two);
|
|
87
|
+
expect(r.ok).toBe(true);
|
|
88
|
+
if (r.ok) {
|
|
89
|
+
expect(r.verdict.status).toBe('selected');
|
|
90
|
+
expect(r.verdict.winnerIndex).toBe(2);
|
|
91
|
+
expect(r.verdict.requiredChecks).toEqual(['test:session']);
|
|
92
|
+
expect(r.verdict.degraded).toBe(false);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
it('accepts needs_more_evidence and ignores any winner', () => {
|
|
96
|
+
const r = parseSelectionVerdict(verdictJson({ status: 'needs_more_evidence', winnerIndex: 1, rationale: 'tie' }), two);
|
|
97
|
+
expect(r.ok).toBe(true);
|
|
98
|
+
if (r.ok)
|
|
99
|
+
expect(r.verdict.winnerIndex).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
it('rejects non-integer winnerIndex', () => {
|
|
102
|
+
expect(parseSelectionVerdict(verdictJson({ status: 'selected', winnerIndex: 1.5 }), two)).toMatchObject({ ok: false });
|
|
103
|
+
expect(parseSelectionVerdict(verdictJson({ status: 'selected', winnerIndex: '2' }), two)).toMatchObject({ ok: false });
|
|
104
|
+
});
|
|
105
|
+
it('rejects a winner pointing at a malformed candidate', () => {
|
|
106
|
+
const r = parseSelectionVerdict(verdictJson({ status: 'selected', winnerIndex: 3 }), [okCandidate(1, 'h1'), okCandidate(2, 'h2'), malformedCandidate(3)]);
|
|
107
|
+
expect(r).toMatchObject({ ok: false });
|
|
108
|
+
});
|
|
109
|
+
it('rejects missing block / invalid JSON / bad status', () => {
|
|
110
|
+
expect(parseSelectionVerdict('no block here', two)).toMatchObject({ ok: false });
|
|
111
|
+
expect(parseSelectionVerdict('<selection-verdict>{bad json}</selection-verdict>', two)).toMatchObject({ ok: false });
|
|
112
|
+
expect(parseSelectionVerdict(verdictJson({ status: 'maybe' }), two)).toMatchObject({ ok: false });
|
|
113
|
+
});
|
|
114
|
+
it('clamps requiredChecks to 5 and drops non-strings', () => {
|
|
115
|
+
const r = parseSelectionVerdict(verdictJson({
|
|
116
|
+
status: 'selected',
|
|
117
|
+
winnerIndex: 1,
|
|
118
|
+
requiredChecks: ['a', 'b', 'c', 'd', 'e', 'f', 42, null],
|
|
119
|
+
}), two);
|
|
120
|
+
expect(r.ok).toBe(true);
|
|
121
|
+
if (r.ok)
|
|
122
|
+
expect(r.verdict.requiredChecks).toEqual(['a', 'b', 'c', 'd', 'e']);
|
|
123
|
+
});
|
|
124
|
+
it('uses the LAST verdict block when the model rambles', () => {
|
|
125
|
+
const raw = `prose <selection-verdict>{"status":"needs_more_evidence"}</selection-verdict> more prose
|
|
126
|
+
<selection-verdict>{"status":"selected","winnerIndex":1}</selection-verdict>`;
|
|
127
|
+
const r = parseSelectionVerdict(raw, two);
|
|
128
|
+
expect(r.ok).toBe(true);
|
|
129
|
+
if (r.ok)
|
|
130
|
+
expect(r.verdict.status).toBe('selected');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
// ── runKrakenSelection ─────────────────────────────────────────────────────
|
|
134
|
+
describe('runKrakenSelection', () => {
|
|
135
|
+
it('zero usable candidates → deterministic needs_more_evidence, no LLM call', async () => {
|
|
136
|
+
let called = 0;
|
|
137
|
+
const v = await runKrakenSelection({
|
|
138
|
+
task: 't',
|
|
139
|
+
candidates: [malformedCandidate(1), malformedCandidate(2)],
|
|
140
|
+
identity: PARENT,
|
|
141
|
+
callModel: async () => {
|
|
142
|
+
called++;
|
|
143
|
+
return '';
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
expect(called).toBe(0);
|
|
147
|
+
expect(v.status).toBe('needs_more_evidence');
|
|
148
|
+
expect(v.degraded).toBe(true);
|
|
149
|
+
expect(v.fallbackReason).toBe('no usable candidates');
|
|
150
|
+
});
|
|
151
|
+
it('single usable candidate → deterministic selection, no LLM call', async () => {
|
|
152
|
+
let called = 0;
|
|
153
|
+
const v = await runKrakenSelection({
|
|
154
|
+
task: 't',
|
|
155
|
+
candidates: [okCandidate(1, 'only'), malformedCandidate(2)],
|
|
156
|
+
identity: PARENT,
|
|
157
|
+
callModel: async () => {
|
|
158
|
+
called++;
|
|
159
|
+
return '';
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
expect(called).toBe(0);
|
|
163
|
+
expect(v.status).toBe('selected');
|
|
164
|
+
expect(v.winnerIndex).toBe(1);
|
|
165
|
+
expect(v.judgedBy).toBe('deterministic');
|
|
166
|
+
});
|
|
167
|
+
it('two candidates → LLM judges with system+user and identity rides the verdict', async () => {
|
|
168
|
+
let seen = null;
|
|
169
|
+
const v = await runKrakenSelection({
|
|
170
|
+
task: 'fix intermittent session loss',
|
|
171
|
+
candidates: [okCandidate(1, 'race in refresh'), okCandidate(2, 'cookie expiry')],
|
|
172
|
+
identity: PARENT,
|
|
173
|
+
callModel: async ({ system, user }) => {
|
|
174
|
+
seen = { system, user };
|
|
175
|
+
return verdictJson({
|
|
176
|
+
status: 'selected',
|
|
177
|
+
winnerIndex: 1,
|
|
178
|
+
rationale: 'grounded in file evidence',
|
|
179
|
+
requiredChecks: ['regression test for concurrent refresh'],
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
expect(seen).not.toBeNull();
|
|
184
|
+
expect(seen.system).toContain('selection verifier');
|
|
185
|
+
expect(seen.user).toContain('fix intermittent session loss');
|
|
186
|
+
expect(v).toMatchObject({
|
|
187
|
+
status: 'selected',
|
|
188
|
+
winnerIndex: 1,
|
|
189
|
+
judgedBy: 'llm',
|
|
190
|
+
degraded: false,
|
|
191
|
+
verifier: PARENT,
|
|
192
|
+
});
|
|
193
|
+
expect(v.requiredChecks).toEqual(['regression test for concurrent refresh']);
|
|
194
|
+
});
|
|
195
|
+
it('model says tie → clean needs_more_evidence (not degraded)', async () => {
|
|
196
|
+
const v = await runKrakenSelection({
|
|
197
|
+
task: 't',
|
|
198
|
+
candidates: [okCandidate(1, 'a'), okCandidate(2, 'b')],
|
|
199
|
+
identity: PARENT,
|
|
200
|
+
callModel: async () => verdictJson({ status: 'needs_more_evidence', rationale: 'equally supported' }),
|
|
201
|
+
});
|
|
202
|
+
expect(v.status).toBe('needs_more_evidence');
|
|
203
|
+
expect(v.degraded).toBe(false);
|
|
204
|
+
expect(v.judgedBy).toBe('llm');
|
|
205
|
+
});
|
|
206
|
+
it('callModel throws (timeout/429/network) → degraded, run continues', async () => {
|
|
207
|
+
const v = await runKrakenSelection({
|
|
208
|
+
task: 't',
|
|
209
|
+
candidates: [okCandidate(1, 'a'), okCandidate(2, 'b')],
|
|
210
|
+
identity: PARENT,
|
|
211
|
+
callModel: async () => {
|
|
212
|
+
throw new Error('429 rate limited');
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
expect(v.status).toBe('needs_more_evidence');
|
|
216
|
+
expect(v.degraded).toBe(true);
|
|
217
|
+
expect(v.fallbackReason).toBe('verifier call failed');
|
|
218
|
+
expect(v.rationale).toContain('429');
|
|
219
|
+
});
|
|
220
|
+
it('malformed verifier response → degraded with reason', async () => {
|
|
221
|
+
const v = await runKrakenSelection({
|
|
222
|
+
task: 't',
|
|
223
|
+
candidates: [okCandidate(1, 'a'), okCandidate(2, 'b')],
|
|
224
|
+
identity: PARENT,
|
|
225
|
+
callModel: async () => 'I think candidate 1 is nicer',
|
|
226
|
+
});
|
|
227
|
+
expect(v.status).toBe('needs_more_evidence');
|
|
228
|
+
expect(v.degraded).toBe(true);
|
|
229
|
+
expect(v.fallbackReason).toContain('malformed verifier response');
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
//# sourceMappingURL=verifier.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.test.js","sourceRoot":"","sources":["../../../src/cli/kraken/verifier.test.ts"],"names":[],"mappings":"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"}
|