@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.
@@ -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 passed = !opts.strict;
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 && !passed ? 'UNVERIFIED' : 'SKIP'}: ${msg}`,
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
- const result = await runVerify(options);
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 {
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"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"}
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 baseline = await this.ladderRunner(rungs, this.config.projectRoot, this.config.ladderOptions);
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
- if (record.score > bestSoFar) {
477
- bestSoFar = record.score;
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 {