@molecule/api-ai-tools 1.9.13 → 1.9.15

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/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-18T09:17:15.122Z
6
+ Generated: 2026-10-18T11:04:38.283Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -2274,22 +2274,43 @@ into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,l
2274
2274
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
2275
2275
 
2276
2276
  ```typescript
2277
- function streamingBackgroundCommand(command: string): { command: string; changes: string[] }
2277
+ function streamingBackgroundCommand(command: string): {
2278
+ command: string
2279
+ changes: string[]
2280
+ error?: string
2281
+ }
2278
2282
  ```
2279
2283
 
2280
2284
  - `command` — The command as the executor sent it.
2281
2285
 
2282
- **Returns:** The command to run and one sentence per change made (empty when none).
2286
+ **Returns:** The command to run and one sentence per change made (empty when none), or `error` when the command starts the suite more than once.
2283
2287
 
2284
- #### `isSuiteRunCommand(command)`
2288
+ #### `suiteRunStatements(command)`
2285
2289
 
2286
- Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
2287
- — rather than a build, an install or a one-off script.
2290
+ Every statement of a command that starts a whole test suite, in command order.
2288
2291
 
2289
- A command that invokes a runner only to print is not one of these
2292
+ This is the ONE count of suite runs in a command: {@link isSuiteRunCommand} asks it,
2293
+ {@link streamingBackgroundCommand} refuses a command carrying more than one — each statement
2294
+ starts a whole walk of the same suite, and only the last of them is rewritten — and a re-run
2295
+ composed from a command ({@link ./suite-overlap.js} `scopedRerunCommand`) keeps only the first.
2296
+
2297
+ A statement that invokes a runner only to print is not one of these
2290
2298
  ({@link commandRunsNoTests}): `npx playwright test --list` prints the tests it would run and
2291
2299
  exits, and `npx playwright install`, `vitest list` and `hurl --help` start no test either.
2292
2300
 
