@molecule/api-ai-tools 1.7.0 → 1.8.2

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-17T00:30:38.264Z
6
+ Generated: 2026-10-17T02:50:55.123Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -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
- /** How many files or tests the runner's own output shows finishing, or null when it shows none. */
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
- /** How many the run was walking, when the runner printed it (Playwright's `[n/K]`), or null. */
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'
@@ -376,6 +448,20 @@ interface ToolBuildConfig {
376
448
  */
377
449
  outlineLargeHtml?: () => boolean
378
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
+
379
465
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
380
466
  onAfterWrite?: (path: string) => Promise<void>
381
467
 
@@ -454,6 +540,19 @@ interface PromptContext {
454
540
  }
455
541
  ```
456
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
+
457
556
  #### `FailedTest`
458
557
 
459
558
  One failed test, as a running suite's log names it.
@@ -514,6 +613,12 @@ interface FailureCause {
514
613
  seconds: number | null
515
614
  /** The distinct tests it hit, in the order the log named them. */
516
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[]
517
622
  }
518
623
  ```
519
624
 
@@ -532,7 +637,17 @@ type CommandRunState = 'ran' | typeof CUT_AT_BUDGET
532
637
  What stopped a run.
533
638
 
534
639
  ```typescript
535
- type CutBy = 'tool-budget' | 'command-timeout'
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'
536
651
  ```
537
652
 
538
653
  #### `DiscoveredSkill` _(deprecated)_
@@ -608,6 +723,20 @@ function commandSegments(command: string): CommandSegment[]
608
723
 
609
724
  **Returns:** One entry per statement or pipeline stage that runs something, in order.
610
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
+
611
740
  #### `commandTimeoutSeconds(command)`
612
741
 
613
742
  The bound a command puts on its own run: the largest `timeout <N>` any of its segments runs
@@ -662,9 +791,29 @@ function commandRunState(result: unknown): CommandRunState
662
791
 
663
792
  **Returns:** The state the result records.
664
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
+
665
811
  #### `suiteTally(text)`
666
812
 
667
- Whether text carries a runner's closing summary ({@link SUITE_TALLY}).
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.
668
817
 
669
818
  ```typescript
670
819
  function suiteTally(text: string): boolean
@@ -674,13 +823,39 @@ function suiteTally(text: string): boolean
674
823
 
675
824
  **Returns:** True when the runner's own end-of-run summary is there.
676
825
 
677
- #### `commandCutRun(command, result, elapsedMs, budgetSeconds)`
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)`
678
850
 
679
851
  The run a command's result records as stopped by a budget, or null when the result is a run that
680
852
  ended (or says nothing about a run).
681
853
 
682
854
  Evidence, in order — never a guess:
683
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}).
684
859
  - `timeout`'s own 124 (the code the backend's wrapper returns whenever the TOOL's budget fired,
685
860
  whatever the pipeline ended with) — and, when the clock says the command's own smaller bound
686
861
  ran out first and less than the budget, that bound instead.
@@ -689,6 +864,8 @@ Evidence, in order — never a guess:
689
864
  killed at its own bound. Its exit code, when the pipeline handed one back, is the filter's.
690
865
  - the run used this tool's whole budget behind a filter that withheld its output, and printed no
691
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`).
692
869
 
693
870
  ```typescript
