@molecule/api-ai-tools 1.6.6 → 1.8.1
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 +613 -52
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +163 -2
- package/dist/cut-run.d.ts +98 -8
- package/dist/cut-run.d.ts.map +1 -1
- package/dist/cut-run.js +182 -29
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/suite-overlap.d.ts +5 -1
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +16 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +290 -82
- package/dist/trace-artifacts.d.ts +147 -0
- package/dist/trace-artifacts.d.ts.map +1 -0
- package/dist/trace-artifacts.js +511 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities.d.ts +198 -26
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +594 -67
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,15 +3,11 @@ 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-
|
|
6
|
+
Generated: 2026-10-17T02:50:55.123Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
10
10
|
|
|
11
|
-
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
-
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
-
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
-
|
|
15
11
|
Shared AI agent tool set for molecule.dev — filesystem, search, and shell
|
|
16
12
|
tools an LLM agent can call, with a swappable execution backend.
|
|
17
13
|
|
|
@@ -24,6 +20,10 @@ WHERE they act: `createLocalBackend(projectRoot)` (host filesystem) or
|
|
|
24
20
|
container). `buildAgentPrompt(ctx)` composes a matching system prompt
|
|
25
21
|
(identity, tool listing, project docs, discovered skills).
|
|
26
22
|
|
|
23
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
24
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
25
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
26
|
+
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```typescript
|
|
@@ -81,6 +81,72 @@ interface CommandSegment {
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
#### `LocatedCommandSegment`
|
|
85
|
+
|
|
86
|
+
One statement of a shell command, with where it sits and the variables it sets for itself.
|
|
87
|
+
|
|
88
|
+
`commandSegments` answers WHAT runs; this answers where, and what the shell binds to it — the
|
|
89
|
+
two things a caller that WRITES into a command needs. The platform composes a run from the
|
|
90
|
+
executor's own command in more than one place, and a value written for the runner belongs in
|
|
91
|
+
the runner's own statement: `APP_BASE=https://… cd app && npx playwright test` sets the variable
|
|
92
|
+
for `cd` alone, so the Playwright config falls back to the local app while the command line
|
|
93
|
+
still names the deployed host (xr-molecule-flash-m103 +28:08).
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
interface LocatedCommandSegment {
|
|
97
|
+
/**
|
|
98
|
+
* What the statement runs, or null when it only sets variables (`BASE=http://…` on its own):
|
|
99
|
+
* that shape persists in the shell, so a caller following assignments across `;`/`&&` has to
|
|
100
|
+
* see it.
|
|
101
|
+
*/
|
|
102
|
+
segment: CommandSegment | null
|
|
103
|
+
/** The statement's own leading `NAME=value` words, as written (`TEST_MODE=fullstack`). */
|
|
104
|
+
assignments: readonly string[]
|
|
105
|
+
/**
|
|
106
|
+
* The offset in the command of the statement's first word, or null for a segment inside a
|
|
107
|
+
* nested `sh -c '<command>'` string — its positions are offsets in THAT string, not in this
|
|
108
|
+
* command, so nothing may be spliced at them.
|
|
109
|
+
*/
|
|
110
|
+
start: number | null
|
|
111
|
+
/**
|
|
112
|
+
* The offset in the command just before the word the statement runs — after its own leading
|
|
113
|
+
* `NAME=value` words and their whitespace. A `NAME=value` written here is in the environment of
|
|
114
|
+
* the program the statement runs (`… TEST_MODE=fullstack <here>npx playwright test`). Null
|
|
115
|
+
* inside a nested `sh -c` string, for the same reason as {@link start}.
|
|
116
|
+
*/
|
|
117
|
+
assignmentOffset: number | null
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `SuiteSummary`
|
|
122
|
+
|
|
123
|
+
What a runner's own closing summary reports, and whether it accounts for the whole run.
|
|
124
|
+
|
|
125
|
+
A summary is not by itself proof that the run finished. Playwright prints one when its process
|
|
126
|
+
is stopped too (SIGTERM/SIGINT), listing only the tests it reached — so a run killed at 36 of
|
|
127
|
+
139 tests ends with the same `36 passed (2.5m)` line as a run that walked all 139. The only
|
|
128
|
+
thing that separates them is the total the runner announced before it started
|
|
129
|
+
(`Running 139 tests using 1 worker`), which {@link suiteSummary} reconciles the summary against.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface SuiteSummary {
|
|
133
|
+
/**
|
|
134
|
+
* The total the runner announced before its first test, or null when it announced none (Hurl
|
|
135
|
+
* announces nothing; Vitest's own summary line carries its total in parentheses).
|
|
136
|
+
*/
|
|
137
|
+
announced: number | null
|
|
138
|
+
/**
|
|
139
|
+
* Every test the summary accounts for — the ones it passed, failed, marked flaky, timed out,
|
|
140
|
+
* interrupted, skipped or left "did not run". This is how far the walk got, which is what a
|
|
141
|
+
* stopped run's sentence says; a summary that accounts for fewer than {@link announced} was
|
|
142
|
+
* printed by a process that stopped early.
|
|
143
|
+
*/
|
|
144
|
+
accounted: number
|
|
145
|
+
/** Whether the summary accounts for every test the runner announced. */
|
|
146
|
+
finished: boolean
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
84
150
|
#### `CutRun`
|
|
85
151
|
|
|
86
152
|
A run a budget stopped, as its result reports it.
|
|
@@ -96,9 +162,15 @@ interface CutRun {
|
|
|
96
162
|
* the pipeline's filter (`… | tail`) rather than to the run — the case that read as success.
|
|
97
163
|
*/
|
|
98
164
|
exitCode: number | null
|
|
99
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* How far the run got, as its own output counts it — the files that finished, or the tests its
|
|
167
|
+
* summary accounts for — or null when the output says nothing.
|
|
168
|
+
*/
|
|
100
169
|
ran: number | null
|
|
101
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* How many the run was walking: the total its runner announced (`Running 139 tests`), or
|
|
172
|
+
* Playwright's `[n/K]`, whichever the output carries; null when neither does.
|
|
173
|
+
*/
|
|
102
174
|
total: number | null
|
|
103
175
|
/** The noun the runner counts in. */
|
|
104
176
|
noun: 'files' | 'tests'
|
|
@@ -186,6 +258,51 @@ interface SuiteRunInFlight {
|
|
|
186
258
|
}
|
|
187
259
|
```
|
|
188
260
|
|
|
261
|
+
#### `TraceRequest`
|
|
262
|
+
|
|
263
|
+
One request a traced page made, as `*.network` records it.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
interface TraceRequest {
|
|
267
|
+
/** The HTTP method (`POST`). */
|
|
268
|
+
method: string
|
|
269
|
+
/** The request's path (`/api/articles/x/favorite`), or the whole URL when it is cross-origin. */
|
|
270
|
+
path: string
|
|
271
|
+
/** The response status, or 0 when the request never got a response. */
|
|
272
|
+
status: number
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### `TraceArtifact`
|
|
277
|
+
|
|
278
|
+
What one trace says: the test it belongs to, the requests it made, and its console errors.
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
interface TraceArtifact {
|
|
282
|
+
/** The failure's spec file, relative to the run's directory, when the trace names one. */
|
|
283
|
+
spec: string | null
|
|
284
|
+
/** The line of the test's last traced step, 0 when the trace names no spec. */
|
|
285
|
+
line: number
|
|
286
|
+
/** The requests the page made, the interesting ones first ({@link traceArtifactsScript}). */
|
|
287
|
+
requests: TraceRequest[]
|
|
288
|
+
/** The console errors and page errors the page raised. */
|
|
289
|
+
console: string[]
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### `DiagnosisTraceRewrite`
|
|
294
|
+
|
|
295
|
+
A command with the trace flag this platform decided to add, and the sentence to report it in.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
interface DiagnosisTraceRewrite {
|
|
299
|
+
/** The command to run. */
|
|
300
|
+
command: string
|
|
301
|
+
/** One sentence naming the flag and why, or null when nothing was added. */
|
|
302
|
+
change: string | null
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
189
306
|
#### `ExecutionBackend`
|
|
190
307
|
|
|
191
308
|
Abstraction over the execution environment.
|
|
@@ -331,6 +448,20 @@ interface ToolBuildConfig {
|
|
|
331
448
|
*/
|
|
332
449
|
outlineLargeHtml?: () => boolean
|
|
333
450
|
|
|
451
|
+
/**
|
|
452
|
+
* The test titles the person's request EXCLUDES by name (molecule-dev's `excludedTestTitles`),
|
|
453
|
+
* asked when a finished suite run's cause note is built. Their failures are not causes to fix:
|
|
454
|
+
* the note drops them, says so at the end, and inverts them out of the narrowed re-run it hands
|
|
455
|
+
* over — a result must never name a test beside "it does not count" (xr-molecule-flash-m101
|
|
456
|
+
* +12:00, where the executor spent a turn and an edit on the excluded test).
|
|
457
|
+
*
|
|
458
|
+
* A getter, not an array, because the request is read per turn while the tool set is built
|
|
459
|
+
* once. Unset means no test is excluded.
|
|
460
|
+
*
|
|
461
|
+
* @returns The quoted titles the request carves out.
|
|
462
|
+
*/
|
|
463
|
+
excludedTestTitles?: () => readonly string[]
|
|
464
|
+
|
|
334
465
|
/** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
|
|
335
466
|
onAfterWrite?: (path: string) => Promise<void>
|
|
336
467
|
|
|
@@ -409,6 +540,19 @@ interface PromptContext {
|
|
|
409
540
|
}
|
|
410
541
|
```
|
|
411
542
|
|
|
543
|
+
#### `SuiteFilterStrip`
|
|
544
|
+
|
|
545
|
+
A command with the output filters after its suite runner removed.
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
interface SuiteFilterStrip {
|
|
549
|
+
/** The command to run. */
|
|
550
|
+
command: string
|
|
551
|
+
/** The pipeline stages dropped, as written (`| grep … | tail -20`), or '' when none were. */
|
|
552
|
+
dropped: string
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
412
556
|
#### `FailedTest`
|
|
413
557
|
|
|
414
558
|
One failed test, as a running suite's log names it.
|
|
@@ -469,6 +613,12 @@ interface FailureCause {
|
|
|
469
613
|
seconds: number | null
|
|
470
614
|
/** The distinct tests it hit, in the order the log named them. */
|
|
471
615
|
tests: FailedTest[]
|
|
616
|
+
/**
|
|
617
|
+
* The FIRST failure's two most useful body lines — the values that differ, or the locator from
|
|
618
|
+
* its `Call log` ({@link failureEvidence}) — so a fix round needs no re-read of the log. Empty
|
|
619
|
+
* for a duration group, whose blocks carried no bodies.
|
|
620
|
+
*/
|
|
621
|
+
evidence: string[]
|
|
472
622
|
}
|
|
473
623
|
```
|
|
474
624
|
|
|
@@ -487,7 +637,17 @@ type CommandRunState = 'ran' | typeof CUT_AT_BUDGET
|
|
|
487
637
|
What stopped a run.
|
|
488
638
|
|
|
489
639
|
```typescript
|
|
490
|
-
type CutBy =
|
|
640
|
+
type CutBy =
|
|
641
|
+
/** This tool's own budget (the backend's `timeout`, just past it). */
|
|
642
|
+
| 'tool-budget'
|
|
643
|
+
/** The `timeout <N>` the command itself carried. */
|
|
644
|
+
| 'command-timeout'
|
|
645
|
+
/**
|
|
646
|
+
* Something outside the run: the runner's summary does not account for the total it announced,
|
|
647
|
+
* and neither of the two bounds above had fired — the process was killed by something this
|
|
648
|
+
* platform does not own (the sandbox's own process supervisor, the box going away, a signal).
|
|
649
|
+
*/
|
|
650
|
+
| 'process-stop'
|
|
491
651
|
```
|
|
492
652
|
|
|
493
653
|
#### `DiscoveredSkill` _(deprecated)_
|
|
@@ -563,6 +723,20 @@ function commandSegments(command: string): CommandSegment[]
|
|
|
563
723
|
|
|
564
724
|
**Returns:** One entry per statement or pipeline stage that runs something, in order.
|
|
565
725
|
|
|
726
|
+
#### `locatedCommandSegments(command)`
|
|
727
|
+
|
|
728
|
+
Every statement of a command, with its own assignments and where it sits — assignment-only
|
|
729
|
+
statements included, because the variables those persist are exactly what a later statement of
|
|
730
|
+
the same command reads.
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
function locatedCommandSegments(command: string): LocatedCommandSegment[]
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
- `command` — The shell command.
|
|
737
|
+
|
|
738
|
+
**Returns:** One entry per statement or pipeline stage, in order.
|
|
739
|
+
|
|
566
740
|
#### `commandTimeoutSeconds(command)`
|
|
567
741
|
|
|
568
742
|
The bound a command puts on its own run: the largest `timeout <N>` any of its segments runs
|
|
@@ -617,9 +791,29 @@ function commandRunState(result: unknown): CommandRunState
|
|
|
617
791
|
|
|
618
792
|
**Returns:** The state the result records.
|
|
619
793
|
|
|
794
|
+
#### `suiteSummary(text)`
|
|
795
|
+
|
|
796
|
+
What the runner's closing summary accounts for, or null when it printed none.
|
|
797
|
+
|
|
798
|
+
Three runners' summaries are read, each by its own wording: Playwright's per-status lines
|
|
799
|
+
(reconciled against its `Running N tests` announcement), Vitest's single `Tests …` line (its own
|
|
800
|
+
parenthesised total), and Hurl's `Executed files:` block (which it prints only at the end, and
|
|
801
|
+
which announces no total — so it is a finish by construction).
|
|
802
|
+
|
|
803
|
+
```typescript
|
|
804
|
+
function suiteSummary(text: string): SuiteSummary | null
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
- `text` — The run's stdout and stderr, RAW (a truncated copy can lose the announcement).
|
|
808
|
+
|
|
809
|
+
**Returns:** The summary, or null when the text carries none.
|
|
810
|
+
|
|
620
811
|
#### `suiteTally(text)`
|
|
621
812
|
|
|
622
|
-
Whether text carries a runner's closing summary
|
|
813
|
+
Whether text carries a runner's closing summary.
|
|
814
|
+
|
|
815
|
+
A summary is NOT a finish on its own — a stopped Playwright run prints one too. Use
|
|
816
|
+
{@link suiteSummary} and its `finished` for that.
|
|
623
817
|
|
|
624
818
|
```typescript
|
|
625
819
|
function suiteTally(text: string): boolean
|
|
@@ -629,13 +823,39 @@ function suiteTally(text: string): boolean
|
|
|
629
823
|
|
|
630
824
|
**Returns:** True when the runner's own end-of-run summary is there.
|
|
631
825
|
|
|
632
|
-
#### `
|
|
826
|
+
#### `suiteRunExitCode(exitCode, output, piped)`
|
|
827
|
+
|
|
828
|
+
The exit code a finished suite run may report.
|
|
829
|
+
|
|
830
|
+
The code in a background result is the code of the command's LAST pipeline stage. When a stage
|
|
831
|
+
after the suite runner still consumes its output (`| tee run.log`, or a filter the platform could
|
|
832
|
+
not prove output-only), that stage produced the code, not the run — and `… | grep … | tail -20`
|
|
833
|
+
exited 0 for a run with a failure in it (xr-molecule-flash-m101 +25:54). No code is reported
|
|
834
|
+
then: the log's own tally decides.
|
|
835
|
+
|
|
836
|
+
The same reading covers a code of 0 beside a tally that names a failure: whatever ended the
|
|
837
|
+
pipeline, a run whose log counts a failure did not pass.
|
|
838
|
+
|
|
839
|
+
```typescript
|
|
840
|
+
function suiteRunExitCode(exitCode: number | null, output: string, piped: boolean): number | null
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
- `exitCode` — The code the pipeline reported (null when it has none).
|
|
844
|
+
- `output` — The run's full log.
|
|
845
|
+
- `piped` — Whether a pipeline stage after the suite runner still consumes its output.
|
|
846
|
+
|
|
847
|
+
**Returns:** The code the run may report, or null when the code is the filter's or contradicts the tally.
|
|
848
|
+
|
|
849
|
+
#### `commandCutRun(command, result, elapsedMs, budgetSeconds, rawOutput)`
|
|
633
850
|
|
|
634
851
|
The run a command's result records as stopped by a budget, or null when the result is a run that
|
|
635
852
|
ended (or says nothing about a run).
|
|
636
853
|
|
|
637
854
|
Evidence, in order — never a guess:
|
|
638
855
|
|
|
856
|
+
- the runner's own summary, when it printed one: it is a finish only if it accounts for the total
|
|
857
|
+
the runner announced. A summary that accounts for fewer is a walk a process stopped early, and
|
|
858
|
+
falls through to the branches below with its own counts ({@link suiteSummary}).
|
|
639
859
|
- `timeout`'s own 124 (the code the backend's wrapper returns whenever the TOOL's budget fired,
|
|
640
860
|
whatever the pipeline ended with) — and, when the clock says the command's own smaller bound
|
|
641
861
|
ran out first and less than the budget, that bound instead.
|
|
@@ -644,6 +864,8 @@ Evidence, in order — never a guess:
|
|
|
644
864
|
killed at its own bound. Its exit code, when the pipeline handed one back, is the filter's.
|
|
645
865
|
- the run used this tool's whole budget behind a filter that withheld its output, and printed no
|
|
646
866
|
closing summary: the budget stopped it, and no code in the result is the run's.
|
|
867
|
+
- a summary that does not account for its announced total, with neither bound fired: something
|
|
868
|
+
outside this platform stopped the process ({@link CutBy} `process-stop`).
|
|
647
869
|
|
|
648
870
|
```typescript
|
|
649
871
|
function commandCutRun(
|
|
@@ -651,6 +873,7 @@ function commandCutRun(
|
|
|
651
873
|
result: unknown,
|
|
652
874
|
elapsedMs: number,
|
|
653
875
|
budgetSeconds?: number,
|
|
876
|
+
rawOutput?: string,
|
|
654
877
|
): CutRun | null
|
|
655
878
|
```
|
|
656
879
|
|
|
@@ -658,6 +881,7 @@ function commandCutRun(
|
|
|
658
881
|
- `result` — Its result.
|
|
659
882
|
- `elapsedMs` — How long the command ran, in ms.
|
|
660
883
|
- `budgetSeconds` — This tool's foreground budget, in seconds (0 when it has none).
|
|
884
|
+
- `rawOutput` — The run's output BEFORE truncation, when the caller has it: the announcement a summary is reconciled against is the first line a long run prints, which is exactly what a size cap drops. Detection reads this; the sentence's last line still comes from `result`.
|
|
661
885
|
|
|
662
886
|
**Returns:** The stopped run, or null.
|
|
663
887
|
|
|
@@ -681,12 +905,14 @@ function cutRunFacts(result: unknown): CutRun | null
|
|
|
681
905
|
|
|
682
906
|
#### `cutRunSentence(run)`
|
|
683
907
|
|
|
684
|
-
The first line of a stopped run's result: how
|
|
685
|
-
|
|
908
|
+
The first line of a stopped run's result: how far it got, what stopped it, the last thing it
|
|
909
|
+
printed, and what the reader must do next.
|
|
686
910
|
|
|
687
911
|
Written to be quotable by the model in one sentence and to leave no room for "nearly passing":
|
|
688
912
|
the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
|
|
689
|
-
read elsewhere (the suite's own file count) fills in the "
|
|
913
|
+
read elsewhere (the suite's own file count) fills in the "of N" it can then state. The sentence
|
|
914
|
+
says how many tests the stop left unrun — a number a reader cannot round up to "the suite
|
|
915
|
+
passed" — and hands over the one move that survives a long run: run it in the background.
|
|
690
916
|
|
|
691
917
|
```typescript
|
|
692
918
|
function cutRunSentence(run: CutRun): string
|
|
@@ -835,7 +1061,7 @@ function suiteCommandTarget(command: string): string
|
|
|
835
1061
|
|
|
836
1062
|
**Returns:** The target key.
|
|
837
1063
|
|
|
838
|
-
#### `scopedRerunCommand(command, locations)`
|
|
1064
|
+
#### `scopedRerunCommand(command, locations, excluded)`
|
|
839
1065
|
|
|
840
1066
|
The whole-suite command made scoped to the tests it should re-run instead: the same
|
|
841
1067
|
invocation — same directory, same settings — with the named `file:line`s in place of the whole
|
|
@@ -843,11 +1069,16 @@ suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flas
|
|
|
843
1069
|
`--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
844
1070
|
|
|
845
1071
|
```typescript
|
|
846
|
-
function scopedRerunCommand(
|
|
1072
|
+
function scopedRerunCommand(
|
|
1073
|
+
command: string,
|
|
1074
|
+
locations: readonly string[],
|
|
1075
|
+
excluded?: readonly string[],
|
|
1076
|
+
): string
|
|
847
1077
|
```
|
|
848
1078
|
|
|
849
1079
|
- `command` — The whole-suite run's command.
|
|
850
1080
|
- `locations` — The failing `file:line`s to re-run, as the run's own log printed them — or, for a run that started outside the suite, the absolute form Playwright matches each file argument against.
|
|
1081
|
+
- `excluded` — The test titles the request carves out. They are inverted out (`--grep-invert`) so a re-run of the failing locations cannot report one of them again — xr-molecule-flash-m101 re-ran the whole 139-test suite and spent ~4 minutes to be told about a test the request had already said does not count.
|
|
851
1082
|
|
|
852
1083
|
**Returns:** The scoped command, or the command unchanged when it runs no Playwright suite.
|
|
853
1084
|
|
|
@@ -1025,6 +1256,107 @@ function buildTools(backend: ExecutionBackend, config?: ToolBuildConfig): AITool
|
|
|
1025
1256
|
|
|
1026
1257
|
**Returns:** Array of AITool objects ready to pass to an AI provider
|
|
1027
1258
|
|
|
1259
|
+
#### `traceArtifactsScript()`
|
|
1260
|
+
|
|
1261
|
+
The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
|
|
1262
|
+
`TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
|
|
1263
|
+
them), each with the test it belongs to, the requests its page made and its console errors.
|
|
1264
|
+
|
|
1265
|
+
It is a standalone program, not an import, for the reason `@molecule/api-code-sandbox` runs it:
|
|
1266
|
+
Playwright's own zip reader is not reachable from a project's `node_modules`, and a dependency
|
|
1267
|
+
on `unzip` would make the read fail on every sandbox that has none. The zip is read with the
|
|
1268
|
+
central directory and `zlib.inflateRawSync` — about forty lines, and the only thing that has to
|
|
1269
|
+
be true of a `trace.zip` is that it IS one.
|
|
1270
|
+
|
|
1271
|
+
A request is kept over a static asset when it failed (`status >= 400`) or never answered
|
|
1272
|
+
(`status === 0`), and over anything else when it is the page talking to the app
|
|
1273
|
+
(`fetch`/`xhr`/`document`): a failing test's trace holds the whole page load, and the answer is
|
|
1274
|
+
always at the end of it.
|
|
1275
|
+
|
|
1276
|
+
```typescript
|
|
1277
|
+
function traceArtifactsScript(): string
|
|
1278
|
+
```
|
|
1279
|
+
|
|
1280
|
+
**Returns:** The program's source, to write to a file and run.
|
|
1281
|
+
|
|
1282
|
+
#### `parseTraceArtifacts(stdout)`
|
|
1283
|
+
|
|
1284
|
+
The traces {@link traceArtifactsScript} printed, parsed.
|
|
1285
|
+
|
|
1286
|
+
A line that is not a complete `TRACE <json>` (a truncated read, a program that died mid-write) is
|
|
1287
|
+
dropped: a partial trace is not evidence about the test it came from.
|
|
1288
|
+
|
|
1289
|
+
```typescript
|
|
1290
|
+
function parseTraceArtifacts(stdout: string): TraceArtifact[]
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
- `stdout` — The program's output.
|
|
1294
|
+
|
|
1295
|
+
**Returns:** One entry per trace it printed.
|
|
1296
|
+
|
|
1297
|
+
#### `failureTraceNote(items, target)`
|
|
1298
|
+
|
|
1299
|
+
The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
1300
|
+
what came back.
|
|
1301
|
+
|
|
1302
|
+
This is the answer to the question a swallowed error hides — a click that changed nothing is
|
|
1303
|
+
usually a request that failed — so the note leads with the failing requests and says what to do
|
|
1304
|
+
with them: read the handler the route reaches, not the selector. It is BOUNDED to
|
|
1305
|
+
{@link MAX_TRACE_NOTE_TESTS} tests with {@link MAX_TRACE_NOTE_REQUESTS} requests each, because it
|
|
1306
|
+
rides in a result the model pays for.
|
|
1307
|
+
|
|
1308
|
+
```typescript
|
|
1309
|
+
function failureTraceNote(items: readonly TraceArtifact[], target: string): string | null
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
- `items` — The traces {@link parseTraceArtifacts} read.
|
|
1313
|
+
- `target` — Where the run went — the deployed host, as the note names it.
|
|
1314
|
+
|
|
1315
|
+
**Returns:** The note, or null when no trace held a request or a console error.
|
|
1316
|
+
|
|
1317
|
+
#### `suiteRecordsTraces(configText)`
|
|
1318
|
+
|
|
1319
|
+
Whether a Playwright config records traces for the tests that fail.
|
|
1320
|
+
|
|
1321
|
+
Only `on` and `retain-on-failure` keep a `trace.zip` for a failure whatever the retries do: the
|
|
1322
|
+
two retry modes need a retry to happen at all, and a diagnosis run is given `--retries=0`
|
|
1323
|
+
(`streamingBackgroundCommand`), so on X1 xr-molecule-flash-m100 the run's own
|
|
1324
|
+
`trace: 'on-first-retry'` recorded nothing — which is what left the failing tests' network log
|
|
1325
|
+
unreadable. A config that sets no `trace:` at all records nothing either (Playwright's default
|
|
1326
|
+
is off); so does one that cannot be read.
|
|
1327
|
+
|
|
1328
|
+
```typescript
|
|
1329
|
+
function suiteRecordsTraces(configText: string): boolean
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
- `configText` — The config's source, or '' when none could be read.
|
|
1333
|
+
|
|
1334
|
+
**Returns:** True when the config records traces for failures.
|
|
1335
|
+
|
|
1336
|
+
#### `diagnosisTraceCommand(args)`
|
|
1337
|
+
|
|
1338
|
+
The command a DIAGNOSIS run needs to leave a readable trace behind, when the suite's config
|
|
1339
|
+
would record none.
|
|
1340
|
+
|
|
1341
|
+
The failing tests' requests are only readable if the run wrote them down (see the module doc),
|
|
1342
|
+
and the config is where the decision about that lives — the platform does not edit the suite's
|
|
1343
|
+
config, so it passes the flag instead, and only where it can do no harm: on a run narrowed to a
|
|
1344
|
+
part of the suite or capped ({@link isDiagnosisRun}), never on the whole-suite proof run, and
|
|
1345
|
+
never over a `--trace` the executor wrote itself. A capped walk is exactly the run whose
|
|
1346
|
+
failures someone is about to work on; the proof run is graded as it is.
|
|
1347
|
+
|
|
1348
|
+
```typescript
|
|
1349
|
+
function diagnosisTraceCommand(args: {
|
|
1350
|
+
command: string
|
|
1351
|
+
cwd: string
|
|
1352
|
+
readFile: (path: string) => Promise<string>
|
|
1353
|
+
}): Promise<DiagnosisTraceRewrite>
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
- `args` — The command, the directory it starts in, and the backend's file read.
|
|
1357
|
+
|
|
1358
|
+
**Returns:** The command to run and the sentence to report, or the command unchanged.
|
|
1359
|
+
|
|
1028
1360
|
#### `shellQuote(s)`
|
|
1029
1361
|
|
|
1030
1362
|
Shell-safe quoting using single quotes. Unlike JSON.stringify (double quotes),
|
|
@@ -1185,19 +1517,19 @@ killed (X1 run flash-m15 lost 20 of its 60 minutes this way, 2026-10-15):
|
|
|
1185
1517
|
|
|
1186
1518
|
- a trailing `| tail -N`, which holds everything back until the command ends.
|
|
1187
1519
|
It is dropped: `wait_for_task` already returns the end of the log.
|
|
1520
|
+
- any output-only filter piped after a SUITE runner (`| grep … | tail -20`), which hides the
|
|
1521
|
+
failing test's own error and leaves the pipeline's exit status — the filter's — as the run's.
|
|
1522
|
+
Every such stage is dropped ({@link stripTrailingSuiteFilters}); `wait_for_task` already
|
|
1523
|
+
returns the end of the full log, and a run whose tally shows a failure must never report 0
|
|
1524
|
+
(xr-molecule-flash-m101 +25:54).
|
|
1188
1525
|
- `playwright test` with no `--reporter`, which runs the config's reporter —
|
|
1189
1526
|
often `html`, which prints nothing per test. `--reporter=list` is added
|
|
1190
|
-
when the playwright run is the command's last step
|
|
1191
|
-
|
|
1192
|
-
shape the platform reads a background suite back in.
|
|
1527
|
+
when the playwright run is the command's last step, piped or not: it writes each result as
|
|
1528
|
+
it finishes, and it is the shape the platform reads a background suite back in.
|
|
1193
1529
|
|
|
1194
1530
|
The run ends up with exactly ONE reporter that prints each failure — `list`. A run that names
|
|
1195
1531
|
any other printing reporter has it REPLACED, never joined: two of them write every test result
|
|
1196
1532
|
into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,line` run (its
|
|
1197
|
-
`line` added by this function one run earlier) report two distinct failures as "4 of this run's
|
|
1198
|
-
tests have already failed the same way", and the executor stopped a 139-test walk at test 21
|
|
1199
|
-
with a single cause in hand. `line` alone is no use either: it writes `[n/N] <title>` with no
|
|
1200
|
-
✓/✘ mark and no duration, so on X1 flash-m62 an 8.3-minute walk of one suite could never trip
|
|
1201
1533
|
|
|
1202
1534
|
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1203
1535
|
|
|
@@ -1247,6 +1579,47 @@ function isPlaywrightRunCommand(command: string): boolean
|
|
|
1247
1579
|
|
|
1248
1580
|
**Returns:** True when the command starts a Playwright run.
|
|
1249
1581
|
|
|
1582
|
+
#### `stripTrailingSuiteFilters(command)`
|
|
1583
|
+
|
|
1584
|
+
Drop the output-only filters piped after the command's suite runner.
|
|
1585
|
+
|
|
1586
|
+
A filter after the runner is not "the end of the log" the executor asked for: it hides the
|
|
1587
|
+
failing test's own error (it usually matches none of `✘|passed|failed`), and the pipeline's exit
|
|
1588
|
+
status becomes the FILTER's, so `… 2>&1 | grep -E "✘|passed|failed|skipped" | tail -20` came back
|
|
1589
|
+
`exitCode: 0` for a run with a failure in it (xr-molecule-flash-m101 +25:54). The tool returns
|
|
1590
|
+
the end of the full log anyway, so the filter only costs diagnosability.
|
|
1591
|
+
|
|
1592
|
+
Only a contiguous suffix of the stages after the runner is cut, and only stages the parse proves
|
|
1593
|
+
are output-only ({@link isOutputOnlyFilter}): a `| tee` or `| xargs` keeps its place, and a
|
|
1594
|
+
caller that reports the run's exit code must then treat it as unknown
|
|
1595
|
+
({@link suiteRunPipedAfterRunner}).
|
|
1596
|
+
|
|
1597
|
+
```typescript
|
|
1598
|
+
function stripTrailingSuiteFilters(command: string): SuiteFilterStrip
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
- `command` — The shell command.
|
|
1602
|
+
|
|
1603
|
+
**Returns:** The rewritten command and the stages dropped.
|
|
1604
|
+
|
|
1605
|
+
#### `suiteRunPipedAfterRunner(command)`
|
|
1606
|
+
|
|
1607
|
+
Whether the command's last step runs a suite whose output still passes through a stage after
|
|
1608
|
+
the runner — one {@link stripTrailingSuiteFilters} could not prove is output-only (`| tee log`,
|
|
1609
|
+
`| xargs …`). The pipeline's exit status is that stage's, not the run's.
|
|
1610
|
+
|
|
1611
|
+
A command whose stages cannot be placed in the text (irregular whitespace inside the run) is
|
|
1612
|
+
reported as piped too: whether a pipe follows the runner is then UNKNOWN, and an unknown exit
|
|
1613
|
+
code that gets trusted is a failing run read as a pass — the failure this rule exists to stop.
|
|
1614
|
+
|
|
1615
|
+
```typescript
|
|
1616
|
+
function suiteRunPipedAfterRunner(command: string): boolean
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
- `command` — The shell command, after the streaming rewrite when one was applied.
|
|
1620
|
+
|
|
1621
|
+
**Returns:** True when a pipe this tool must not drop still follows the suite runner, or when the command runs a suite the parse cannot place.
|
|
1622
|
+
|
|
1250
1623
|
#### `suiteRunPriorityLaunch(command, launch)`
|
|
1251
1624
|
|
|
1252
1625
|
Start a background suite run at lower CPU and IO priority, so the sandbox it
|
|
@@ -1661,10 +2034,12 @@ function failureBlocks(s: string): string[]
|
|
|
1661
2034
|
The shell command whose stdout is the interesting part of a log that is still being written:
|
|
1662
2035
|
the failure lines so far (capped at {@link RUNNING_FAILURE_LINES}), then the end of the log.
|
|
1663
2036
|
|
|
1664
|
-
A Playwright `list` run prints one line per test as it finishes
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
2037
|
+
A Playwright `list` run prints one line per test as it finishes, so the LAST bytes of a running
|
|
2038
|
+
log are its least informative part — in X1 flash-m61 the last 3 000 bytes of a 139-test walk
|
|
2039
|
+
were the skip list, and all 61 failures were above them, which cost the run the grep it had to
|
|
2040
|
+
make anyway. A background run also carries each failure's own error as it fails
|
|
2041
|
+
({@link PLAYWRIGHT_FAILURES_INLINE_ENV}), and {@link RUNNING_FAILURE_LINE_RE} already matches
|
|
2042
|
+
those blocks' headers and their `Error:` lines, so this slice shows the causes too.
|
|
1668
2043
|
|
|
1669
2044
|
```typescript
|
|
1670
2045
|
function runningOutputSliceCommand(log: string, own?: string): string
|
|
@@ -1700,9 +2075,11 @@ function runningCauseCommand(log: string, own?: string): string
|
|
|
1700
2075
|
The cause repeating among a running suite's failures, or null.
|
|
1701
2076
|
|
|
1702
2077
|
Playwright's `list` reporter prints one line per test as it finishes — its mark, its title
|
|
1703
|
-
path and how long it ran — and
|
|
1704
|
-
|
|
1705
|
-
|
|
2078
|
+
path and how long it ran — and, on a run the platform started in the background, each failure's
|
|
2079
|
+
own error with it ({@link PLAYWRIGHT_FAILURES_INLINE_ENV}); a `list` run with no error bodies in
|
|
2080
|
+
its log (an older Playwright, a log the bodies have been cut from) carries only the duration as
|
|
2081
|
+
a signature. Where the log carries the failure bodies (either inline, or in a finished run's
|
|
2082
|
+
summary) the signature is the
|
|
1706
2083
|
error itself — {@link failureMessage}: the error class plus the locator or the values that
|
|
1707
2084
|
differ — which is stronger (it needs no assumption about how long a failure should take), so
|
|
1708
2085
|
the message is looked for first and the duration is then left with the tests whose bodies the
|
|
@@ -1715,8 +2092,8 @@ each was the test timeout and one missing `navigate('/')` was the cause of all o
|
|
|
1715
2092
|
|
|
1716
2093
|
The quorum is of DISTINCT tests — {@link REPEATING_FAILURE_MIN} of them — because a log that
|
|
1717
2094
|
prints each failure twice (a second reporter, a retry) had X1 flash-m83 told that four of its
|
|
1718
|
-
|
|
1719
|
-
|
|
2095
|
+
|
|
2096
|
+
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1720
2097
|
|
|
1721
2098
|
```typescript
|
|
1722
2099
|
function repeatingFailure(log: string): RepeatingFailure | null
|
|
@@ -1751,7 +2128,69 @@ function universalCause(log: string, found: RepeatingFailure): UniversalCause |
|
|
|
1751
2128
|
|
|
1752
2129
|
**Returns:** The cause's count and files, or null when the run is not failing whole.
|
|
1753
2130
|
|
|
1754
|
-
#### `
|
|
2131
|
+
#### `earlyReturnCause(found, universal)`
|
|
2132
|
+
|
|
2133
|
+
The ONE finding that ends a wait early, and the only one a note may hand a stop command over
|
|
2134
|
+
for: a PROVEN cause that is failing the whole run.
|
|
2135
|
+
|
|
2136
|
+
Both halves are load-bearing, each of them learned from a run that lost minutes to the other.
|
|
2137
|
+
|
|
2138
|
+
- **Failing the WHOLE run** is {@link universalCause}: every failure the log names, across
|
|
2139
|
+
{@link UNIVERSAL_CAUSE_FILES} spec files or more, with nothing passing since the first — the
|
|
2140
|
+
dont-stop-runs rule. X1 flash-m83 (2026-10-16) stopped eight local runs at their first
|
|
2141
|
+
repeating cause in 14.5 minutes and never finished one, and a run that still has other causes
|
|
2142
|
+
left to name is the only thing that will name them.
|
|
2143
|
+
- **A PROVEN cause** is an error repeated VERBATIM. A shared DURATION is a LEAD, never a cause:
|
|
2144
|
+
a suite whose config sets a timeout spends that timeout on every failure that waits for
|
|
2145
|
+
anything, so three unrelated tests timing out together look exactly like one broken run
|
|
2146
|
+
(`~7 s` is a property of the CONFIG, not of the cause — {@link normalizedSignature}). X1
|
|
2147
|
+
xr-molecule-flash-m99 (2026-10-16) was told `status: 'repeating_failure'` twice on that
|
|
2148
|
+
shape — three `url-navigation.spec.ts` tests at ~16.2 s, one `timeout: 15_000` plus teardown
|
|
2149
|
+
— read the early return as "stop waiting, edit now", and voided a run that was still walking;
|
|
2150
|
+
the second cost a 224 s wait that ended with no result at all.
|
|
2151
|
+
|
|
2152
|
+
Neither half alone is enough, and {@link repeatingFailure} finding a signature is not either:
|
|
2153
|
+
|
|
2154
|
+
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
2155
|
+
|
|
2156
|
+
```typescript
|
|
2157
|
+
function earlyReturnCause(
|
|
2158
|
+
found: RepeatingFailure,
|
|
2159
|
+
universal: UniversalCause | null | undefined,
|
|
2160
|
+
): UniversalCause | null
|
|
2161
|
+
```
|
|
2162
|
+
|
|
2163
|
+
- `found` — What {@link repeatingFailure} found mid-run.
|
|
2164
|
+
- `universal` — What {@link universalCause} found for it, if anything.
|
|
2165
|
+
|
|
2166
|
+
**Returns:** The cause to stop the run over, or null — in which case nothing may be stopped, the wait keeps going, and the finding rides the run's result as a note.
|
|
2167
|
+
|
|
2168
|
+
#### `matchingExcludedTitles(heading, excluded)`
|
|
2169
|
+
|
|
2170
|
+
The titles a request excludes that a test's heading names — the ONE matcher for "is this
|
|
2171
|
+
failure one the request does not count?".
|
|
2172
|
+
|
|
2173
|
+
The request quotes a test's own title (`… except "should require login to post comment"`,
|
|
2174
|
+
extracted by molecule-dev's `excludedTestTitles`) while a run's heading carries more of it
|
|
2175
|
+
(`comments.spec.ts › Comments › should require login to post comment`), so containment is the
|
|
2176
|
+
comparison. Whitespace is normalised and case is ignored: a brief may wrap the title across a
|
|
2177
|
+
line (`xr-molecule-flash-m44`), and the reporter's own casing never has to match the brief's.
|
|
2178
|
+
|
|
2179
|
+
Two readers used to answer this separately — the causes note (which therefore asked the
|
|
2180
|
+
executor to fix a test the same result said not to) and molecule-dev's exclusion notes — and
|
|
2181
|
+
xr-molecule-flash-m101 paid for the gap (+12:00: a turn and an edit spent on the excluded
|
|
2182
|
+
test).
|
|
2183
|
+
|
|
2184
|
+
```typescript
|
|
2185
|
+
function matchingExcludedTitles(heading: string, excluded: readonly string[]): string[]
|
|
2186
|
+
```
|
|
2187
|
+
|
|
2188
|
+
- `heading` — The text to match against: a test's title, its whole `file › describe › title` path, or (for a note that only has the run's output) the run's text.
|
|
2189
|
+
- `excluded` — The titles the request carves out, if the caller knows them.
|
|
2190
|
+
|
|
2191
|
+
**Returns:** The excluded titles this heading names, in the request's order.
|
|
2192
|
+
|
|
2193
|
+
#### `failuresByCause(log, excluded)`
|
|
1755
2194
|
|
|
1756
2195
|
Every failure a run names, grouped by the cause behind it — one group per cause, with the
|
|
1757
2196
|
tests it hit.
|
|
@@ -1761,21 +2200,28 @@ after another with no statement of which of them share a cause, so a run of 19 f
|
|
|
1761
2200
|
bodies to read; X1 flash-m83 (2026-10-16) spent three deploys (6.7 minutes) each fixing a
|
|
1762
2201
|
single cause, two of them found by runs stopped before they walked the rest.
|
|
1763
2202
|
|
|
1764
|
-
The signature is the cause itself where the log carries the failures' bodies (a finished run,
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
why a duration group is labelled as a duration and not as a
|
|
1768
|
-
printed twice, or failed twice, is one test.
|
|
2203
|
+
The signature is the cause itself where the log carries the failures' bodies (a finished run, a
|
|
2204
|
+
background `list` run — whose failures print as they fail — or a `line` reporter) —
|
|
2205
|
+
{@link failureMessage}: the error class plus the locator or the values that differ — and how long
|
|
2206
|
+
they took where it does not, which is why a duration group is labelled as a duration and not as a
|
|
2207
|
+
proven cause. A test the log printed twice, or failed twice, is one test.
|
|
2208
|
+
|
|
2209
|
+
A test the request EXCLUDES by name is not a cause to fix: its failure does not count for
|
|
2210
|
+
this request ({@link matchingExcludedTitles}), so it is dropped here rather than handed to the
|
|
2211
|
+
executor as work — a result that names it beside "it does not count" is the contradiction
|
|
2212
|
+
xr-molecule-flash-m101 filed (+12:00: the model spent a turn and an edit on the excluded test,
|
|
2213
|
+
on a page that was passing).
|
|
1769
2214
|
|
|
1770
2215
|
```typescript
|
|
1771
|
-
function failuresByCause(log: string): FailureCause[]
|
|
2216
|
+
function failuresByCause(log: string, excluded?: readonly string[]): FailureCause[]
|
|
1772
2217
|
```
|
|
1773
2218
|
|
|
1774
2219
|
- `log` — The log of a finished or running run.
|
|
2220
|
+
- `excluded` — The test titles the request carves out, if the caller knows them.
|
|
1775
2221
|
|
|
1776
2222
|
**Returns:** The causes, the one with the most tests first.
|
|
1777
2223
|
|
|
1778
|
-
#### `failureCausesNote(log)`
|
|
2224
|
+
#### `failureCausesNote(log, excluded, run)`
|
|
1779
2225
|
|
|
1780
2226
|
The one-line-per-cause summary of a finished run's failures, or null when the run named fewer
|
|
1781
2227
|
than two of them.
|
|
@@ -1784,15 +2230,26 @@ This is what makes a run worth finishing: every cause the run found, in the resu
|
|
|
1784
2230
|
executor reads, so one round of fixes covers all of them rather than the one cause a run
|
|
1785
2231
|
stopped early happened to reach.
|
|
1786
2232
|
|
|
2233
|
+
A test the request excludes is not one of the causes — it is dropped and named once, at the
|
|
2234
|
+
end, so a result never asks to fix a test it also says to ignore. Where the caller knows how
|
|
2235
|
+
the run was started, the note also hands over the narrowed re-run of exactly those tests
|
|
2236
|
+
({@link rerunOfRunningSuite}) instead of leaving the executor to re-run the whole suite.
|
|
2237
|
+
|
|
1787
2238
|
```typescript
|
|
1788
|
-
function failureCausesNote(
|
|
2239
|
+
function failureCausesNote(
|
|
2240
|
+
log: string,
|
|
2241
|
+
excluded?: readonly string[],
|
|
2242
|
+
run?: { command: string; cwd?: string; log?: string },
|
|
2243
|
+
): string | null
|
|
1789
2244
|
```
|
|
1790
2245
|
|
|
1791
2246
|
- `log` — The finished run's output.
|
|
2247
|
+
- `excluded` — The test titles the request carves out, if the caller knows them.
|
|
2248
|
+
- `run` — How the run was started, when the caller knows it: the narrowed re-run is built from `command`/`cwd`, and `log` (the path the output was written to) is what the "left out" sentence greps when more causes exist than the result has room for.
|
|
1792
2249
|
|
|
1793
2250
|
**Returns:** The note, or null.
|
|
1794
2251
|
|
|
1795
|
-
#### `rerunOfRunningSuite(run, locations)`
|
|
2252
|
+
#### `rerunOfRunningSuite(run, locations, excluded)`
|
|
1796
2253
|
|
|
1797
2254
|
The command that re-runs the tests at `locations` the way the run IN FLIGHT ran them: the same
|
|
1798
2255
|
invocation, in the same directory, narrowed to those tests — or null when the running command
|
|
@@ -1809,11 +2266,13 @@ the same app and database, and that is exactly what is refused while the run wal
|
|
|
1809
2266
|
function rerunOfRunningSuite(
|
|
1810
2267
|
run: { command: string; cwd?: string },
|
|
1811
2268
|
locations: readonly string[],
|
|
2269
|
+
excluded?: readonly string[],
|
|
1812
2270
|
): string | null
|
|
1813
2271
|
```
|
|
1814
2272
|
|
|
1815
2273
|
- `run` — The run in flight: the command it was started with, as it was run, and the directory that command ran in.
|
|
1816
2274
|
- `locations` — The failing `file:line`s to re-run, as the run's log printed them.
|
|
2275
|
+
- `excluded` — The test titles the request carves out: the re-run inverts them out (`--grep-invert`) so it cannot report them again.
|
|
1817
2276
|
|
|
1818
2277
|
**Returns:** The re-run command, or null when there is no suite to narrow.
|
|
1819
2278
|
|
|
@@ -1826,18 +2285,19 @@ what that function could actually see in the log — including the count, which
|
|
|
1826
2285
|
tests, so the note never names one or two tests as a cause.
|
|
1827
2286
|
|
|
1828
2287
|
The note names the cause early so the executor can START FIXING while the run keeps walking,
|
|
1829
|
-
and it
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
2288
|
+
and it hands a stop command over only when {@link earlyReturnCause} says so — the SAME
|
|
2289
|
+
predicate the wait uses to decide whether to return early, so a finding the wait keeps waiting
|
|
2290
|
+
on is a finding this note never asks to stop for. X1 flash-m76 (2026-10-16) spent 4.0 minutes
|
|
2291
|
+
walking 15 tests whose one cause was certain at the third, 47 s in, and the note answers
|
|
2292
|
+
exactly that; X1 flash-m83 then stopped eight runs at their first repeating cause in 14.5
|
|
2293
|
+
minutes and never finished one, so the note must not say to stop when the run still has other
|
|
2294
|
+
causes to name. X1 xr-molecule-flash-m99 (2026-10-16) is the third shape: a shared duration is
|
|
2295
|
+
not a cause at all, so a lead reaches the executor as a note on a result it is still waiting
|
|
2296
|
+
for — never as a verdict with a stop command in it.
|
|
1834
2297
|
|
|
1835
2298
|
The run is STILL WALKING while this note is written — both callers produce it mid-run — so a
|
|
1836
2299
|
re-run of one of its tests is a second run against the same app and database, and the platform
|
|
1837
2300
|
refuses that ({@link suiteRunInTheWay}, the same rule its overlap hold answers with):
|
|
1838
|
-
xr-molecule-flash-m89 (2026-10-16) read _"Start on them now, while the run keeps walking.
|
|
1839
|
-
`null-fields.spec.ts:53` alone reproduces the first one."_ and sent exactly that re-run 23 s
|
|
1840
|
-
later, where the hold answered *"A playwright suite is already running against this app and
|
|
1841
2301
|
|
|
1842
2302
|
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1843
2303
|
|
|
@@ -1855,7 +2315,7 @@ function repeatingFailureNote(
|
|
|
1855
2315
|
```
|
|
1856
2316
|
|
|
1857
2317
|
- `found` — What {@link repeatingFailure} found.
|
|
1858
|
-
- `o` — The run's facts: its `taskId`, its `log` path, `stop` — the shell command that stops it, printed only for
|
|
2318
|
+
- `o` — The run's facts: its `taskId`, its `log` path, `stop` — the shell command that stops it, printed only for an {@link earlyReturnCause} — `universal`, what {@link universalCause} found (absent means the run is not failing whole), and `run`, how the run was started ({@link rerunOfRunningSuite}) when the caller knows it.
|
|
1859
2319
|
|
|
1860
2320
|
**Returns:** The note.
|
|
1861
2321
|
|
|
@@ -1982,6 +2442,80 @@ per-request limit, with each request bounded. xr-molecule-flash-m93 (+06:22,
|
|
|
1982
2442
|
const SUITE_OVER_BUDGET_ADVICE: string
|
|
1983
2443
|
```
|
|
1984
2444
|
|
|
2445
|
+
#### `MAX_TRACE_ARTIFACTS`
|
|
2446
|
+
|
|
2447
|
+
How many traces one read reports, newest first.
|
|
2448
|
+
|
|
2449
|
+
```typescript
|
|
2450
|
+
const MAX_TRACE_ARTIFACTS: 12
|
|
2451
|
+
```
|
|
2452
|
+
|
|
2453
|
+
#### `MAX_TRACED_REQUESTS`
|
|
2454
|
+
|
|
2455
|
+
How many requests one test's trace contributes.
|
|
2456
|
+
|
|
2457
|
+
```typescript
|
|
2458
|
+
const MAX_TRACED_REQUESTS: 6
|
|
2459
|
+
```
|
|
2460
|
+
|
|
2461
|
+
#### `MAX_TRACED_CONSOLE`
|
|
2462
|
+
|
|
2463
|
+
How many console errors one test's trace contributes.
|
|
2464
|
+
|
|
2465
|
+
```typescript
|
|
2466
|
+
const MAX_TRACED_CONSOLE: 4
|
|
2467
|
+
```
|
|
2468
|
+
|
|
2469
|
+
#### `MAX_TRACE_NOTE_TESTS`
|
|
2470
|
+
|
|
2471
|
+
How many tests the note in a run's result names.
|
|
2472
|
+
|
|
2473
|
+
```typescript
|
|
2474
|
+
const MAX_TRACE_NOTE_TESTS: 5
|
|
2475
|
+
```
|
|
2476
|
+
|
|
2477
|
+
#### `MAX_TRACE_NOTE_REQUESTS`
|
|
2478
|
+
|
|
2479
|
+
How many requests the note names per test.
|
|
2480
|
+
|
|
2481
|
+
```typescript
|
|
2482
|
+
const MAX_TRACE_NOTE_REQUESTS: 4
|
|
2483
|
+
```
|
|
2484
|
+
|
|
2485
|
+
#### `TRACE_NOTE_URL_CHARS`
|
|
2486
|
+
|
|
2487
|
+
How long a URL the note prints, before the middle is cut.
|
|
2488
|
+
|
|
2489
|
+
```typescript
|
|
2490
|
+
const TRACE_NOTE_URL_CHARS: 96
|
|
2491
|
+
```
|
|
2492
|
+
|
|
2493
|
+
#### `DIAGNOSIS_TRACE_FLAG`
|
|
2494
|
+
|
|
2495
|
+
The `--trace` flag this platform adds, and the only value it adds it with.
|
|
2496
|
+
|
|
2497
|
+
```typescript
|
|
2498
|
+
const DIAGNOSIS_TRACE_FLAG: '--trace=retain-on-failure'
|
|
2499
|
+
```
|
|
2500
|
+
|
|
2501
|
+
#### `PLAYWRIGHT_FAILURES_INLINE_ENV`
|
|
2502
|
+
|
|
2503
|
+
Playwright's switch for printing each failure's own error as that test fails, instead of only in
|
|
2504
|
+
the summary at the end: `list` reads it in its constructor (`ListReporterOptions.printFailuresInline`,
|
|
2505
|
+
`playwright/lib/reporters/list.js` — 1.63, the version the toolchain pins), and when it prints
|
|
2506
|
+
inline the end-of-run repeat is suppressed with it (`epilogue(!printFailuresInline)`), so one
|
|
2507
|
+
reporter still prints each failure exactly once.
|
|
2508
|
+
|
|
2509
|
+
A background suite is put on `list` (see {@link streamingBackgroundCommand}) and this with it, so
|
|
2510
|
+
the log names its causes WHILE it walks. Without it the log's only failure lines are `✘` rows:
|
|
2511
|
+
X1 xr-molecule-flash-m99 (+19:27 → +21:00, 2026-10-16) was told to `grep -n -A 20 '<test name>'`
|
|
2512
|
+
the log, got nothing back through four greps, inferred causes from the spec prose instead, and
|
|
2513
|
+
edited `app/src` while the run was still walking — which voids the run.
|
|
2514
|
+
|
|
2515
|
+
```typescript
|
|
2516
|
+
const PLAYWRIGHT_FAILURES_INLINE_ENV: 'PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE'
|
|
2517
|
+
```
|
|
2518
|
+
|
|
1985
2519
|
#### `MAX_READ_SIZE`
|
|
1986
2520
|
|
|
1987
2521
|
Max file size for read_file (5MB).
|
|
@@ -2217,12 +2751,39 @@ How many of a repeating cause's failures the note lists before it says "and N mo
|
|
|
2217
2751
|
const REPEATING_FAILURE_LISTED: 5
|
|
2218
2752
|
```
|
|
2219
2753
|
|
|
2754
|
+
#### `TOOL_RESULT_CHARS`
|
|
2755
|
+
|
|
2756
|
+
The platform's cap on one tool result's text: `MAX_TOOL_OUTPUT_CHARS` in molecule-dev's
|
|
2757
|
+
`api/src/ai/chat-handler.ts`, which cuts a longer string field's MIDDLE. A finished run's
|
|
2758
|
+
stdout is cut to it, so the cause list must not depend on reading the log back — and must
|
|
2759
|
+
itself fit inside it, or the cut lands in the list.
|
|
2760
|
+
|
|
2761
|
+
```typescript
|
|
2762
|
+
const TOOL_RESULT_CHARS: 8000
|
|
2763
|
+
```
|
|
2764
|
+
|
|
2765
|
+
#### `FAILURE_CAUSES_CHARS`
|
|
2766
|
+
|
|
2767
|
+
The characters a finished run's cause list may use of {@link TOOL_RESULT_CHARS}. The recap
|
|
2768
|
+
measured by xr-molecule-flash-m102 (+12:43) was `12 failures, 8 causes`: one cause line is
|
|
2769
|
+
~200 chars and ~80 more with its two body lines, so 8 causes is ~2.3 k against the 8 k cap —
|
|
2770
|
+
the old 5-cause cap withheld 3 causes that the same result had room for.
|
|
2771
|
+
|
|
2772
|
+
The rest of the cap is left to the header, the narrowed re-run command and the exclusion
|
|
2773
|
+
line, which ride the same note.
|
|
2774
|
+
|
|
2775
|
+
```typescript
|
|
2776
|
+
const FAILURE_CAUSES_CHARS: number
|
|
2777
|
+
```
|
|
2778
|
+
|
|
2220
2779
|
#### `FAILURE_CAUSES_LISTED`
|
|
2221
2780
|
|
|
2222
|
-
How many causes a finished run's summary lists
|
|
2781
|
+
How many causes a finished run's summary lists at most. The character budget above is the
|
|
2782
|
+
bound that must hold (it is what keeps the note on the surviving side of the tool-result
|
|
2783
|
+
cut); this is the bound past which a list stops being read at all.
|
|
2223
2784
|
|
|
2224
2785
|
```typescript
|
|
2225
|
-
const FAILURE_CAUSES_LISTED:
|
|
2786
|
+
const FAILURE_CAUSES_LISTED: 12
|
|
2226
2787
|
```
|
|
2227
2788
|
|
|
2228
2789
|
#### `FAILURE_CAUSE_TESTS_LISTED`
|