@molecule/api-ai-tools 1.6.4 → 1.6.6
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 +520 -3
- package/dist/command-position.d.ts +27 -1
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +109 -28
- package/dist/cut-run.d.ts +124 -0
- package/dist/cut-run.d.ts.map +1 -0
- package/dist/cut-run.js +267 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/suite-overlap.d.ts +132 -0
- package/dist/suite-overlap.d.ts.map +1 -0
- package/dist/suite-overlap.js +208 -0
- package/dist/tools.d.ts +45 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +333 -27
- package/dist/utilities.d.ts +113 -2
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +171 -12
- package/package.json +1 -1
package/dist/utilities.d.ts
CHANGED
|
@@ -274,6 +274,81 @@ export declare function isValidGlob(pattern: string): boolean;
|
|
|
274
274
|
* `-path '…'` alternatives, ORed inside parens when there is more than one.
|
|
275
275
|
*/
|
|
276
276
|
export declare function findPatternArgs(pattern: string): string;
|
|
277
|
+
/**
|
|
278
|
+
* The `-path` alternatives of {@link findPatternArgs}, for a pattern whose
|
|
279
|
+
* directory the caller has already rooted the search at.
|
|
280
|
+
*
|
|
281
|
+
* `find_files` narrows the search root itself when the pattern spells its own
|
|
282
|
+
* directory out (see {@link literalDirPrefix}) — `node_modules` plus
|
|
283
|
+
* `@molecule/api-resource-user/dist/*.d.ts` searched that one directory. The
|
|
284
|
+
* remainder (`*.d.ts`) must then keep the `-path` form rather than collapsing to
|
|
285
|
+
* `-name`: `*` crosses `/` in an fnmatch glob, so the `-path` form still finds a
|
|
286
|
+
* declaration file in a `dist/utilities/` subdirectory, which `-name '*.d.ts'`
|
|
287
|
+
* would drop (a silent partial answer, which is worse than the timeout it
|
|
288
|
+
* replaced).
|
|
289
|
+
*
|
|
290
|
+
* @param pattern - The pattern's remainder, with a `/` in it or not.
|
|
291
|
+
* @returns One or more `-path '…'` alternatives, ORed inside parens when there
|
|
292
|
+
* is more than one.
|
|
293
|
+
*/
|
|
294
|
+
export declare function findPathArgs(pattern: string): string;
|
|
295
|
+
/**
|
|
296
|
+
* The directory a GLOB pattern spells out, when it spells one out: everything
|
|
297
|
+
* before the pattern's first glob character, trimmed back to the last `/`.
|
|
298
|
+
*
|
|
299
|
+
* A pattern is matched against the whole path with a leading "any parent"
|
|
300
|
+
* anchor ({@link findPatternArgs}), so `find` visits every entry below the
|
|
301
|
+
* search root. Handed `/workspace/node_modules` and
|
|
302
|
+
* `@molecule/api-resource-user/dist/*.d.ts` — a directory named outright —
|
|
303
|
+
* `find` walked the sandbox's entire dependency tree and hit the tool's 10 s
|
|
304
|
+
* deadline without printing a result (xr-molecule-flash-m94: the one directory
|
|
305
|
+
* the model meant answered a `list_files` in 0 s). The literal prefix is that
|
|
306
|
+
* directory, and rooting the search at it is what keeps the call instant.
|
|
307
|
+
*
|
|
308
|
+
* A BARE first segment (`helpers/*`, `bonds/*`, `e2e/*.spec.ts`) is NOT one:
|
|
309
|
+
* `helpers/*` means "a `helpers/` directory anywhere below the root", which the
|
|
310
|
+
* tool documents and a live run relied on (xr-molecule-flash-m77 — a path
|
|
311
|
+
* pattern is not anchored to one depth). Rooting those at
|
|
312
|
+
* `<root>/helpers` would answer with a SUBSET, silently: the nested
|
|
313
|
+
* `api/src/helpers/` matches would disappear, and a missing file reads as
|
|
314
|
+
* "no such file". So a prefix is returned only when it names a path of two or
|
|
315
|
+
* more segments (`@molecule/api-resource-user/dist`), which is the shape that
|
|
316
|
+
* is a directory rather than a directory name.
|
|
317
|
+
*
|
|
318
|
+
* @param pattern - A pattern already accepted by {@link isValidGlob}.
|
|
319
|
+
* @returns The literal directory prefix, without a trailing slash, or null when
|
|
320
|
+
* the pattern has no glob character, no `/`, or only a bare name before one.
|
|
321
|
+
*/
|
|
322
|
+
export declare function literalDirPrefix(pattern: string): string | null;
|
|
323
|
+
/**
|
|
324
|
+
* Whether a backend error is the sandbox's own command deadline.
|
|
325
|
+
*
|
|
326
|
+
* The E2B SDK answers a timed-out `exec` with its generic text —
|
|
327
|
+
* `[deadline_exceeded] the operation timed out: … It can be modified by passing
|
|
328
|
+
* 'timeoutMs' when making the request. Use '0' to disable the timeout.` — which
|
|
329
|
+
* names an option the executor cannot set and says nothing about the search
|
|
330
|
+
* that died. Handing that sentence to the model is what xr-molecule-flash-m94
|
|
331
|
+
* read after `find_files` spent 10 s walking `node_modules`; the tool answers
|
|
332
|
+
* the deadline in its own words instead (see {@link searchTimeoutError}).
|
|
333
|
+
*
|
|
334
|
+
* @param error - The error the backend threw.
|
|
335
|
+
* @returns True when the error reads as a timeout or a deadline.
|
|
336
|
+
*/
|
|
337
|
+
export declare function isCommandTimeout(error: unknown): boolean;
|
|
338
|
+
/**
|
|
339
|
+
* The answer a search tool gives when the backend's deadline fires: what was
|
|
340
|
+
* searched, for what, how long the tool allowed, and the two ways to get an
|
|
341
|
+
* answer — a narrower `path`, or a listing. Never the vendor's `timeoutMs`
|
|
342
|
+
* sentence (see {@link isCommandTimeout}).
|
|
343
|
+
*
|
|
344
|
+
* @param tool - The tool that timed out (`find_files`, `search_files`).
|
|
345
|
+
* @param searched - The directory the search actually covered.
|
|
346
|
+
* @param pattern - The pattern it was searching for.
|
|
347
|
+
* @param seconds - The tool's own limit, in seconds.
|
|
348
|
+
* @param hint - What to do differently — the narrower target or the listing tool.
|
|
349
|
+
* @returns The message's text.
|
|
350
|
+
*/
|
|
351
|
+
export declare function searchTimeoutError(tool: string, searched: string, pattern: string, seconds: number, hint: string): string;
|
|
277
352
|
/**
|
|
278
353
|
* Validate a file tool's `path` argument is a non-empty string. A weak model
|
|
279
354
|
* sometimes omits it or passes a non-string, which would otherwise crash
|
|
@@ -691,6 +766,27 @@ export declare function failuresByCause(log: string): FailureCause[];
|
|
|
691
766
|
* @returns The note, or null.
|
|
692
767
|
*/
|
|
693
768
|
export declare function failureCausesNote(log: string): string | null;
|
|
769
|
+
/**
|
|
770
|
+
* The command that re-runs the tests at `locations` the way the run IN FLIGHT ran them: the same
|
|
771
|
+
* invocation, in the same directory, narrowed to those tests — or null when the running command
|
|
772
|
+
* is not a Playwright run this can narrow.
|
|
773
|
+
*
|
|
774
|
+
* The `locations` are the paths the run's own log printed, which are relative to the directory
|
|
775
|
+
* that run started in, so a re-run has to resolve them there: the command names that directory
|
|
776
|
+
* unless it already changes into one of its own, because the executor's next command starts
|
|
777
|
+
* wherever it starts. The returned command is for AFTER the run ends — it is a second run against
|
|
778
|
+
* the same app and database, and that is exactly what is refused while the run walks
|
|
779
|
+
* ({@link suiteRunInTheWay}).
|
|
780
|
+
*
|
|
781
|
+
* @param run - The run in flight: the command it was started with, as it was run, and the
|
|
782
|
+
* directory that command ran in.
|
|
783
|
+
* @param locations - The failing `file:line`s to re-run, as the run's log printed them.
|
|
784
|
+
* @returns The re-run command, or null when there is no suite to narrow.
|
|
785
|
+
*/
|
|
786
|
+
export declare function rerunOfRunningSuite(run: {
|
|
787
|
+
command: string;
|
|
788
|
+
cwd?: string;
|
|
789
|
+
}, locations: readonly string[]): string | null;
|
|
694
790
|
/**
|
|
695
791
|
* The sentence that names a cause repeating among a running suite's failures, mid-run.
|
|
696
792
|
*
|
|
@@ -705,10 +801,21 @@ export declare function failureCausesNote(log: string): string | null;
|
|
|
705
801
|
* stopped eight runs at their first repeating cause in 14.5 minutes and never finished one, so
|
|
706
802
|
* the note must not say to stop when the run still has other causes to name.
|
|
707
803
|
*
|
|
804
|
+
* The run is STILL WALKING while this note is written — both callers produce it mid-run — so a
|
|
805
|
+
* re-run of one of its tests is a second run against the same app and database, and the platform
|
|
806
|
+
* refuses that ({@link suiteRunInTheWay}, the same rule its overlap hold answers with):
|
|
807
|
+
* xr-molecule-flash-m89 (2026-10-16) read *"Start on them now, while the run keeps walking.
|
|
808
|
+
* `null-fields.spec.ts:53` alone reproduces the first one."* and sent exactly that re-run 23 s
|
|
809
|
+
* later, where the hold answered *"A playwright suite is already running against this app and
|
|
810
|
+
* database … Wait for it."* The note therefore hands over the read-and-fix to do NOW and the
|
|
811
|
+
* re-run for after the run ends — the exact command where the caller knows how the run was
|
|
812
|
+
* started ({@link rerunOfRunningSuite}), the timing alone where it does not.
|
|
813
|
+
*
|
|
708
814
|
* @param found - What {@link repeatingFailure} found.
|
|
709
815
|
* @param o - The run's facts: its `taskId`, its `log` path, `stop` — the shell command that
|
|
710
|
-
* stops it, printed only for a {@link UniversalCause} —
|
|
711
|
-
* {@link universalCause} found (absent means the run is not failing whole)
|
|
816
|
+
* stops it, printed only for a {@link UniversalCause} — `universal`, what
|
|
817
|
+
* {@link universalCause} found (absent means the run is not failing whole), and `run`, how the
|
|
818
|
+
* run was started ({@link rerunOfRunningSuite}) when the caller knows it.
|
|
712
819
|
* @returns The note.
|
|
713
820
|
*/
|
|
714
821
|
export declare function repeatingFailureNote(found: RepeatingFailure, o: {
|
|
@@ -716,6 +823,10 @@ export declare function repeatingFailureNote(found: RepeatingFailure, o: {
|
|
|
716
823
|
log: string;
|
|
717
824
|
stop: string;
|
|
718
825
|
universal?: UniversalCause | null;
|
|
826
|
+
run?: {
|
|
827
|
+
command: string;
|
|
828
|
+
cwd?: string;
|
|
829
|
+
};
|
|
719
830
|
}): string;
|
|
720
831
|
/**
|
|
721
832
|
* Attempt a whitespace-tolerant replacement when an exact `old_string` match
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
package/dist/utilities.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { posix } from 'path';
|
|
8
8
|
import { commandSegments } from './command-position.js';
|
|
9
9
|
import { commandRunsNoTests } from './no-test-run.js';
|
|
10
|
+
import { isDiagnosisRun, scopedRerunCommand, suiteCommandTarget, suiteRunInTheWay, withoutHeredocs, } from './suite-overlap.js';
|
|
10
11
|
/**
|
|
11
12
|
* Shell-safe quoting using single quotes. Unlike JSON.stringify (double quotes),
|
|
12
13
|
* single-quoted strings prevent command substitution ($(), backticks) and variable expansion.
|
|
@@ -386,8 +387,11 @@ export function streamingBackgroundCommand(command) {
|
|
|
386
387
|
// A diagnosis or a re-run (capped, last-failed, grepped, or named files) walks each failure
|
|
387
388
|
// once: the suite's own `retries` doubled every failure of X1 flash-m71's first whole run,
|
|
388
389
|
// which had dropped the `--retries=0` it used one command earlier. The uncapped proof run
|
|
389
|
-
// keeps the suite's retries
|
|
390
|
-
|
|
390
|
+
// keeps the suite's retries — including the `--max-failures=0` the platform's own note asks
|
|
391
|
+
// for on it (xr-molecule-flash-m89). Which runs those are is {@link isDiagnosisRun}'s answer
|
|
392
|
+
// and nobody else's: the platform's notes ask the same function, so a run cannot be a
|
|
393
|
+
// diagnosis to one of them and the whole suite to the other.
|
|
394
|
+
const diagnosis = isDiagnosisRun(lastStep);
|
|
391
395
|
// The last step's reporters: one printer, and it is `list`. Every branch below only ever moves
|
|
392
396
|
// the command TO that shape — a reporter set is never left with two printers in it.
|
|
393
397
|
const reporters = finalPlaywrightRun ? lastReporterFlag(lastStep) : null;
|
|
@@ -437,7 +441,7 @@ export function streamingBackgroundCommand(command) {
|
|
|
437
441
|
changes.push('Added `--reporter=list` so each test result is written to the log as it finishes ' +
|
|
438
442
|
'(the config reporter may print nothing until the end).');
|
|
439
443
|
}
|
|
440
|
-
if (finalPlaywrightRun &&
|
|
444
|
+
if (finalPlaywrightRun && diagnosis && !/--retries\b/.test(lastStep)) {
|
|
441
445
|
out = `${out.trimEnd()} --retries=0`;
|
|
442
446
|
changes.push('Added `--retries=0`: a diagnosis or re-run walks each failure once; keep the suite’s retries for the proof run.');
|
|
443
447
|
}
|
|
@@ -680,9 +684,104 @@ export function isValidGlob(pattern) {
|
|
|
680
684
|
export function findPatternArgs(pattern) {
|
|
681
685
|
if (!pattern.includes('/'))
|
|
682
686
|
return `-name ${shellQuote(pattern)}`;
|
|
687
|
+
return findPathArgs(pattern);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* The `-path` alternatives of {@link findPatternArgs}, for a pattern whose
|
|
691
|
+
* directory the caller has already rooted the search at.
|
|
692
|
+
*
|
|
693
|
+
* `find_files` narrows the search root itself when the pattern spells its own
|
|
694
|
+
* directory out (see {@link literalDirPrefix}) — `node_modules` plus
|
|
695
|
+
* `@molecule/api-resource-user/dist/*.d.ts` searched that one directory. The
|
|
696
|
+
* remainder (`*.d.ts`) must then keep the `-path` form rather than collapsing to
|
|
697
|
+
* `-name`: `*` crosses `/` in an fnmatch glob, so the `-path` form still finds a
|
|
698
|
+
* declaration file in a `dist/utilities/` subdirectory, which `-name '*.d.ts'`
|
|
699
|
+
* would drop (a silent partial answer, which is worse than the timeout it
|
|
700
|
+
* replaced).
|
|
701
|
+
*
|
|
702
|
+
* @param pattern - The pattern's remainder, with a `/` in it or not.
|
|
703
|
+
* @returns One or more `-path '…'` alternatives, ORed inside parens when there
|
|
704
|
+
* is more than one.
|
|
705
|
+
*/
|
|
706
|
+
export function findPathArgs(pattern) {
|
|
683
707
|
const paths = pathPatternVariants(pattern).map((p) => `-path ${shellQuote(`*/${p}`)}`);
|
|
684
708
|
return paths.length === 1 ? paths[0] : `\\( ${paths.join(' -o ')} \\)`;
|
|
685
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* The directory a GLOB pattern spells out, when it spells one out: everything
|
|
712
|
+
* before the pattern's first glob character, trimmed back to the last `/`.
|
|
713
|
+
*
|
|
714
|
+
* A pattern is matched against the whole path with a leading "any parent"
|
|
715
|
+
* anchor ({@link findPatternArgs}), so `find` visits every entry below the
|
|
716
|
+
* search root. Handed `/workspace/node_modules` and
|
|
717
|
+
* `@molecule/api-resource-user/dist/*.d.ts` — a directory named outright —
|
|
718
|
+
* `find` walked the sandbox's entire dependency tree and hit the tool's 10 s
|
|
719
|
+
* deadline without printing a result (xr-molecule-flash-m94: the one directory
|
|
720
|
+
* the model meant answered a `list_files` in 0 s). The literal prefix is that
|
|
721
|
+
* directory, and rooting the search at it is what keeps the call instant.
|
|
722
|
+
*
|
|
723
|
+
* A BARE first segment (`helpers/*`, `bonds/*`, `e2e/*.spec.ts`) is NOT one:
|
|
724
|
+
* `helpers/*` means "a `helpers/` directory anywhere below the root", which the
|
|
725
|
+
* tool documents and a live run relied on (xr-molecule-flash-m77 — a path
|
|
726
|
+
* pattern is not anchored to one depth). Rooting those at
|
|
727
|
+
* `<root>/helpers` would answer with a SUBSET, silently: the nested
|
|
728
|
+
* `api/src/helpers/` matches would disappear, and a missing file reads as
|
|
729
|
+
* "no such file". So a prefix is returned only when it names a path of two or
|
|
730
|
+
* more segments (`@molecule/api-resource-user/dist`), which is the shape that
|
|
731
|
+
* is a directory rather than a directory name.
|
|
732
|
+
*
|
|
733
|
+
* @param pattern - A pattern already accepted by {@link isValidGlob}.
|
|
734
|
+
* @returns The literal directory prefix, without a trailing slash, or null when
|
|
735
|
+
* the pattern has no glob character, no `/`, or only a bare name before one.
|
|
736
|
+
*/
|
|
737
|
+
export function literalDirPrefix(pattern) {
|
|
738
|
+
const stripped = pattern.replace(/^(?:\.?\/)+/, '');
|
|
739
|
+
const firstGlob = stripped.search(/[?*[]/);
|
|
740
|
+
// No glob at all: the pattern IS a path, and the whole-tree form already
|
|
741
|
+
// finds it at any depth.
|
|
742
|
+
if (firstGlob === -1)
|
|
743
|
+
return null;
|
|
744
|
+
const at = stripped.slice(0, firstGlob).lastIndexOf('/');
|
|
745
|
+
if (at <= 0)
|
|
746
|
+
return null;
|
|
747
|
+
const prefix = stripped.slice(0, at);
|
|
748
|
+
return prefix.includes('/') ? prefix : null;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Whether a backend error is the sandbox's own command deadline.
|
|
752
|
+
*
|
|
753
|
+
* The E2B SDK answers a timed-out `exec` with its generic text —
|
|
754
|
+
* `[deadline_exceeded] the operation timed out: … It can be modified by passing
|
|
755
|
+
* 'timeoutMs' when making the request. Use '0' to disable the timeout.` — which
|
|
756
|
+
* names an option the executor cannot set and says nothing about the search
|
|
757
|
+
* that died. Handing that sentence to the model is what xr-molecule-flash-m94
|
|
758
|
+
* read after `find_files` spent 10 s walking `node_modules`; the tool answers
|
|
759
|
+
* the deadline in its own words instead (see {@link searchTimeoutError}).
|
|
760
|
+
*
|
|
761
|
+
* @param error - The error the backend threw.
|
|
762
|
+
* @returns True when the error reads as a timeout or a deadline.
|
|
763
|
+
*/
|
|
764
|
+
export function isCommandTimeout(error) {
|
|
765
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
766
|
+
return /deadline[_ ]?exceeded|timed out|timeout/i.test(message);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* The answer a search tool gives when the backend's deadline fires: what was
|
|
770
|
+
* searched, for what, how long the tool allowed, and the two ways to get an
|
|
771
|
+
* answer — a narrower `path`, or a listing. Never the vendor's `timeoutMs`
|
|
772
|
+
* sentence (see {@link isCommandTimeout}).
|
|
773
|
+
*
|
|
774
|
+
* @param tool - The tool that timed out (`find_files`, `search_files`).
|
|
775
|
+
* @param searched - The directory the search actually covered.
|
|
776
|
+
* @param pattern - The pattern it was searching for.
|
|
777
|
+
* @param seconds - The tool's own limit, in seconds.
|
|
778
|
+
* @param hint - What to do differently — the narrower target or the listing tool.
|
|
779
|
+
* @returns The message's text.
|
|
780
|
+
*/
|
|
781
|
+
export function searchTimeoutError(tool, searched, pattern, seconds, hint) {
|
|
782
|
+
return (`${tool} timed out after ${seconds}s searching ${searched} for "${pattern}" — the sandbox stopped the ` +
|
|
783
|
+
`command at its deadline, so there is no result (this is not an empty directory). ${hint}`);
|
|
784
|
+
}
|
|
686
785
|
/**
|
|
687
786
|
* The `-path` globs for a pattern that contains a slash, one per way each
|
|
688
787
|
* doubled-star directory segment can stand for zero directories.
|
|
@@ -1707,6 +1806,33 @@ export function failureCausesNote(log) {
|
|
|
1707
1806
|
return (`${total} failure${total === 1 ? '' : 's'} in this run, grouped by cause — fix every cause ` +
|
|
1708
1807
|
`below before the next deploy:\n${lines.join('\n')}${moreCauses}`);
|
|
1709
1808
|
}
|
|
1809
|
+
/**
|
|
1810
|
+
* The command that re-runs the tests at `locations` the way the run IN FLIGHT ran them: the same
|
|
1811
|
+
* invocation, in the same directory, narrowed to those tests — or null when the running command
|
|
1812
|
+
* is not a Playwright run this can narrow.
|
|
1813
|
+
*
|
|
1814
|
+
* The `locations` are the paths the run's own log printed, which are relative to the directory
|
|
1815
|
+
* that run started in, so a re-run has to resolve them there: the command names that directory
|
|
1816
|
+
* unless it already changes into one of its own, because the executor's next command starts
|
|
1817
|
+
* wherever it starts. The returned command is for AFTER the run ends — it is a second run against
|
|
1818
|
+
* the same app and database, and that is exactly what is refused while the run walks
|
|
1819
|
+
* ({@link suiteRunInTheWay}).
|
|
1820
|
+
*
|
|
1821
|
+
* @param run - The run in flight: the command it was started with, as it was run, and the
|
|
1822
|
+
* directory that command ran in.
|
|
1823
|
+
* @param locations - The failing `file:line`s to re-run, as the run's log printed them.
|
|
1824
|
+
* @returns The re-run command, or null when there is no suite to narrow.
|
|
1825
|
+
*/
|
|
1826
|
+
export function rerunOfRunningSuite(run, locations) {
|
|
1827
|
+
if (locations.length === 0)
|
|
1828
|
+
return null;
|
|
1829
|
+
const scoped = scopedRerunCommand(run.command, locations);
|
|
1830
|
+
if (scoped === run.command)
|
|
1831
|
+
return null;
|
|
1832
|
+
if (!run.cwd || commandSegments(withoutHeredocs(run.command)).some((s) => s.program === 'cd'))
|
|
1833
|
+
return scoped;
|
|
1834
|
+
return `cd ${shellQuote(run.cwd)} && ${scoped}`;
|
|
1835
|
+
}
|
|
1710
1836
|
/**
|
|
1711
1837
|
* The sentence that names a cause repeating among a running suite's failures, mid-run.
|
|
1712
1838
|
*
|
|
@@ -1721,10 +1847,21 @@ export function failureCausesNote(log) {
|
|
|
1721
1847
|
* stopped eight runs at their first repeating cause in 14.5 minutes and never finished one, so
|
|
1722
1848
|
* the note must not say to stop when the run still has other causes to name.
|
|
1723
1849
|
*
|
|
1850
|
+
* The run is STILL WALKING while this note is written — both callers produce it mid-run — so a
|
|
1851
|
+
* re-run of one of its tests is a second run against the same app and database, and the platform
|
|
1852
|
+
* refuses that ({@link suiteRunInTheWay}, the same rule its overlap hold answers with):
|
|
1853
|
+
* xr-molecule-flash-m89 (2026-10-16) read *"Start on them now, while the run keeps walking.
|
|
1854
|
+
* `null-fields.spec.ts:53` alone reproduces the first one."* and sent exactly that re-run 23 s
|
|
1855
|
+
* later, where the hold answered *"A playwright suite is already running against this app and
|
|
1856
|
+
* database … Wait for it."* The note therefore hands over the read-and-fix to do NOW and the
|
|
1857
|
+
* re-run for after the run ends — the exact command where the caller knows how the run was
|
|
1858
|
+
* started ({@link rerunOfRunningSuite}), the timing alone where it does not.
|
|
1859
|
+
*
|
|
1724
1860
|
* @param found - What {@link repeatingFailure} found.
|
|
1725
1861
|
* @param o - The run's facts: its `taskId`, its `log` path, `stop` — the shell command that
|
|
1726
|
-
* stops it, printed only for a {@link UniversalCause} —
|
|
1727
|
-
* {@link universalCause} found (absent means the run is not failing whole)
|
|
1862
|
+
* stops it, printed only for a {@link UniversalCause} — `universal`, what
|
|
1863
|
+
* {@link universalCause} found (absent means the run is not failing whole), and `run`, how the
|
|
1864
|
+
* run was started ({@link rerunOfRunningSuite}) when the caller knows it.
|
|
1728
1865
|
* @returns The note.
|
|
1729
1866
|
*/
|
|
1730
1867
|
export function repeatingFailureNote(found, o) {
|
|
@@ -1752,11 +1889,33 @@ export function repeatingFailureNote(found, o) {
|
|
|
1752
1889
|
? `every one with \`${found.message}\``
|
|
1753
1890
|
: `each after ~${found.seconds}s — the same duration to within half a second`;
|
|
1754
1891
|
const first = found.tests[0];
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1892
|
+
const firstAt = first?.where ?? '';
|
|
1893
|
+
// The re-run this note names, and whether a run is in its way — the SAME rule the platform's
|
|
1894
|
+
// overlap hold answers with (@link suiteRunInTheWay), asked of the same pair: the app the
|
|
1895
|
+
// composed re-run would measure, and the app the run in flight measures (both read off the two
|
|
1896
|
+
// commands, so a re-run aimed anywhere else is not refused).
|
|
1897
|
+
const rerun = firstAt && o.run ? rerunOfRunningSuite(o.run, [firstAt]) : null;
|
|
1898
|
+
const measured = o.run ? suiteCommandTarget(o.run.command) : null;
|
|
1899
|
+
const inTheWay = measured === null
|
|
1900
|
+
? null
|
|
1901
|
+
: suiteRunInTheWay({ target: rerun ? suiteCommandTarget(rerun) : measured }, [
|
|
1902
|
+
// The composer only narrows a `playwright test` run, and the sentence below says the run
|
|
1903
|
+
// is still walking, so the run in flight is a suite either way.
|
|
1904
|
+
{ runsSuite: true, target: measured },
|
|
1905
|
+
]);
|
|
1906
|
+
// Where the run's own app is not known (this process did not start it — a restart between the
|
|
1907
|
+
// launch and the wait), the note still knows the run is WALKING, which is the whole of the
|
|
1908
|
+
// reason the re-run waits: nothing may start against the app it walks.
|
|
1909
|
+
const aRunIsInTheWay = measured === null || inTheWay !== null;
|
|
1910
|
+
const reproduce = firstAt
|
|
1911
|
+
? ` Read \`${firstAt}\` and the code it drives now, and fix it: nothing may run against this ` +
|
|
1912
|
+
`app while the run walks — a second run against the same app and database is refused — so ` +
|
|
1913
|
+
`run it alone once the run has ended.`
|
|
1914
|
+
: '';
|
|
1915
|
+
// Where the note knows how the run was started, it also hands over the exact command for after
|
|
1916
|
+
// it: the whole-suite command it must not send again, narrowed to the failing test, in the
|
|
1917
|
+
// directory the run started in. Without that record it says the timing and nothing more.
|
|
1918
|
+
const thenRun = aRunIsInTheWay && rerun ? `\nSend this once the run ends:\n ${rerun}` : '';
|
|
1760
1919
|
const where = `task ${o.taskId}, log ${o.log}`;
|
|
1761
1920
|
if (o.universal) {
|
|
1762
1921
|
const files = o.universal.files.map(shortFile);
|
|
@@ -1777,13 +1936,13 @@ export function repeatingFailureNote(found, o) {
|
|
|
1777
1936
|
if (isCause)
|
|
1778
1937
|
return (`${found.count} different tests in this run have already failed the same way — ${signature} — ` +
|
|
1779
1938
|
`so one cause is repeating:\n${rows}${more}\n` +
|
|
1780
|
-
`Start fixing it now, while the run keeps walking.${reproduce} ${keepGoing}`);
|
|
1939
|
+
`Start fixing it now, while the run keeps walking.${reproduce} ${keepGoing}${thenRun}`);
|
|
1781
1940
|
return (`${found.count} different tests in this run each failed after ~${found.seconds}s — the same ` +
|
|
1782
1941
|
`duration to within half a second. A shared duration is a lead, not a cause: a suite whose ` +
|
|
1783
1942
|
`config sets a timeout spends that timeout on every failure that waits for anything, so ` +
|
|
1784
1943
|
`different missing elements land on the same number. Read these failures before fixing one ` +
|
|
1785
1944
|
`of them:\n${rows}${more}\n` +
|
|
1786
|
-
`Start on them now, while the run keeps walking.${reproduce} ${keepGoing}`);
|
|
1945
|
+
`Start on them now, while the run keeps walking.${reproduce} ${keepGoing}${thenRun}`);
|
|
1787
1946
|
}
|
|
1788
1947
|
/**
|
|
1789
1948
|
* Attempt a whitespace-tolerant replacement when an exact `old_string` match
|