@molecule/api-ai-tools 1.9.12 → 1.9.14

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-18T07:36:48.788Z
6
+ Generated: 2026-10-18T09:17:15.122Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -522,6 +522,52 @@ interface TraceArtifact {
522
522
  }
523
523
  ```
524
524
 
525
+ #### `TraceRunFailure`
526
+
527
+ What the runner's own report says about ONE failing test: where the test is declared, which
528
+ Playwright call its failure names, and the per-test deadline the failure measured against.
529
+
530
+ A trace cannot tell a navigation that ran out of budget from a selector wait that did. On
531
+ xr-molecule-flash-m153 a 15 s `page.waitForSelector` timeout left a trace whose `Frame.goto`
532
+ never finished (the test was cut off mid-navigation) with a `/src/components/*.tsx` module still
533
+ pending when the context closed — and the note read that as an asset hang, then sent the run to
534
+ re-run the same tests unchanged against the same dev server while the same result's
535
+ `testRunNote` was telling it to measure a production build.
536
+
537
+ ```typescript
538
+ interface TraceRunFailure {
539
+ /** The spec file the runner's heading names (`e2e/url-navigation.spec.ts`). */
540
+ spec: string
541
+ /** The line the test is DECLARED on, from that heading (`206`). */
542
+ line: number
543
+ /** The Playwright call the failure names (`page.waitForSelector`, `page.goto`), or null. */
544
+ operation: string | null
545
+ /** The per-test deadline the failure names, in ms (`15000`), or null when it names none. */
546
+ budgetMs: number | null
547
+ }
548
+ ```
549
+
550
+ #### `TraceRunReport`
551
+
552
+ The runner's own report, beside the traces it wrote.
553
+
554
+ ```typescript
555
+ interface TraceRunReport {
556
+ /**
557
+ * One entry per failing test, as the runner printed them. Empty when the caller supplied none:
558
+ * without the runner's reported operation a pending asset is never called the cause.
559
+ */
560
+ failures?: readonly TraceRunFailure[]
561
+ /**
562
+ * True when the same result's `testRunNote` already names a production build as the next
563
+ * measurement (a local dev-server walk that ran out of its per-test budget). The note then
564
+ * states the ONE next step instead of directing an unchanged same-target re-run that
565
+ * contradicts it.
566
+ */
567
+ productionBuildAdvised?: boolean
568
+ }
569
+ ```
570
+
525
571
  #### `DiagnosisTraceRewrite`
526
572
 
527
573
  A command with the trace flag this platform decided to add, and the sentence to report it in.
@@ -1953,7 +1999,7 @@ function stalledDocumentSubresource(request: TraceRequest): boolean
1953
1999
 
1954
2000
  **Returns:** True when the request is a document subresource that never finished.
1955
2001
 
1956
- #### `failureTraceNote(items, target, earlierFailures, apiPrefixes)`
2002
+ #### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
1957
2003
 
1958
2004
  The note a failed suite run's result carries: what each failing test's page actually sent, and
1959
2005
  what came back.
@@ -1985,6 +2031,7 @@ function failureTraceNote(
1985
2031
  target: string,
1986
2032
  earlierFailures?: readonly string[],
1987
2033
  apiPrefixes?: readonly string[],
2034
+ report?: TraceRunReport,
1988
2035
  ): string | null
1989
2036
  ```
1990
2037
 
