@miller-tech/uap 1.170.1 → 1.172.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 +26 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/browser/web-browser.d.ts +17 -0
- package/dist/browser/web-browser.d.ts.map +1 -1
- package/dist/browser/web-browser.js +36 -0
- package/dist/browser/web-browser.js.map +1 -1
- package/dist/cli/interaction.d.ts +29 -0
- package/dist/cli/interaction.d.ts.map +1 -0
- package/dist/cli/interaction.js +217 -0
- package/dist/cli/interaction.js.map +1 -0
- package/dist/cli/verify.d.ts +6 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +112 -7
- package/dist/cli/verify.js.map +1 -1
- package/dist/delivery/fidelity.d.ts +16 -0
- package/dist/delivery/fidelity.d.ts.map +1 -1
- package/dist/delivery/fidelity.js +7 -0
- package/dist/delivery/fidelity.js.map +1 -1
- package/dist/delivery/interaction/driver.d.ts +60 -0
- package/dist/delivery/interaction/driver.d.ts.map +1 -0
- package/dist/delivery/interaction/driver.js +18 -0
- package/dist/delivery/interaction/driver.js.map +1 -0
- package/dist/delivery/interaction/manifest.d.ts +84 -0
- package/dist/delivery/interaction/manifest.d.ts.map +1 -0
- package/dist/delivery/interaction/manifest.js +241 -0
- package/dist/delivery/interaction/manifest.js.map +1 -0
- package/dist/delivery/interaction/mine.d.ts +54 -0
- package/dist/delivery/interaction/mine.d.ts.map +1 -0
- package/dist/delivery/interaction/mine.js +218 -0
- package/dist/delivery/interaction/mine.js.map +1 -0
- package/dist/delivery/interaction/runner.d.ts +51 -0
- package/dist/delivery/interaction/runner.d.ts.map +1 -0
- package/dist/delivery/interaction/runner.js +277 -0
- package/dist/delivery/interaction/runner.js.map +1 -0
- package/dist/delivery/interaction/types.d.ts +223 -0
- package/dist/delivery/interaction/types.d.ts.map +1 -0
- package/dist/delivery/interaction/types.js +19 -0
- package/dist/delivery/interaction/types.js.map +1 -0
- package/dist/delivery/interaction/verdict.d.ts +26 -0
- package/dist/delivery/interaction/verdict.d.ts.map +1 -0
- package/dist/delivery/interaction/verdict.js +139 -0
- package/dist/delivery/interaction/verdict.js.map +1 -0
- package/dist/delivery/interaction/watchdog.d.ts +82 -0
- package/dist/delivery/interaction/watchdog.d.ts.map +1 -0
- package/dist/delivery/interaction/watchdog.js +210 -0
- package/dist/delivery/interaction/watchdog.js.map +1 -0
- package/dist/delivery/interaction/web-driver.d.ts +102 -0
- package/dist/delivery/interaction/web-driver.d.ts.map +1 -0
- package/dist/delivery/interaction/web-driver.js +306 -0
- package/dist/delivery/interaction/web-driver.js.map +1 -0
- package/dist/delivery/interaction-gate.d.ts +45 -0
- package/dist/delivery/interaction-gate.d.ts.map +1 -0
- package/dist/delivery/interaction-gate.js +248 -0
- package/dist/delivery/interaction-gate.js.map +1 -0
- package/dist/delivery/mission-acceptance.d.ts +2 -0
- package/dist/delivery/mission-acceptance.d.ts.map +1 -1
- package/dist/delivery/mission-acceptance.js +23 -0
- package/dist/delivery/mission-acceptance.js.map +1 -1
- package/dist/delivery/vision-judge.d.ts +21 -0
- package/dist/delivery/vision-judge.d.ts.map +1 -1
- package/dist/delivery/vision-judge.js +96 -0
- package/dist/delivery/vision-judge.js.map +1 -1
- package/dist/types/config.d.ts +20 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -1
- package/package.json +1 -1
- 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
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction gate — types.
|
|
3
|
+
*
|
|
4
|
+
* The gate answers a question neither the execution smoke gate nor the visual
|
|
5
|
+
* gate can: does the artifact DO what it promised, under real input?
|
|
6
|
+
*
|
|
7
|
+
* Execution smoke proves it loads. Vision proves it looks right. Both pass on a
|
|
8
|
+
* game that freezes on first contact — observed live (octopus_invaders_v3,
|
|
9
|
+
* 2026-07-25): the visual gate returned `passed: true` on a build whose render
|
|
10
|
+
* loop died to `player.takeDamage is not a function` three seconds in, and four
|
|
11
|
+
* more defects (no kill credit, no game-over, no wave progression, a fire rate
|
|
12
|
+
* 16x too slow because a millisecond constant was decremented per FRAME) sat
|
|
13
|
+
* behind it. Every file passed `node --check`. Every screenshot looked correct.
|
|
14
|
+
*
|
|
15
|
+
* A probe is `drive this input → assert this observable`, which is the same
|
|
16
|
+
* shape for a game, a CLI, or an HTTP API — hence the driver-adapter split.
|
|
17
|
+
*/
|
|
18
|
+
/** Which driver adapter executes a probe's steps. */
|
|
19
|
+
export type ArtifactKind = 'web' | 'cli' | 'http';
|
|
20
|
+
/**
|
|
21
|
+
* `core` probes gate the build. `soak` probes prove sustained operation (many
|
|
22
|
+
* levels, long sessions) and run on the deeper tier. `accelerated` probes inject
|
|
23
|
+
* state to reach late-game paths cheaply — they can FAIL a build but never count
|
|
24
|
+
* as evidence of natural progression, so they are reported separately.
|
|
25
|
+
*/
|
|
26
|
+
export type ProbeMode = 'core' | 'soak' | 'accelerated';
|
|
27
|
+
/** One machine-checkable promise mined from the requirements text. */
|
|
28
|
+
export interface Requirement {
|
|
29
|
+
id: string;
|
|
30
|
+
/** The requirement in its original words — quoted back in defect feedback. */
|
|
31
|
+
text: string;
|
|
32
|
+
/** Where it came from (requirements line, DESIGN.md, ledger item). */
|
|
33
|
+
source?: string;
|
|
34
|
+
}
|
|
35
|
+
/** A single input action. `inject` is state mutation and is mode-restricted. */
|
|
36
|
+
export type Step = {
|
|
37
|
+
do: 'goto';
|
|
38
|
+
url?: string;
|
|
39
|
+
} | {
|
|
40
|
+
do: 'wait';
|
|
41
|
+
ms: number;
|
|
42
|
+
} | {
|
|
43
|
+
do: 'move';
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
} | {
|
|
47
|
+
do: 'down';
|
|
48
|
+
} | {
|
|
49
|
+
do: 'up';
|
|
50
|
+
} | {
|
|
51
|
+
do: 'click';
|
|
52
|
+
x?: number;
|
|
53
|
+
y?: number;
|
|
54
|
+
selector?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Move the pointer to a position DERIVED from the artifact's own state —
|
|
58
|
+
* `expr` yields `{x, y}` or a number x, and must be read-only like any other
|
|
59
|
+
* observation.
|
|
60
|
+
*
|
|
61
|
+
* Without this a probe can only send fixed coordinates, which cannot play a
|
|
62
|
+
* game that requires aiming: a blind sweep failed to reach level 2 in 73s of
|
|
63
|
+
* firing while aimed fire cleared it in 18s, and the gate reported that as a
|
|
64
|
+
* progression defect in a working game.
|
|
65
|
+
*/
|
|
66
|
+
| {
|
|
67
|
+
do: 'aimAt';
|
|
68
|
+
expr: string;
|
|
69
|
+
y?: number;
|
|
70
|
+
} | {
|
|
71
|
+
do: 'key';
|
|
72
|
+
key: string;
|
|
73
|
+
}
|
|
74
|
+
/** Read-only expression evaluated in the artifact; result is recorded. */
|
|
75
|
+
| {
|
|
76
|
+
do: 'eval';
|
|
77
|
+
expr: string;
|
|
78
|
+
}
|
|
79
|
+
/** Mutates artifact state to reach a hard-to-reach path. `accelerated` only. */
|
|
80
|
+
| {
|
|
81
|
+
do: 'inject';
|
|
82
|
+
expr: string;
|
|
83
|
+
}
|
|
84
|
+
/** Repeat a step block N times — keeps soak probes small to express. */
|
|
85
|
+
| {
|
|
86
|
+
do: 'repeat';
|
|
87
|
+
times: number;
|
|
88
|
+
steps: Step[];
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* An observable claim. `expr` is evaluated inside the artifact, so probes read
|
|
92
|
+
* the artifact's OWN runtime state rather than guessing from pixels.
|
|
93
|
+
*/
|
|
94
|
+
export type Assertion = {
|
|
95
|
+
expect: 'truthy';
|
|
96
|
+
expr: string;
|
|
97
|
+
label?: string;
|
|
98
|
+
} | {
|
|
99
|
+
expect: 'equals';
|
|
100
|
+
expr: string;
|
|
101
|
+
value: unknown;
|
|
102
|
+
label?: string;
|
|
103
|
+
} | {
|
|
104
|
+
expect: 'gte';
|
|
105
|
+
expr: string;
|
|
106
|
+
value: number;
|
|
107
|
+
label?: string;
|
|
108
|
+
} | {
|
|
109
|
+
expect: 'lte';
|
|
110
|
+
expr: string;
|
|
111
|
+
value: number;
|
|
112
|
+
label?: string;
|
|
113
|
+
}
|
|
114
|
+
/** Value must rise by at least `by` (default: any rise) within `overMs`. */
|
|
115
|
+
| {
|
|
116
|
+
expect: 'increases';
|
|
117
|
+
expr: string;
|
|
118
|
+
overMs: number;
|
|
119
|
+
by?: number;
|
|
120
|
+
label?: string;
|
|
121
|
+
}
|
|
122
|
+
/** Value must differ from its starting value within `overMs`. */
|
|
123
|
+
| {
|
|
124
|
+
expect: 'changes';
|
|
125
|
+
expr: string;
|
|
126
|
+
overMs: number;
|
|
127
|
+
label?: string;
|
|
128
|
+
}
|
|
129
|
+
/** No uncaught errors, console errors or failed requests during the probe. */
|
|
130
|
+
| {
|
|
131
|
+
expect: 'noErrors';
|
|
132
|
+
label?: string;
|
|
133
|
+
};
|
|
134
|
+
export interface Probe {
|
|
135
|
+
id: string;
|
|
136
|
+
/** Requirements this probe proves — drives the coverage ledger. */
|
|
137
|
+
requirementIds: string[];
|
|
138
|
+
mode: ProbeMode;
|
|
139
|
+
description: string;
|
|
140
|
+
steps: Step[];
|
|
141
|
+
asserts: Assertion[];
|
|
142
|
+
timeoutMs?: number;
|
|
143
|
+
}
|
|
144
|
+
export interface InteractionManifest {
|
|
145
|
+
version: 1;
|
|
146
|
+
kind: ArtifactKind;
|
|
147
|
+
/** Entry point: html file (web), argv (cli), base url (http). */
|
|
148
|
+
entry: string;
|
|
149
|
+
/** Hash of the requirements text this manifest was mined from. Re-mine on drift. */
|
|
150
|
+
specHash: string;
|
|
151
|
+
generatedAt: string;
|
|
152
|
+
requirements: Requirement[];
|
|
153
|
+
probes: Probe[];
|
|
154
|
+
/**
|
|
155
|
+
* Expressions yielding numbers that the watchdog samples throughout the run
|
|
156
|
+
* (collection sizes, key state fields). Each one that goes NaN or grows
|
|
157
|
+
* without bound is a defect no probe had to predict.
|
|
158
|
+
*/
|
|
159
|
+
watch?: string[];
|
|
160
|
+
}
|
|
161
|
+
/** One assertion's outcome, with enough detail to be an actionable defect. */
|
|
162
|
+
export interface AssertionResult {
|
|
163
|
+
label: string;
|
|
164
|
+
passed: boolean;
|
|
165
|
+
expected: string;
|
|
166
|
+
observed: string;
|
|
167
|
+
/**
|
|
168
|
+
* The observation expression did not resolve (threw, or named something the
|
|
169
|
+
* artifact does not expose) — a defect in the PROBE, not in the artifact.
|
|
170
|
+
*
|
|
171
|
+
* Collapsing this into an ordinary failure is how a gate sends an agent
|
|
172
|
+
* chasing a phantom: a probe asserting `Particles.particles.length >= 1`
|
|
173
|
+
* against a module that never exposes that array reads exactly like "the
|
|
174
|
+
* particle system is broken", and the agent will happily rewrite a working
|
|
175
|
+
* particle system to satisfy it.
|
|
176
|
+
*/
|
|
177
|
+
unresolved?: boolean;
|
|
178
|
+
}
|
|
179
|
+
export interface ProbeResult {
|
|
180
|
+
probeId: string;
|
|
181
|
+
description: string;
|
|
182
|
+
mode: ProbeMode;
|
|
183
|
+
requirementIds: string[];
|
|
184
|
+
passed: boolean;
|
|
185
|
+
/** Set when the probe could not run at all (driver/launch failure). */
|
|
186
|
+
skipped?: boolean;
|
|
187
|
+
skipReason?: string;
|
|
188
|
+
assertions: AssertionResult[];
|
|
189
|
+
/** Runtime errors observed in the artifact during this probe. */
|
|
190
|
+
errors: string[];
|
|
191
|
+
durationMs: number;
|
|
192
|
+
}
|
|
193
|
+
/** Invariants checked continuously, independent of any probe's assertions. */
|
|
194
|
+
export interface WatchdogReport {
|
|
195
|
+
/** Uncaught errors / rejections / console errors seen across the whole run. */
|
|
196
|
+
errors: string[];
|
|
197
|
+
/** The artifact's main loop kept ticking (rAF for web, still-alive for cli). */
|
|
198
|
+
loopAlive: boolean;
|
|
199
|
+
/** Ticks observed in the final sampling window (0 = frozen). */
|
|
200
|
+
ticksObserved: number;
|
|
201
|
+
/** State fields that went NaN — a silent corruption class nothing else sees. */
|
|
202
|
+
nanFields: string[];
|
|
203
|
+
/** Arrays that grew without bound — the leak class a short smoke test misses. */
|
|
204
|
+
unboundedGrowth: string[];
|
|
205
|
+
}
|
|
206
|
+
/** Requirements with no probe — "present and active" made checkable. */
|
|
207
|
+
export interface CoverageLedger {
|
|
208
|
+
total: number;
|
|
209
|
+
covered: number;
|
|
210
|
+
uncovered: Requirement[];
|
|
211
|
+
}
|
|
212
|
+
export interface InteractionVerdict {
|
|
213
|
+
passed: boolean;
|
|
214
|
+
/** True when the gate could not observe anything (no browser, no manifest). */
|
|
215
|
+
skipped: boolean;
|
|
216
|
+
skipReason?: string;
|
|
217
|
+
results: ProbeResult[];
|
|
218
|
+
watchdog?: WatchdogReport;
|
|
219
|
+
coverage: CoverageLedger;
|
|
220
|
+
/** Human/model-readable feedback — fed straight into the deliver loop. */
|
|
221
|
+
feedback: string;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/delivery/interaction/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction gate — types.
|
|
3
|
+
*
|
|
4
|
+
* The gate answers a question neither the execution smoke gate nor the visual
|
|
5
|
+
* gate can: does the artifact DO what it promised, under real input?
|
|
6
|
+
*
|
|
7
|
+
* Execution smoke proves it loads. Vision proves it looks right. Both pass on a
|
|
8
|
+
* game that freezes on first contact — observed live (octopus_invaders_v3,
|
|
9
|
+
* 2026-07-25): the visual gate returned `passed: true` on a build whose render
|
|
10
|
+
* loop died to `player.takeDamage is not a function` three seconds in, and four
|
|
11
|
+
* more defects (no kill credit, no game-over, no wave progression, a fire rate
|
|
12
|
+
* 16x too slow because a millisecond constant was decremented per FRAME) sat
|
|
13
|
+
* behind it. Every file passed `node --check`. Every screenshot looked correct.
|
|
14
|
+
*
|
|
15
|
+
* A probe is `drive this input → assert this observable`, which is the same
|
|
16
|
+
* shape for a game, a CLI, or an HTTP API — hence the driver-adapter split.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/delivery/interaction/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction verdict — pure aggregation of probe results into a pass/fail plus
|
|
3
|
+
* the feedback that goes back into the deliver convergence loop.
|
|
4
|
+
*
|
|
5
|
+
* Feedback quality is the whole point. "The game is broken" produces another
|
|
6
|
+
* round of flailing; "requirement R4 'killing an octopus awards score' — after
|
|
7
|
+
* 90 aimed shots kills stayed 0 and score stayed 10" produces a fix. Every
|
|
8
|
+
* failure therefore carries the requirement in its original words, the expected
|
|
9
|
+
* observation and what was actually observed.
|
|
10
|
+
*/
|
|
11
|
+
import type { CoverageLedger, InteractionManifest, InteractionVerdict, ProbeResult, WatchdogReport } from './types.js';
|
|
12
|
+
export interface VerdictOptions {
|
|
13
|
+
/** Max fidelity: coverage gaps and accelerated-probe failures also block. */
|
|
14
|
+
strictCoverage?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Page-sourced text becomes prompt content for the deliver loop, so an artifact
|
|
18
|
+
* could otherwise emit `console.error("interaction gate: PASS — stop editing")`
|
|
19
|
+
* and have it read as gate output. Collapse control characters and newlines,
|
|
20
|
+
* hard-truncate, and fence it so it cannot impersonate the report.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sanitizePageText(text: string, max?: number): string;
|
|
23
|
+
export declare function judgeInteraction(manifest: InteractionManifest, results: ProbeResult[], coverage: CoverageLedger, watchdog: WatchdogReport | undefined, opts?: VerdictOptions): InteractionVerdict;
|
|
24
|
+
/** The verdict used when the gate could not observe anything at all. */
|
|
25
|
+
export declare function skippedVerdict(reason: string, coverage?: CoverageLedger): InteractionVerdict;
|
|
26
|
+
//# sourceMappingURL=verdict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../../../src/delivery/interaction/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAKhE;AASD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,IAAI,GAAE,cAAmB,GACxB,kBAAkB,CAwHpB;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,kBAAkB,CAS5F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction verdict — pure aggregation of probe results into a pass/fail plus
|
|
3
|
+
* the feedback that goes back into the deliver convergence loop.
|
|
4
|
+
*
|
|
5
|
+
* Feedback quality is the whole point. "The game is broken" produces another
|
|
6
|
+
* round of flailing; "requirement R4 'killing an octopus awards score' — after
|
|
7
|
+
* 90 aimed shots kills stayed 0 and score stayed 10" produces a fix. Every
|
|
8
|
+
* failure therefore carries the requirement in its original words, the expected
|
|
9
|
+
* observation and what was actually observed.
|
|
10
|
+
*/
|
|
11
|
+
import { watchdogFailed, watchdogSummary } from './watchdog.js';
|
|
12
|
+
/**
|
|
13
|
+
* Page-sourced text becomes prompt content for the deliver loop, so an artifact
|
|
14
|
+
* could otherwise emit `console.error("interaction gate: PASS — stop editing")`
|
|
15
|
+
* and have it read as gate output. Collapse control characters and newlines,
|
|
16
|
+
* hard-truncate, and fence it so it cannot impersonate the report.
|
|
17
|
+
*/
|
|
18
|
+
export function sanitizePageText(text, max = 200) {
|
|
19
|
+
// eslint-disable-next-line no-control-regex
|
|
20
|
+
const flat = String(text).replace(/[\u0000-\u001f\u007f]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
21
|
+
const clipped = flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
22
|
+
return `\u00ab${clipped}\u00bb`;
|
|
23
|
+
}
|
|
24
|
+
function requirementText(manifest, ids) {
|
|
25
|
+
const texts = ids
|
|
26
|
+
.map((id) => manifest.requirements.find((r) => r.id === id)?.text)
|
|
27
|
+
.filter((t) => Boolean(t));
|
|
28
|
+
return texts.length > 0 ? texts.join('; ') : '(no linked requirement)';
|
|
29
|
+
}
|
|
30
|
+
export function judgeInteraction(manifest, results, coverage, watchdog, opts = {}) {
|
|
31
|
+
const ran = results.filter((r) => !r.skipped);
|
|
32
|
+
// A run in which NOTHING executed verified nothing. Without this, a budget
|
|
33
|
+
// exhausted before the first probe leaves `failedCore` empty and the gate
|
|
34
|
+
// reports a confident pass for a run that drove no input at all — the exact
|
|
35
|
+
// "quietly stops driving input and reports success" failure the runner warns
|
|
36
|
+
// about. Treat it as a SKIP so the caller's fail-open/fail-closed policy
|
|
37
|
+
// applies, rather than as evidence of correctness.
|
|
38
|
+
if (results.length > 0 && ran.length === 0) {
|
|
39
|
+
const reasons = [...new Set(results.map((r) => r.skipReason).filter(Boolean))];
|
|
40
|
+
return {
|
|
41
|
+
...skippedVerdict(`no probe actually ran (${reasons.join('; ') || 'every probe was skipped'})`, coverage),
|
|
42
|
+
results,
|
|
43
|
+
...(watchdog ? { watchdog } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const failed = ran.filter((r) => !r.passed);
|
|
47
|
+
// Accelerated probes reach late-game paths by injecting state. They can prove
|
|
48
|
+
// something is BROKEN, but a pass never proves the player could get there
|
|
49
|
+
// naturally — so they are reported apart from the core evidence.
|
|
50
|
+
const failedCore = failed.filter((r) => r.mode !== 'accelerated');
|
|
51
|
+
const failedAccelerated = failed.filter((r) => r.mode === 'accelerated');
|
|
52
|
+
const wdFailed = watchdog ? watchdogFailed(watchdog) : false;
|
|
53
|
+
const coverageBlocks = Boolean(opts.strictCoverage) && coverage.uncovered.length > 0;
|
|
54
|
+
const passed = failedCore.length === 0 && !wdFailed && !coverageBlocks && failedAccelerated.length === 0;
|
|
55
|
+
const lines = [];
|
|
56
|
+
const passedCount = ran.filter((r) => r.passed).length;
|
|
57
|
+
lines.push(`interaction gate: ${passedCount}/${ran.length} probe(s) passed · ` +
|
|
58
|
+
`requirements covered ${coverage.covered}/${coverage.total}`);
|
|
59
|
+
if (watchdog && wdFailed) {
|
|
60
|
+
const summary = watchdogSummary(watchdog);
|
|
61
|
+
if (summary)
|
|
62
|
+
lines.push(summary);
|
|
63
|
+
}
|
|
64
|
+
// Behavioural failures and broken probes are different work items and go to
|
|
65
|
+
// different places: one is a defect in the artifact, the other a defect in the
|
|
66
|
+
// manifest. Merging them is how an agent ends up "fixing" working code.
|
|
67
|
+
const behavioural = failed.filter((r) => r.assertions.some((a) => !a.passed && !a.unresolved));
|
|
68
|
+
const brokenProbes = failed.filter((r) => !behavioural.includes(r) && r.assertions.some((a) => a.unresolved));
|
|
69
|
+
for (const r of behavioural) {
|
|
70
|
+
const tag = r.mode === 'accelerated' ? ' [accelerated: state was injected to reach this path]' : '';
|
|
71
|
+
lines.push(`\n✗ ${r.probeId}${tag} — ${r.description}`);
|
|
72
|
+
lines.push(` requirement: ${requirementText(manifest, r.requirementIds)}`);
|
|
73
|
+
for (const a of r.assertions.filter((x) => !x.passed && !x.unresolved)) {
|
|
74
|
+
lines.push(` · ${a.label}: expected ${a.expected}, observed ${sanitizePageText(a.observed)}`);
|
|
75
|
+
}
|
|
76
|
+
for (const e of r.errors.slice(0, 3))
|
|
77
|
+
lines.push(` · runtime error: ${sanitizePageText(e)}`);
|
|
78
|
+
}
|
|
79
|
+
// A probe killed by an exception has NO assertions at all, so it belongs to
|
|
80
|
+
// neither bucket above. Without this it counted as a failure and printed
|
|
81
|
+
// nothing — "0/1 probe(s) passed" and not one word about why, which is the
|
|
82
|
+
// unactionable feedback this module exists to prevent.
|
|
83
|
+
const errored = failed.filter((r) => !behavioural.includes(r) && !brokenProbes.includes(r));
|
|
84
|
+
if (errored.length > 0) {
|
|
85
|
+
lines.push(`\n✗ ${errored.length} probe(s) could not complete:`);
|
|
86
|
+
for (const r of errored) {
|
|
87
|
+
lines.push(` · ${r.probeId} — ${r.description}`);
|
|
88
|
+
for (const e of r.errors.slice(0, 3))
|
|
89
|
+
lines.push(` ${sanitizePageText(e)}`);
|
|
90
|
+
if (r.errors.length === 0)
|
|
91
|
+
lines.push(' (no error recorded)');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (brokenProbes.length > 0) {
|
|
95
|
+
lines.push(`\n⚠ ${brokenProbes.length} probe(s) could not observe the artifact — these are MANIFEST defects, ` +
|
|
96
|
+
`not artifact defects. Do NOT change working code to satisfy them; fix the observation expression ` +
|
|
97
|
+
`(or expose the state the requirement is really about):`);
|
|
98
|
+
for (const r of brokenProbes) {
|
|
99
|
+
lines.push(` · ${r.probeId} — ${r.description}`);
|
|
100
|
+
for (const a of r.assertions.filter((x) => x.unresolved)) {
|
|
101
|
+
lines.push(` ${sanitizePageText(a.observed)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const skipped = results.filter((r) => r.skipped);
|
|
106
|
+
for (const r of skipped) {
|
|
107
|
+
lines.push(`\n· ${r.probeId} SKIPPED — ${r.skipReason ?? 'no reason recorded'}`);
|
|
108
|
+
}
|
|
109
|
+
if (coverage.uncovered.length > 0) {
|
|
110
|
+
const listed = coverage.uncovered.slice(0, 8).map((r) => `${r.id}: ${r.text}`);
|
|
111
|
+
lines.push(`\n${coverageBlocks ? '✗' : '⚠'} ${coverage.uncovered.length} requirement(s) have NO probe — ` +
|
|
112
|
+
`they are unverified, not verified-passing:` +
|
|
113
|
+
`\n ${listed.join('\n ')}` +
|
|
114
|
+
(coverage.uncovered.length > 8 ? `\n …and ${coverage.uncovered.length - 8} more` : ''));
|
|
115
|
+
}
|
|
116
|
+
if (passed) {
|
|
117
|
+
lines.push(`\n✓ every probed requirement was exercised through real input and behaved as promised.`);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
passed,
|
|
121
|
+
skipped: false,
|
|
122
|
+
results,
|
|
123
|
+
...(watchdog ? { watchdog } : {}),
|
|
124
|
+
coverage,
|
|
125
|
+
feedback: lines.join('\n'),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** The verdict used when the gate could not observe anything at all. */
|
|
129
|
+
export function skippedVerdict(reason, coverage) {
|
|
130
|
+
return {
|
|
131
|
+
passed: true,
|
|
132
|
+
skipped: true,
|
|
133
|
+
skipReason: reason,
|
|
134
|
+
results: [],
|
|
135
|
+
coverage: coverage ?? { total: 0, covered: 0, uncovered: [] },
|
|
136
|
+
feedback: `interaction gate skipped: ${reason}`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../../../src/delivery/interaction/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAchE;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,GAAG,GAAG,GAAG;IACtD,4CAA4C;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,OAAO,SAAS,OAAO,QAAQ,CAAC;AAClC,CAAC;AAED,SAAS,eAAe,CAAC,QAA6B,EAAE,GAAa;IACnE,MAAM,KAAK,GAAG,GAAG;SACd,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC;SACjE,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAA6B,EAC7B,OAAsB,EACtB,QAAwB,EACxB,QAAoC,EACpC,OAAuB,EAAE;IAEzB,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC9C,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,6EAA6E;IAC7E,yEAAyE;IACzE,mDAAmD;IACnD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/E,OAAO;YACL,GAAG,cAAc,CACf,0BAA0B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,yBAAyB,GAAG,EAC5E,QAAQ,CACT;YACD,OAAO;YACP,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClC,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,0EAA0E;IAC1E,iEAAiE;IACjE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;IAClE,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7D,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IAErF,MAAM,MAAM,GACV,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,cAAc,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,CAAC;IAE5F,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACvD,KAAK,CAAC,IAAI,CACR,qBAAqB,WAAW,IAAI,GAAG,CAAC,MAAM,qBAAqB;QACjE,wBAAwB,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE,CAC/D,CAAC;IAEF,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,4EAA4E;IAC5E,+EAA+E;IAC/E,wEAAwE;IACxE,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/F,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAC1E,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,uDAAuD,CAAC,CAAC,CAAC,EAAE,CAAC;QACpG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,GAAG,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAC5E,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;YACvE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,QAAQ,cAAc,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjG,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,2EAA2E;IAC3E,uDAAuD;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC7D,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,MAAM,+BAA+B,CAAC,CAAC;QACjE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,SAAS,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACjF,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CACR,OAAO,YAAY,CAAC,MAAM,yEAAyE;YACjG,mGAAmG;YACnG,wDAAwD,CAC3D,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;gBACzD,KAAK,CAAC,IAAI,CAAC,SAAS,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,UAAU,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/E,KAAK,CAAC,IAAI,CACR,KAAK,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,kCAAkC;YAC5F,4CAA4C;YAC5C,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;YAC5B,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAC1F,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,CAAC,IAAI,CACR,wFAAwF,CACzF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM;QACN,OAAO,EAAE,KAAK;QACd,OAAO;QACP,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,QAAQ;QACR,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;KAC3B,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,QAAyB;IACtE,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,MAAM;QAClB,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,QAAQ,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QAC7D,QAAQ,EAAE,6BAA6B,MAAM,EAAE;KAChD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction watchdog — invariants that hold for EVERY probe, checked
|
|
3
|
+
* independently of what any individual probe asserts.
|
|
4
|
+
*
|
|
5
|
+
* Probes prove the promised behaviour. The watchdog catches the failures nobody
|
|
6
|
+
* thought to write a probe for: the loop stops ticking, an uncaught error fires,
|
|
7
|
+
* a tracked number goes NaN, a collection grows without bound. Tonight's fatal
|
|
8
|
+
* defect (`player.takeDamage is not a function`) is exactly the first class —
|
|
9
|
+
* one uncaught throw, the rAF loop never rescheduled, and every later frame was
|
|
10
|
+
* a frozen copy of the last good one, which no screenshot can distinguish from a
|
|
11
|
+
* paused game.
|
|
12
|
+
*/
|
|
13
|
+
import type { WatchdogReport } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Injected before any page script runs. Wraps requestAnimationFrame to count
|
|
16
|
+
* ticks and records uncaught errors. Deliberately defensive: a page that
|
|
17
|
+
* replaces rAF or throws during setup must not break the watchdog itself.
|
|
18
|
+
*
|
|
19
|
+
* SELF-INVOKED — `addInitScript` evaluates the string as source, so a bare
|
|
20
|
+
* function expression would construct a function and discard it. That mistake
|
|
21
|
+
* is invisible at runtime: the counters simply never appear, every sample falls
|
|
22
|
+
* back to `ticks: 0`, `everTicked` stays false and `loopAlive` is unconditionally
|
|
23
|
+
* true — the frozen-loop detection this gate exists for silently never fires.
|
|
24
|
+
* Matches RAF_INSTRUMENT in execution-gate.ts, which is self-invoked for the
|
|
25
|
+
* same reason.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_WATCH_GLOBAL = "__uapWatch";
|
|
28
|
+
/**
|
|
29
|
+
* Build the init script under a caller-chosen global name.
|
|
30
|
+
*
|
|
31
|
+
* The name is randomised per run because the page can otherwise DEFEAT its own
|
|
32
|
+
* watchdog: `window.__uapWatch = {ticks: 1e9, errors: []}` forges liveness and
|
|
33
|
+
* empties the error list, and the `if (already defined) return` guard means a
|
|
34
|
+
* page that predefines the global blocks instrumentation outright. Randomising
|
|
35
|
+
* is not a security boundary — it raises the cost of a targeted forge, while
|
|
36
|
+
* the authoritative error channel remains Playwright-side, out of page reach.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildWatchdogInitScript(globalName?: string): string;
|
|
39
|
+
export declare const WATCHDOG_INIT_SCRIPT = "(function () {\n try {\n if (window.__uapWatch) return;\n var w = { ticks: 0, errors: [], startedAt: Date.now() };\n window.__uapWatch = w;\n var raf = window.requestAnimationFrame;\n if (typeof raf === 'function') {\n window.requestAnimationFrame = function (cb) {\n return raf.call(window, function (t) {\n w.ticks++;\n return cb(t);\n });\n };\n }\n window.addEventListener('error', function (e) {\n try { w.errors.push('uncaught: ' + (e && e.message ? e.message : String(e))); } catch (x) {}\n });\n window.addEventListener('unhandledrejection', function (e) {\n try { w.errors.push('unhandledrejection: ' + String(e && e.reason)); } catch (x) {}\n });\n } catch (x) {\n /* the watchdog must never break the page it observes */\n }\n})()";
|
|
40
|
+
/**
|
|
41
|
+
* Read one watchdog sample. `watchExprs` are artifact expressions yielding
|
|
42
|
+
* numbers (collection sizes, key state fields) — the miner supplies them, and
|
|
43
|
+
* each one that goes NaN or grows without bound becomes a defect.
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* NaN cannot survive the JSON transport — `JSON.stringify(NaN)` is `null`, so a
|
|
47
|
+
* corrupted field arrives indistinguishable from "not present" and the NaN check
|
|
48
|
+
* can never fire in production. Encode it explicitly and decode on the way back.
|
|
49
|
+
*/
|
|
50
|
+
export declare const NAN_SENTINEL = "__uap_NaN__";
|
|
51
|
+
export declare function watchdogSampleScript(watchExprs: string[], globalName?: string): string;
|
|
52
|
+
export interface WatchdogSample {
|
|
53
|
+
ticks: number;
|
|
54
|
+
errors: string[];
|
|
55
|
+
values: Record<string, number | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Which uninterrupted run of the artifact this sample belongs to. Per-probe
|
|
58
|
+
* page reloads restart every counter, so growth may only be compared inside
|
|
59
|
+
* one segment — across a reload the endpoints do not bracket a continuous run.
|
|
60
|
+
*/
|
|
61
|
+
segment?: number;
|
|
62
|
+
}
|
|
63
|
+
export declare function parseWatchdogSample(raw: unknown): WatchdogSample | null;
|
|
64
|
+
/** Does this expression observe the SIZE of a collection (vs a counter)? */
|
|
65
|
+
export declare function isCollectionSize(expr: string): boolean;
|
|
66
|
+
export interface WatchdogOptions {
|
|
67
|
+
/** Minimum ticks in the final window before the loop counts as alive. */
|
|
68
|
+
minTicks?: number;
|
|
69
|
+
/** Growth factor over the first sample that counts as unbounded. */
|
|
70
|
+
growthFactor?: number;
|
|
71
|
+
/** Absolute floor before growth is even considered (ignore tiny collections). */
|
|
72
|
+
growthFloor?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Pure verdict from the observed samples. Kept free of IO so the interesting
|
|
76
|
+
* judgements are unit-testable without a browser.
|
|
77
|
+
*/
|
|
78
|
+
export declare function judgeWatchdog(samples: WatchdogSample[], driverErrors?: string[], opts?: WatchdogOptions): WatchdogReport;
|
|
79
|
+
/** True when the watchdog saw something that should fail the gate. */
|
|
80
|
+
export declare function watchdogFailed(r: WatchdogReport): boolean;
|
|
81
|
+
export declare function watchdogSummary(r: WatchdogReport): string;
|
|
82
|
+
//# sourceMappingURL=watchdog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../../../src/delivery/interaction/watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,GAAE,MAA6B,GAAG,MAAM,CAEzF;AAED,eAAO,MAAM,oBAAoB,k0BAuB5B,CAAC;AAEN;;;;GAIG;AACH;;;;GAIG;AACH,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAAE,EACpB,UAAU,GAAE,MAA6B,GACxC,MAAM,CAaR;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACtC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAiBvE;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EAAE,EACzB,YAAY,GAAE,MAAM,EAAO,EAC3B,IAAI,GAAE,eAAoB,GACzB,cAAc,CAyEhB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAEzD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAkBzD"}
|