@miller-tech/uap 1.56.0 → 1.58.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/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +7 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +3 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +16 -0
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/verify.d.ts +12 -1
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +60 -5
- package/dist/cli/verify.js.map +1 -1
- package/dist/delivery/acceptance-judge.d.ts +59 -0
- package/dist/delivery/acceptance-judge.d.ts.map +1 -0
- package/dist/delivery/acceptance-judge.js +203 -0
- package/dist/delivery/acceptance-judge.js.map +1 -0
- package/dist/delivery/convergence-loop.d.ts +28 -0
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +55 -3
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
package/dist/cli/verify.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* (--strict, --runtime-only) to block a session from finishing on broken code.
|
|
10
10
|
*/
|
|
11
11
|
import { detectRungs, runTieredLadder, tierOf } from '../delivery/verifier-ladder.js';
|
|
12
|
+
import { runAcceptanceGate, formatAcceptanceReport } from '../delivery/acceptance-judge.js';
|
|
12
13
|
/** Core verify logic — pure-ish (no process.exit), so it is unit-testable. */
|
|
13
14
|
export async function runVerify(opts = {}) {
|
|
14
15
|
const dir = opts.dir ?? process.cwd();
|
|
@@ -20,10 +21,20 @@ export async function runVerify(opts = {}) {
|
|
|
20
21
|
if (opts.runtimeOnly) {
|
|
21
22
|
rungs = rungs.filter((r) => tierOf(r) === 'runtime');
|
|
22
23
|
}
|
|
24
|
+
// The acceptance gate (behavioral completeness) can run with or without
|
|
25
|
+
// runtime gates; compute it once and fold it into the result below.
|
|
26
|
+
const acceptance = opts.acceptanceSpec && opts.acceptanceExecutor
|
|
27
|
+
? await runAcceptanceGate({ spec: opts.acceptanceSpec, projectRoot: dir, executor: opts.acceptanceExecutor })
|
|
28
|
+
: undefined;
|
|
29
|
+
// Acceptance is a JUDGMENT: it only affects the exit code under --strict, so
|
|
30
|
+
// its nondeterminism never hard-blocks a session by default.
|
|
31
|
+
const acceptanceBlocks = Boolean(acceptance && !acceptance.passed && opts.strict);
|
|
32
|
+
const acceptanceReport = acceptance ? `\n${formatAcceptanceReport(acceptance)}` : '';
|
|
23
33
|
if (rungs.length === 0) {
|
|
24
34
|
// Fail-closed in strict mode: refuse to report "verified" when nothing could
|
|
25
35
|
// actually be checked. Otherwise it's an honest no-op, not a failure.
|
|
26
|
-
const
|
|
36
|
+
const gatePassed = !opts.strict;
|
|
37
|
+
const passed = gatePassed && !acceptanceBlocks;
|
|
27
38
|
const msg = opts.runtimeOnly
|
|
28
39
|
? 'no runnable artifact detected — nothing to execute'
|
|
29
40
|
: 'no verifiable gates detected (no build/test/runnable artifact)';
|
|
@@ -31,8 +42,9 @@ export async function runVerify(opts = {}) {
|
|
|
31
42
|
passed,
|
|
32
43
|
exitCode: passed ? 0 : 1,
|
|
33
44
|
empty: true,
|
|
34
|
-
report: `${opts.strict && !
|
|
45
|
+
report: `${opts.strict && !gatePassed ? 'UNVERIFIED' : 'SKIP'}: ${msg}${acceptanceReport}`,
|
|
35
46
|
rungs,
|
|
47
|
+
acceptance,
|
|
36
48
|
};
|
|
37
49
|
}
|
|
38
50
|
const maxTier = opts.full ? 'deploy-dev' : 'runtime';
|
|
@@ -40,7 +52,7 @@ export async function runVerify(opts = {}) {
|
|
|
40
52
|
maxTier,
|
|
41
53
|
...(opts.timeoutMs ? { timeoutMs: opts.timeoutMs } : {}),
|
|
42
54
|
});
|
|
43
|
-
const report = formatReport(ladder);
|
|
55
|
+
const report = formatReport(ladder) + acceptanceReport;
|
|
44
56
|
// Exit-code contract for the Stop hook: 0 = verified, 1 = a REAL gate failure
|
|
45
57
|
// (the code is broken), 3 = INFRA failure (gate timed out / could not spawn /
|
|
46
58
|
// killed by signal). The hook hard-blocks only on 1; 3 fails OPEN so a flaky
|
|
@@ -50,13 +62,16 @@ export async function runVerify(opts = {}) {
|
|
|
50
62
|
const realFailures = ladder.results.filter((r) => !r.passed && !r.skipped && r.failureReason === 'exit');
|
|
51
63
|
exitCode = realFailures.length > 0 ? 1 : 3;
|
|
52
64
|
}
|
|
65
|
+
if (acceptanceBlocks && exitCode === 0)
|
|
66
|
+
exitCode = 1;
|
|
53
67
|
return {
|
|
54
|
-
passed: ladder.passed,
|
|
68
|
+
passed: ladder.passed && !acceptanceBlocks,
|
|
55
69
|
exitCode,
|
|
56
70
|
empty: false,
|
|
57
71
|
report,
|
|
58
72
|
ladder,
|
|
59
73
|
rungs,
|
|
74
|
+
acceptance,
|
|
60
75
|
};
|
|
61
76
|
}
|
|
62
77
|
function formatReport(ladder) {
|
|
@@ -77,15 +92,55 @@ function formatReport(ladder) {
|
|
|
77
92
|
: `NOT VERIFIED ✗ (${Math.round(ladder.score * 100)}% of gates passed)`;
|
|
78
93
|
return `${header}\n${lines.join('\n')}`;
|
|
79
94
|
}
|
|
95
|
+
/** Build a text executor (prompt → completion) for the acceptance judge. */
|
|
96
|
+
async function buildAcceptanceExecutor(modelPreset, endpoint) {
|
|
97
|
+
const { OpenAICompatClient } = await import('../models/openai-compat-client.js');
|
|
98
|
+
const { ModelPresets } = await import('../models/types.js');
|
|
99
|
+
const presetId = modelPreset ?? process.env.UAP_DELIVER_MODEL ?? 'qwen35-a3b';
|
|
100
|
+
const model = ModelPresets[presetId];
|
|
101
|
+
if (!model) {
|
|
102
|
+
throw new Error(`Unknown model preset '${presetId}'. Available: ${Object.keys(ModelPresets).join(', ')}`);
|
|
103
|
+
}
|
|
104
|
+
const resolved = endpoint ? { ...model, endpoint } : model;
|
|
105
|
+
// Privacy signal: the acceptance gate ships project source to the model. Warn
|
|
106
|
+
// when that endpoint is NOT local so a remote target is a conscious choice.
|
|
107
|
+
const ep = resolved.endpoint ?? process.env.UAP_INFERENCE_ENDPOINT ?? '';
|
|
108
|
+
if (ep && !/^https?:\/\/(localhost|127\.|0\.0\.0\.0|\[?::1\]?|192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.)/i.test(ep)) {
|
|
109
|
+
process.stderr.write(`uap verify: acceptance gate will send project source to a NON-LOCAL endpoint (${ep}).\n`);
|
|
110
|
+
}
|
|
111
|
+
const client = new OpenAICompatClient();
|
|
112
|
+
return async (prompt) => {
|
|
113
|
+
const r = await client.complete(resolved, prompt, { temperature: 0.1 });
|
|
114
|
+
return r.content;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
80
117
|
/** CLI entry: print the report and exit with the gate-derived code. */
|
|
81
118
|
export async function verifyCommand(options) {
|
|
82
|
-
|
|
119
|
+
// Resolve the acceptance spec (a file path) + model executor lazily so the
|
|
120
|
+
// common `uap verify` path never touches the model layer.
|
|
121
|
+
let opts = options;
|
|
122
|
+
if (options.acceptanceFile) {
|
|
123
|
+
const { readFileSync } = await import('fs');
|
|
124
|
+
let spec;
|
|
125
|
+
try {
|
|
126
|
+
spec = readFileSync(options.acceptanceFile, 'utf-8');
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
process.stderr.write(`uap verify: cannot read acceptance spec '${options.acceptanceFile}'\n`);
|
|
130
|
+
process.exit(2);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const executor = await buildAcceptanceExecutor(options.model, options.endpoint);
|
|
134
|
+
opts = { ...options, acceptanceSpec: spec, acceptanceExecutor: executor };
|
|
135
|
+
}
|
|
136
|
+
const result = await runVerify(opts);
|
|
83
137
|
if (options.json) {
|
|
84
138
|
process.stdout.write(JSON.stringify({
|
|
85
139
|
passed: result.passed,
|
|
86
140
|
empty: result.empty,
|
|
87
141
|
score: result.ladder?.score ?? null,
|
|
88
142
|
results: result.ladder?.results ?? [],
|
|
143
|
+
acceptance: result.acceptance ?? null,
|
|
89
144
|
}, null, 2) + '\n');
|
|
90
145
|
}
|
|
91
146
|
else {
|
package/dist/cli/verify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAmD,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Acceptance judge — behavioral completeness, beyond "does it crash".
|
|
3
|
+
*
|
|
4
|
+
* The execution gate proves the artifact RUNS; it cannot tell whether the spec
|
|
5
|
+
* was actually implemented. A generated game can load cleanly yet never call
|
|
6
|
+
* particles.draw(), render octopi with smooth arcs instead of pixel grids, or
|
|
7
|
+
* skip the boss every 5 levels. This gate extracts the spec's explicit
|
|
8
|
+
* requirements and judges each against the produced code (+ an optional runtime
|
|
9
|
+
* note), via a text LLM — so it works with the local model (no vision needed).
|
|
10
|
+
*
|
|
11
|
+
* It is a JUDGMENT, not a deterministic check: callers treat it as advisory or
|
|
12
|
+
* gate on it explicitly. The executor is injected (a prompt→text function), so
|
|
13
|
+
* the logic is fully unit-testable with a mock.
|
|
14
|
+
*/
|
|
15
|
+
import type { LoopExecutor } from './convergence-loop.js';
|
|
16
|
+
export interface AcceptanceCriterion {
|
|
17
|
+
requirement: string;
|
|
18
|
+
met: boolean;
|
|
19
|
+
reason: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AcceptanceResult {
|
|
22
|
+
passed: boolean;
|
|
23
|
+
/** Fraction of criteria met (1 when none were extracted). */
|
|
24
|
+
score: number;
|
|
25
|
+
criteria: AcceptanceCriterion[];
|
|
26
|
+
/** Set when the model output could not be parsed (fail-open → passed:true). */
|
|
27
|
+
parseError?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AcceptanceOptions {
|
|
30
|
+
spec: string;
|
|
31
|
+
projectRoot: string;
|
|
32
|
+
/** Model call: prompt → text. */
|
|
33
|
+
executor: LoopExecutor;
|
|
34
|
+
/** Pre-gathered evidence (overrides the file walk; for tests). */
|
|
35
|
+
evidence?: string;
|
|
36
|
+
/** One-line runtime observation (e.g. the execution gate's outputTail). */
|
|
37
|
+
runtimeNote?: string;
|
|
38
|
+
/** Max source files to include as evidence (default 40). */
|
|
39
|
+
maxFiles?: number;
|
|
40
|
+
/** Max total evidence chars (default 60000). */
|
|
41
|
+
maxChars?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Bounded walk gathering source-file evidence (path-labelled, truncated). */
|
|
44
|
+
export declare function gatherEvidence(projectRoot: string, maxFiles?: number, maxChars?: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Extract the first PARSEABLE balanced top-level JSON object from model text.
|
|
47
|
+
* If a balanced object fails to parse (e.g. a stray `{…}` fragment in the
|
|
48
|
+
* preamble), it resumes scanning from the next `{` rather than giving up.
|
|
49
|
+
*/
|
|
50
|
+
export declare function extractJsonObject(text: string): Record<string, unknown> | null;
|
|
51
|
+
/**
|
|
52
|
+
* Run the acceptance gate. Fails OPEN (passed:true, parseError set) when the
|
|
53
|
+
* model output is unparseable or the executor throws — a judgment gate must
|
|
54
|
+
* never wedge delivery on its own nondeterminism.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runAcceptanceGate(opts: AcceptanceOptions): Promise<AcceptanceResult>;
|
|
57
|
+
/** Render a short human report of an acceptance result. */
|
|
58
|
+
export declare function formatAcceptanceReport(result: AcceptanceResult): string;
|
|
59
|
+
//# sourceMappingURL=acceptance-judge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acceptance-judge.d.ts","sourceRoot":"","sources":["../../src/delivery/acceptance-judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAaD,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,SAAoB,EAAE,QAAQ,SAAoB,GAAG,MAAM,CA4CtH;AAyBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAiC9E;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyC1F;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CASvE"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Acceptance judge — behavioral completeness, beyond "does it crash".
|
|
3
|
+
*
|
|
4
|
+
* The execution gate proves the artifact RUNS; it cannot tell whether the spec
|
|
5
|
+
* was actually implemented. A generated game can load cleanly yet never call
|
|
6
|
+
* particles.draw(), render octopi with smooth arcs instead of pixel grids, or
|
|
7
|
+
* skip the boss every 5 levels. This gate extracts the spec's explicit
|
|
8
|
+
* requirements and judges each against the produced code (+ an optional runtime
|
|
9
|
+
* note), via a text LLM — so it works with the local model (no vision needed).
|
|
10
|
+
*
|
|
11
|
+
* It is a JUDGMENT, not a deterministic check: callers treat it as advisory or
|
|
12
|
+
* gate on it explicitly. The executor is injected (a prompt→text function), so
|
|
13
|
+
* the logic is fully unit-testable with a mock.
|
|
14
|
+
*/
|
|
15
|
+
import { lstatSync, readdirSync, readFileSync } from 'fs';
|
|
16
|
+
import { join, relative } from 'path';
|
|
17
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'out', 'coverage', '.uap', '.uap-deliver', 'agents', '.worktrees']);
|
|
18
|
+
const SRC_EXT = /\.(js|mjs|cjs|ts|tsx|jsx|html|css|json|py|go|rs|java|rb|md|txt)$/i;
|
|
19
|
+
/** Never ship likely-secret files into the LLM prompt (esp. for remote endpoints). */
|
|
20
|
+
const SECRET_FILE_RE = /(^\.env)|secret|credential|\.pem$|\.key$|id_rsa/i;
|
|
21
|
+
const DEFAULT_MAX_FILES = 40;
|
|
22
|
+
// Generous by default — modern local models have very large context windows
|
|
23
|
+
// (e.g. qwen3.6 ≈ 184K tokens), and truncating implementation files causes the
|
|
24
|
+
// judge to report implemented features as "not visible" (false MISS).
|
|
25
|
+
const DEFAULT_MAX_CHARS = 60_000;
|
|
26
|
+
const PER_FILE_CHARS = 20_000;
|
|
27
|
+
/** Bounded walk gathering source-file evidence (path-labelled, truncated). */
|
|
28
|
+
export function gatherEvidence(projectRoot, maxFiles = DEFAULT_MAX_FILES, maxChars = DEFAULT_MAX_CHARS) {
|
|
29
|
+
const root = projectRoot;
|
|
30
|
+
const files = [];
|
|
31
|
+
const walk = (dir, depth) => {
|
|
32
|
+
if (depth > 8 || files.length >= maxFiles)
|
|
33
|
+
return;
|
|
34
|
+
let entries;
|
|
35
|
+
try {
|
|
36
|
+
entries = readdirSync(dir);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const e of entries) {
|
|
42
|
+
if (files.length >= maxFiles)
|
|
43
|
+
return;
|
|
44
|
+
if (SKIP_DIRS.has(e) || (e.startsWith('.') && e !== '.'))
|
|
45
|
+
continue;
|
|
46
|
+
const abs = join(dir, e);
|
|
47
|
+
let st;
|
|
48
|
+
try {
|
|
49
|
+
st = lstatSync(abs); // lstat: do NOT follow symlinks (avoid cycles + escaping projectRoot)
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (st.isSymbolicLink())
|
|
55
|
+
continue;
|
|
56
|
+
if (st.isDirectory())
|
|
57
|
+
walk(abs, depth + 1);
|
|
58
|
+
else if (st.isFile() && SRC_EXT.test(e) && e !== 'package-lock.json' && !SECRET_FILE_RE.test(e) && st.size <= 200_000)
|
|
59
|
+
files.push(abs);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
walk(root, 0);
|
|
63
|
+
let out = '';
|
|
64
|
+
let used = 0;
|
|
65
|
+
for (const abs of files) {
|
|
66
|
+
if (used >= maxChars)
|
|
67
|
+
break;
|
|
68
|
+
let content;
|
|
69
|
+
try {
|
|
70
|
+
content = readFileSync(abs, 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const rel = relative(root, abs);
|
|
76
|
+
const budget = Math.min(content.length, maxChars - used, PER_FILE_CHARS);
|
|
77
|
+
out += `\n=== ${rel} ===\n${content.slice(0, budget)}\n`;
|
|
78
|
+
used += budget;
|
|
79
|
+
}
|
|
80
|
+
return out.trim();
|
|
81
|
+
}
|
|
82
|
+
function buildPrompt(spec, evidence, runtimeNote) {
|
|
83
|
+
return [
|
|
84
|
+
'You are a strict acceptance reviewer. Decide whether the IMPLEMENTATION satisfies',
|
|
85
|
+
'the EXPLICIT, checkable requirements in the SPEC. Judge ONLY from the code shown',
|
|
86
|
+
'(and the runtime note if given) — do not assume anything not visible.',
|
|
87
|
+
'',
|
|
88
|
+
'Extract each concrete, verifiable requirement from the spec (ignore vague aesthetic',
|
|
89
|
+
'wishes). For each, decide if the code clearly implements it. Be conservative: if the',
|
|
90
|
+
'code does not show it, mark it not met.',
|
|
91
|
+
'',
|
|
92
|
+
'=== SPEC ===',
|
|
93
|
+
spec.slice(0, 6_000),
|
|
94
|
+
'',
|
|
95
|
+
runtimeNote ? `=== RUNTIME OBSERVATION ===\n${runtimeNote}\n` : '',
|
|
96
|
+
'=== IMPLEMENTATION (code) ===',
|
|
97
|
+
evidence.slice(0, 64_000),
|
|
98
|
+
'',
|
|
99
|
+
'Respond with ONLY a JSON object, no prose, no code fences:',
|
|
100
|
+
'{"criteria":[{"requirement":"<short>","met":true|false,"reason":"<short>"}],"pass":true|false}',
|
|
101
|
+
'Set "pass" to true only if every important requirement is met.',
|
|
102
|
+
].join('\n');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Extract the first PARSEABLE balanced top-level JSON object from model text.
|
|
106
|
+
* If a balanced object fails to parse (e.g. a stray `{…}` fragment in the
|
|
107
|
+
* preamble), it resumes scanning from the next `{` rather than giving up.
|
|
108
|
+
*/
|
|
109
|
+
export function extractJsonObject(text) {
|
|
110
|
+
let from = text.indexOf('{');
|
|
111
|
+
while (from !== -1) {
|
|
112
|
+
let depth = 0;
|
|
113
|
+
let inStr = false;
|
|
114
|
+
let esc = false;
|
|
115
|
+
let end = -1;
|
|
116
|
+
for (let i = from; i < text.length; i++) {
|
|
117
|
+
const ch = text[i];
|
|
118
|
+
if (inStr) {
|
|
119
|
+
if (esc)
|
|
120
|
+
esc = false;
|
|
121
|
+
else if (ch === '\\')
|
|
122
|
+
esc = true;
|
|
123
|
+
else if (ch === '"')
|
|
124
|
+
inStr = false;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (ch === '"')
|
|
128
|
+
inStr = true;
|
|
129
|
+
else if (ch === '{')
|
|
130
|
+
depth++;
|
|
131
|
+
else if (ch === '}') {
|
|
132
|
+
depth--;
|
|
133
|
+
if (depth === 0) {
|
|
134
|
+
end = i;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (end === -1)
|
|
140
|
+
return null; // no balanced close — give up
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(text.slice(from, end + 1));
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
from = text.indexOf('{', from + 1); // malformed — try the next candidate
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Run the acceptance gate. Fails OPEN (passed:true, parseError set) when the
|
|
152
|
+
* model output is unparseable or the executor throws — a judgment gate must
|
|
153
|
+
* never wedge delivery on its own nondeterminism.
|
|
154
|
+
*/
|
|
155
|
+
export async function runAcceptanceGate(opts) {
|
|
156
|
+
const evidence = opts.evidence ?? gatherEvidence(opts.projectRoot, opts.maxFiles, opts.maxChars);
|
|
157
|
+
if (!evidence.trim()) {
|
|
158
|
+
return { passed: true, score: 1, criteria: [], parseError: 'no source evidence found' };
|
|
159
|
+
}
|
|
160
|
+
let raw;
|
|
161
|
+
try {
|
|
162
|
+
raw = await opts.executor(buildPrompt(opts.spec, evidence, opts.runtimeNote));
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
return { passed: true, score: 1, criteria: [], parseError: `executor error: ${String(e).slice(0, 120)}` };
|
|
166
|
+
}
|
|
167
|
+
const parsed = extractJsonObject(raw);
|
|
168
|
+
const rawCriteria = Array.isArray(parsed?.criteria) ? parsed.criteria : null;
|
|
169
|
+
if (!parsed || !rawCriteria) {
|
|
170
|
+
return { passed: true, score: 1, criteria: [], parseError: 'unparseable judge verdict' };
|
|
171
|
+
}
|
|
172
|
+
const criteria = rawCriteria
|
|
173
|
+
.map((c) => {
|
|
174
|
+
const o = (c ?? {});
|
|
175
|
+
return {
|
|
176
|
+
requirement: String(o.requirement ?? o.text ?? '').slice(0, 300),
|
|
177
|
+
met: o.met === true,
|
|
178
|
+
reason: String(o.reason ?? '').slice(0, 300),
|
|
179
|
+
};
|
|
180
|
+
})
|
|
181
|
+
.filter((c) => c.requirement);
|
|
182
|
+
if (criteria.length === 0) {
|
|
183
|
+
return { passed: true, score: 1, criteria: [], parseError: 'no criteria extracted' };
|
|
184
|
+
}
|
|
185
|
+
const metCount = criteria.filter((c) => c.met).length;
|
|
186
|
+
const score = metCount / criteria.length;
|
|
187
|
+
// Ignore the model's self-reported "pass" (unreliable) and require every
|
|
188
|
+
// extracted criterion to be met — conservative, matching the gate's purpose.
|
|
189
|
+
const passed = metCount === criteria.length;
|
|
190
|
+
return { passed, score, criteria };
|
|
191
|
+
}
|
|
192
|
+
/** Render a short human report of an acceptance result. */
|
|
193
|
+
export function formatAcceptanceReport(result) {
|
|
194
|
+
if (result.parseError && result.criteria.length === 0) {
|
|
195
|
+
return `ACCEPTANCE: skipped (${result.parseError})`;
|
|
196
|
+
}
|
|
197
|
+
const head = result.passed
|
|
198
|
+
? `ACCEPTANCE ✓ (${result.criteria.length}/${result.criteria.length} requirements met)`
|
|
199
|
+
: `ACCEPTANCE ✗ (${result.criteria.filter((c) => c.met).length}/${result.criteria.length} requirements met)`;
|
|
200
|
+
const lines = result.criteria.map((c) => ` [${c.met ? 'MET ' : 'MISS'}] ${c.requirement}${c.met ? '' : ` — ${c.reason}`}`);
|
|
201
|
+
return [head, ...lines].join('\n');
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=acceptance-judge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acceptance-judge.js","sourceRoot":"","sources":["../../src/delivery/acceptance-judge.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,6 +77,9 @@ export interface IterationRecord {
|
|
|
77
77
|
candidates?: CandidateSummary[];
|
|
78
78
|
/** Judge rationale when a tie-break decided the winner (explorer mode) */
|
|
79
79
|
judgeRationale?: string;
|
|
80
|
+
/** Fraction of spec criteria met when the acceptance gate ran (0..1); undefined
|
|
81
|
+
* when no acceptance gate is configured or objective gates didn't pass. */
|
|
82
|
+
acceptanceMet?: number;
|
|
80
83
|
durationMs: number;
|
|
81
84
|
}
|
|
82
85
|
export interface DeliveryResult {
|
|
@@ -205,17 +208,42 @@ export interface ConvergenceConfig {
|
|
|
205
208
|
}
|
|
206
209
|
/** Default prompt strategy: lean contract + structured retry context. */
|
|
207
210
|
export declare const defaultPromptBuilder: PromptBuilder;
|
|
211
|
+
/**
|
|
212
|
+
* Optional behavioral-completeness gate, run AFTER objective gates pass: judges
|
|
213
|
+
* whether the spec's requirements are actually implemented and returns
|
|
214
|
+
* `{ passed, feedback }` (feedback = the unmet criteria). Injected by the CLI so
|
|
215
|
+
* the loop stays decoupled from the acceptance-judge module. The implementation
|
|
216
|
+
* is expected to fail open internally (a judgment must not wedge delivery).
|
|
217
|
+
*/
|
|
218
|
+
export type AcceptanceGate = (projectRoot: string) => Promise<{
|
|
219
|
+
passed: boolean;
|
|
220
|
+
feedback: string;
|
|
221
|
+
/** Fraction of spec criteria met (0..1); lets the loop see acceptance progress. */
|
|
222
|
+
score?: number;
|
|
223
|
+
}>;
|
|
208
224
|
export declare class ConvergenceLoop {
|
|
209
225
|
private readonly config;
|
|
210
226
|
private readonly executor;
|
|
211
227
|
private readonly ladderRunner;
|
|
212
228
|
private readonly applier;
|
|
213
229
|
private readonly promptBuilder;
|
|
230
|
+
private readonly acceptanceGate?;
|
|
214
231
|
constructor(config: ConvergenceConfig, executor: LoopExecutor, seams?: {
|
|
215
232
|
ladderRunner?: LadderRunner;
|
|
216
233
|
applier?: Applier;
|
|
217
234
|
promptBuilder?: PromptBuilder;
|
|
235
|
+
acceptanceGate?: AcceptanceGate;
|
|
218
236
|
});
|
|
237
|
+
/**
|
|
238
|
+
* Behavioral-completeness check, run ONCE on a committed verdict (the baseline
|
|
239
|
+
* or a turn's final/winner ladder) — never per explorer candidate. When the
|
|
240
|
+
* objective gates passed but the spec's requirements aren't all met, flip the
|
|
241
|
+
* verdict to not-passed and append the gaps as feedback so the loop iterates to
|
|
242
|
+
* complete the spec. Fails open (returns the objective verdict) on any error.
|
|
243
|
+
* Returns the (possibly-flipped) ladder and the criteria-met fraction so the
|
|
244
|
+
* caller can treat acceptance progress as forward motion for stagnation.
|
|
245
|
+
*/
|
|
246
|
+
private judgeAcceptance;
|
|
219
247
|
/** Single-candidate turn: execute → apply → verify. */
|
|
220
248
|
private runSingleTurn;
|
|
221
249
|
/** Explorer turn: best-of-N candidates, commit the winner (Phase 2). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convergence-loop.d.ts","sourceRoot":"","sources":["../../src/delivery/convergence-loop.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"convergence-loop.d.ts","sourceRoot":"","sources":["../../src/delivery/convergence-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -181,12 +181,40 @@ export class ConvergenceLoop {
|
|
|
181
181
|
ladderRunner;
|
|
182
182
|
applier;
|
|
183
183
|
promptBuilder;
|
|
184
|
+
acceptanceGate;
|
|
184
185
|
constructor(config, executor, seams = {}) {
|
|
185
186
|
this.config = config;
|
|
186
187
|
this.executor = executor;
|
|
187
188
|
this.ladderRunner = seams.ladderRunner ?? runLadder;
|
|
188
189
|
this.applier = seams.applier ?? applyFileBlocks;
|
|
189
190
|
this.promptBuilder = seams.promptBuilder ?? defaultPromptBuilder;
|
|
191
|
+
this.acceptanceGate = seams.acceptanceGate;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Behavioral-completeness check, run ONCE on a committed verdict (the baseline
|
|
195
|
+
* or a turn's final/winner ladder) — never per explorer candidate. When the
|
|
196
|
+
* objective gates passed but the spec's requirements aren't all met, flip the
|
|
197
|
+
* verdict to not-passed and append the gaps as feedback so the loop iterates to
|
|
198
|
+
* complete the spec. Fails open (returns the objective verdict) on any error.
|
|
199
|
+
* Returns the (possibly-flipped) ladder and the criteria-met fraction so the
|
|
200
|
+
* caller can treat acceptance progress as forward motion for stagnation.
|
|
201
|
+
*/
|
|
202
|
+
async judgeAcceptance(ladder) {
|
|
203
|
+
if (!this.acceptanceGate || !ladder.passed)
|
|
204
|
+
return { ladder };
|
|
205
|
+
let acc;
|
|
206
|
+
try {
|
|
207
|
+
acc = await this.acceptanceGate(this.config.projectRoot);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return { ladder }; // fail open — the judge must never block delivery
|
|
211
|
+
}
|
|
212
|
+
if (acc.passed || !acc.feedback)
|
|
213
|
+
return { ladder, acceptanceMet: acc.score };
|
|
214
|
+
return {
|
|
215
|
+
ladder: { ...ladder, passed: false, feedback: `${ladder.feedback}\n\n${acc.feedback}`.trim() },
|
|
216
|
+
acceptanceMet: acc.score,
|
|
217
|
+
};
|
|
190
218
|
}
|
|
191
219
|
/** Single-candidate turn: execute → apply → verify. */
|
|
192
220
|
async runSingleTurn(prompt, rungs, executor, ladderRunner, applyOptions) {
|
|
@@ -308,6 +336,7 @@ export class ConvergenceLoop {
|
|
|
308
336
|
const untilDelivered = this.config.untilDelivered ?? false;
|
|
309
337
|
const maxTurnsCeiling = Math.max(maxTurns, this.config.maxTurnsCeiling ?? DEFAULT_MAX_TURNS_CEILING);
|
|
310
338
|
let bestSoFar = -1;
|
|
339
|
+
let bestAcceptance = -1;
|
|
311
340
|
let stagnantTurns = 0;
|
|
312
341
|
let executor = this.executor;
|
|
313
342
|
let explorerSettings = this.config.explorer;
|
|
@@ -315,8 +344,11 @@ export class ConvergenceLoop {
|
|
|
315
344
|
const history = [];
|
|
316
345
|
const previousOutputChars = this.config.previousOutputChars ?? DEFAULT_PREVIOUS_OUTPUT_CHARS;
|
|
317
346
|
// Baseline: a green tree means there is nothing for the loop to deliver.
|
|
347
|
+
// Acceptance gaps count as "not delivered", so an objective-green project
|
|
348
|
+
// with an incomplete spec still runs turns instead of short-circuiting.
|
|
318
349
|
if (this.config.baselineCheck ?? true) {
|
|
319
|
-
const
|
|
350
|
+
const rawBaseline = await this.ladderRunner(rungs, this.config.projectRoot, this.config.ladderOptions);
|
|
351
|
+
const baseline = (await this.judgeAcceptance(rawBaseline)).ladder;
|
|
320
352
|
if (baseline.passed) {
|
|
321
353
|
return {
|
|
322
354
|
success: true,
|
|
@@ -422,6 +454,15 @@ export class ConvergenceLoop {
|
|
|
422
454
|
const outcome = explorerSettings
|
|
423
455
|
? await this.runExplorerTurn(instruction, prompt, rungs, explorerSettings, executor, ladderRunner, applyOptions)
|
|
424
456
|
: await this.runSingleTurn(prompt, rungs, executor, ladderRunner, applyOptions);
|
|
457
|
+
// Acceptance: judge ONCE on this turn's committed verdict (single-turn
|
|
458
|
+
// result or explorer winner) — not per candidate — flipping it to
|
|
459
|
+
// not-passed with gap feedback when the spec isn't fully implemented.
|
|
460
|
+
let acceptanceMet;
|
|
461
|
+
if (outcome.ladder) {
|
|
462
|
+
const judged = await this.judgeAcceptance(outcome.ladder);
|
|
463
|
+
outcome.ladder = judged.ladder;
|
|
464
|
+
acceptanceMet = judged.acceptanceMet;
|
|
465
|
+
}
|
|
425
466
|
finalOutput = outcome.output || finalOutput;
|
|
426
467
|
if (outcome.ladder) {
|
|
427
468
|
finalFeedback = outcome.ladder.feedback;
|
|
@@ -437,6 +478,7 @@ export class ConvergenceLoop {
|
|
|
437
478
|
strategy: outcome.strategy,
|
|
438
479
|
candidates: outcome.candidates,
|
|
439
480
|
judgeRationale: outcome.judgeRationale,
|
|
481
|
+
acceptanceMet,
|
|
440
482
|
durationMs: Date.now() - turnStart,
|
|
441
483
|
};
|
|
442
484
|
history.push(record);
|
|
@@ -473,8 +515,18 @@ export class ConvergenceLoop {
|
|
|
473
515
|
// Stop extending (let the loop end) once progress stalls — an
|
|
474
516
|
// unattended run must converge or give up, never spin forever.
|
|
475
517
|
if (untilDelivered) {
|
|
476
|
-
|
|
477
|
-
|
|
518
|
+
// Progress = a better objective score OR more acceptance criteria met.
|
|
519
|
+
// Without the acceptance term, an objective-green run pins score at 1.0,
|
|
520
|
+
// so acceptance-driven completion would always read as "stagnant" and the
|
|
521
|
+
// loop would give up after STAGNATION_LIMIT turns regardless of real
|
|
522
|
+
// spec progress.
|
|
523
|
+
const objectiveProgress = record.score > bestSoFar;
|
|
524
|
+
const acceptanceProgress = acceptanceMet !== undefined && acceptanceMet > bestAcceptance;
|
|
525
|
+
if (objectiveProgress || acceptanceProgress) {
|
|
526
|
+
if (objectiveProgress)
|
|
527
|
+
bestSoFar = record.score;
|
|
528
|
+
if (acceptanceProgress)
|
|
529
|
+
bestAcceptance = acceptanceMet;
|
|
478
530
|
stagnantTurns = 0;
|
|
479
531
|
}
|
|
480
532
|
else {
|