codeloop-mcp-server 0.1.18 → 0.1.20

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.
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GACzB,IAAI,CAGN;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAmB7B;AAED,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAWnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjE"}
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GACzB,IAAI,CAGN;AAED,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAmB7B;AAYD,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjE"}
@@ -42,16 +42,34 @@ export function loadRunMeta(runIdOrDir, baseDir) {
42
42
  return null;
43
43
  }
44
44
  }
45
+ /**
46
+ * Real run IDs generated by `generateRunId()` have the shape
47
+ * `run_<ms-timestamp>_<6-char-rand>`. Phantom directories (`run_visual_v2`,
48
+ * `run_test`, ad-hoc seed folders, etc.) silently broke design_compare and
49
+ * check_workflow because the old lexicographic sort placed `run_v…` before
50
+ * every numeric run (digits < letters in ASCII). We now require the strict
51
+ * shape and sort by parsed timestamp numerically, descending.
52
+ */
53
+ const RUN_ID_PATTERN = /^run_(\d+)_[a-z0-9]+$/i;
45
54
  export function listRuns(baseDir) {
46
55
  const base = baseDir || getArtifactsBaseDir();
47
56
  if (!existsSync(base)) {
48
57
  return [];
49
58
  }
50
- return readdirSync(base, { withFileTypes: true })
51
- .filter((d) => d.isDirectory() && d.name.startsWith("run_"))
52
- .map((d) => d.name)
53
- .sort()
54
- .reverse();
59
+ const validRuns = [];
60
+ for (const entry of readdirSync(base, { withFileTypes: true })) {
61
+ if (!entry.isDirectory())
62
+ continue;
63
+ const match = entry.name.match(RUN_ID_PATTERN);
64
+ if (!match)
65
+ continue;
66
+ const ts = parseInt(match[1], 10);
67
+ if (Number.isNaN(ts))
68
+ continue;
69
+ validRuns.push({ name: entry.name, ts });
70
+ }
71
+ validRuns.sort((a, b) => b.ts - a.ts);
72
+ return validRuns.map((r) => r.name);
55
73
  }
