@miller-tech/uap 1.213.0 → 1.214.1
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 +2 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +107 -4
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/verify.d.ts +5 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +50 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/cli/wizard-config.d.ts.map +1 -1
- package/dist/cli/wizard-config.js +26 -1
- package/dist/cli/wizard-config.js.map +1 -1
- package/dist/delivery/await-run.d.ts +47 -0
- package/dist/delivery/await-run.d.ts.map +1 -1
- package/dist/delivery/await-run.js +392 -2
- package/dist/delivery/await-run.js.map +1 -1
- package/dist/delivery/capability-profile.d.ts +124 -0
- package/dist/delivery/capability-profile.d.ts.map +1 -0
- package/dist/delivery/capability-profile.js +392 -0
- package/dist/delivery/capability-profile.js.map +1 -0
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +21 -1
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/dist/delivery/execution-gate.d.ts +29 -0
- package/dist/delivery/execution-gate.d.ts.map +1 -1
- package/dist/delivery/execution-gate.js +237 -9
- package/dist/delivery/execution-gate.js.map +1 -1
- package/dist/delivery/mission-acceptance.d.ts +4 -0
- package/dist/delivery/mission-acceptance.d.ts.map +1 -1
- package/dist/delivery/mission-acceptance.js +35 -1
- package/dist/delivery/mission-acceptance.js.map +1 -1
- package/dist/delivery/run-state.d.ts +31 -0
- package/dist/delivery/run-state.d.ts.map +1 -1
- package/dist/delivery/run-state.js +49 -0
- package/dist/delivery/run-state.js.map +1 -1
- package/dist/mcp-router/tools/deliver.d.ts.map +1 -1
- package/dist/mcp-router/tools/deliver.js +5 -2
- package/dist/mcp-router/tools/deliver.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lib/llama-upstream.sh +64 -16
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/confidence_escalation.py +57 -0
- 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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convergence-loop.d.ts","sourceRoot":"","sources":["../../src/delivery/convergence-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
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';
|
|
@@ -686,6 +687,25 @@ export class ConvergenceLoop {
|
|
|
686
687
|
/* fail-soft: capture stays as it was */
|
|
687
688
|
}
|
|
688
689
|
}
|
|
690
|
+
// The capability BASELINE joins the capture for the same reason as the gate
|
|
691
|
+
// script and the runner configs: it is a trust anchor that run_bash can
|
|
692
|
+
// reach. It lived in `.uap/`, which the applier protects but the bash sweep
|
|
693
|
+
// SKIPS by design (the harness writes there constantly) and this capture did
|
|
694
|
+
// not cover — so `echo '{}' > .uap/capability/baseline.json`, or simply
|
|
695
|
+
// deleting it, silently disarmed both the acceptance check AND the keep-best
|
|
696
|
+
// veto that stops a gutted tree becoming the run's best snapshot.
|
|
697
|
+
//
|
|
698
|
+
// `current.json` is deliberately NOT protected: the gate rewrites it every
|
|
699
|
+
// run, so it is an output, not an anchor.
|
|
700
|
+
try {
|
|
701
|
+
const capBaseline = baselinePath(this.config.projectRoot);
|
|
702
|
+
if (existsSync(capBaseline)) {
|
|
703
|
+
protectedList = [...new Set([...(protectedList ?? []), capBaseline])];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
/* fail-soft */
|
|
708
|
+
}
|
|
689
709
|
const applyOptions = protectTests
|
|
690
710
|
? { protectedFiles, protectGateConfigs: true }
|
|
691
711
|
: undefined;
|