@miller-tech/uap 1.213.0 → 1.215.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.
Files changed (60) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +2 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/cli/deliver.d.ts +66 -0
  5. package/dist/cli/deliver.d.ts.map +1 -1
  6. package/dist/cli/deliver.js +227 -7
  7. package/dist/cli/deliver.js.map +1 -1
  8. package/dist/cli/verify.d.ts +5 -0
  9. package/dist/cli/verify.d.ts.map +1 -1
  10. package/dist/cli/verify.js +50 -1
  11. package/dist/cli/verify.js.map +1 -1
  12. package/dist/cli/wizard-config.d.ts.map +1 -1
  13. package/dist/cli/wizard-config.js +26 -1
  14. package/dist/cli/wizard-config.js.map +1 -1
  15. package/dist/delivery/await-run.d.ts +47 -0
  16. package/dist/delivery/await-run.d.ts.map +1 -1
  17. package/dist/delivery/await-run.js +392 -2
  18. package/dist/delivery/await-run.js.map +1 -1
  19. package/dist/delivery/capability-profile.d.ts +124 -0
  20. package/dist/delivery/capability-profile.d.ts.map +1 -0
  21. package/dist/delivery/capability-profile.js +392 -0
  22. package/dist/delivery/capability-profile.js.map +1 -0
  23. package/dist/delivery/convergence-loop.d.ts +38 -0
  24. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  25. package/dist/delivery/convergence-loop.js +98 -1
  26. package/dist/delivery/convergence-loop.js.map +1 -1
  27. package/dist/delivery/epic-controller.d.ts +9 -0
  28. package/dist/delivery/epic-controller.d.ts.map +1 -1
  29. package/dist/delivery/epic-controller.js +1 -0
  30. package/dist/delivery/epic-controller.js.map +1 -1
  31. package/dist/delivery/epic-mission.d.ts +10 -2
  32. package/dist/delivery/epic-mission.d.ts.map +1 -1
  33. package/dist/delivery/epic-mission.js +1 -1
  34. package/dist/delivery/epic-mission.js.map +1 -1
  35. package/dist/delivery/escalation.d.ts +8 -0
  36. package/dist/delivery/escalation.d.ts.map +1 -1
  37. package/dist/delivery/escalation.js +8 -1
  38. package/dist/delivery/escalation.js.map +1 -1
  39. package/dist/delivery/execution-gate.d.ts +29 -0
  40. package/dist/delivery/execution-gate.d.ts.map +1 -1
  41. package/dist/delivery/execution-gate.js +237 -9
  42. package/dist/delivery/execution-gate.js.map +1 -1
  43. package/dist/delivery/mission-acceptance.d.ts +4 -0
  44. package/dist/delivery/mission-acceptance.d.ts.map +1 -1
  45. package/dist/delivery/mission-acceptance.js +35 -1
  46. package/dist/delivery/mission-acceptance.js.map +1 -1
  47. package/dist/delivery/run-state.d.ts +31 -0
  48. package/dist/delivery/run-state.d.ts.map +1 -1
  49. package/dist/delivery/run-state.js +49 -0
  50. package/dist/delivery/run-state.js.map +1 -1
  51. package/dist/mcp-router/tools/deliver.d.ts.map +1 -1
  52. package/dist/mcp-router/tools/deliver.js +5 -2
  53. package/dist/mcp-router/tools/deliver.js.map +1 -1
  54. package/package.json +1 -1
  55. package/scripts/lib/llama-upstream.sh +64 -16
  56. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  57. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  58. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  59. package/tools/agents/scripts/confidence_escalation.py +57 -0
  60. package/tools/agents/tests/test_confidence_escalation.py +140 -2
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Capability profile — a RUNTIME measurement of how much the deliverable
3
+ * actually does, persisted like a visual baseline and compared across a run.
4
+ *
5
+ * WHY THIS EXISTS. Every other rung asks "does it run cleanly?". None asks
6
+ * "does it still do as much?", and those are different questions with opposite
7
+ * failure modes: a build can buy a clean verdict by doing LESS. Measured on
8
+ * octopus_invaders_v4 (2026-08-19) — one run took the gate score 0% -> 100%
9
+ * while the deliverable lost most of its function:
10
+ *
11
+ * draw calls 12 -> 3 rAF loops 6 -> 2
12
+ * listeners 8 -> 3 frames/7s 419 -> 25
13
+ * bundle 65.8KB -> 25.8KB
14
+ *
15
+ * The acceptance judge scored that 8/9. The execution gate passed it: it DOES
16
+ * render, it DOES tick, no console errors. `--keep-best`/no-regress
17
+ * (src/delivery/snapshot.ts) could not catch it either, because that rolls back
18
+ * on the GATE SCORE and the gate score went UP.
19
+ *
20
+ * WHAT THIS FILE IS AND IS NOT. It is the PURE half: the profile shape, the
21
+ * comparison, the feedback, and the on-disk baseline. The MEASUREMENT lives in
22
+ * execution-gate.ts, inside the browser session that rung already opens — see
23
+ * the note on ordering below. Splitting it that way is deliberate and was the
24
+ * main finding of architecture review:
25
+ *
26
+ * - Ordering becomes STRUCTURAL rather than a convention. A profile only
27
+ * exists as a byproduct of a page that loaded and ticked, so it is
28
+ * impossible to grade the capability of a build that did not run. The old
29
+ * arrangement enforced that by putting a call in the right place, which is
30
+ * the kind of rule that survives exactly until someone moves the call.
31
+ * - It stops duplicating a browser launch, a 1.5s settle, and a verbatim copy
32
+ * of the start-click selector block. That copy had ALREADY drifted on
33
+ * introduction (it gained an ArrowLeft the execution gate does not send), and
34
+ * a drifted start-click means both sides measure the menu, agree, and the
35
+ * rung goes silently inert. This repo has shipped inert probes twice.
36
+ * - It makes the reading reachable from `uap verify`, not just from deliver. A
37
+ * deliver-only bar re-creates the Generator!=Evaluator divergence this
38
+ * codebase already records: the sessions that edit files directly never go
39
+ * through the convergence loop at all, and a direct-edit session is exactly
40
+ * what produced the recorded regression.
41
+ *
42
+ * FAILS OPEN, always. An unmeasurable page, an absent browser, a baseline that
43
+ * never rendered — all return "no regression". You cannot regress from broken,
44
+ * and a capability check that guesses is worse than none: it blocks working
45
+ * deliveries on browser noise. Every threshold is deliberately loose; this
46
+ * catches gutting, not drift.
47
+ *
48
+ * WHAT THIS IS NOT: a defence against a page that is deliberately lying.
49
+ *
50
+ * Say it plainly, because the numbers look like evidence and will be trusted as
51
+ * evidence. The counters live INSIDE the page's own realm and count API
52
+ * INVOCATIONS, not rendered output — so a page that wants a rich profile can
53
+ * simply call the instrumented APIs. Measured against this exact code: a page
54
+ * drawing a single 40x40 rect, by adding 64 no-op listeners, calling
55
+ * `useProgram` 64 times on a null context, and issuing 5000 zero-area
56
+ * `fillRect`s per frame, reported 5001 draws/frame, 64 shader programs, 64 event
57
+ * types and 60fps. Nothing here detects that, and sealing more surfaces does not
58
+ * converge: you cannot instrument a realm the adversary controls from inside it.
59
+ * Closing that needs an out-of-realm oracle — screenshot pixels captured
60
+ * host-side over CDP, which the visual gate already collects.
61
+ *
62
+ * So the honest claim is narrower than the motivating incident makes it sound.
63
+ * The octopus regression was a model SIMPLIFYING under gate pressure, not a
64
+ * model gaming a metric it knew about, and that is the case this catches: an
65
+ * unintentional loss of function that every other rung reads as success. Treat a
66
+ * clean capability verdict as "nothing obviously fell off", never as "the
67
+ * deliverable was not gutted".
68
+ *
69
+ * What the hardening here DOES buy is that the gate cannot be turned off by
70
+ * accident or by one line: the hooks are sealed under a per-run random key, the
71
+ * pristine `querySelector`/`performance.now` are captured at install time (a
72
+ * one-line override of either used to disable the whole reading), the baseline
73
+ * is in the integrity capture, and no value from the page or from disk reaches a
74
+ * threshold without coercion.
75
+ */
76
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
77
+ import { dirname, join } from 'node:path';
78
+ export function unmeasured(reason, driver) {
79
+ return {
80
+ measured: false,
81
+ reason,
82
+ elapsedMs: 0,
83
+ frames: 0,
84
+ drawCalls: 0,
85
+ glDrawCalls: 0,
86
+ ctx2dDrawCalls: 0,
87
+ programsUsed: 0,
88
+ listenerTypes: 0,
89
+ listeners: 0,
90
+ canvas: false,
91
+ ...(driver ? { driver } : {}),
92
+ };
93
+ }
94
+ /** Frames per second, from the page's own measured interval. */
95
+ export const fpsOf = (p) => p.elapsedMs > 0 ? (p.frames * 1000) / p.elapsedMs : 0;
96
+ /** Draw calls per frame — a property of the SCENE, invariant to frame rate. */
97
+ export const drawsPerFrame = (p) => p.frames > 0 ? p.drawCalls / p.frames : 0;
98
+ // ---------------------------------------------------------------------------
99
+ // Thresholds
100
+ // ---------------------------------------------------------------------------
101
+ /**
102
+ * Scene-content ratios. Loose on purpose: the target is a build that lost most
103
+ * of its function, not one that lost a little. A tight threshold fails ordinary
104
+ * refactors (batching draws, merging two shader programs) and teaches the loop
105
+ * to pad its numbers back up, which is worse than not checking.
106
+ */
107
+ const DRAW_RATIO = 0.6;
108
+ const PROGRAM_RATIO = 0.6;
109
+ const LISTENER_RATIO = 0.6;
110
+ /**
111
+ * Frame rate is judged as an ABSOLUTE LIVENESS FLOOR, not a ratio — this is the
112
+ * single most important calibration decision in the file.
113
+ *
114
+ * A ratio cannot work here. The baseline is taken before the run's first model
115
+ * call; the candidate is taken minutes later on a box that is simultaneously
116
+ * decoding tokens on the same GPU, under software WebGL, possibly alongside
117
+ * other concurrent deliver tasks. Contention alone plausibly moves frame rate
118
+ * by 2-4x. The ratio this file originally used (0.4) trips at 2.5x — inside the
119
+ * noise, so it WOULD have misfired, blocking healthy turns and then telling the
120
+ * model to "restore the missing behaviour" that was never missing.
121
+ *
122
+ * A floor is load-invariant in the way that matters. On the measured octopus
123
+ * numbers — 59.9 fps healthy, 3.6 fps gutted — this fires with 2.8x of margin,
124
+ * while a healthy page would have to fall below 10 fps (6x contention) to
125
+ * false-fire. Same catch, 2.4x more headroom.
126
+ */
127
+ const DEAD_FPS = 10;
128
+ const ALIVE_FPS = 30;
129
+ /** Below these, a metric is too small for a ratio to mean anything. */
130
+ const MIN_DRAWS_PER_FRAME = 0.5;
131
+ const MIN_PROGRAMS = 2;
132
+ const MIN_LISTENER_TYPES = 3;
133
+ /**
134
+ * How many metrics must drop before the turn is BLOCKED.
135
+ *
136
+ * One is too few. Each metric has a legitimate way to fall on its own: merging
137
+ * five shaders into an uber-shader takes programs 5->1; switching a per-entity
138
+ * draw path to an instanced one legitimately collapses draws-per-frame; moving
139
+ * to delegated event handling collapses listener types. Blocking on any single
140
+ * one of those, with feedback that says "restore the missing behaviour", asks
141
+ * the model to undo a correct optimisation.
142
+ *
143
+ * Two is enough to catch the thing this exists for: the recorded regression
144
+ * dropped ALL FOUR at once. A lone drop is still reported — as a note the judge
145
+ * sees, not a block.
146
+ */
147
+ const BLOCK_AT_FINDINGS = 2;
148
+ /**
149
+ * Compare a post-run profile against the pre-run baseline. PURE — unit-tested
150
+ * without a browser.
151
+ */
152
+ export function compareCapability(baseline, current) {
153
+ const none = (skipped) => ({ regressed: false, skipped, findings: [], table: [] });
154
+ if (!capabilityEnabled())
155
+ return none('capability check disabled (UAP_CAPABILITY=0)');
156
+ if (!baseline?.measured)
157
+ return none('no capability baseline to compare against');
158
+ // A vanished entry page is the MOST complete regression possible, and the
159
+ // naive "either side unmeasured -> skip" rule forgave it outright: delete
160
+ // index.html and the reading becomes 'no-entry', which used to read as
161
+ // "cannot measure". Everything else that cannot be measured is an
162
+ // ENVIRONMENT failure and must still fail open.
163
+ if (!current?.measured) {
164
+ // Corroborated: the baseline must itself name the page that has vanished.
165
+ // Otherwise a single string written to current.json asserts the existence of
166
+ // something that was never measured, and that assertion bypasses the
167
+ // two-finding rule to block every turn.
168
+ if (current?.reason === 'no-entry' && baseline.entry) {
169
+ return {
170
+ regressed: true,
171
+ findings: ['the web entry page is GONE — there is no longer a page to run'],
172
+ table: [`entry: ${baseline.entry ?? '(a page)'} -> (none)`],
173
+ };
174
+ }
175
+ return none(`capability not measurable now (${current?.reason ?? 'unknown'})`);
176
+ }
177
+ // Two drivers have different frame-rate regimes and the fallback is silent.
178
+ if (baseline.driver && current.driver && baseline.driver !== current.driver) {
179
+ return none(`different browser drivers (${baseline.driver} then ${current.driver}) — not comparable`);
180
+ }
181
+ // You cannot regress from broken. A baseline that never rendered and never
182
+ // ticked gives every candidate an infinite improvement ratio.
183
+ if (baseline.frames === 0 && baseline.drawCalls === 0) {
184
+ return none('baseline never rendered — nothing to regress from');
185
+ }
186
+ const bFps = fpsOf(baseline);
187
+ const cFps = fpsOf(current);
188
+ const bDpf = drawsPerFrame(baseline);
189
+ const cDpf = drawsPerFrame(current);
190
+ const table = [
191
+ `frame rate: ${bFps.toFixed(1)} -> ${cFps.toFixed(1)} fps (${baseline.frames} frames in ${baseline.elapsedMs}ms -> ${current.frames} in ${current.elapsedMs}ms)`,
192
+ `draw calls per frame: ${bDpf.toFixed(2)} -> ${cDpf.toFixed(2)} (${baseline.drawCalls} -> ${current.drawCalls} total)`,
193
+ `shader programs used: ${baseline.programsUsed} -> ${current.programsUsed}`,
194
+ `event types handled: ${baseline.listenerTypes} -> ${current.listenerTypes}`,
195
+ ];
196
+ const findings = [];
197
+ const fmt = (n) => (Number.isInteger(n) ? String(n) : n.toFixed(2));
198
+ const ratioCheck = (label, base, cur, floor, ratio, unit) => {
199
+ if (!Number.isFinite(base) || !Number.isFinite(cur))
200
+ return;
201
+ if (base < floor)
202
+ return; // too small to judge
203
+ if (cur >= base * ratio)
204
+ return;
205
+ const pct = Math.round((1 - cur / base) * 100);
206
+ findings.push(`${label} dropped ${pct}%: ${fmt(base)} -> ${fmt(cur)} ${unit}`);
207
+ };
208
+ // Liveness: an absolute floor, not a ratio (see DEAD_FPS).
209
+ if (Number.isFinite(bFps) && Number.isFinite(cFps) && bFps >= ALIVE_FPS && cFps < DEAD_FPS) {
210
+ findings.push(`the render loop is no longer running at a usable rate: ${bFps.toFixed(1)} -> ${cFps.toFixed(1)} fps`);
211
+ }
212
+ // Draws-per-frame is only meaningful when BOTH sides actually drove rAF. A
213
+ // page whose loop is setInterval never touches requestAnimationFrame, so it
214
+ // reads 0 frames while still drawing — and dividing by a floored 1 turned
215
+ // draws-per-frame into draws-per-window, a number ~300x too large. Measured:
216
+ // a setInterval -> rAF refactor (an IMPROVEMENT) reported "draw calls per
217
+ // frame dropped 100%: 6300 -> 21".
218
+ if (baseline.frames > 0 && current.frames > 0) {
219
+ ratioCheck('draw calls per frame', bDpf, cDpf, MIN_DRAWS_PER_FRAME, DRAW_RATIO, 'per frame');
220
+ }
221
+ ratioCheck('distinct shader programs', baseline.programsUsed, current.programsUsed, MIN_PROGRAMS, PROGRAM_RATIO, 'programs');
222
+ ratioCheck('distinct event types handled', baseline.listenerTypes, current.listenerTypes, MIN_LISTENER_TYPES, LISTENER_RATIO, 'types');
223
+ return { regressed: findings.length >= BLOCK_AT_FINDINGS, findings, table };
224
+ }
225
+ /**
226
+ * The sentence the model is given. Verbatim numbers, no score: a percentage is
227
+ * what stalled the run this whole class of gate came from — the model needs to
228
+ * know WHAT it deleted, not how badly it is doing.
229
+ */
230
+ export function capabilityFeedback(cmp) {
231
+ if (!cmp.regressed)
232
+ return '';
233
+ return [
234
+ 'CAPABILITY REGRESSION — the app still runs, but it now DOES LESS than the build you started from.',
235
+ '',
236
+ ...cmp.findings.map((f) => `- ${f}`),
237
+ '',
238
+ 'Measured before your changes vs now:',
239
+ ...cmp.table.map((t) => ` ${t}`),
240
+ '',
241
+ 'This is not a rendering-cleanliness problem: a smaller, emptier app renders perfectly cleanly.',
242
+ 'Something that used to be drawn, animated, or responded to is no longer there. Restore the',
243
+ 'missing behaviour — the features, entities, effects and input handling from the original build —',
244
+ 'without reintroducing errors. Do NOT simplify the app further to make gates pass.',
245
+ ].join('\n');
246
+ }
247
+ /**
248
+ * What to say when the check did NOT block: a single-metric drop, or the reason
249
+ * it declined to judge at all.
250
+ *
251
+ * The skip half is the important half. `skipped` used to be returned and read by
252
+ * nobody — not the acceptance gate, not the keep-best veto, not verify — so a
253
+ * check that had silently disarmed itself (a driver swap mid-run, a vanished
254
+ * baseline, an unmeasurable page) was indistinguishable from one that ran and
255
+ * found nothing. That is exactly the inert-rung failure this module's header
256
+ * warns about, reproduced in the module's own output.
257
+ */
258
+ export function capabilityNote(cmp) {
259
+ if (cmp.skipped)
260
+ return `Capability check did not run: ${cmp.skipped}.`;
261
+ if (cmp.regressed || cmp.findings.length === 0)
262
+ return '';
263
+ return `Capability note (not blocking): ${cmp.findings.join('; ')}.`;
264
+ }
265
+ /** True when the check produced no opinion — callers should SAY so, not assume health. */
266
+ export const capabilityInactive = (cmp) => Boolean(cmp.skipped);
267
+ // ---------------------------------------------------------------------------
268
+ // Persistence
269
+ // ---------------------------------------------------------------------------
270
+ /**
271
+ * On-disk layout, mirroring `.uap/visual/` (see visual-baseline.ts):
272
+ * .uap/capability/current.json written by the execution gate every run
273
+ * .uap/capability/baseline.json pinned pre-run, or by `uap verify --approve`
274
+ *
275
+ * Disk is what makes this reachable from BOTH sides. The execution gate often
276
+ * runs in a spawned runner (synthesizeExecutionRung), so a return value does
277
+ * not cross that boundary — the visual gate has the same constraint and solves
278
+ * it the same way, with files. It also survives `--resume`, where an in-memory
279
+ * baseline would be re-taken from the already-modified tree.
280
+ */
281
+ /** Opt-out, matching every other gate in this subsystem. Off means the profile
282
+ * is never written and every comparison declines. */
283
+ export const capabilityEnabled = () => !['0', 'false', 'off', 'no'].includes((process.env.UAP_CAPABILITY ?? '').trim().toLowerCase());
284
+ export const CAPABILITY_DIR = join('.uap', 'capability');
285
+ export const currentPath = (root) => join(root, CAPABILITY_DIR, 'current.json');
286
+ export const baselinePath = (root) => join(root, CAPABILITY_DIR, 'baseline.json');
287
+ function readProfile(path) {
288
+ try {
289
+ if (!existsSync(path))
290
+ return null;
291
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
292
+ return sanitizeProfile(raw);
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ }
298
+ function writeProfile(path, profile) {
299
+ try {
300
+ mkdirSync(dirname(path), { recursive: true });
301
+ writeFileSync(path, `${JSON.stringify(profile, null, 2)}\n`);
302
+ return true;
303
+ }
304
+ catch {
305
+ return false;
306
+ }
307
+ }
308
+ /**
309
+ * Drop any previous run's reading. Called at the START of every execution-gate
310
+ * run so `current.json` exists only when THIS run measured something — see the
311
+ * comment at that call site for what staleness cost.
312
+ */
313
+ export function invalidateCapabilityCurrent(root) {
314
+ try {
315
+ if (existsSync(currentPath(root)))
316
+ rmSync(currentPath(root), { force: true });
317
+ }
318
+ catch {
319
+ /* fail-soft: a stale reading is better than a crashed gate */
320
+ }
321
+ }
322
+ /**
323
+ * Drop a baseline this run could not re-pin.
324
+ *
325
+ * A baseline is only meaningful for the tree it was taken from. Left in place
326
+ * across runs it becomes a reading of a DIFFERENT app: a run that pins 14
327
+ * draws/frame on a rich game, followed later by an unrelated run on a
328
+ * deliberately simpler project, blocks every turn against a stale number — and
329
+ * the operator has to know about an --approve flag to escape. Better to have no
330
+ * opinion than a confidently wrong one.
331
+ */
332
+ export function invalidateCapabilityBaseline(root) {
333
+ try {
334
+ if (existsSync(baselinePath(root)))
335
+ rmSync(baselinePath(root), { force: true });
336
+ }
337
+ catch {
338
+ /* fail-soft */
339
+ }
340
+ }
341
+ export const readCapabilityBaseline = (root) => readProfile(baselinePath(root));
342
+ export const readCapabilityCurrent = (root) => readProfile(currentPath(root));
343
+ export const writeCapabilityCurrent = (root, p) => writeProfile(currentPath(root), p);
344
+ export const writeCapabilityBaseline = (root, p) => writeProfile(baselinePath(root), p);
345
+ /**
346
+ * Coerce an untrusted reading into a profile, or null.
347
+ *
348
+ * EVERY number here is attacker-controllable: the page under test is code a
349
+ * model just wrote, and it can replace the read-back hook. `typeof x ===
350
+ * 'number'` is not validation — `typeof NaN` and `typeof Infinity` are both
351
+ * 'number', and a NaN baseline propagates through every threshold comparison
352
+ * (`NaN < floor` is false, `NaN >= base*ratio` is false) to produce
353
+ * "dropped NaN%" findings on EVERY turn: a permanent, unactionable block.
354
+ * Measured before this guard existed.
355
+ */
356
+ const REASONS = ['no-entry', 'no-browser', 'not-installed', 'no-reading', 'error'];
357
+ export function sanitizeProfile(raw) {
358
+ if (!raw || typeof raw !== 'object')
359
+ return null;
360
+ const o = raw;
361
+ // A page cannot be trusted to report a plausible magnitude either; the cap is
362
+ // far above any real reading (60fps for a minute is 3600 frames).
363
+ const CAP = 10_000_000;
364
+ const num = (k) => {
365
+ const v = o[k];
366
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? Math.min(v, CAP) : 0;
367
+ };
368
+ if (typeof o.measured !== 'boolean')
369
+ return null;
370
+ const gl = num('glDrawCalls');
371
+ const ctx = num('ctx2dDrawCalls');
372
+ return {
373
+ measured: o.measured,
374
+ // Validated against the union, not cast into it. This is the only string
375
+ // read from disk that steers control flow (see the no-entry branch in
376
+ // compareCapability), so an unrecognised value must become "no reason"
377
+ // rather than a verdict.
378
+ ...(REASONS.includes(o.reason) ? { reason: o.reason } : {}),
379
+ elapsedMs: num('elapsedMs'),
380
+ frames: num('frames'),
381
+ drawCalls: gl + ctx,
382
+ glDrawCalls: gl,
383
+ ctx2dDrawCalls: ctx,
384
+ programsUsed: num('programsUsed'),
385
+ listenerTypes: num('listenerTypes'),
386
+ listeners: num('listeners'),
387
+ canvas: o.canvas === true,
388
+ ...(typeof o.driver === 'string' ? { driver: o.driver } : {}),
389
+ ...(typeof o.entry === 'string' ? { entry: o.entry } : {}),
390
+ };
391
+ }
392
+ //# sourceMappingURL=capability-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-profile.js","sourceRoot":"","sources":["../../src/delivery/capability-profile.ts"],"names":[],"mappings":"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"}
@@ -345,6 +345,44 @@ export interface ConvergenceConfig {
345
345
  * Bounded by the ceiling and the stagnation guard so it cannot loop forever.
346
346
  */
347
347
  untilDelivered?: boolean;
348
+ /**
349
+ * End this run after N consecutive turns that moved NEITHER the gate score
350
+ * nor the acceptance score. Off (undefined) unless a caller sets it.
351
+ *
352
+ * WHY THIS IS OPT-IN, AND WHY IT IS NOT A SECOND STAGNATION RAIL
353
+ * A previous attempt at plateau detection lived here unconditionally and was
354
+ * removed: `untilDelivered` already stops EXTENDING at STAGNATION_LIMIT, and
355
+ * an always-on rail was measured unreachable (its streak can never exceed
356
+ * `stagnantTurns`) or redundant. Nothing about that changed.
357
+ *
358
+ * What changed is the CALLER. An epic attempt has somewhere better to spend
359
+ * the time — a fresh attempt on the same epic — and the epic controller is
360
+ * the only layer that knows whether one is left. So this is set only when a
361
+ * retry remains, and the decision it encodes is "yield to a fresh attempt",
362
+ * not "give up on the mission". On the LAST attempt it stays off and the run
363
+ * plays out exactly as before.
364
+ *
365
+ * MEASURED (2026-08-20, rust-pg-ext, qwen3.8-27b), four flat attempts:
366
+ * fix-bioband-derive a1: 5 turns flat @60% -> a2 PASSED in 2 turns
367
+ * fix-slope-arithmetic a1: 5 turns flat @40%, a2: 5 turns flat @40%
368
+ * At the shipped threshold those three attempts give back 21.2 minutes of
369
+ * wall clock. What that buys is narrower than it first looks, and the first
370
+ * draft of this comment overstated it twice:
371
+ *
372
+ * - No turn budget is TRANSFERRED. Every attempt gets its own `maxTurns`
373
+ * regardless, so a fresh attempt is not lengthened by an earlier one
374
+ * ending sooner; the epic's total exploration shrinks. Wall clock is the
375
+ * saving, and the only one.
376
+ * - That run did not die on its clock one epic short. All seven epics were
377
+ * attempted; `fix-slope-arithmetic` failed on merit after three attempts
378
+ * and a re-plan ("epic controller incomplete — failed epic(s): …").
379
+ * Reclaiming its minutes would not have changed that outcome.
380
+ *
381
+ * The real case is still good: time spent on turns whose score is provably
382
+ * not moving is time no longer available to the other epics, to the re-plan,
383
+ * or to the run's wall-clock budget.
384
+ */
385
+ abortOnFlatTurns?: number;
348
386
  /**
349
387
  * Hard upper bound on turns when untilDelivered is set. Library default 50;
350
388
  * the `uap deliver --until-delivered` CLI defaults it to 30 (override with
@@ -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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"}
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { join } from 'node:path';
18
18
  import { execSync } from 'child_process';
19
- import { statSync } from 'fs';
19
+ import { existsSync, statSync } from 'fs';
20
20
  import { ENDPOINT_UNREACHABLE } from '../models/long-fetch.js';
21
21
  /**
22
22
  * Consecutive unreachable-endpoint turns before the run gives up. Two, so a
@@ -25,6 +25,7 @@ import { ENDPOINT_UNREACHABLE } from '../models/long-fetch.js';
25
25
  */
26
26
  const UNREACHABLE_ABORT_LIMIT = 2;
27
27
  import { bindFrozenFragments, resolveBoundVariant, } from '../self-tuning/prompt-variants.js';
28
+ import { baselinePath } from './capability-profile.js';
28
29
  import { mergeRedetectedRungs, detectRungs, runLadder } from './verifier-ladder.js';
29
30
  import { applyFileBlocks, listGateConfigFiles } from './applier.js';
30
31
  import { snapshotProtection } from './spec-imports.js';
@@ -606,6 +607,26 @@ export class ConvergenceLoop {
606
607
  // run must not read as turn-1 movement.
607
608
  let lastTurnFingerprint = null;
608
609
  let bestScoreSeen = -1;
610
+ // Yield-rail state (opt-in; see abortOnFlatTurns). Sanitised once here so
611
+ // a caller-supplied value cannot be NaN/fractional/negative downstream.
612
+ const rawFlatLimit = this.config.abortOnFlatTurns;
613
+ const flatTurnLimit = typeof rawFlatLimit === 'number' && Number.isFinite(rawFlatLimit) && rawFlatLimit >= 1
614
+ // SCALED to the turn budget, never below what the caller asked for.
615
+ //
616
+ // A fixed count is wrong across budgets. Replaying this rail over 157
617
+ // deliver logs (51 attempts where it would arm), every breakthrough it
618
+ // still destroys at the default is a LONG attempt — 6 to 8 turns, from
619
+ // runs whose maxTurns is 20, where three flat turns is a much weaker
620
+ // signal than it is out of five. Quartering the budget tracks that:
621
+ // maxTurns 5 -> 3 (the floor), maxTurns 20 -> 5.
622
+ ? Math.max(Math.floor(rawFlatLimit), Math.ceil(maxTurns / 4))
623
+ : undefined;
624
+ let flatTurnStreak = 0;
625
+ // Acceptance high-water for the yield rail only — NOT bestAcceptance, which
626
+ // untilDelivered advances in a block ABOVE this one (comparing against it
627
+ // here is always false) and never advances at all without untilDelivered
628
+ // (making every judged turn look like progress). Both readings were wrong.
629
+ let bestAcceptanceSeen = -1;
609
630
  let stallReason;
610
631
  let executor = this.executor;
611
632
  let explorerSettings = this.config.explorer;
@@ -686,6 +707,25 @@ export class ConvergenceLoop {
686
707
  /* fail-soft: capture stays as it was */
687
708
  }
688
709
  }
710
+ // The capability BASELINE joins the capture for the same reason as the gate
711
+ // script and the runner configs: it is a trust anchor that run_bash can
712
+ // reach. It lived in `.uap/`, which the applier protects but the bash sweep
713
+ // SKIPS by design (the harness writes there constantly) and this capture did
714
+ // not cover — so `echo '{}' > .uap/capability/baseline.json`, or simply
715
+ // deleting it, silently disarmed both the acceptance check AND the keep-best
716
+ // veto that stops a gutted tree becoming the run's best snapshot.
717
+ //
718
+ // `current.json` is deliberately NOT protected: the gate rewrites it every
719
+ // run, so it is an output, not an anchor.
720
+ try {
721
+ const capBaseline = baselinePath(this.config.projectRoot);
722
+ if (existsSync(capBaseline)) {
723
+ protectedList = [...new Set([...(protectedList ?? []), capBaseline])];
724
+ }
725
+ }
726
+ catch {
727
+ /* fail-soft */
728
+ }
689
729
  const applyOptions = protectTests
690
730
  ? { protectedFiles, protectGateConfigs: true }
691
731
  : undefined;
@@ -1127,6 +1167,63 @@ export class ConvergenceLoop {
1127
1167
  `reproduce this result.`;
1128
1168
  break;
1129
1169
  }
1170
+ // The YIELD rail (opt-in — see abortOnFlatTurns). Deliberately unlike the
1171
+ // rail above: it does NOT require provablyIdle, because the attempts it
1172
+ // exists for wrote real edits on every turn and were invisible to that
1173
+ // check. Acceptance movement counts as progress for the same reason
1174
+ // untilDelivered counts it — an objective-green run pins the gate score
1175
+ // at 1.0 while a multi-criterion spec completes criterion by criterion.
1176
+ //
1177
+ // Escalation does NOT reset it, and that is the one exemption this rail
1178
+ // drops. In all FOUR flat attempts measured, `↑ turn 3: enable critic`
1179
+ // fired and the score did not move once afterwards. Yielding to a fresh
1180
+ // attempt is a bigger change of approach than enabling a critic inside
1181
+ // the attempt that is already failing — and the fresh attempt gets the
1182
+ // escalation ladder too.
1183
+ if (flatTurnLimit !== undefined) {
1184
+ const acceptanceMoved = acceptanceMet !== undefined && acceptanceMet > bestAcceptanceSeen;
1185
+ if (acceptanceMoved)
1186
+ bestAcceptanceSeen = acceptanceMet;
1187
+ // A directive that REPLACES THE EXECUTOR is exempt — and only that.
1188
+ //
1189
+ // The first version of this rail exempted no escalation at all, on the
1190
+ // evidence that `↑ turn 3: enable critic` fired in four measured flat
1191
+ // attempts and moved the score in none. That evidence is about the
1192
+ // CRITIC tier and does not reach the tier above it: with a stronger
1193
+ // model configured, `defaultEscalationLadder` appends a `switchExecutor`
1194
+ // tier at turn 5, and `createRepairEscalation` returns one whenever
1195
+ // compile errors are RISING — i.e. on exactly the flat turn this rail
1196
+ // fires. Measured against the real ladder: unexempted, an attempt that
1197
+ // master delivers on turn 6 with the stronger model was abandoned on
1198
+ // turn 3, and the one-shot repair pass never ran once.
1199
+ //
1200
+ // A directive is decided on THIS turn and consumed by the NEXT one, so
1201
+ // breaking here discards it. Give a new executor the single turn it
1202
+ // needs to prove itself; the critic and reseed tiers stay unexempted,
1203
+ // because those are the ones the measurement found worthless.
1204
+ const executorChanged = Boolean(directive.switchExecutor);
1205
+ // `inconclusive` stays exempt: a turn that never reached a model is
1206
+ // missing data, not a flat result, and flaky turns must not burn an
1207
+ // attempt.
1208
+ if (inconclusive || scoreMoved || acceptanceMoved || executorChanged)
1209
+ flatTurnStreak = 0;
1210
+ else
1211
+ flatTurnStreak++;
1212
+ if (flatTurnStreak >= flatTurnLimit) {
1213
+ // SHORT by design. This is prepended to finalFeedback, and
1214
+ // epic-mission slices that to 400 chars to build the next attempt's
1215
+ // `lastFailure` — so every character here evicts a character of real
1216
+ // gate feedback from the retry's steering. The 339-char first draft
1217
+ // left ~58 chars of gate output and cut the actionable line, which
1218
+ // defeated the recovery the rail depends on. Rationale lives above,
1219
+ // not on the wire. Deliberately does NOT claim the tree changed: this
1220
+ // rail never consults `provablyIdle` or `filesApplied`.
1221
+ stallReason =
1222
+ `plateau: ${flatTurnStreak} turns with no gate (best ` +
1223
+ `${(bestScoreSeen * 100).toFixed(0)}%) or acceptance progress — yielding to a fresh attempt.`;
1224
+ break;
1225
+ }
1226
+ }
1130
1227
  // Phase 3: structured critique of the failed turn (fail-soft). Skipped
1131
1228
  // on the last turn — prevContext is never consumed, so it would waste
1132
1229
  // a model call.