56
74
  export function getRunDir(runId, baseDir) {
57
75
  const base = baseDir || getArtifactsBaseDir();
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/evidence/artifacts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Action-type buckets used to measure how DEEPLY an interaction recording
3
+ * exercised an app.
4
+ *
5
+ * The earlier `interaction_replay_evidence` gate is binary — it passes if
6
+ * an interaction log exists at all. That's too weak. The Photometry DB
7
+ * E2E session ran 47 entries but 28/29 successful interactions were pure
8
+ * navigation; only 1 was `type` and 0 were form submissions / toggles /
9
+ * uploads / gestures. The replay gate still passed.
10
+ *
11
+ * These buckets let a downstream gate distinguish "I clicked a sidebar
12
+ * link 8 times" from "I exercised every form field, submitted, toggled
13
+ * modes, and dragged a slider". Buckets are intentionally coarse — fine
14
+ * enough to spot a navigation-only run, broad enough that the agent
15
+ * doesn't have to game the categories.
16
+ */
17
+ export interface InteractionBuckets {
18
+ navigation: number;
19
+ input: number;
20
+ commit: number;
21
+ toggle: number;
22
+ gesture: number;
23
+ upload: number;
24
+ click: number;
25
+ keystroke: number;
26
+ inspect: number;
27
+ wait: number;
28
+ other: number;
29
+ }
30
+ export interface InteractionCoverage {
31
+ total: number;
32
+ successful: number;
33
+ buckets: InteractionBuckets;
34
+ unique_screens: number;
35
+ /** Aggregate "depth points" — successful actions in buckets that exercise app state. */
36
+ depth_actions: number;
37
+ }
38
+ export interface DepthMinimums {
39
+ enabled: boolean;
40
+ total_successful: number;
41
+ input: number;
42
+ commit: number;
43
+ click: number;
44
+ navigation: number;
45
+ toggle: number;
46
+ gesture: number;
47
+ upload: number;
48
+ }
49
+ export declare const DEFAULT_DEPTH_MINIMUMS: DepthMinimums;
50
+ /**
51
+ * Aggregate every interaction across every run in the project's artifacts
52
+ * directory. Runs without logs are skipped silently.
53
+ */
54
+ export declare function collectInteractionCoverage(cwd: string): InteractionCoverage;
55
+ export interface DepthVerdict {
56
+ passed: boolean;
57
+ reason: string;
58
+ shortfalls: Array<{
59
+ bucket: keyof InteractionBuckets | "total_successful";
60
+ required: number;
61
+ have: number;
62
+ }>;
63
+ }
64
+ /**
65
+ * Compare the observed coverage against the project's minimums. Returns
66
+ * the verdict plus a human-readable reason that lists EVERY shortfall —
67
+ * the agent uses this to know which buckets to fill in before re-gating.
68
+ */
69
+ export declare function evaluateDepth(coverage: InteractionCoverage, minimums: DepthMinimums): DepthVerdict;
70
+ /**
71
+ * Merge user-supplied minimums onto DEFAULT_DEPTH_MINIMUMS. Unknown / non-numeric
72
+ * fields fall back to the default.
73
+ */
74
+ export declare function resolveDepthMinimums(override: Partial<DepthMinimums> | undefined): DepthMinimums;
75
+ //# sourceMappingURL=interaction_coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction_coverage.d.ts","sourceRoot":"","sources":["../../src/evidence/interaction_coverage.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAUpC,CAAC;AA+MF;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CA0C3E;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9G;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,aAAa,GACtB,YAAY,CA4Cd;AA4CD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,SAAS,GAC3C,aAAa,CAqBf"}
@@ -0,0 +1,347 @@
1
+ import { existsSync, readdirSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { getArtifactsBaseDir, getRunDir, listRuns } from "./artifacts.js";
4
+ export const DEFAULT_DEPTH_MINIMUMS = {
5
+ enabled: true,
6
+ total_successful: 20,
7
+ input: 3,
8
+ commit: 1,
9
+ click: 10,
10
+ navigation: 5,
11
+ toggle: 0,
12
+ gesture: 0,
13
+ upload: 0,
14
+ };
15
+ function emptyBuckets() {
16
+ return {
17
+ navigation: 0,
18
+ input: 0,
19
+ commit: 0,
20
+ toggle: 0,
21
+ gesture: 0,
22
+ upload: 0,
23
+ click: 0,
24
+ keystroke: 0,
25
+ inspect: 0,
26
+ wait: 0,
27
+ other: 0,
28
+ };
29
+ }
30
+ const COMMIT_HINT_WORDS = [
31
+ "submit",
32
+ "save",
33
+ "update",
34
+ "send",
35
+ "create",
36
+ "apply",
37
+ "confirm",
38
+ "login",
39
+ "log in",
40
+ "log-in",
41
+ "sign in",
42
+ "sign-in",
43
+ "sign up",
44
+ "sign-up",
45
+ "signup",
46
+ "register",
47
+ "ok",
48
+ "enter",
49
+ "search",
50
+ "go",
51
+ ];
52
+ function isCommitClick(args) {
53
+ const hints = [args.selector, args.text, args.aria_label, args.label, args.target, args.automationId]
54
+ .filter((v) => typeof v === "string")
55
+ .map((v) => v.toLowerCase());
56
+ if (hints.length === 0)
57
+ return false;
58
+ return hints.some((h) => COMMIT_HINT_WORDS.some((w) => h.includes(w)));
59
+ }
60
+ function isCommitKey(args) {
61
+ const key = args.key?.toLowerCase() ?? "";
62
+ const combo = args.combo?.toLowerCase() ?? "";
63
+ return /(?:^|[+ ])(enter|return)$/.test(key) || /(?:^|[+ ])(enter|return)$/.test(combo);
64
+ }
65
+ function bucketOne(entry, buckets) {
66
+ const action = (entry.action ?? "").toLowerCase();
67
+ const args = entry.input_args ?? {};
68
+ const success = entry.success !== false; // default to true when absent
69
+ // `sequence` is a batch wrapper; walk its children and don't double-count.
70
+ if (action === "sequence" && Array.isArray(entry.steps)) {
71
+ for (const child of entry.steps)
72
+ bucketOne(child, buckets);
73
+ return;
74
+ }
75
+ if (!success) {
76
+ // Failed interactions don't count toward coverage but we still bucket
77
+ // them so the agent can see what it tried. Keep simple: skip entirely.
78
+ return;
79
+ }
80
+ switch (action) {
81
+ case "navigate_url":
82
+ case "navigate_back":
83
+ case "navigate_forward":
84
+ case "deep_link":
85
+ case "back_button":
86
+ case "home_button":
87
+ buckets.navigation += 1;
88
+ return;
89
+ case "type":
90
+ case "fill":
91
+ buckets.input += 1;
92
+ return;
93
+ case "type_and_submit":
94
+ case "type_and_tab":
95
+ buckets.input += 1;
96
+ buckets.commit += 1;
97
+ return;
98
+ case "fill_form": {
99
+ const fields = args.fields ?? [];
100
+ buckets.input += Math.max(1, fields.length);
101
+ if (args.submit_selector || args.submit)
102
+ buckets.commit += 1;
103
+ return;
104
+ }
105
+ case "toggle":
106
+ case "select_option":
107
+ case "select":
108
+ buckets.toggle += 1;
109
+ return;
110
+ case "scroll":
111
+ case "drag_drop":
112
+ case "swipe":
113
+ case "long_press":
114
+ case "hover":
115
+ case "pinch":
116
+ case "rotate_device":
117
+ buckets.gesture += 1;
118
+ return;
119
+ case "upload_file":
120
+ buckets.upload += 1;
121
+ return;
122
+ case "wait":
123
+ case "sleep":
124
+ buckets.wait += 1;
125
+ return;
126
+ case "win_ui_inspect":
127
+ buckets.inspect += 1;
128
+ return;
129
+ case "click":
130
+ case "double_click":
131
+ case "right_click":
132
+ case "tap":
133
+ if (isCommitClick(args))
134
+ buckets.commit += 1;
135
+ else
136
+ buckets.click += 1;
137
+ return;
138
+ case "keystroke":
139
+ case "hotkey":
140
+ if (isCommitKey(args))
141
+ buckets.commit += 1;
142
+ else
143
+ buckets.keystroke += 1;
144
+ return;
145
+ case "win_ui_automate": {
146
+ // Sub-action lives in args.action for UIA dispatch.
147
+ const sub = args.action?.toLowerCase() ?? "";
148
+ if (sub === "setvalue" || sub === "set_value") {
149
+ buckets.input += 1;
150
+ }
151
+ else if (sub === "toggle") {
152
+ buckets.toggle += 1;
153
+ }
154
+ else if (sub === "invoke") {
155
+ // Invoke on a UIA button — treat like a click; flag commits via name hint.
156
+ if (isCommitClick(args))
157
+ buckets.commit += 1;
158
+ else
159
+ buckets.click += 1;
160
+ }
161
+ else {
162
+ buckets.other += 1;
163
+ }
164
+ return;
165
+ }
166
+ default:
167
+ buckets.other += 1;
168
+ return;
169
+ }
170
+ }
171
+ function parseLogFile(path) {
172
+ try {
173
+ const raw = readFileSync(path, "utf-8");
174
+ const out = [];
175
+ for (const line of raw.split("\n")) {
176
+ const trimmed = line.trim();
177
+ if (!trimmed)
178
+ continue;
179
+ try {
180
+ out.push(JSON.parse(trimmed));
181
+ }
182
+ catch {
183
+ /* skip malformed */
184
+ }
185
+ }
186
+ return out;
187
+ }
188
+ catch {
189
+ return [];
190
+ }
191
+ }
192
+ function uniqueScreens(entries) {
193
+ const seen = new Set();
194
+ for (const e of entries) {
195
+ const args = e.input_args ?? {};
196
+ const url = typeof args.url === "string" ? args.url : "";
197
+ const app = typeof args.app_name === "string" ? args.app_name : "";
198
+ const key = url || app;
199
+ if (key)
200
+ seen.add(key);
201
+ }
202
+ return seen.size;
203
+ }
204
+ /**
205
+ * Aggregate every interaction across every run in the project's artifacts
206
+ * directory. Runs without logs are skipped silently.
207
+ */
208
+ export function collectInteractionCoverage(cwd) {
209
+ const baseDir = getArtifactsBaseDir(cwd);
210
+ const buckets = emptyBuckets();
211
+ const allEntries = [];
212
+ if (!existsSync(baseDir)) {
213
+ return { total: 0, successful: 0, buckets, unique_screens: 0, depth_actions: 0 };
214
+ }
215
+ const runs = listRuns(baseDir);
216
+ for (const runId of runs) {
217
+ const logsDir = join(getRunDir(runId, baseDir), "logs");
218
+ if (!existsSync(logsDir))
219
+ continue;
220
+ const files = readdirSync(logsDir).filter((f) => f === "interaction_log.jsonl" || f.startsWith("interaction_log") && f.endsWith(".jsonl"));
221
+ for (const f of files) {
222
+ const entries = parseLogFile(join(logsDir, f));
223
+ allEntries.push(...entries);
224
+ for (const e of entries)
225
+ bucketOne(e, buckets);
226
+ }
227
+ }
228
+ const total = allEntries.length;
229
+ const successful = allEntries.filter((e) => e.success !== false).length;
230
+ const depthActions = buckets.navigation +
231
+ buckets.input +
232
+ buckets.commit +
233
+ buckets.toggle +
234
+ buckets.gesture +
235
+ buckets.upload +
236
+ buckets.click +
237
+ buckets.keystroke;
238
+ return {
239
+ total,
240
+ successful,
241
+ buckets,
242
+ unique_screens: uniqueScreens(allEntries),
243
+ depth_actions: depthActions,
244
+ };
245
+ }
246
+ /**
247
+ * Compare the observed coverage against the project's minimums. Returns
248
+ * the verdict plus a human-readable reason that lists EVERY shortfall —
249
+ * the agent uses this to know which buckets to fill in before re-gating.
250
+ */
251
+ export function evaluateDepth(coverage, minimums) {
252
+ if (!minimums.enabled) {
253
+ return {
254
+ passed: true,
255
+ reason: "Interaction depth gate is disabled in .codeloop/config.json (interaction_depth_minimums.enabled=false).",
256
+ shortfalls: [],
257
+ };
258
+ }
259
+ const shortfalls = [];
260
+ const checks = [
261
+ { bucket: "total_successful", required: minimums.total_successful, have: coverage.successful },
262
+ { bucket: "input", required: minimums.input, have: coverage.buckets.input },
263
+ { bucket: "commit", required: minimums.commit, have: coverage.buckets.commit },
264
+ { bucket: "click", required: minimums.click, have: coverage.buckets.click },
265
+ { bucket: "navigation", required: minimums.navigation, have: coverage.buckets.navigation },
266
+ { bucket: "toggle", required: minimums.toggle, have: coverage.buckets.toggle },
267
+ { bucket: "gesture", required: minimums.gesture, have: coverage.buckets.gesture },
268
+ { bucket: "upload", required: minimums.upload, have: coverage.buckets.upload },
269
+ ];
270
+ for (const c of checks) {
271
+ if (c.required > 0 && c.have < c.required)
272
+ shortfalls.push(c);
273
+ }
274
+ if (shortfalls.length === 0) {
275
+ const b = coverage.buckets;
276
+ return {
277
+ passed: true,
278
+ reason: `Deep interaction coverage met: ${coverage.successful} successful actions (click=${b.click}, navigation=${b.navigation}, input=${b.input}, commit=${b.commit}, toggle=${b.toggle}, gesture=${b.gesture}, upload=${b.upload}).`,
279
+ shortfalls: [],
280
+ };
281
+ }
282
+ const lines = shortfalls.map((s) => ` - ${s.bucket}: need >= ${s.required}, have ${s.have}`);
283
+ const hint = buildHint(shortfalls);
284
+ return {
285
+ passed: false,
286
+ reason: `Deep interaction coverage NOT met. Shortfalls:\n${lines.join("\n")}\n${hint}`,
287
+ shortfalls,
288
+ };
289
+ }
290
+ function buildHint(shortfalls) {
291
+ const tips = [];
292
+ const has = (b) => shortfalls.some((s) => s.bucket === b);
293
+ if (has("input")) {
294
+ tips.push('Fill input fields with codeloop_interact action: "type" or "fill_form" (or "win_ui_automate" with action: "setValue" on a WPF TextBox).');
295
+ }
296
+ if (has("commit")) {
297
+ tips.push('Submit at least one form: click a button whose name contains submit/save/update/login/signup, or send action "type_and_submit" / "hotkey" enter after typing.');
298
+ }
299
+ if (has("toggle")) {
300
+ tips.push('Exercise a toggle, switch, or dropdown via action: "toggle" / "select_option" (or "win_ui_automate" with action: "toggle" on a ToggleSwitch).');
301
+ }
302
+ if (has("click")) {
303
+ tips.push('Click more interactive elements — buttons, list items, expanders, tabs. Pure navigation does not count toward click coverage.');
304
+ }
305
+ if (has("navigation")) {
306
+ tips.push('Visit more screens. Use action: "navigate_url" for web, "click" on sidebar/menu items for desktop, "deep_link" for mobile.');
307
+ }
308
+ if (has("gesture")) {
309
+ tips.push('Add at least one gesture: scroll (action: "scroll"), drag (action: "drag_drop"), swipe (action: "swipe" on mobile), or hover (action: "hover").');
310
+ }
311
+ if (has("upload")) {
312
+ tips.push('Exercise a file upload via action: "upload_file".');
313
+ }
314
+ if (has("total_successful")) {
315
+ tips.push("Overall successful-interaction count is too low; combine the above to reach the minimum.");
316
+ }
317
+ return tips.length > 0 ? `Next steps:\n * ${tips.join("\n * ")}` : "";
318
+ }
319
+ /**
320
+ * Merge user-supplied minimums onto DEFAULT_DEPTH_MINIMUMS. Unknown / non-numeric
321
+ * fields fall back to the default.
322
+ */
323
+ export function resolveDepthMinimums(override) {
324
+ if (!override)
325
+ return DEFAULT_DEPTH_MINIMUMS;
326
+ const out = { ...DEFAULT_DEPTH_MINIMUMS };
327
+ if (typeof override.enabled === "boolean")
328
+ out.enabled = override.enabled;
329
+ const numFields = [
330
+ "total_successful",
331
+ "input",
332
+ "commit",
333
+ "click",
334
+ "navigation",
335
+ "toggle",
336
+ "gesture",
337
+ "upload",
338
+ ];
339
+ for (const k of numFields) {
340
+ const v = override[k];
341
+ if (typeof v === "number" && v >= 0 && Number.isFinite(v)) {
342
+ out[k] = v;
343
+ }
344
+ }
345
+ return out;
346
+ }
347
+ //# sourceMappingURL=interaction_coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction_coverage.js","sourceRoot":"","sources":["../../src/evidence/interaction_coverage.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -313,6 +313,7 @@ Returns: per-screen pixel diff scores + worst-failing reference, actual, and dif
313
313
  figma_file_url: z.string().optional().describe("Figma file URL or key. Triggers a Figma sync into designs/ before comparing."),
314
314
  figma_token: z.string().optional().describe("Figma personal access token. Falls back to FIGMA_API_TOKEN env var."),
315
315
  designs_dir: z.string().optional().describe("Override the default designs/ folder."),
316
+ run_id: z.string().optional().describe("Explicit run_id whose screenshots/ folder should be the source of actuals. When omitted, design_compare picks the newest run that has PNGs. Honour this when the caller has just captured fresh screenshots into a specific run so a stale or seed run cannot shadow the comparison."),
316
317
  project_dir: z.string().optional().describe("Absolute path to the project root. Defaults to CODELOOP_PROJECT_DIR or cwd."),
317
318
  }, async (params) => {
318
319
  const authResult = await withAuth(async () => {
@@ -327,6 +328,7 @@ Returns: per-screen pixel diff scores + worst-failing reference, actual, and dif
327
328
  figma_file_url: params.figma_file_url,
328
329
  figma_token: params.figma_token,
329
330
  designs_dir: params.designs_dir,
331
+ run_id: params.run_id,
330
332
  };
331
333
  const cwd = params.project_dir || projectDir;
332
334
  const result = await runDesignCompare(input, config, cwd);
@@ -1287,6 +1289,10 @@ Returns: checklist of completed and pending verification steps.`, {
1287
1289
  const coveredRoutes = new Set();
1288
1290
  let discoveredRoutes = [];
1289
1291
  let uncoveredRoutes = [];
1292
+ // Deep-interaction bucket breakdown — set even for non-UI projects so
1293
+ // the diagnostic step can show "n/a" with the same shape.
1294
+ let depthDetail = "Not a UI project — interaction depth not applicable";
1295
+ let depthStatus = "n/a";
1290
1296
  if (isUIProject) {
1291
1297
  try {
1292
1298
  const { readFileSync: rfs } = await import("fs");
@@ -1319,6 +1325,28 @@ Returns: checklist of completed and pending verification steps.`, {
1319
1325
  uncoveredRoutes = discoveredRoutes.filter(route => {
1320
1326
  return !coveredRoutes.has(route) && ![...coveredRoutes].some(c => c.includes(route) || route.includes(c));
1321
1327
  });
1328
+ // Deep-interaction depth check — same machinery the
1329
+ // interaction_depth_evidence gate uses, surfaced HERE so the
1330
+ // agent sees "input=1, commit=0 — need >= 3 / >= 1" BEFORE
1331
+ // calling gate_check and gets a structured next step.
1332
+ const { collectInteractionCoverage, evaluateDepth, resolveDepthMinimums, } = await import("./evidence/interaction_coverage.js");
1333
+ const minimums = resolveDepthMinimums(config.interaction_depth_minimums);
1334
+ const coverage = collectInteractionCoverage(cwd);
1335
+ const verdict = evaluateDepth(coverage, minimums);
1336
+ const b = coverage.buckets;
1337
+ const breakdown = `click=${b.click}, navigation=${b.navigation}, input=${b.input}, commit=${b.commit}, toggle=${b.toggle}, gesture=${b.gesture}, upload=${b.upload}, keystroke=${b.keystroke}, inspect=${b.inspect}`;
1338
+ if (!minimums.enabled) {
1339
+ depthStatus = "n/a";
1340
+ depthDetail = `Depth gate disabled in .codeloop/config.json. Observed buckets: ${breakdown}.`;
1341
+ }
1342
+ else if (verdict.passed) {
1343
+ depthStatus = "done";
1344
+ depthDetail = `${coverage.successful} successful interactions across ${runs.length} run(s) (${breakdown}). Depth minimums met.`;
1345
+ }
1346
+ else {
1347
+ depthStatus = "PENDING";
1348
+ depthDetail = `${coverage.successful} successful interactions across ${runs.length} run(s) (${breakdown}). ${verdict.reason}`;
1349
+ }
1322
1350
  }
1323
1351
  catch { /* best-effort coverage check */ }
1324
1352
  }
@@ -1381,6 +1409,11 @@ Returns: checklist of completed and pending verification steps.`, {
1381
1409
  ? `All ${discoveredRoutes.length} discovered routes covered by ${interactionCount} interactions`
1382
1410
  : `${uncoveredRoutes.length} of ${discoveredRoutes.length} routes NOT covered by interactions. Uncovered: ${uncoveredRoutes.join(", ")}. Navigate to these routes and interact with their elements.`,
1383
1411
  },
1412
+ {
1413
+ step: "7. Deep interaction depth",
1414
+ status: depthStatus,
1415
+ detail: depthDetail,
1416
+ },
1384
1417
  ];
1385
1418
  const pendingSteps = steps.filter(s => s.status === "PENDING" || s.status === "NEEDS_RERUN");
1386
1419
  const allDone = pendingSteps.length === 0;