@@ -1992,6 +2039,7 @@ function failureTraceNote(
1992
2039
  - `target` — Where the run went — the deployed host, as the note names it.
1993
2040
  - `earlierFailures` — The failing `file:line`s earlier walks of this suite left, so a repeat is ranked above a one-off. Empty when the caller knows of no earlier walk.
1994
2041
  - `apiPrefixes` — The API path prefixes the app's client config names (`['/api']`), read from the app by the caller. Empty when the caller could not read one; the traces' own requests may still supply it. Never a hard-coded literal.
2042
+ - `report` — The runner's own report for this run ({@link TraceRunReport}): each failing test's declared location, the operation its failure names and the deadline it names, plus whether the same result's `testRunNote` already points at a production build. A pending asset is called the cause only when the runner says the failing operation for THAT test is the navigation it was loading; without the report nothing is called a navigation stall.
1995
2043
 
1996
2044
  **Returns:** The note, or null when no trace held a recorded response, a real failure or a console error.
1997
2045
 
@@ -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,CAu/ExF"}
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"}
package/dist/tools.js CHANGED
@@ -849,6 +849,65 @@ export function buildTools(backend, config) {
849
849
  result = isEnvFilePath(path) ? redactSecrets(result) : redactSecretsInCode(result);
850
850
  return result;
851
851
  }
852
+ // ── Binary reads ───────────────────────────────────────────────
853
+ /** Extensions whose files are never text: a `read_file` on one is refused by name. */
854
+ const BINARY_FILE_EXTENSIONS = new Set([
855
+ 'avif',
856
+ 'gif',
857
+ 'gz',
858
+ 'ico',
859
+ 'jpeg',
860
+ 'jpg',
861
+ 'otf',
862
+ 'pdf',
863
+ 'png',
864
+ 'tgz',
865
+ 'ttf',
866
+ 'wasm',
867
+ 'webp',
868
+ 'woff',
869
+ 'woff2',
870
+ 'zip',
871
+ ]);
872
+ /** How many chars of a read are inspected for binary markers. */
873
+ const BINARY_SAMPLE_CHARS = 512;
874
+ /**
875
+ * A replacement character (a byte UTF-8 could not decode) marks binary only in
876
+ * bulk: one literal U+FFFD in a source string is ordinary text.
877
+ */
878
+ const BINARY_MIN_REPLACEMENTS = 8;
879
+ const BINARY_REPLACEMENT_DENSITY = 0.05;
880
+ /**
881
+ * The kind of a BINARY file, or `null` for text.
882
+ *
883
+ * `read_file` asks the backend for a string, so a PNG comes back as UTF-8
884
+ * replacement characters and used to be handed to the model whole — 21,191
885
+ * chars of unreadable bytes it cannot look at, ~12k tokens per read, asked for
886
+ * twice in one run (xr-molecule-flash-m155 +09:58). A known binary extension is
887
+ * refused by name; a file without one is caught from the bytes themselves, by
888
+ * a NUL byte or by the density of replacement characters in its first
889
+ * {@link BINARY_SAMPLE_CHARS} chars.
890
+ *
891
+ * @param path - The already-resolved path.
892
+ * @param content - The content the backend decoded as UTF-8.
893
+ * @returns The binary kind to name (e.g. `png`, `binary data`), or `null` for text.
894
+ */
895
+ function binaryFileKind(path, content) {
896
+ const ext = posix.extname(path).slice(1).toLowerCase();
897
+ if (BINARY_FILE_EXTENSIONS.has(ext))
898
+ return ext;
899
+ const sample = content.slice(0, BINARY_SAMPLE_CHARS);
900
+ if (sample.includes('\u0000'))
901
+ return 'binary data';
902
+ let replacements = 0;
903
+ for (const char of sample)
904
+ if (char === '\uFFFD')
905
+ replacements += 1;
906
+ return replacements >= BINARY_MIN_REPLACEMENTS &&
907
+ replacements / sample.length >= BINARY_REPLACEMENT_DENSITY
908
+ ? 'binary data'
909
+ : null;
910
+ }
852
911
  // ── Empty-read verification ────────────────────────────────────
853
912
  /**
854
913
  * The file's size in bytes as the backend's own shell reports it, or `null`
@@ -1022,6 +1081,19 @@ export function buildTools(backend, config) {
1022
1081
  ...(verdict.note ? { note: verdict.note } : {}),
1023
1082
  });
1024
1083
  }
1084
+ // A binary file decoded as UTF-8 is unreadable by definition — say what it
1085
+ // is instead of returning the bytes (see binaryFileKind).
1086
+ const binaryKind = binaryFileKind(path, content);
1087
+ if (binaryKind) {
1088
+ // The read already happened, so the decoded length is the fallback size;
1089
+ // `wc -c` gives the file's real byte count when the backend can run it.
1090
+ const bytes = (await probeFileSize(path)) ?? content.length;
1091
+ return {
1092
+ error: `${path} is a binary file (${binaryKind}, ${Math.max(1, Math.round(bytes / 1024))} KB). ` +
1093
+ `read_file returns text only — to look at a page use screenshot_preview; to inspect an ` +
1094
+ `image you produced, measure it in a script and print the numbers (its size, pixel counts).`,
1095
+ };
1096
+ }
1025
1097
  if (content.length > MAX_READ_SIZE)
1026
1098
  return {
1027
1099
  error: `File too large (${Math.round(content.length / 1024)}KB). Maximum is ${MAX_READ_SIZE / 1024 / 1024}MB.`,
@@ -2911,8 +2983,13 @@ export function buildTools(backend, config) {
2911
2983
  : { piped: false, unplaced: false };
2912
2984
  const reported = suiteRunExitCode(exitCode, output, placement.piped);
2913
2985
  const noCodeNote = suiteExitCodeNote(placement, reported);
2986
+ // A FINISHED result says so, even when the code it would report is withheld: a filtered
2987
+ // pipeline's code is not the run's verdict ({@link FILTERED_EXIT_NOTE}), so `exitCode` alone
2988
+ // cannot tell a caller this is the delivered result rather than a progress return — and a
2989
+ // caller that tracks background tasks would hand the same result back as fresh news later.
2914
2990
  return {
2915
2991
  taskId,
2992
+ status: 'completed',
2916
2993
  exitCode: reported,
2917
2994
  waitedMs,
2918
2995
  ...(causes || noCodeNote
@@ -279,6 +279,42 @@ export declare function unansweredRequest(request: TraceRequest): boolean;
279
279
  * @returns True when the request is a document subresource that never finished.
280
280
  */
281
281
  export declare function stalledDocumentSubresource(request: TraceRequest): boolean;
282
+ /**
283
+ * What the runner's own report says about ONE failing test: where the test is declared, which
284
+ * Playwright call its failure names, and the per-test deadline the failure measured against.
285
+ *
286
+ * A trace cannot tell a navigation that ran out of budget from a selector wait that did. On
287
+ * xr-molecule-flash-m153 a 15 s `page.waitForSelector` timeout left a trace whose `Frame.goto`
288
+ * never finished (the test was cut off mid-navigation) with a `/src/components/*.tsx` module still
289
+ * pending when the context closed — and the note read that as an asset hang, then sent the run to
290
+ * re-run the same tests unchanged against the same dev server while the same result's
291
+ * `testRunNote` was telling it to measure a production build.
292
+ */
293
+ export interface TraceRunFailure {
294
+ /** The spec file the runner's heading names (`e2e/url-navigation.spec.ts`). */
295
+ spec: string;
296
+ /** The line the test is DECLARED on, from that heading (`206`). */
297
+ line: number;
298
+ /** The Playwright call the failure names (`page.waitForSelector`, `page.goto`), or null. */
299
+ operation: string | null;
300
+ /** The per-test deadline the failure names, in ms (`15000`), or null when it names none. */
301
+ budgetMs: number | null;
302
+ }
303
+ /** The runner's own report, beside the traces it wrote. */
304
+ export interface TraceRunReport {
305
+ /**
306
+ * One entry per failing test, as the runner printed them. Empty when the caller supplied none:
307
+ * without the runner's reported operation a pending asset is never called the cause.
308
+ */
309
+ failures?: readonly TraceRunFailure[];
310
+ /**
311
+ * True when the same result's `testRunNote` already names a production build as the next
312
+ * measurement (a local dev-server walk that ran out of its per-test budget). The note then
313
+ * states the ONE next step instead of directing an unchanged same-target re-run that
314
+ * contradicts it.
315
+ */
316
+ productionBuildAdvised?: boolean;
317
+ }
282
318
  /**
283
319
  * The note a failed suite run's result carries: what each failing test's page actually sent, and
284
320
  * what came back.
@@ -321,12 +357,16 @@ export declare function stalledDocumentSubresource(request: TraceRequest): boole
321
357
  * transport error (`net::ERR_CONTENT_DECODING_FAILED` and friends) and the document it hit: nothing
322
358
  * rendered, so no row below is app evidence (xr-molecule-flash-m118 +37:09).
323
359
  *
324
- * When a navigation never finished and one of the document's own subresources (a stylesheet, a
325
- * script, an asset under `/assets/`) never got a response either, that request takes a row and the
326
- * note says the page was waiting on it. It is not an API call, but on a `page.goto` timeout it is
327
- * the only request that can explain the timeout — the deployed app was serving the page while one
328
- * fetch stalled for 27 s (xr-molecule-flash-m135 +34:10). The page shell's incidental requests
329
- * (`/sw.js`, the manifest, a favicon, a font) stay out, for m125's reason.
360
+ * When a navigation the runner reports as the failure never finished and one of the document's
361
+ * own subresources (a stylesheet, a script, an asset under `/assets/`) never got a response either
362
+ * — having started inside that wait — the request takes a row and the note says the page was
363
+ * waiting on it. It is not an API call, but on a `page.goto` timeout it is the only request that
364
+ * can explain the timeout — the deployed app was serving the page while one fetch stalled for 27 s
365
+ * (xr-molecule-flash-m135 +34:10). The page shell's incidental requests (`/sw.js`, the manifest, a
366
+ * favicon, a font) stay out, for m125's reason. An asset merely unfinished when a selector wait
367
+ * ran out the test budget, or one a teardown/`afterEach` trace holds, is not a cause: the note
368
+ * says it "had no recorded response when the test ended; that alone does not establish the cause"
369
+ * (xr-molecule-flash-m153 +13:43).
330
370
  *
331
371
  * A 404 whose path lacks the API prefix the app's own calls carry is called out with that prefix
332
372
  * ({@link missingApiPrefixRequests}): xr-molecule-flash-m145 posted `/users` to `vite preview`,
@@ -351,10 +391,15 @@ export declare function stalledDocumentSubresource(request: TraceRequest): boole
351
391
  * @param apiPrefixes - The API path prefixes the app's client config names (`['/api']`), read from
352
392
  * the app by the caller. Empty when the caller could not read one; the traces' own requests may
353
393
  * still supply it. Never a hard-coded literal.
394
+ * @param report - The runner's own report for this run ({@link TraceRunReport}): each failing
395
+ * test's declared location, the operation its failure names and the deadline it names, plus
396
+ * whether the same result's `testRunNote` already points at a production build. A pending asset
397
+ * is called the cause only when the runner says the failing operation for THAT test is the
398
+ * navigation it was loading; without the report nothing is called a navigation stall.
354
399
  * @returns The note, or null when no trace held a recorded response, a real failure or a console
355
400
  * error.
356
401
  */
357
- export declare function failureTraceNote(items: readonly TraceArtifact[], target: string, earlierFailures?: readonly string[], apiPrefixes?: readonly string[]): string | null;
402
+ export declare function failureTraceNote(items: readonly TraceArtifact[], target: string, earlierFailures?: readonly string[], apiPrefixes?: readonly string[], report?: TraceRunReport): string | null;
358
403
  /**
359
404
  * Whether a Playwright config records traces for the tests that fail.
360
405
  *
@@ -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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"}
@@ -452,6 +452,9 @@ for (const one of found.slice(0, MAX_TRACES)) {
452
452
  ...(r.sent ? { sent: r.sent } : {}),
453
453
  ...(r.got ? { got: r.got } : {}),
454
454
  type: r.type || undefined,
455
+ // The trace's own clock, so the note can tell a request the navigation was waiting on from
456
+ // one that was already pending before it started (m135/m153).
457
+ ...(typeof r.startedAt === 'number' ? { startedAt: r.startedAt } : {}),
455
458
  ...(r.sentAuthorization ? { sentAuthorization: true } : {}),
456
459
  ...(r.authorization ? { authorization: r.authorization } : {}),
457
460
  ...(r.sentCookie ? { sentCookie: true } : {}),
@@ -965,6 +968,97 @@ function repeatsEarlierFailure(item, earlier) {
965
968
  }
966
969
  return false;
967
970
  }
971
+ /** The Playwright navigation calls whose wait a pending document subresource can explain. */
972
+ const NAVIGATION_OPERATIONS = new Set(['goto', 'waitForURL', 'waitForLoadState', 'reload']);
973
+ /**
974
+ * The method a runner-reported operation names (`page.waitForSelector` → `waitForSelector`), or
975
+ * null when it is not one of the navigation calls a stalled asset can explain.
976
+ *
977
+ * @param operation - The call the runner's error text names, or null.
978
+ * @returns The navigation method, or null.
979
+ */
980
+ function navigationMethod(operation) {
981
+ if (operation === null)
982
+ return null;
983
+ const method = operation.slice(operation.lastIndexOf('.') + 1);
984
+ return NAVIGATION_OPERATIONS.has(method) ? method : null;
985
+ }
986
+ /**
987
+ * The runner's report for the test THIS trace belongs to, or undefined when it belongs to none.
988
+ *
989
+ * The trace names the line of its LAST recorded step; the runner's summary names the test's
990
+ * DECLARATION line, and the step sits inside the body just after it ({@link
991
+ * RECURRENCE_LINE_WINDOW}). A line BEFORE the declaration is a hook — m153's teardown trace sits
992
+ * at `url-navigation.spec.ts:107`, the Pagination suite's `afterEach`, while the failing test is
993
+ * declared at `:206` — and a hook is not the failing test's operation.
994
+ *
995
+ * @param item - One traced failure.
996
+ * @param failures - The runner's report, one entry per failing test.
997
+ * @returns The matching failure, or undefined.
998
+ */
999
+ function runFailureOf(item, failures) {
1000
+ if (item.spec === null)
1001
+ return undefined;
1002
+ for (const failure of failures) {
1003
+ if (!sameLocationFile(item.spec, failure.spec))
1004
+ continue;
1005
+ if (item.line < failure.line)
1006
+ continue;
1007
+ if (item.line - failure.line <= RECURRENCE_LINE_WINDOW)
1008
+ return failure;
1009
+ }
1010
+ return undefined;
1011
+ }
1012
+ /**
1013
+ * Whether a pending request's interval covers a navigation's wait: it started while the
1014
+ * navigation was loading (at or after the wait began) and it has no recorded response, so it was
1015
+ * still pending when the wait ran out. A request that started before the wait belongs to the page
1016
+ * already on screen, not to the navigation that never finished.
1017
+ *
1018
+ * @param request - One request the trace recorded.
1019
+ * @param wait - The navigation action the runner reported.
1020
+ * @returns True when the request's pending interval covers the wait.
1021
+ */
1022
+ function requestCoversWait(request, wait) {
1023
+ if (typeof request.startedAt !== 'number')
1024
+ return false;
1025
+ return request.startedAt >= wait.start - 1;
1026
+ }
1027
+ /**
1028
+ * The stalled-navigation reading for one trace — or null.
1029
+ *
1030
+ * All three facts must hold together: the runner says THIS test's failing operation is a
1031
+ * navigation ({@link NAVIGATION_OPERATIONS}); the trace's record of that call never finished; and
1032
+ * a document subresource still pending at the end started inside that wait
1033
+ * ({@link requestCoversWait}). An asset merely unfinished when the test was cancelled, or one a
1034
+ * teardown/`afterEach` trace holds, meets none of these and is not a cause (m153).
1035
+ *
1036
+ * @param item - One traced failure.
1037
+ * @param report - The runner's report and the run's next step.
1038
+ * @returns The wait and the assets it was waiting on, or null.
1039
+ */
1040
+ function stalledNavigation(item, report) {
1041
+ const failure = runFailureOf(item, report.failures ?? []);
1042
+ if (failure === undefined)
1043
+ return null;
1044
+ const method = navigationMethod(failure.operation);
1045
+ if (method === null)
1046
+ return null;
1047
+ let wait = null;
1048
+ for (const action of item.actions) {
1049
+ if (!action.api.endsWith(`.${method}`))
1050
+ continue;
1051
+ if (wait === null || action.start > wait.start)
1052
+ wait = action;
1053
+ }
1054
+ // The navigation is the failure only while it is still running at the end of the test.
1055
+ if (wait === null || wait.end !== null)
1056
+ return null;
1057
+ const assets = item.requests.filter((request) => stalledDocumentSubresource(request) && requestCoversWait(request, wait));
1058
+ if (assets.length === 0)
1059
+ return null;
1060
+ return { reported: failure.operation ?? wait.api, wait, assets, budgetMs: failure.budgetMs };
1061
+ }
968
1062
  /**
969
1063
  * Whether a trace holds evidence the note can explain: a request that got a RECORDED response, a
970
1064
  * real failure (4xx/5xx or a network error), or a console line. A request with no response is
@@ -974,31 +1068,38 @@ function repeatsEarlierFailure(item, earlier) {
974
1068
  *
975
1069
  * A request the page shell made (m125 +29:33: `/sw.js`, a font) is not evidence about the step
976
1070
  * either, answered or not. The exception is a document subresource that never finished while the
977
- * page's own navigation was still open: the navigation waits on it, so it is the reason the
978
- * `page.goto` timed out ({@link stalledDocumentSubresource}).
1071
+ * navigation the RUNNER reports failing was still open: that navigation waits on it, so it is the
1072
+ * reason the `page.goto` timed out ({@link stalledNavigation}).
979
1073
  *
980
1074
  * @param item - One traced failure.
1075
+ * @param report - The runner's report and the run's next step.
981
1076
  * @returns True when the trace holds a request or console line worth a row.
982
1077
  */
983
- function traceRowEvidence(item) {
1078
+ function traceRowEvidence(item, report) {
984
1079
  return (item.console.length > 0 ||
985
1080
  item.requests.some((request) => !isStaticAssetRequest(request) && !unansweredRequest(request)) ||
986
- // The one shape in which a subresource with no response IS the cause: the navigation it was
987
- // part of never finished (see {@link stalledDocumentSubresource}).
988
- (navigationNeverFinished(item) && item.requests.some(stalledDocumentSubresource)));
1081
+ // The one shape in which a subresource with no response IS the cause: the navigation the
1082
+ // runner reported was still running at the end (see {@link stalledNavigation}).
1083
+ stalledNavigation(item, report) !== null);
989
1084
  }
990
1085
  /**
991
1086
  * The note's row order: a row with a REAL failure first, then a row whose failure an earlier walk
992
1087
  * also had, then the rest. A recurrence is the only evidence a row carries when its requests were
993
1088
  * all answered (m112's favorite cause: the click's own request answered, the button never
994
1089
  * changed).
1090
+ *
1091
+ * @param item - One traced failure.
1092
+ * @param earlier - The failing `file:line`s earlier walks of this suite left.
1093
+ * @param report - The runner's report and the run's next step.
1094
+ * @returns The row's tier: 0 first, 2 last.
995
1095
  */
996
- function traceRowTier(item, earlier) {
1096
+ function traceRowTier(item, earlier, report) {
997
1097
  if (item.requests.some(failedRequest))
998
1098
  return 0;
999
- // A navigation that never finished because its own subresource never got a response is as
1000
- // strong a cause as a failed request — it is the one fact the timeout was waiting on (m135).
1001
- if (navigationNeverFinished(item) && item.requests.some(stalledDocumentSubresource))
1099
+ // A navigation the runner reported, still waiting on a subresource that never got a response,
1100
+ // is as strong a cause as a failed request — it is the one fact the timeout was waiting on
1101
+ // (m135).
1102
+ if (stalledNavigation(item, report) !== null)
1002
1103
  return 0;
1003
1104
  if (repeatsEarlierFailure(item, earlier))
1004
1105
  return 1;
@@ -1077,19 +1178,36 @@ function sameOriginPath(request) {
1077
1178
  * the request carried no `Authorization` header at all and the app's hand-rolled session store
1078
1179
  * never handed its token to the shared client.
1079
1180
  *
1181
+ * xr-molecule-flash-m156 (+16:15): the same 401 shape after a reload, and the page's own console
1182
+ * named the cause — `@molecule/app-http` logged `no client is bonded`. The auth setup ran before
1183
+ * the HTTP setup and kept the fallback client, so the restored token landed on a client nothing
1184
+ * sent through. When the trace carries that line, the no-header sentence names the ordering fix
1185
+ * instead of leaving the run to re-derive it.
1186
+ *
1080
1187
  * The trace cannot name the file that made the call: a real HAR entry carries the request, response,
1081
1188
  * `_resourceType`, `_monotonicTime` and a frame ref, but no initiator stack. So the sentence names
1082
1189
  * the shared client and the auth bond instead of guessing a file.
1083
1190
  *
1084
1191
  * @param request - The refused request, a 401 or 403 on the app's own origin.
1192
+ * @param unbondedClient - Whether the traced page logged `no client is bonded`, which explains a
1193
+ * request that carried no `Authorization` header.
1085
1194
  * @returns The sentence the note appends.
1086
1195
  */
1087
- function authRefusalSentence(request) {
1196
+ function authRefusalSentence(request, unbondedClient = false) {
1088
1197
  const label = requestLabel(request);
1089
1198
  // The header's scheme, when it parsed; presence alone is still the server's half of the wire.
1090
1199
  const carried = request.authorization !== undefined
1091
1200
  ? `with \`Authorization: ${request.authorization} …\``
1092
1201
  : 'with an `Authorization` header';
1202
+ // The console line names the mechanism the no-header branch otherwise has to guess at:
1203
+ // `getClient()` ran before `setupHttpDefault()` and the token was mirrored into the
1204
+ // client that later setup replaced (xr-molecule-flash-m156).
1205
+ const unbonded = unbondedClient
1206
+ ? ' The page logged `no client is bonded` — something called `getClient()` from ' +
1207
+ '`@molecule/app-http` before `setupHttpDefault()` ran and kept that fallback client; ' +
1208
+ 'in `app/src/bonds/index.ts` call `setupHttpDefault()` before `setupAuthDefault()`, ' +
1209
+ 'and read the client per request instead of capturing it at setup.'
1210
+ : '';
1093
1211
  if (request.status === 403) {
1094
1212
  return request.sentAuthorization === true
1095
1213
  ? `The browser sent \`${label}\` ${carried} and the API answered 403 — the token was ` +
@@ -1099,7 +1217,8 @@ function authRefusalSentence(request) {
1099
1217
  '`Authorization` header, so either the client did not attach the token (check that the ' +
1100
1218
  'request goes through the app’s HTTP client that the auth bond configures, and that the ' +
1101
1219
  'token is stored where the client reads it) or the route refuses this user: read the ' +
1102
- 'route’s role/permission check in your own source.';
1220
+ 'route’s role/permission check in your own source.' +
1221
+ unbonded;
1103
1222
  }
1104
1223
  if (request.sentAuthorization === true) {
1105
1224
  return (`The browser sent \`${label}\` ${carried} and the API refused it: the token was sent and ` +
@@ -1117,7 +1236,8 @@ function authRefusalSentence(request) {
1117
1236
  ? 'The request did carry a `Cookie` header, so if this app signs in with a session cookie ' +
1118
1237
  'that is the credential the server did not accept. '
1119
1238
  : '') +
1120
- 'The API handler is not the problem.');
1239
+ 'The API handler is not the problem.' +
1240
+ unbonded);
1121
1241
  }
1122
1242
  /**
1123
1243
  * The note's credential-refusal advice, one sentence per distinct shape — at most two.
@@ -1133,23 +1253,67 @@ function authRefusalSentence(request) {
1133
1253
  function authRefusalAdvice(items) {
1134
1254
  const sentences = [];
1135
1255
  const seen = new Set();
1256
+ // A page that logged `no client is bonded` explains every request it sent without a
1257
+ // token, so the advice names that mechanism once for the run
1258
+ // (xr-molecule-flash-m156).
1259
+ const unbondedClient = items.some((item) => item.console.some((line) => line.includes('no client is bonded')));
1136
1260
  for (const item of items) {
1137
1261
  for (const request of item.requests) {
1138
1262
  if (request.status !== 401 && request.status !== 403)
1139
1263
  continue;
1140
1264
  if (sameOriginPath(request) === null)
1141
1265
  continue;
1266
+ // A 4xx on the logout endpoint is the shape the row itself answers: the auth client
1267
+ // ignores it and clears the session, and the page owes the navigation (m156).
1268
+ if (isLogoutRequest(request))
1269
+ continue;
1142
1270
  const key = `${request.status} ${request.sentAuthorization ? (request.authorization ?? 'header') : 'none'} ${request.sentCookie ? 'cookie' : ''}`;
1143
1271
  if (seen.has(key))
1144
1272
  continue;
1145
1273
  seen.add(key);
1146
- sentences.push(authRefusalSentence(request));
1274
+ sentences.push(authRefusalSentence(request, unbondedClient));
1147
1275
  if (sentences.length === 2)
1148
1276
  return ` ${sentences.join(' ')}`;
1149
1277
  }
1150
1278
  }
1151
1279
  return sentences.length === 0 ? '' : ` ${sentences.join(' ')}`;
1152
1280
  }
1281
+ /**
1282
+ * Whether a request went to an auth client's logout endpoint. The client's default
1283
+ * `logoutEndpoint` is `/users/logout` (`@molecule/app-auth`), and a project may name its own
1284
+ * (`/auth/logout`), so the last segment is what identifies it — never a literal path.
1285
+ *
1286
+ * @param request - One request a trace recorded.
1287
+ * @returns True for a POST to a path ending in `/logout`.
1288
+ */
1289
+ function isLogoutRequest(request) {
1290
+ if (request.method !== 'POST')
1291
+ return false;
1292
+ return /\/(?:users\/)?logout$/i.test(requestPath(request));
1293
+ }
1294
+ /**
1295
+ * The sentence a 4xx on the auth client's logout endpoint owes when the page then stayed put.
1296
+ *
1297
+ * `@molecule/app-auth`'s `logout()` calls the endpoint inside a `.catch` and clears the session in
1298
+ * `finally` (`packages/app/core/auth/src/client.ts`): a 401 there is EXPECTED on a sign-out and is
1299
+ * not the failure. What the test waits for is the URL moving, which the page has to do itself.
1300
+ * xr-molecule-flash-m156 (+24:02 → +33:52) read `POST /api/users/logout → 401` as a broken
1301
+ * endpoint, added a backend logout route, and paid a re-run and a redeploy — while the real cause
1302
+ * was the `navigate('/')` the refactor had dropped.
1303
+ *
1304
+ * @param request - The 4xx logout request.
1305
+ * @param pageUrl - The URL the page stayed at, when the trace recorded one.
1306
+ * @returns The sentence the note appends to the row.
1307
+ */
1308
+ function logoutIgnoredSentence(request, pageUrl) {
1309
+ const where = pageUrl === undefined ? 'The page' : `\`${pageUrl}\``;
1310
+ return (`The ${request.status} on \`${requestLabel(request)}\` is ignored by the auth client — ` +
1311
+ '`logout()` catches that call and clears the session in `finally` (`@molecule/app-auth`), so ' +
1312
+ 'the request is not what broke the test and the API is not the fix; do not add or change a ' +
1313
+ `logout route. ${where} stayed on the same URL: the page has to navigate itself after ` +
1314
+ "`logout()` (call `navigate('/')`, or the route the suite waits for) in the handler that " +
1315
+ 'signs out.');
1316
+ }
1153
1317
  /**
1154
1318
  * The API path prefixes the traced requests themselves show.
1155
1319
  *
@@ -1267,12 +1431,16 @@ function missingApiPrefixSentence(request) {
1267
1431
  * transport error (`net::ERR_CONTENT_DECODING_FAILED` and friends) and the document it hit: nothing
1268
1432
  * rendered, so no row below is app evidence (xr-molecule-flash-m118 +37:09).
1269
1433
  *
1270
- * When a navigation never finished and one of the document's own subresources (a stylesheet, a
1271
- * script, an asset under `/assets/`) never got a response either, that request takes a row and the
1272
- * note says the page was waiting on it. It is not an API call, but on a `page.goto` timeout it is
1273
- * the only request that can explain the timeout — the deployed app was serving the page while one
1274
- * fetch stalled for 27 s (xr-molecule-flash-m135 +34:10). The page shell's incidental requests
1275
- * (`/sw.js`, the manifest, a favicon, a font) stay out, for m125's reason.
1434
+ * When a navigation the runner reports as the failure never finished and one of the document's
1435
+ * own subresources (a stylesheet, a script, an asset under `/assets/`) never got a response either
1436
+ * — having started inside that wait — the request takes a row and the note says the page was
1437
+ * waiting on it. It is not an API call, but on a `page.goto` timeout it is the only request that
1438
+ * can explain the timeout — the deployed app was serving the page while one fetch stalled for 27 s
1439
+ * (xr-molecule-flash-m135 +34:10). The page shell's incidental requests (`/sw.js`, the manifest, a
1440
+ * favicon, a font) stay out, for m125's reason. An asset merely unfinished when a selector wait
1441
+ * ran out the test budget, or one a teardown/`afterEach` trace holds, is not a cause: the note
1442
+ * says it "had no recorded response when the test ended; that alone does not establish the cause"
1443
+ * (xr-molecule-flash-m153 +13:43).
1276
1444
  *
1277
1445
  * A 404 whose path lacks the API prefix the app's own calls carry is called out with that prefix
1278
1446
  * ({@link missingApiPrefixRequests}): xr-molecule-flash-m145 posted `/users` to `vite preview`,
@@ -1297,17 +1465,22 @@ function missingApiPrefixSentence(request) {
1297
1465
  * @param apiPrefixes - The API path prefixes the app's client config names (`['/api']`), read from
1298
1466
  * the app by the caller. Empty when the caller could not read one; the traces' own requests may
1299
1467
  * still supply it. Never a hard-coded literal.
1468
+ * @param report - The runner's own report for this run ({@link TraceRunReport}): each failing
1469
+ * test's declared location, the operation its failure names and the deadline it names, plus
1470
+ * whether the same result's `testRunNote` already points at a production build. A pending asset
1471
+ * is called the cause only when the runner says the failing operation for THAT test is the
1472
+ * navigation it was loading; without the report nothing is called a navigation stall.
1300
1473
  * @returns The note, or null when no trace held a recorded response, a real failure or a console
1301
1474
  * error.
1302
1475
  */
1303
- export function failureTraceNote(items, target, earlierFailures = [], apiPrefixes = []) {
1304
- const candidates = items.filter(traceRowEvidence);
1476
+ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixes = [], report = {}) {
1477
+ const candidates = items.filter((item) => traceRowEvidence(item, report));
1305
1478
  if (candidates.length === 0)
1306
1479
  return null;
1307
1480
  // Ranked, then deduplicated BY LOCATION — in that order, so the row kept for a location is the
1308
1481
  // one carrying the strongest evidence (m112's duplicate `afterEach` rows).
1309
1482
  const ranked = candidates
1310
- .map((item, index) => ({ item, index, tier: traceRowTier(item, earlierFailures) }))
1483
+ .map((item, index) => ({ item, index, tier: traceRowTier(item, earlierFailures, report) }))
1311
1484
  .sort((left, right) => left.tier - right.tier || left.index - right.index);
1312
1485
  const traced = [];
1313
1486
  const seen = new Set();
@@ -1342,17 +1515,30 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1342
1515
  : '';
1343
1516
  // A credential refusal (401/403 on the app's own API) is not the handler's answer: the trace's
1344
1517
  // own `Authorization` record says whether the CLIENT attached the token at all (m148 +29:18).
1345
- const authRefusals = failedRequests.filter((request) => sameOriginPath(request) !== null && (request.status === 401 || request.status === 403));
1518
+ const authRefusals = failedRequests.filter((request) => sameOriginPath(request) !== null &&
1519
+ (request.status === 401 || request.status === 403) &&
1520
+ // A 4xx on the logout endpoint is not a credential refusal to diagnose: the auth client
1521
+ // ignores it and clears the session anyway, and the row says so itself (m156).
1522
+ !isLogoutRequest(request));
1346
1523
  const everyFailureAuth = failedRequests.length > 0 && authRefusals.length === failedRequests.length;
1524
+ // Every recorded failure is the logout endpoint answering 4xx: the row's own sentence explains
1525
+ // it, and neither the handler lead nor the credential reading applies (m156 +24:02).
1526
+ const onlyLogoutFailures = failedRequests.length > 0 && failedRequests.every((request) => isLogoutRequest(request));
1347
1527
  const authAdvice = authRefusalAdvice(traced);
1348
- // The document subresources that never finished anywhere in this read — the assets a timed-out
1349
- // navigation was waiting on, when there is no failed request to explain the timeout.
1350
- const stalledAssets = [
1351
- ...new Set(traced
1352
- .filter(navigationNeverFinished)
1353
- .flatMap((item) => item.requests.filter(stalledDocumentSubresource))
1354
- .map(requestLabel)),
1528
+ // The stalled-navigation reading, per traced test: non-null only when the RUNNER says this
1529
+ // test's failing operation is a navigation and a document subresource still pending at the end
1530
+ // started inside that wait ({@link stalledNavigation}).
1531
+ const stalls = traced.map((item) => stalledNavigation(item, report));
1532
+ // "Every failure this run traced…" is only honest when every traced test meets that bar — one
1533
+ // stalled trace beside a selector-wait failure is not the cause of the run (m153).
1534
+ const everyTracedStalls = stalls.every((stall) => stall !== null);
1535
+ const stalledAssets = everyTracedStalls
1536
+ ? [...new Set(stalls.flatMap((stall) => stall?.assets.map(requestLabel) ?? []))]
1537
+ : [];
1538
+ const stalledWaits = [
1539
+ ...new Set(stalls.flatMap((stall) => (stall === null ? [] : [stall.reported]))),
1355
1540
  ];
1541
+ const anyStalled = stalls.some((stall) => stall !== null);
1356
1542
  const rows = [];
1357
1543
  for (const [index, item] of traced.entries()) {
1358
1544
  // The test's LAST action before the failing assertion leads the row (m148): the requests it
@@ -1360,10 +1546,24 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1360
1546
  // page's own last call (a redirect target's `GET /api/tags`) and the shell's files do not.
1361
1547
  const lead = item.lead;
1362
1548
  const step = item.lastAction;
1363
- // A navigation that never finished has one explanation — the stalled asset it was waiting on
1364
- // (m135) — and that leads the row instead of the action's own document request.
1365
- const stalledNav = navigationNeverFinished(item) && item.requests.some(stalledDocumentSubresource);
1366
- const stepRequests = step !== undefined && !stalledNav ? step.requests : [];
1549
+ // A navigation the runner reported, still waiting on a stalled asset (m135), leads the row
1550
+ // instead of the action's own document request.
1551
+ const stall = stalls[index] ?? null;
1552
+ const stalledNav = stall !== null;
1553
+ // The document subresources that never finished inside the navigation the runner reported:
1554
+ // the page was waiting on them when the test's budget ran out (m135).
1555
+ const stalled = stall?.assets ?? [];
1556
+ // The same kind of pending asset on a trace the runner does NOT report as a navigation
1557
+ // failure (a selector wait that ran out the test budget, an `afterEach`/teardown trace): it is
1558
+ // not a cause, and it is still worth naming so the executor does not have to guess (m153).
1559
+ const stalledKeys = new Set(stalled.map(requestKey));
1560
+ const pendingAssets = item.requests.filter((request) => stalledDocumentSubresource(request) && !stalledKeys.has(requestKey(request)));
1561
+ const pendingKeys = new Set(pendingAssets.map(requestKey));
1562
+ // A pending asset says nothing about what the action got back, so it never rides the action
1563
+ // lead — the honest bucket below names it (m153).
1564
+ const stepRequests = step !== undefined && !stalledNav
1565
+ ? step.requests.filter((request) => !pendingKeys.has(requestKey(request)))
1566
+ : [];
1367
1567
  const stepKeys = new Set(stepRequests.map(requestKey));
1368
1568
  const isLead = (request) => lead !== undefined && requestKey(request) === requestKey(lead);
1369
1569
  // The page shell's own requests take no row, in any bucket: an aborted `/sw.js` would
@@ -1372,20 +1572,16 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1372
1572
  const failed = requests.filter(failedRequest);
1373
1573
  const unanswered = requests.filter(unansweredRequest);
1374
1574
  const answered = requests.filter((request) => !failedRequest(request) && !unansweredRequest(request));
1375
- // A document subresource that never finished, on a trace whose navigation never finished:
1376
- // the page was waiting on it when the test's budget ran out (m135).
1377
- const stalled = navigationNeverFinished(item)
1378
- ? item.requests.filter(stalledDocumentSubresource)
1379
- : [];
1575
+ const budget = stall?.budgetMs ?? null;
1380
1576
  let room = MAX_TRACE_NOTE_REQUESTS;
1381
1577
  const parts = [];
1382
1578
  if (step !== undefined && !stalledNav) {
1383
1579
  const head = `the test's last action was ${step.api} at ${actionClock(item, step.start)}`;
1384
- if (step.requests.length === 0) {
1580
+ if (stepRequests.length === 0) {
1385
1581
  parts.push(`${head}, and it sent no request`);
1386
1582
  }
1387
1583
  else {
1388
- const shown = step.requests.slice(0, room);
1584
+ const shown = stepRequests.slice(0, room);
1389
1585
  room -= shown.length;
1390
1586
  // The response body is the point (m148: the register `POST /api/users` answered 201 and
1391
1587
  // the page rendered signed-out anyway) — printed whatever the status.
@@ -1398,6 +1594,19 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1398
1594
  ? `the page then navigated to ${step.pageAfter}`
1399
1595
  : `the page stayed at ${step.pageAfter}`);
1400
1596
  }
1597
+ // A 4xx on the logout endpoint with the page unmoved is not a backend failure: the auth
1598
+ // client swallows the call and clears the session, and the URL can only move if the page
1599
+ // navigates itself (xr-molecule-flash-m156 +24:02: the refactor dropped `navigate('/')` and
1600
+ // the run went and added a logout route instead).
1601
+ const stepLast = stepRequests.length > 0 ? stepRequests[stepRequests.length - 1] : undefined;
1602
+ if (stepLast !== undefined &&
1603
+ stepLast.status >= 400 &&
1604
+ stepLast.status < 500 &&
1605
+ isLogoutRequest(stepLast) &&
1606
+ step.pageBefore !== undefined &&
1607
+ step.pageAfter !== undefined &&
1608
+ step.pageBefore === step.pageAfter)
1609
+ parts.push(logoutIgnoredSentence(stepLast, step.pageAfter));
1401
1610
  }
1402
1611
  else if (lead !== undefined) {
1403
1612
  parts.push(leadRequestLine(lead));
@@ -1412,18 +1621,25 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1412
1621
  const shown = stalled.slice(0, room);
1413
1622
  room -= shown.length;
1414
1623
  parts.push(`never finished: ${shown.map(requestLabel).join('; ')} — a document subresource with no ` +
1415
- 'response, still in flight when the navigation timed out');
1624
+ `response, still in flight when the ${stall?.reported ?? 'navigation'} wait ran out` +
1625
+ (budget === null ? '' : ` of the suite's ${Math.round(budget / 1000)} s per-test budget`));
1626
+ }
1627
+ if (pendingAssets.length > 0 && room > 0) {
1628
+ const shown = pendingAssets.slice(0, room);
1629
+ room -= shown.length;
1630
+ parts.push(`had no recorded response when the test ended; that alone does not establish the cause: ` +
1631
+ shown.map(requestLabel).join('; '));
1416
1632
  }
1417
1633
  if (unanswered.length > 0 && room > 0) {
1418
1634
  const shown = unanswered.slice(0, room);
1419
1635
  room -= shown.length;
1420
- parts.push(`no response recorded — still in flight when the test ended, so not a server failure: ` +
1421
- shown.map(requestLabel).join('; '));
1636
+ parts.push(`no response recorded — still in flight when the test ended, so not a server failure; ` +
1637
+ `that alone does not establish the cause: ${shown.map(requestLabel).join('; ')}`);
1422
1638
  }
1423
1639
  if (answered.length > 0 && room > 0) {
1424
1640
  // An action that sent nothing leaves the page's own load as the only requests there are:
1425
1641
  // name them for what they are, not as the action's answer.
1426
- const label = step !== undefined && !stalledNav && step.requests.length === 0
1642
+ const label = step !== undefined && !stalledNav && stepRequests.length === 0
1427
1643
  ? 'also answered (started before the action)'
1428
1644
  : 'answered';
1429
1645
  parts.push(`${label}: ${answered.slice(0, room).map(requestLine).join('; ')}`);
@@ -1459,22 +1675,30 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1459
1675
  '`MOL_E2E_PREVIEW_PLUGIN=0 npx vite preview --host 127.0.0.1 --port 4173 --strictPort`. '
1460
1676
  : '') +
1461
1677
  'Fix the server before the app.\n\n';
1462
- // A navigation that never finished WITH a subresource that never got a response, and no
1678
+ // A navigation the RUNNER reported failing, with a subresource that never got a response, and no
1463
1679
  // transport error: the page was waiting on the asset when the budget ran out. That is the host
1464
1680
  // or the network serving that file, not the app's state — say it before any app-side reading
1465
1681
  // (xr-molecule-flash-m135 +34:10: three deployed goto timeouts on tests passing locally in
1466
1682
  // 1.2-5.6 s, one 88 KB stylesheet fetch measured at 27 s, and the note said "the cause is in
1467
- // the page").
1683
+ // the page"). Every traced test must meet the bar; one stalled trace beside a selector-wait
1684
+ // failure is not the run's cause (m153).
1468
1685
  const stalledLead = transport !== null || stalledAssets.length === 0
1469
1686
  ? ''
1470
- : `Every failure this run traced is a \`page.goto\` that never finished, and the document's ` +
1471
- `own ${stalledAssets.length === 1 ? 'subresource' : 'subresources'} ${stalledAssets.join(', ')} ` +
1687
+ : `Every failure this run traced is a navigation the runner reported failing ` +
1688
+ `(${stalledWaits.join(', ')}), and the document's own ` +
1689
+ `${stalledAssets.length === 1 ? 'subresource' : 'subresources'} ${stalledAssets.join(', ')} ` +
1472
1690
  `never got a response — the navigation was waiting on ${stalledAssets.length === 1 ? 'it' : 'them'} ` +
1473
1691
  `when the test's budget ran out. That is the host or the network serving the asset, not the ` +
1474
- `app's state or a selector: re-run the same tests once, unchanged, against ${target} before ` +
1475
- `editing anything, and if the stall repeats measure that exact fetch ` +
1476
- `(\`curl -o /dev/null -w '%{time_starttransfer} %{time_total}' <url>\`) before treating it as ` +
1477
- `the app's.\n\n`;
1692
+ `app's state or a selector: ` +
1693
+ (report.productionBuildAdvised
1694
+ ? `the run's own test note already names the next measurement — the same suite against ` +
1695
+ `a production build, because the dev server's page load is not the built app's — take ` +
1696
+ `that one step first, and come back to the app only if the same navigation times out ` +
1697
+ `there too with a stalled asset in the trace.\n\n`
1698
+ : `re-run the same tests once, unchanged, against ${target} before editing anything, ` +
1699
+ `and if the stall repeats measure that exact fetch ` +
1700
+ `(\`curl -o /dev/null -w '%{time_starttransfer} %{time_total}' <url>\`) before ` +
1701
+ `treating it as the app's.\n\n`);
1478
1702
  return (transportLead +
1479
1703
  stalledLead +
1480
1704
  `The platform read the failing tests' own Playwright traces from the run against ${target} ` +
@@ -1485,19 +1709,31 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
1485
1709
  (anyFailed
1486
1710
  ? everyFailureLacksPrefix
1487
1711
  ? ''
1488
- : everyFailureAuth
1489
- ? // Every failure was a credential refusal: the handler is not the problem, and the
1490
- // auth advice already names what is (m148 was sent to the handler six walks running).
1491
- authAdvice
1492
- : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1493
- 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1494
- 'user action silently — log it.' +
1495
- authAdvice
1496
- : stalledAssets.length > 0
1497
- ? 'No listed request failed — none got a 4xx/5xx or a network error — but the document did ' +
1498
- 'not finish loading because its own subresource never answered. Do not read this as a ' +
1499
- 'page-state bug: re-run these tests against the same URL first, and change the app only ' +
1500
- 'if the same navigation times out again with no stalled asset in the trace.'
1712
+ : onlyLogoutFailures
1713
+ ? // The rows already say it: a 4xx on the logout endpoint is ignored by the auth client
1714
+ // and the page's own navigation is the missing step. The handler lead would send the
1715
+ // run at the backend (m156 +33:52 added a logout route).
1716
+ ''
1717
+ : everyFailureAuth
1718
+ ? // Every failure was a credential refusal: the handler is not the problem, and the
1719
+ // auth advice already names what is (m148 was sent to the handler six walks running).
1720
+ authAdvice
1721
+ : 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
1722
+ 'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
1723
+ 'user action silently — log it.' +
1724
+ authAdvice
1725
+ : anyStalled
1726
+ ? report.productionBuildAdvised
1727
+ ? 'No listed request failed — none got a 4xx/5xx or a network error — but a navigation ' +
1728
+ 'the runner reported failing was still waiting on a document subresource that never ' +
1729
+ 'answered. Do not read this as a page-state bug, and do not re-run the same tests ' +
1730
+ 'unchanged: the run’s own test note names the production build as the next ' +
1731
+ 'measurement — take that one step first, and change the app only if the same ' +
1732
+ 'navigation times out there with no stalled asset in the trace.'
1733
+ : 'No listed request failed — none got a 4xx/5xx or a network error — but the document did ' +
1734
+ 'not finish loading because its own subresource never answered. Do not read this as a ' +
1735
+ 'page-state bug: re-run these tests against the same URL first, and change the app only ' +
1736
+ 'if the same navigation times out again with no stalled asset in the trace.'
1501
1737
  : 'No listed request failed — none got a 4xx/5xx or a network error — so the cause is in the ' +
1502
1738
  "page, not the API. Start with what the test's last action sent (the first line of each " +
1503
1739
  'row) and what the page did next: a request that answered (2xx included) while the page ' +
@@ -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
@@ -478,12 +478,18 @@ export function streamingBackgroundCommand(command) {
478
478
  };
479
479
  // A diagnosis or a re-run (capped, last-failed, grepped, or named files) walks each failure
480
480
  // 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.
481
+ // which had dropped the `--retries=0` it used one command earlier. A whole run whose TARGET is
482
+ // local — its env names `localhost`/`127.0.0.1`/`0.0.0.0`, or names no URL at all, as the
483
+ // wrapper config's default does — is not the proof run either and walks them once too:
484
+ // xr-molecule-flash-m155 (+11:34) spent 10 minutes on its first local whole run, and the
485
+ // suite's `retries: 1` walked each of its 68 failures a second time (0 flaky) for nothing.
486
+ // The suite's retries are kept only for a run that names a non-local URL — the deployed proof
487
+ // (xr-molecule-flash-m89), where they absorb a network blip. Which runs are diagnoses is
488
+ // {@link isDiagnosisRun}'s answer and nobody else's: the platform's notes ask the same
489
+ // function, so a run cannot be a diagnosis to one of them and the whole suite to the other.
490
+ // Which targets are local is {@link suiteCommandTarget}'s answer.
486
491
  const diagnosis = isDiagnosisRun(lastStep);
492
+ const localTarget = suiteCommandTarget(lastStep) === 'local';
487
493
  // The last step's reporters: one printer, and it is `list`. Every branch below only ever moves
488
494
  // the command TO that shape — a reporter set is never left with two printers in it.
489
495
  const reporters = finalPlaywrightRun ? lastReporterFlag(lastStep) : null;
@@ -549,9 +555,9 @@ export function streamingBackgroundCommand(command) {
549
555
  'summary at the end — so a run that is still walking already says what its failures are, ' +
550
556
  'and you need not wait for it to end to start on them.');
551
557
  }
552
- if (finalPlaywrightRun && diagnosis && !/--retries\b/.test(lastStep)) {
558
+ if (finalPlaywrightRun && (diagnosis || localTarget) && !/--retries\b/.test(lastStep)) {
553
559
  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.');
560
+ changes.push('Added `--retries=0`: a local run walks each failure once; the suite’s retries apply on the deployed proof run.');
555
561
  }
556
562
  return { command: out, changes };
557
563
  }
@@ -1953,6 +1959,23 @@ function largestGroup(groups) {
1953
1959
  function testKey(t) {
1954
1960
  return `${t.where}|${t.title}`;
1955
1961
  }
1962
+ /**
1963
+ * One test's identity with the runner's retry marker taken off: `… a.spec.ts:32 › does x
1964
+ * (retry #1)` is the same test as `… a.spec.ts:32 › does x`.
1965
+ *
1966
+ * A retried failure prints twice in a `list` log — its first attempt, then the same test with
1967
+ * `(retry #1)` after the title — while its failure block names it without the marker. Keyed
1968
+ * literally, the retry row is a test no cause claims, so it fell through to the duration arm and
1969
+ * was listed as a "shared duration" cause beside the proven cause it repeated: on
1970
+ * xr-molecule-flash-m155 (+21:50) every retry row of the run became one of six extra causes
1971
+ * duplicating the causes above them.
1972
+ *
1973
+ * @param t - A failed test, as a result line or a failure block spelled it.
1974
+ * @returns The key a test's retry row shares with its failure block.
1975
+ */
1976
+ function retrylessTestKey(t) {
1977
+ return `${t.where}|${t.title.replace(/\s*\(retry #\d+\)$/, '')}`;
1978
+ }
1956
1979
  /**
1957
1980
  * The same tests with each one kept once, in first-seen order.
1958
1981
  *
@@ -2600,6 +2623,10 @@ function groupFailures(log, excluded, pageErrors = []) {
2600
2623
  const listed = failedTests(text);
2601
2624
  named.push(...listed);
2602
2625
  const claimed = new Set(causes.flatMap((c) => c.tests.map(testKey)));
2626
+ // The causes above as a test can recognise itself: a retried failure's second row carries the
2627
+ // runner's `(retry #1)` marker, its failure block does not, and comparing the literal keys
2628
+ // read the same test as an unclaimed one (xr-molecule-flash-m155 +21:50).
2629
+ const proven = new Set(causes.flatMap((c) => c.tests.map(retrylessTestKey)));
2603
2630
  const unclaimed = distinctTests(listed).filter((t) => t.seconds !== null &&
2604
2631
  !claimed.has(testKey(t)) &&
2605
2632
  // A flaky test's `✘` row is its first attempt; its `✓` retry follows it in the same log,
@@ -2615,14 +2642,21 @@ function groupFailures(log, excluded, pageErrors = []) {
2615
2642
  else
2616
2643
  byDuration.push({ seconds: t.seconds, tests: [t] });
2617
2644
  }
2618
- for (const g of byDuration)
2645
+ for (const g of byDuration) {
2646
+ // A duration band is a LEAD, not a cause: a test already carrying a proven cause above is
2647
+ // not named a second time here, and a band whose every member is one is not a cause at all —
2648
+ // it reads as one more thing to fix. m155's retry rows made six such bands.
2649
+ const tests = g.tests.filter((t) => !proven.has(retrylessTestKey(t)));
2650
+ if (tests.length === 0)
2651
+ continue;
2619
2652
  causes.push({
2620
2653
  message: null,
2621
2654
  seconds: g.seconds,
2622
- tests: g.tests,
2655
+ tests,
2623
2656
  evidence: [],
2624
2657
  kind: 'assertion',
2625
2658
  });
2659
+ }
2626
2660
  const ignored = distinctTests(named).filter((t) => matchingExcludedTitles(t.title, excluded).length > 0);
2627
2661
  return {
2628
2662
  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.12",
3
+ "version": "1.9.14",
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",