694
871
  function commandCutRun(
@@ -696,6 +873,7 @@ function commandCutRun(
696
873
  result: unknown,
697
874
  elapsedMs: number,
698
875
  budgetSeconds?: number,
876
+ rawOutput?: string,
699
877
  ): CutRun | null
700
878
  ```
701
879
 
@@ -703,6 +881,7 @@ function commandCutRun(
703
881
  - `result` — Its result.
704
882
  - `elapsedMs` — How long the command ran, in ms.
705
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`.
706
885
 
707
886
  **Returns:** The stopped run, or null.
708
887
 
@@ -726,12 +905,14 @@ function cutRunFacts(result: unknown): CutRun | null
726
905
 
727
906
  #### `cutRunSentence(run)`
728
907
 
729
- The first line of a stopped run's result: how long it was allowed, what stopped it, how far it
730
- got, and the last thing it printed.
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.
731
910
 
732
911
  Written to be quotable by the model in one sentence and to leave no room for "nearly passing":
733
912
  the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
734
- read elsewhere (the suite's own file count) fills in the "M of K" it can then state.
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.
735
916
 
736
917
  ```typescript
737
918
  function cutRunSentence(run: CutRun): string
@@ -880,7 +1061,7 @@ function suiteCommandTarget(command: string): string
880
1061
 
881
1062
  **Returns:** The target key.
882
1063
 
883
- #### `scopedRerunCommand(command, locations)`
1064
+ #### `scopedRerunCommand(command, locations, excluded)`
884
1065
 
885
1066
  The whole-suite command made scoped to the tests it should re-run instead: the same
886
1067
  invocation — same directory, same settings — with the named `file:line`s in place of the whole
@@ -888,11 +1069,16 @@ suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flas
888
1069
  `--retries=0` set (a re-run of a known failure does not need to pay for retries).
889
1070
 
890
1071
  ```typescript
891
- function scopedRerunCommand(command: string, locations: readonly string[]): string
1072
+ function scopedRerunCommand(
1073
+ command: string,
1074
+ locations: readonly string[],
1075
+ excluded?: readonly string[],
1076
+ ): string
892
1077
  ```
893
1078
 
894
1079
  - `command` — The whole-suite run's command.
895
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.
896
1082
 
897
1083
  **Returns:** The scoped command, or the command unchanged when it runs no Playwright suite.
898
1084
 
@@ -1331,19 +1517,19 @@ killed (X1 run flash-m15 lost 20 of its 60 minutes this way, 2026-10-15):
1331
1517
 
1332
1518
  - a trailing `| tail -N`, which holds everything back until the command ends.
1333
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).
1334
1525
  - `playwright test` with no `--reporter`, which runs the config's reporter —
1335
1526
  often `html`, which prints nothing per test. `--reporter=list` is added
1336
- when the playwright run is the command's last step and nothing is piped or
1337
- redirected from it: it writes each result as it finishes, and it is the
1338
- 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.
1339
1529
 
1340
1530
  The run ends up with exactly ONE reporter that prints each failure — `list`. A run that names
1341
1531
  any other printing reporter has it REPLACED, never joined: two of them write every test result
1342
1532
  into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,line` run (its
1343
- `line` added by this function one run earlier) report two distinct failures as "4 of this run's
1344
- tests have already failed the same way", and the executor stopped a 139-test walk at test 21
1345
- with a single cause in hand. `line` alone is no use either: it writes `[n/N] <title>` with no
1346
- ✓/✘ mark and no duration, so on X1 flash-m62 an 8.3-minute walk of one suite could never trip
1347
1533
 
1348
1534
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
1349
1535
 
@@ -1393,6 +1579,47 @@ function isPlaywrightRunCommand(command: string): boolean
1393
1579
 
1394
1580
  **Returns:** True when the command starts a Playwright run.
1395
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
+
1396
1623
  #### `suiteRunPriorityLaunch(command, launch)`
1397
1624
 
1398
1625
  Start a background suite run at lower CPU and IO priority, so the sandbox it
@@ -1938,7 +2165,32 @@ function earlyReturnCause(
1938
2165
 
1939
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.
1940
2167
 
1941
- #### `failuresByCause(log)`
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)`
1942
2194
 
1943
2195
  Every failure a run names, grouped by the cause behind it — one group per cause, with the
1944
2196
  tests it hit.
@@ -1954,15 +2206,22 @@ background `list` run — whose failures print as they fail — or a `line` repo
1954
2206
  they took where it does not, which is why a duration group is labelled as a duration and not as a
1955
2207
  proven cause. A test the log printed twice, or failed twice, is one test.
1956
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).
2214
+
1957
2215
  ```typescript
1958
- function failuresByCause(log: string): FailureCause[]
2216
+ function failuresByCause(log: string, excluded?: readonly string[]): FailureCause[]
1959
2217
  ```
1960
2218
 
1961
2219
  - `log` — The log of a finished or running run.
2220
+ - `excluded` — The test titles the request carves out, if the caller knows them.
1962
2221
 
1963
2222
  **Returns:** The causes, the one with the most tests first.
1964
2223
 
1965
- #### `failureCausesNote(log)`
2224
+ #### `failureCausesNote(log, excluded, run)`
1966
2225
 
1967
2226
  The one-line-per-cause summary of a finished run's failures, or null when the run named fewer
1968
2227
  than two of them.
@@ -1971,15 +2230,26 @@ This is what makes a run worth finishing: every cause the run found, in the resu
1971
2230
  executor reads, so one round of fixes covers all of them rather than the one cause a run
1972
2231
  stopped early happened to reach.
1973
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
+
1974
2238
  ```typescript
1975
- function failureCausesNote(log: string): string | null
2239
+ function failureCausesNote(
2240
+ log: string,
2241
+ excluded?: readonly string[],
2242
+ run?: { command: string; cwd?: string; log?: string },
2243
+ ): string | null
1976
2244
  ```
1977
2245
 
1978
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.
1979
2249
 
1980
2250
  **Returns:** The note, or null.
1981
2251
 
1982
- #### `rerunOfRunningSuite(run, locations)`
2252
+ #### `rerunOfRunningSuite(run, locations, excluded)`
1983
2253
 
1984
2254
  The command that re-runs the tests at `locations` the way the run IN FLIGHT ran them: the same
1985
2255
  invocation, in the same directory, narrowed to those tests — or null when the running command
@@ -1996,11 +2266,13 @@ the same app and database, and that is exactly what is refused while the run wal
1996
2266
  function rerunOfRunningSuite(
1997
2267
  run: { command: string; cwd?: string },
1998
2268
  locations: readonly string[],
2269
+ excluded?: readonly string[],
1999
2270
  ): string | null
2000
2271
  ```
2001
2272
 
2002
2273
  - `run` — The run in flight: the command it was started with, as it was run, and the directory that command ran in.
2003
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.
2004
2276
 
2005
2277
  **Returns:** The re-run command, or null when there is no suite to narrow.
2006
2278
 
@@ -2479,12 +2751,39 @@ How many of a repeating cause's failures the note lists before it says "and N mo
2479
2751
  const REPEATING_FAILURE_LISTED: 5
2480
2752
  ```
2481
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
+
2482
2779
  #### `FAILURE_CAUSES_LISTED`
2483
2780
 
2484
- How many causes a finished run's summary lists before it says "and N more".
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.
2485
2784
 
2486
2785
  ```typescript
2487
- const FAILURE_CAUSES_LISTED: 5
2786
+ const FAILURE_CAUSES_LISTED: 12
2488
2787
  ```
2489
2788
 
2490
2789
  #### `FAILURE_CAUSE_TESTS_LISTED`
@@ -58,6 +58,48 @@ export declare function durationSeconds(word: string): number | null;
58
58
  * @returns One entry per statement or pipeline stage that runs something, in order.
59
59
  */
60
60
  export declare function commandSegments(command: string): CommandSegment[];
61
+ /**
62
+ * One statement of a shell command, with where it sits and the variables it sets for itself.
63
+ *
64
+ * `commandSegments` answers WHAT runs; this answers where, and what the shell binds to it — the
65
+ * two things a caller that WRITES into a command needs. The platform composes a run from the
66
+ * executor's own command in more than one place, and a value written for the runner belongs in
67
+ * the runner's own statement: `APP_BASE=https://… cd app && npx playwright test` sets the variable
68
+ * for `cd` alone, so the Playwright config falls back to the local app while the command line
69
+ * still names the deployed host (xr-molecule-flash-m103 +28:08).
70
+ */
71
+ export interface LocatedCommandSegment {
72
+ /**
73
+ * What the statement runs, or null when it only sets variables (`BASE=http://…` on its own):
74
+ * that shape persists in the shell, so a caller following assignments across `;`/`&&` has to
75
+ * see it.
76
+ */
77
+ segment: CommandSegment | null;
78
+ /** The statement's own leading `NAME=value` words, as written (`TEST_MODE=fullstack`). */
79
+ assignments: readonly string[];
80
+ /**
81
+ * The offset in the command of the statement's first word, or null for a segment inside a
82
+ * nested `sh -c '<command>'` string — its positions are offsets in THAT string, not in this
83
+ * command, so nothing may be spliced at them.
84
+ */
85
+ start: number | null;
86
+ /**
87
+ * The offset in the command just before the word the statement runs — after its own leading
88
+ * `NAME=value` words and their whitespace. A `NAME=value` written here is in the environment of
89
+ * the program the statement runs (`… TEST_MODE=fullstack <here>npx playwright test`). Null
90
+ * inside a nested `sh -c` string, for the same reason as {@link start}.
91
+ */
92
+ assignmentOffset: number | null;
93
+ }
94
+ /**
95
+ * Every statement of a command, with its own assignments and where it sits — assignment-only
96
+ * statements included, because the variables those persist are exactly what a later statement of
97
+ * the same command reads.
98
+ *
99
+ * @param command - The shell command.
100
+ * @returns One entry per statement or pipeline stage, in order.
101
+ */
102
+ export declare function locatedCommandSegments(command: string): LocatedCommandSegment[];
61
103
  /**
62
104
  * The bound a command puts on its own run: the largest `timeout <N>` any of its segments runs
63
105
  * under (`timeout 280 hurl --test …` → 280), or null when it carries none.
@@ -1 +1 @@
1
- {"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAmFD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAyPD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAEjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAoCD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
1
+ {"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AA2MD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAkVD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAqDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}