2301
+ ```typescript
2302
+ function suiteRunStatements(command: string): string[]
2303
+ ```
2304
+
2305
+ - `command` — The shell command.
2306
+
2307
+ **Returns:** One entry per suite-running statement, as the parser read it (empty when none).
2308
+
2309
+ #### `isSuiteRunCommand(command)`
2310
+
2311
+ Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
2312
+ — rather than a build, an install or a one-off script.
2313
+
2293
2314
  ```typescript
2294
2315
  function isSuiteRunCommand(command: string): boolean
2295
2316
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAc1F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAClB,MAAM,CAqCR;AAgHD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQvD"}
1
+ {"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAc1F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAClB,MAAM,CA0CR;AAgHD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQvD"}
@@ -31,7 +31,7 @@
31
31
  import { posix } from 'node:path';
32
32
  import { commandSegments, locatedCommandSegments } from './command-position.js';
33
33
  import { commandRunsNoTests } from './no-test-run.js';
34
- import { shellQuote } from './utilities.js';
34
+ import { shellQuote, suiteRunStatements } from './utilities.js';
35
35
  /**
36
36
  * A command with its heredoc bodies removed: the runs, kills and hosts it names in a file it
37
37
  * WRITES are not what the command itself does.
@@ -180,7 +180,12 @@ export function scopedRerunCommand(command, locations, excluded = [], config, cw
180
180
  .replace(/\s*--last-failed\b/g, '')
181
181
  .replace(/\s*--retries(?:[= ]\d+)?/g, '')
182
182
  .replace(/^(playwright\s+test)\b/, `$1 ${narrowed.join(' ')} --retries=0${carry}${invert}`);
183
- return `${head}${scoped}${tail}`;
183
+ // A command that starts the suite AGAIN after this statement is a chain of walks, not a re-run:
184
+ // everything from the next suite runner on is dropped, so the composed command carries ONE walk.
185
+ // X1 xr-molecule-flash-m157 (+14:02) was handed a re-run that still carried the chain's second
186
+ // whole walk — 4.5 minutes measuring code it had not changed.
187
+ const rerunTail = suiteRunStatements(tail).length > 0 ? '' : tail;
188
+ return `${head}${scoped}${rerunTail}`;
184
189
  }
185
190
  /** A directory a `cd` can name literally: no `~`, no variable, no glob. */
186
191
  const LITERAL_DIR = /^[A-Za-z0-9_./+-]+$/;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAkJ9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAkOD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAsEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA2GD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAokFxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAkJ9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAiSD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAsEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA2GD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAqlFxF"}
package/dist/tools.js CHANGED
@@ -192,6 +192,58 @@ function logProgress(log) {
192
192
  const total = Number(last?.[2]);
193
193
  return last && total > 0 && done >= 0 && done <= total ? { done, total } : null;
194
194
  }
195
+ /**
196
+ * Playwright's start-of-walk line, `Running 138 tests using 1 worker` — the first thing a walk
197
+ * prints, so a second one in one log is a second walk.
198
+ */
199
+ const WALK_START = /^[ \t]*Running \d+ tests?\b/gm;
200
+ /**
201
+ * One status line of a runner's closing tally — ` 4 failed`, ` 70 passed (4.5m)`,
202
+ * ` 65 skipped`.
203
+ */
204
+ const TALLY_STATUS = /^[ \t]*(\d+)[ \t]+(passed|failed|flaky|skipped|interrupted|timed out|did not run)\b[^\n]*/gm;
205
+ /**
206
+ * A runner's CLOSING tally, the line that only prints once a walk has finished: Playwright's
207
+ * duration-bearing `70 passed (4.5m)`, a `1 flaky` summary, or Hurl's `Executed files:` count.
208
+ *
209
+ * The same reading as molecule-dev's `suite-proof.ts` `FINAL_TALLY_RE` (the platform's finished-run
210
+ * detector); this package cannot import it, so the shape is mirrored here and nowhere else.
211
+ */
212
+ const FINISHED_TALLY = /\b\d+[ \t]+(?:passed|failed)[ \t]+\(\d+(?:\.\d+)?(?:ms|s|m|h)\)|^[ \t]*\d+[ \t]+flaky\b|^[ \t]*Executed files:/m;
213
+ /**
214
+ * Whether a task's log holds a SECOND walk of the same suite — a finished tally followed by a
215
+ * fresh `Running N tests` header — and what the finished walk's tally said.
216
+ *
217
+ * A command that chained two suite runs starts the second walk the moment the first ends, and the
218
+ * wait used to read that log as one slow run ("slow, not stuck") for the whole second walk. X1
219
+ * xr-molecule-flash-m157 (+14:02) waited out 4.5 minutes of a duplicate walk that measured nothing:
220
+ * the first walk had already printed `4 failed / 65 skipped / 70 passed (4.5m)`.
221
+ *
222
+ * @param slice - The log slice this wait read.
223
+ * @returns The finished walk's tally lines, or null when the log shows one walk (or none).
224
+ */
225
+ function secondSuiteWalk(slice) {
226
+ const header = [...slice.matchAll(WALK_START)].at(-1);
227
+ if (!header || header.index === undefined)
228
+ return null;
229
+ const finished = FINISHED_TALLY.exec(slice);
230
+ // One walk prints its header FIRST and its tally last, so a tally before the last header is
231
+ // another walk's. The slice repeats what it greps and then the log's own tail, so the same
232
+ // tally can appear twice; one line per status, taken at its last position, keeps it once.
233
+ if (!finished || finished.index === undefined || finished.index > header.index)
234
+ return null;
235
+ const seen = new Map();
236
+ for (const match of slice.matchAll(TALLY_STATUS)) {
237
+ if (match.index === undefined || match.index >= header.index)
238
+ continue;
239
+ seen.set(match[2] ?? '', { at: match.index, text: match[0].trim().replace(/\s+/g, ' ') });
240
+ }
241
+ const tally = [...seen.values()]
242
+ .sort((a, b) => a.at - b.at)
243
+ .map((v) => v.text)
244
+ .join(' / ');
245
+ return { tally: tally || 'its closing tally' };
246
+ }
195
247
  /**
196
248
  * What a wait knows about the command BESIDES "it has not exited": how long it
197
249
  * has been running, whether its output is still growing, how far its own log
@@ -232,11 +284,17 @@ function waitProgressClauses(o) {
232
284
  ? `it has been running at least ${durationText(o.waitedMs)}`
233
285
  : `it has been running ${durationText(sinceStart)}`,
234
286
  ];
235
- if (o.stillWriting)
287
+ // A log that already finished one walk and started another says so instead of "slow, not
288
+ // stuck": the growth is the SECOND walk printing, and its counter is not this run's progress.
289
+ const secondWalk = secondSuiteWalk(o.slice);
290
+ if (o.stillWriting && !secondWalk)
236
291
  clauses.push(`its output is still growing (${o.bytes} bytes) — slow, not stuck`);
237
- else if (o.bytes !== null)
292
+ else if (o.bytes !== null && !secondWalk)
238
293
  clauses.push(`its log has not grown during this wait (${o.bytes} bytes)`);
239
- const counter = logProgress(o.slice);
294
+ if (secondWalk)
295
+ clauses.push(`the first walk in this command has finished (${secondWalk.tally}); the command started a ` +
296
+ 'SECOND walk of the same suite — stop it (it measures nothing new) and read the first tally');
297
+ const counter = secondWalk ? null : logProgress(o.slice);
240
298
  if (counter) {
241
299
  clauses.push(`its log is at ${counter.done} of ${counter.total}`);
242
300
  // The rate is the task's own elapsed time, never this call's wait: with no
@@ -2289,6 +2347,11 @@ export function buildTools(backend, config) {
2289
2347
  // Keep the log growing while it runs (see streamingBackgroundCommand):
2290
2348
  // an empty log is what wait_for_task reads as stuck.
2291
2349
  const streaming = streamingBackgroundCommand(command);
2350
+ // A command that starts the suite more than once is refused here, before anything is
2351
+ // written or launched: each walk measures the same code, and only the last is rewritten
2352
+ // (X1 xr-molecule-flash-m157 +14:02 — two whole walks chained with `;`).
2353
+ if (streaming.error)
2354
+ return { error: streaming.error };
2292
2355
  // …and the trace flag this tool added (above), which the streaming rewrite never saw:
2293
2356
  // its changes are what this branch reports the run's own settings in.
2294
2357
  if (traced.change)
@@ -2859,6 +2922,14 @@ export function buildTools(backend, config) {
2859
2922
  // instruction — and not on a finished run's result, where `failureCausesNote` reads the
2860
2923
  // whole log instead of the 40 KB tail a mid-run look is given.
2861
2924
  const midRun = midRunNote ? ` ${midRunNote}` : '';
2925
+ // A log that already finished one walk and started a second is not a run to keep waiting
2926
+ // on: the first walk's tally is the measurement and the second only repeats it, so the
2927
+ // advice below hands over the stop instead of "keep waiting" (m157 +14:02).
2928
+ const secondWalk = secondSuiteWalk(soFar) !== null;
2929
+ const stopInstead = secondWalk
2930
+ ? ` Stop it (\`exec_command: ${await taskStopCommand(backend, taskId)}\`) and read the ` +
2931
+ `first walk's tally in ${log} — the second walk measures nothing new.`
2932
+ : null;
2862
2933
  if (stillWriting && waitedBefore + waitedMs >= stuckAfterMs()) {
2863
2934
  waitedByTask.set(taskId, 0);
2864
2935
  return {
@@ -2866,8 +2937,11 @@ export function buildTools(backend, config) {
2866
2937
  status: 'running',
2867
2938
  waitedMs,
2868
2939
  ...sliceField,
2869
- note: `${progress} Call wait_for_task again to keep waiting — it returns the moment the ` +
2870
- `command ends and reports the failures as they appear.${midRun}`,
2940
+ note: `${progress}` +
2941
+ (stopInstead ??
2942
+ ` Call wait_for_task again to keep waiting — it returns the moment the ` +
2943
+ `command ends and reports the failures as they appear.`) +
2944
+ `${midRun}`,
2871
2945
  };
2872
2946
  }
2873
2947
  if (waitedBefore + waitedMs >= stuckAfterMs()) {
@@ -2889,24 +2963,26 @@ export function buildTools(backend, config) {
2889
2963
  // No "Still running after Ns" lead here either: the clauses already say
2890
2964
  // how long it has been running, and the lead said it a second time in
2891
2965
  // the other format (X1 flash-m87 §+47:20).
2892
- note: `${progress} Call wait_for_task again to ` +
2893
- `keep waiting — its result carries the log output. ` +
2894
- (soFar.trim()
2895
- ? `outputSoFar is the failure lines so far (up to ${RUNNING_FAILURE_LINES}) and then the end of ` +
2896
- `the log, not the whole run — \`grep -n '✘' ${log} | head -40\` lists every failure, ` +
2897
- `\`grep -n -A 20 '<test name>' ${log}\` reads one failure's error: this run writes ` +
2898
- `each failure's own report into the log as that test fails, right under its ✘ line, ` +
2899
- `not only in the summary at the end. `
2900
- : `Nothing has been printed yet — \`grep -n '✘' ${log} | head -40\` lists failures as they appear. `) +
2901
- // What an edit does to this run depends on what the run MEASURES: the sandbox's own
2902
- // app is re-served from app/src as it is edited, so the run's result stops counting; a
2903
- // run against the deployed build is untouched by a local edit, and the executor can
2904
- // spend the rest of the run fixing (xr-molecule-flash-m122 +19:00: a deployed run was
2905
- // told editing changes what it tests, then handed "Start fixing it now" in the same
2906
- // result — it waited out 1 min 11 s of free fixing time).
2907
- (started !== undefined && suiteCommandTarget(started.command) !== 'local'
2908
- ? `Meanwhile fix what it has already named: this run tests the deployed build, so editing app/src or api/src now does not change what it tests — fix the causes it has already named while it walks, run nothing against the app until it ends, then deploy once.`
2909
- : `Meanwhile do work that leaves the code under test alone (read the failures so far, plan the fixes): editing app/src or api/src now changes the code the run is testing, and its result no longer counts.`) +
2966
+ note: `${progress}` +
2967
+ (stopInstead ??
2968
+ ` Call wait_for_task again to ` +
2969
+ `keep waiting — its result carries the log output. ` +
2970
+ (soFar.trim()
2971
+ ? `outputSoFar is the failure lines so far (up to ${RUNNING_FAILURE_LINES}) and then the end of ` +
2972
+ `the log, not the whole run — \`grep -n '✘' ${log} | head -40\` lists every failure, ` +
2973
+ `\`grep -n -A 20 '<test name>' ${log}\` reads one failure's error: this run writes ` +
2974
+ `each failure's own report into the log as that test fails, right under its ✘ line, ` +
2975
+ `not only in the summary at the end. `
2976
+ : `Nothing has been printed yet — \`grep -n '✘' ${log} | head -40\` lists failures as they appear. `) +
2977
+ // What an edit does to this run depends on what the run MEASURES: the sandbox's own
2978
+ // app is re-served from app/src as it is edited, so the run's result stops counting; a
2979
+ // run against the deployed build is untouched by a local edit, and the executor can
2980
+ // spend the rest of the run fixing (xr-molecule-flash-m122 +19:00: a deployed run was
2981
+ // told editing changes what it tests, then handed "Start fixing it now" in the same
2982
+ // result — it waited out 1 min 11 s of free fixing time).
2983
+ (started !== undefined && suiteCommandTarget(started.command) !== 'local'
2984
+ ? `Meanwhile fix what it has already named: this run tests the deployed build, so editing app/src or api/src now does not change what it tests — fix the causes it has already named while it walks, run nothing against the app until it ends, then deploy once.`
2985
+ : `Meanwhile do work that leaves the code under test alone (read the failures so far, plan the fixes): editing app/src or api/src now changes the code the run is testing, and its result no longer counts.`)) +
2910
2986
  `${midRun}`,
2911
2987
  };
2912
2988
  }
@@ -1 +1 @@
1
- {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
@@ -42,6 +42,13 @@ export const MAX_TRACED_CONSOLE = 4;
42
42
  export const MAX_TRACED_ACTIONS = 40;
43
43
  /** How much of a 4xx request's sent body (or its response body) the trace read keeps. */
44
44
  export const MAX_TRACED_BODY_CHARS = 300;
45
+ /**
46
+ * How much of a websocket's frames the trace read keeps. Longer than {@link MAX_TRACED_BODY_CHARS}
47
+ * because a frame read back to its own message still has the message, the module id and the
48
+ * location to carry — Vite's transform error alone is a sentence longer than the plain body cap,
49
+ * and the note prints its first 120–200 characters anyway (xr-molecule-flash-m157).
50
+ */
51
+ const MAX_TRACED_WEBSOCKET_CHARS = 600;
45
52
  /** How many tests the note in a run's result names. */
46
53
  export const MAX_TRACE_NOTE_TESTS = 5;
47
54
  /** How many requests the note names per test. */
@@ -97,6 +104,7 @@ const MAX_NOTE = ${MAX_TRACE_NOTE_REQUESTS}
97
104
  const MAX_CONSOLE = ${MAX_TRACED_CONSOLE}
98
105
  const MAX_ACTIONS = ${MAX_TRACED_ACTIONS}
99
106
  const MAX_BODY = ${MAX_TRACED_BODY_CHARS}
107
+ const MAX_WS = ${MAX_TRACED_WEBSOCKET_CHARS}
100
108
  const MAX_URL = 200
101
109
  // A console line that is not an error but still has to reach the note (m148): one a molecule
102
110
  // package wrote — it names the package, the \`[molecule/…]\` prefix, or the \`[scope]\` prefix
@@ -199,6 +207,51 @@ function bodyText(buf, entries, value, max) {
199
207
  return text.indexOf('\\u0000') === -1 ? text : ''
200
208
  }
201
209
 
210
+ // A websocket's content is a JSONL stream of frames (\`{type, time, opcode, data}\`), and the data
211
+ // is the page's own message. The envelope is ~35 characters on every frame, so cutting the raw
212
+ // stream lands inside it and never reaches the text that names a cause: xr-molecule-flash-m157's
213
+ // Vite HMR error frame was \`{"type":"receive","time":…,"opcode":1,"data":"{\\"type\\":\\"error\\",
214
+ // \\"err\\":{\\"message\\":\\"Cannot import non-asset file /styles.css which is inside /public …\` and
215
+ // every note stopped at \`{"type":"error","err":{"message":"Cannot…\`. Read the frames and print
216
+ // what they carry — a Vite error's message, id and loc, or the JSON a frame sends — so the cut
217
+ // lands in the message itself.
218
+ function websocketText(raw) {
219
+ const errors = []
220
+ const others = []
221
+ for (const line of String(raw).split('\\n')) {
222
+ if (!line.trim()) continue
223
+ let frame
224
+ try { frame = JSON.parse(line) } catch (e) { continue }
225
+ if (!frame || typeof frame.data !== 'string') continue
226
+ let data = frame.data
227
+ try { data = JSON.parse(frame.data) } catch (e) { /* a text frame: print the data as it is */ }
228
+ const err = data && typeof data === 'object' && data.type === 'error' ? data.err : null
229
+ if (err && typeof err.message === 'string') {
230
+ let text = 'vite error: ' + err.message
231
+ if (err.id !== undefined && err.id !== null && err.id !== '')
232
+ text += ' (id: ' + String(err.id) + ')'
233
+ const loc = err.loc
234
+ if (loc && typeof loc === 'object') {
235
+ const file = typeof loc.file === 'string' && loc.file ? loc.file : String(err.id || '')
236
+ text += ' (loc: ' + file +
237
+ (typeof loc.line === 'number' ? ':' + loc.line + ':' + (typeof loc.column === 'number' ? loc.column : 0) : '') +
238
+ ')'
239
+ } else if (typeof loc === 'string' && loc) text += ' (loc: ' + loc + ')'
240
+ errors.push(text)
241
+ } else if (typeof data === 'string') others.push(data)
242
+ else others.push(JSON.stringify(data))
243
+ }
244
+ return errors.concat(others).join(' ')
245
+ }
246
+
247
+ // What a request got back, as the note reads it: a websocket's frames are read as the messages
248
+ // they carry (m157), every other request's body as stored.
249
+ function gotText(buf, entries, r) {
250
+ if (r.type !== 'websocket') return bodyText(buf, entries, r.content, MAX_BODY)
251
+ const raw = bodyText(buf, entries, r.content, 8000)
252
+ return (websocketText(raw) || raw).slice(0, MAX_WS)
253
+ }
254
+
202
255
  let origin = ''
203
256
  function pathOf(url) {
204
257
  try {
@@ -431,7 +484,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
431
484
  for (const i of actionIndexes) {
432
485
  const r = requests[i]
433
486
  const sent = bodyText(buf, entries, r.postData, MAX_BODY)
434
- const got = bodyText(buf, entries, r.content, MAX_BODY)
487
+ const got = gotText(buf, entries, r)
435
488
  if (sent) r.sent = sent
436
489
  if (got) r.got = got
437
490
  }
@@ -439,7 +492,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
439
492
  if (r.status < 400 || r.status >= 500) continue
440
493
  if (/^https?:/i.test(r.path)) continue
441
494
  const sent = bodyText(buf, entries, r.postData, MAX_BODY)
442
- const got = bodyText(buf, entries, r.content, MAX_BODY)
495
+ const got = gotText(buf, entries, r)
443
496
  if (sent) r.sent = sent
444
497
  if (got) r.got = got
445
498
  }
@@ -509,7 +562,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
509
562
  if (leadIndex !== -1) {
510
563
  const r = requests[leadIndex]
511
564
  const sent = bodyText(buf, entries, r.postData, MAX_BODY)
512
- const got = bodyText(buf, entries, r.content, MAX_BODY)
565
+ const got = gotText(buf, entries, r)
513
566
  lead = wire(r)
514
567
  if (sent) lead.sent = sent
515
568
  if (got) lead.got = got
@@ -1178,19 +1231,36 @@ function sameOriginPath(request) {
1178
1231
  * the request carried no `Authorization` header at all and the app's hand-rolled session store
1179
1232
  * never handed its token to the shared client.
1180
1233
  *
1234
+ * xr-molecule-flash-m156 (+16:15): the same 401 shape after a reload, and the page's own console
1235
+ * named the cause — `@molecule/app-http` logged `no client is bonded`. The auth setup ran before
1236
+ * the HTTP setup and kept the fallback client, so the restored token landed on a client nothing
1237
+ * sent through. When the trace carries that line, the no-header sentence names the ordering fix
1238
+ * instead of leaving the run to re-derive it.
1239
+ *
1181
1240
  * The trace cannot name the file that made the call: a real HAR entry carries the request, response,
1182
1241
  * `_resourceType`, `_monotonicTime` and a frame ref, but no initiator stack. So the sentence names
1183
1242
  * the shared client and the auth bond instead of guessing a file.
1184
1243
  *
1185
1244
  * @param request - The refused request, a 401 or 403 on the app's own origin.
1245
+ * @param unbondedClient - Whether the traced page logged `no client is bonded`, which explains a
1246
+ * request that carried no `Authorization` header.
1186
1247
  * @returns The sentence the note appends.
1187
1248
  */
1188
- function authRefusalSentence(request) {
1249
+ function authRefusalSentence(request, unbondedClient = false) {
1189
1250
  const label = requestLabel(request);
1190
1251
  // The header's scheme, when it parsed; presence alone is still the server's half of the wire.
1191
1252
  const carried = request.authorization !== undefined
1192
1253
  ? `with \`Authorization: ${request.authorization} …\``
1193
1254
  : 'with an `Authorization` header';
1255
+ // The console line names the mechanism the no-header branch otherwise has to guess at:
1256
+ // `getClient()` ran before `setupHttpDefault()` and the token was mirrored into the
1257
+ // client that later setup replaced (xr-molecule-flash-m156).
1258
+ const unbonded = unbondedClient
1259
+ ? ' The page logged `no client is bonded` — something called `getClient()` from ' +
1260
+ '`@molecule/app-http` before `setupHttpDefault()` ran and kept that fallback client; ' +
1261
+ 'in `app/src/bonds/index.ts` call `setupHttpDefault()` before `setupAuthDefault()`, ' +
1262
+ 'and read the client per request instead of capturing it at setup.'
1263
+ : '';
1194
1264
  if (request.status === 403) {
1195
1265
  return request.sentAuthorization === true
1196
1266
  ? `The browser sent \`${label}\` ${carried} and the API answered 403 — the token was ` +
@@ -1200,7 +1270,8 @@ function authRefusalSentence(request) {
1200
1270
  '`Authorization` header, so either the client did not attach the token (check that the ' +
1201
1271
  'request goes through the app’s HTTP client that the auth bond configures, and that the ' +
1202
1272
  'token is stored where the client reads it) or the route refuses this user: read the ' +
1203
- 'route’s role/permission check in your own source.';
1273
+ 'route’s role/permission check in your own source.' +
1274
+ unbonded;
1204
1275
  }
1205
1276
  if (request.sentAuthorization === true) {
1206
1277
  return (`The browser sent \`${label}\` ${carried} and the API refused it: the token was sent and ` +
@@ -1218,7 +1289,8 @@ function authRefusalSentence(request) {
1218
1289
  ? 'The request did carry a `Cookie` header, so if this app signs in with a session cookie ' +
1219
1290
  'that is the credential the server did not accept. '
1220
1291
  : '') +
1221
- 'The API handler is not the problem.');
1292
+ 'The API handler is not the problem.' +
1293
+ unbonded);
1222
1294
  }
1223
1295
  /**
1224
1296
  * The note's credential-refusal advice, one sentence per distinct shape — at most two.
@@ -1234,23 +1306,67 @@ function authRefusalSentence(request) {
1234
1306
  function authRefusalAdvice(items) {
1235
1307
  const sentences = [];
1236
1308
  const seen = new Set();
1309
+ // A page that logged `no client is bonded` explains every request it sent without a
1310
+ // token, so the advice names that mechanism once for the run
1311
+ // (xr-molecule-flash-m156).
1312
+ const unbondedClient = items.some((item) => item.console.some((line) => line.includes('no client is bonded')));
1237
1313
  for (const item of items) {
1238
1314
  for (const request of item.requests) {
1239
1315
  if (request.status !== 401 && request.status !== 403)
1240
1316
  continue;
1241
1317
  if (sameOriginPath(request) === null)
1242
1318
  continue;
1319
+ // A 4xx on the logout endpoint is the shape the row itself answers: the auth client
1320
+ // ignores it and clears the session, and the page owes the navigation (m156).
1321
+ if (isLogoutRequest(request))
1322
+ continue;
1243
1323
  const key = `${request.status} ${request.sentAuthorization ? (request.authorization ?? 'header') : 'none'} ${request.sentCookie ? 'cookie' : ''}`;
1244
1324
  if (seen.has(key))
1245
1325
  continue;
1246
1326
  seen.add(key);
1247
- sentences.push(authRefusalSentence(request));
1327
+ sentences.push(authRefusalSentence(request, unbondedClient));
1248
1328
  if (sentences.length === 2)
1249
1329
  return ` ${sentences.join(' ')}`;
1250
1330
  }
1251
1331
  }
1252
1332
  return sentences.length === 0 ? '' : ` ${sentences.join(' ')}`;
1253
1333
  }
1334
+ /**
1335
+ * Whether a request went to an auth client's logout endpoint. The client's default
1336
+ * `logoutEndpoint` is `/users/logout` (`@molecule/app-auth`), and a project may name its own
1337
+ * (`/auth/logout`), so the last segment is what identifies it — never a literal path.
1338
+ *
1339
+ * @param request - One request a trace recorded.
1340
+ * @returns True for a POST to a path ending in `/logout`.
1341
+ */
1342
+ function isLogoutRequest(request) {
1343
+ if (request.method !== 'POST')
1344
+ return false;
1345
+ return /\/(?:users\/)?logout$/i.test(requestPath(request));
1346
+ }
1347
+ /**
1348
+ * The sentence a 4xx on the auth client's logout endpoint owes when the page then stayed put.
1349
+ *
1350
+ * `@molecule/app-auth`'s `logout()` calls the endpoint inside a `.catch` and clears the session in
1351
+ * `finally` (`packages/app/core/auth/src/client.ts`): a 401 there is EXPECTED on a sign-out and is
1352
+ * not the failure. What the test waits for is the URL moving, which the page has to do itself.
1353
+ * xr-molecule-flash-m156 (+24:02 → +33:52) read `POST /api/users/logout → 401` as a broken
1354
+ * endpoint, added a backend logout route, and paid a re-run and a redeploy — while the real cause
1355
+ * was the `navigate('/')` the refactor had dropped.
1356
+ *
1357
+ * @param request - The 4xx logout request.
1358
+ * @param pageUrl - The URL the page stayed at, when the trace recorded one.
1359
+ * @returns The sentence the note appends to the row.
1360
+ */
1361
+ function logoutIgnoredSentence(request, pageUrl) {
1362
+ const where = pageUrl === undefined ? 'The page' : `\`${pageUrl}\``;
1363
+ return (`The ${request.status} on \`${requestLabel(request)}\` is ignored by the auth client — ` +
1364
+ '`logout()` catches that call and clears the session in `finally` (`@molecule/app-auth`), so ' +
1365
+ 'the request is not what broke the test and the API is not the fix; do not add or change a ' +
1366
+ `logout route. ${where} stayed on the same URL: the page has to navigate itself after ` +
1367
+ "`logout()` (call `navigate('/')`, or the route the suite waits for) in the handler that " +
1368
+ 'signs out.');
1369
+ }
1254
1370
  /**
1255
1371
  * The API path prefixes the traced requests themselves show.
1256
1372
  *
@@ -1452,8 +1568,15 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1452
1568
  : '';
1453
1569
  // A credential refusal (401/403 on the app's own API) is not the handler's answer: the trace's
1454
1570
  // own `Authorization` record says whether the CLIENT attached the token at all (m148 +29:18).
1455
- const authRefusals = failedRequests.filter((request) => sameOriginPath(request) !== null && (request.status === 401 || request.status === 403));
1571
+ const authRefusals = failedRequests.filter((request) => sameOriginPath(request) !== null &&
1572
+ (request.status === 401 || request.status === 403) &&
1573
+ // A 4xx on the logout endpoint is not a credential refusal to diagnose: the auth client
1574
+ // ignores it and clears the session anyway, and the row says so itself (m156).
1575
+ !isLogoutRequest(request));
1456
1576
  const everyFailureAuth = failedRequests.length > 0 && authRefusals.length === failedRequests.length;
1577
+ // Every recorded failure is the logout endpoint answering 4xx: the row's own sentence explains
1578
+ // it, and neither the handler lead nor the credential reading applies (m156 +24:02).
1579
+ const onlyLogoutFailures = failedRequests.length > 0 && failedRequests.every((request) => isLogoutRequest(request));
1457
1580
  const authAdvice = authRefusalAdvice(traced);
1458
1581
  // The stalled-navigation reading, per traced test: non-null only when the RUNNER says this
1459
1582
  // test's failing operation is a navigation and a document subresource still pending at the end
@@ -1524,6 +1647,19 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1524
1647
  ? `the page then navigated to ${step.pageAfter}`
1525
1648
  : `the page stayed at ${step.pageAfter}`);
1526
1649
  }
1650
+ // A 4xx on the logout endpoint with the page unmoved is not a backend failure: the auth
1651
+ // client swallows the call and clears the session, and the URL can only move if the page
1652
+ // navigates itself (xr-molecule-flash-m156 +24:02: the refactor dropped `navigate('/')` and
1653
+ // the run went and added a logout route instead).
1654
+ const stepLast = stepRequests.length > 0 ? stepRequests[stepRequests.length - 1] : undefined;
1655
+ if (stepLast !== undefined &&
1656
+ stepLast.status >= 400 &&
1657
+ stepLast.status < 500 &&
1658
+ isLogoutRequest(stepLast) &&
1659
+ step.pageBefore !== undefined &&
1660
+ step.pageAfter !== undefined &&
1661
+ step.pageBefore === step.pageAfter)
1662
+ parts.push(logoutIgnoredSentence(stepLast, step.pageAfter));
1527
1663
  }
1528
1664
  else if (lead !== undefined) {
1529
1665
  parts.push(leadRequestLine(lead));
@@ -1626,14 +1762,19 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1626
1762
  (anyFailed
1627
1763
  ? everyFailureLacksPrefix
1628
1764
  ? ''
1629
- : everyFailureAuth
1630
- ? // Every failure was a credential refusal: the handler is not the problem, and the
1631
- // auth advice already names what is (m148 was sent to the handler six walks running).
1632
- authAdvice
1633
- : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1634
- 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1635
- 'user action silently — log it.' +
1636
- authAdvice
1765
+ : onlyLogoutFailures
1766
+ ? // The rows already say it: a 4xx on the logout endpoint is ignored by the auth client
1767
+ // and the page's own navigation is the missing step. The handler lead would send the
1768
+ // run at the backend (m156 +33:52 added a logout route).
1769
+ ''
1770
+ : everyFailureAuth
1771
+ ? // Every failure was a credential refusal: the handler is not the problem, and the
1772
+ // auth advice already names what is (m148 was sent to the handler six walks running).
1773
+ authAdvice
1774
+ : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1775
+ 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1776
+ 'user action silently — log it.' +
1777
+ authAdvice
1637
1778
  : anyStalled
1638
1779
  ? report.productionBuildAdvised
1639
1780
  ? 'No listed request failed — none got a 4xx/5xx or a network error — but a navigation ' +
@@ -174,22 +174,45 @@ export declare const PLAYWRIGHT_FAILURES_INLINE_ENV = "PLAYWRIGHT_LIST_PRINT_FAI
174
174
  * something: `npx playwright test --list` takes two seconds and writes nothing per test, so
175
175
  * neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}).
176
176
  *
177
+ * A command that starts the suite MORE THAN ONCE is not launched at all: every run it carries is
178
+ * a whole walk of the same suite against the same app, and only the last step is rewritten, so the
179
+ * earlier walks run unmeasured for nothing. X1 xr-molecule-flash-m157 (+14:02) chained two whole
180
+ * walks of one 138-test suite with `;` — 4.5 minutes each — and the second measured nothing the
181
+ * first had not. The platform already refuses a second CONCURRENT run against one app; this is the
182
+ * same hazard inside one command ({@link suiteRunStatements}). `error` then carries the refusal,
183
+ * naming each suite step and the ONE command to send instead (the last suite step, rewritten as
184
+ * this function rewrites a run), and nothing is launched.
185
+ *
177
186
  * @param command - The command as the executor sent it.
178
- * @returns The command to run and one sentence per change made (empty when none).
187
+ * @returns The command to run and one sentence per change made (empty when none), or `error` when
188
+ * the command starts the suite more than once.
179
189
  */
180
190
  export declare function streamingBackgroundCommand(command: string): {
181
191
  command: string;
182
192
  changes: string[];
193
+ error?: string;
183
194
  };
184
195
  /**
185
- * Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
186
- * — rather than a build, an install or a one-off script.
196
+ * Every statement of a command that starts a whole test suite, in command order.
187
197
  *
188
- * A command that invokes a runner only to print is not one of these
198
+ * This is the ONE count of suite runs in a command: {@link isSuiteRunCommand} asks it,
199
+ * {@link streamingBackgroundCommand} refuses a command carrying more than one — each statement
200
+ * starts a whole walk of the same suite, and only the last of them is rewritten — and a re-run
201
+ * composed from a command ({@link ./suite-overlap.js} `scopedRerunCommand`) keeps only the first.
202
+ *
203
+ * A statement that invokes a runner only to print is not one of these
189
204
  * ({@link commandRunsNoTests}): `npx playwright test --list` prints the tests it would run and
190
205
  * exits, and `npx playwright install`, `vitest list` and `hurl --help` start no test either.
191
206
  *
192
207
  * @param command - The shell command.
208
+ * @returns One entry per suite-running statement, as the parser read it (empty when none).
209
+ */
210
+ export declare function suiteRunStatements(command: string): string[];
211
+ /**
212
+ * Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
213
+ * — rather than a build, an install or a one-off script.
214
+ *
215
+ * @param command - The shell command.
193
216
  * @returns True when the command starts one of those runners.
194
217
  */
195
218
  export declare function isSuiteRunCommand(command: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
package/dist/utilities.js CHANGED
@@ -388,8 +388,18 @@ export const PLAYWRIGHT_FAILURES_INLINE_ENV = 'PLAYWRIGHT_LIST_PRINT_FAILURES_IN
388
388
  * something: `npx playwright test --list` takes two seconds and writes nothing per test, so
389
389
  * neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}).
390
390
  *
391
+ * A command that starts the suite MORE THAN ONCE is not launched at all: every run it carries is
392
+ * a whole walk of the same suite against the same app, and only the last step is rewritten, so the
393
+ * earlier walks run unmeasured for nothing. X1 xr-molecule-flash-m157 (+14:02) chained two whole
394
+ * walks of one 138-test suite with `;` — 4.5 minutes each — and the second measured nothing the
395
+ * first had not. The platform already refuses a second CONCURRENT run against one app; this is the
396
+ * same hazard inside one command ({@link suiteRunStatements}). `error` then carries the refusal,
397
+ * naming each suite step and the ONE command to send instead (the last suite step, rewritten as
398
+ * this function rewrites a run), and nothing is launched.
399
+ *
391
400
  * @param command - The command as the executor sent it.
392
- * @returns The command to run and one sentence per change made (empty when none).
401
+ * @returns The command to run and one sentence per change made (empty when none), or `error` when
402
+ * the command starts the suite more than once.
393
403
  */
394
404
  export function streamingBackgroundCommand(command) {
395
405
  // A command that runs no test is not a run to keep a growing log for: nothing about it is
@@ -442,6 +452,30 @@ export function streamingBackgroundCommand(command) {
442
452
  // options are parsed and placed against this text, while the closer is kept in the command.
443
453
  const lastStep = rawLastStep.replace(/[\s)}]+$/, '');
444
454
  const stepStart = rawLastStep === '' ? out.length : Math.max(0, out.lastIndexOf(rawLastStep));
455
+ // A command that starts the suite more than once is not launched: each statement is a whole walk
456
+ // of the same suite against the same app, only the last of them is rewritten, and the earlier
457
+ // ones measure nothing the first did. X1 xr-molecule-flash-m157 (+14:02) chained two whole walks
458
+ // of one 138-test suite with `;` (4.5 minutes each); the platform already refuses a second
459
+ // CONCURRENT run against one app, and this is the same hazard inside one command.
460
+ const suiteSteps = suiteRunStatements(out);
461
+ if (suiteSteps.length > 1) {
462
+ // The ONE run to send instead: the last step that runs the suite, rewritten exactly as this
463
+ // function rewrites a run (its own count is one — guarded, so this cannot recurse).
464
+ const lastSuitePart = [...parts]
465
+ .reverse()
466
+ .map((p) => p.trim())
467
+ .find((p) => isSuiteRunCommand(p));
468
+ const single = lastSuitePart && suiteRunStatements(lastSuitePart).length === 1
469
+ ? streamingBackgroundCommand(lastSuitePart).command
470
+ : (lastSuitePart ?? '');
471
+ return {
472
+ command: out,
473
+ changes: [],
474
+ error: `This command runs the suite ${suiteSteps.length === 2 ? 'twice' : `${suiteSteps.length} times`} ` +
475
+ `(${suiteSteps.map((s, i) => `step ${i + 1}: \`${s}\``).join(', ')}). One walk is the ` +
476
+ `measurement; run exactly one: \`${single}\``,
477
+ };
478
+ }
445
479
  // The last step runs Playwright — whether or not anything is piped from it. Keying this on
446
480
  // "nothing is piped out of the last step" withheld the reporter, the inline-failure switch and
447
481
  // `--retries=0` from exactly the runs whose output was filtered (xr-molecule-flash-m101
@@ -478,12 +512,18 @@ export function streamingBackgroundCommand(command) {
478
512
  };
479
513
  // A diagnosis or a re-run (capped, last-failed, grepped, or named files) walks each failure
480
514
  // once: the suite's own `retries` doubled every failure of X1 flash-m71's first whole run,
481
- // which had dropped the `--retries=0` it used one command earlier. The uncapped proof run
482
- // keeps the suite's retries — including the `--max-failures=0` the platform's own note asks
483
- // for on it (xr-molecule-flash-m89). Which runs those are is {@link isDiagnosisRun}'s answer
484
- // and nobody else's: the platform's notes ask the same function, so a run cannot be a
485
- // diagnosis to one of them and the whole suite to the other.
515
+ // which had dropped the `--retries=0` it used one command earlier. A whole run whose TARGET is
516
+ // local — its env names `localhost`/`127.0.0.1`/`0.0.0.0`, or names no URL at all, as the
517
+ // wrapper config's default does — is not the proof run either and walks them once too:
518
+ // xr-molecule-flash-m155 (+11:34) spent 10 minutes on its first local whole run, and the
519
+ // suite's `retries: 1` walked each of its 68 failures a second time (0 flaky) for nothing.
520
+ // The suite's retries are kept only for a run that names a non-local URL — the deployed proof
521
+ // (xr-molecule-flash-m89), where they absorb a network blip. Which runs are diagnoses is
522
+ // {@link isDiagnosisRun}'s answer and nobody else's: the platform's notes ask the same
523
+ // function, so a run cannot be a diagnosis to one of them and the whole suite to the other.
524
+ // Which targets are local is {@link suiteCommandTarget}'s answer.
486
525
  const diagnosis = isDiagnosisRun(lastStep);
526
+ const localTarget = suiteCommandTarget(lastStep) === 'local';
487
527
  // The last step's reporters: one printer, and it is `list`. Every branch below only ever moves
488
528
  // the command TO that shape — a reporter set is never left with two printers in it.
489
529
  const reporters = finalPlaywrightRun ? lastReporterFlag(lastStep) : null;
@@ -549,9 +589,9 @@ export function streamingBackgroundCommand(command) {
549
589
  'summary at the end — so a run that is still walking already says what its failures are, ' +
550
590
  'and you need not wait for it to end to start on them.');
551
591
  }
552
- if (finalPlaywrightRun && diagnosis && !/--retries\b/.test(lastStep)) {
592
+ if (finalPlaywrightRun && (diagnosis || localTarget) && !/--retries\b/.test(lastStep)) {
553
593
  if (addToRun('--retries=0'))
554
- changes.push('Added `--retries=0`: a diagnosis or re-run walks each failure once; keep the suite’s retries for the proof run.');
594
+ changes.push('Added `--retries=0`: a local run walks each failure once; the suite’s retries apply on the deployed proof run.');
555
595
  }
556
596
  return { command: out, changes };
557
597
  }
@@ -575,20 +615,39 @@ const SUITE_RUN_COMMANDS = [
575
615
  /^hurl\s+[^|\n]*(?:--test|\.hurl)/,
576
616
  ];
577
617
  /**
578
- * Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
579
- * — rather than a build, an install or a one-off script.
618
+ * Every statement of a command that starts a whole test suite, in command order.
619
+ *
620
+ * This is the ONE count of suite runs in a command: {@link isSuiteRunCommand} asks it,
621
+ * {@link streamingBackgroundCommand} refuses a command carrying more than one — each statement
622
+ * starts a whole walk of the same suite, and only the last of them is rewritten — and a re-run
623
+ * composed from a command ({@link ./suite-overlap.js} `scopedRerunCommand`) keeps only the first.
580
624
  *
581
- * A command that invokes a runner only to print is not one of these
625
+ * A statement that invokes a runner only to print is not one of these
582
626
  * ({@link commandRunsNoTests}): `npx playwright test --list` prints the tests it would run and
583
627
  * exits, and `npx playwright install`, `vitest list` and `hurl --help` start no test either.
584
628
  *
585
629
  * @param command - The shell command.
630
+ * @returns One entry per suite-running statement, as the parser read it (empty when none).
631
+ */
632
+ export function suiteRunStatements(command) {
633
+ if (commandRunsNoTests(command))
634
+ return [];
635
+ return commandSegments(command)
636
+ .filter((segment) => SUITE_RUN_COMMANDS.some((re) => re.test(segment.text)) &&
637
+ // A statement that only PRINTS (`playwright test --list`) is not a walk, so
638
+ // `npx playwright test --list && npx playwright test` is one run, not two.
639
+ !commandRunsNoTests(segment.text))
640
+ .map((segment) => segment.text);
641
+ }
642
+ /**
643
+ * Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
644
+ * — rather than a build, an install or a one-off script.
645
+ *
646
+ * @param command - The shell command.
586
647
  * @returns True when the command starts one of those runners.
587
648
  */
588
649
  export function isSuiteRunCommand(command) {
589
- if (commandRunsNoTests(command))
590
- return false;
591
- return commandSegments(command).some((segment) => SUITE_RUN_COMMANDS.some((re) => re.test(segment.text)));
650
+ return suiteRunStatements(command).length > 0;
592
651
  }
593
652
  /**
594
653
  * The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
@@ -1953,6 +2012,23 @@ function largestGroup(groups) {
1953
2012
  function testKey(t) {
1954
2013
  return `${t.where}|${t.title}`;
1955
2014
  }
2015
+ /**
2016
+ * One test's identity with the runner's retry marker taken off: `… a.spec.ts:32 › does x
2017
+ * (retry #1)` is the same test as `… a.spec.ts:32 › does x`.
2018
+ *
2019
+ * A retried failure prints twice in a `list` log — its first attempt, then the same test with
2020
+ * `(retry #1)` after the title — while its failure block names it without the marker. Keyed
2021
+ * literally, the retry row is a test no cause claims, so it fell through to the duration arm and
2022
+ * was listed as a "shared duration" cause beside the proven cause it repeated: on
2023
+ * xr-molecule-flash-m155 (+21:50) every retry row of the run became one of six extra causes
2024
+ * duplicating the causes above them.
2025
+ *
2026
+ * @param t - A failed test, as a result line or a failure block spelled it.
2027
+ * @returns The key a test's retry row shares with its failure block.
2028
+ */
2029
+ function retrylessTestKey(t) {
2030
+ return `${t.where}|${t.title.replace(/\s*\(retry #\d+\)$/, '')}`;
2031
+ }
1956
2032
  /**
1957
2033
  * The same tests with each one kept once, in first-seen order.
1958
2034
  *
@@ -2600,6 +2676,10 @@ function groupFailures(log, excluded, pageErrors = []) {
2600
2676
  const listed = failedTests(text);
2601
2677
  named.push(...listed);
2602
2678
  const claimed = new Set(causes.flatMap((c) => c.tests.map(testKey)));
2679
+ // The causes above as a test can recognise itself: a retried failure's second row carries the
2680
+ // runner's `(retry #1)` marker, its failure block does not, and comparing the literal keys
2681
+ // read the same test as an unclaimed one (xr-molecule-flash-m155 +21:50).
2682
+ const proven = new Set(causes.flatMap((c) => c.tests.map(retrylessTestKey)));
2603
2683
  const unclaimed = distinctTests(listed).filter((t) => t.seconds !== null &&
2604
2684
  !claimed.has(testKey(t)) &&
2605
2685
  // A flaky test's `✘` row is its first attempt; its `✓` retry follows it in the same log,
@@ -2615,14 +2695,21 @@ function groupFailures(log, excluded, pageErrors = []) {
2615
2695
  else
2616
2696
  byDuration.push({ seconds: t.seconds, tests: [t] });
2617
2697
  }
2618
- for (const g of byDuration)
2698
+ for (const g of byDuration) {
2699
+ // A duration band is a LEAD, not a cause: a test already carrying a proven cause above is
2700
+ // not named a second time here, and a band whose every member is one is not a cause at all —
2701
+ // it reads as one more thing to fix. m155's retry rows made six such bands.
2702
+ const tests = g.tests.filter((t) => !proven.has(retrylessTestKey(t)));
2703
+ if (tests.length === 0)
2704
+ continue;
2619
2705
  causes.push({
2620
2706
  message: null,
2621
2707
  seconds: g.seconds,
2622
- tests: g.tests,
2708
+ tests,
2623
2709
  evidence: [],
2624
2710
  kind: 'assertion',
2625
2711
  });
2712
+ }
2626
2713
  const ignored = distinctTests(named).filter((t) => matchingExcludedTitles(t.title, excluded).length > 0);
2627
2714
  return {
2628
2715
  causes: causes.sort((a, b) => b.tests.length - a.tests.length),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.9.13",
3
+ "version": "1.9.15",
4
4
  "description": "Shared AI agent tools with backend abstraction for sandbox and local execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",