@molecule/api-ai-tools 1.14.4 → 1.14.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.
Files changed (45) hide show
  1. package/README.md +236 -190
  2. package/dist/browser-probe.d.ts +4 -4
  3. package/dist/browser-probe.d.ts.map +1 -1
  4. package/dist/browser-probe.js +5 -5
  5. package/dist/command-position.d.ts +15 -16
  6. package/dist/command-position.d.ts.map +1 -1
  7. package/dist/command-position.js +21 -23
  8. package/dist/cut-run.d.ts +9 -9
  9. package/dist/cut-run.js +6 -6
  10. package/dist/failure-kind.d.ts +6 -6
  11. package/dist/failure-kind.js +6 -6
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +2 -2
  14. package/dist/inline-script.d.ts +5 -5
  15. package/dist/inline-script.js +6 -6
  16. package/dist/line-diff.d.ts +2 -3
  17. package/dist/line-diff.d.ts.map +1 -1
  18. package/dist/line-diff.js +2 -3
  19. package/dist/no-test-run.d.ts +5 -5
  20. package/dist/no-test-run.d.ts.map +1 -1
  21. package/dist/no-test-run.js +9 -8
  22. package/dist/output-filter.d.ts +2 -2
  23. package/dist/output-filter.js +2 -2
  24. package/dist/pipeline-exit-note.d.ts +1 -1
  25. package/dist/pipeline-exit-note.js +1 -1
  26. package/dist/playwright-report.d.ts +1 -1
  27. package/dist/playwright-report.js +1 -1
  28. package/dist/server-command.d.ts +2 -2
  29. package/dist/server-command.js +2 -2
  30. package/dist/sleep-peek.d.ts +1 -1
  31. package/dist/sleep-peek.js +1 -1
  32. package/dist/suite-overlap.d.ts +24 -25
  33. package/dist/suite-overlap.d.ts.map +1 -1
  34. package/dist/suite-overlap.js +41 -43
  35. package/dist/tools.d.ts +26 -28
  36. package/dist/tools.d.ts.map +1 -1
  37. package/dist/tools.js +98 -69
  38. package/dist/trace-artifacts.d.ts +64 -65
  39. package/dist/trace-artifacts.d.ts.map +1 -1
  40. package/dist/trace-artifacts.js +73 -75
  41. package/dist/types.d.ts +5 -5
  42. package/dist/utilities.d.ts +107 -91
  43. package/dist/utilities.d.ts.map +1 -1
  44. package/dist/utilities.js +191 -143
  45. package/package.json +2 -2
package/dist/utilities.js CHANGED
@@ -455,7 +455,7 @@ export function outputIsWithheldUntilExit(command) {
455
455
  * The file a command sends its own output to, when it redirects it (`> out.txt`,
456
456
  * `>> log`, `&> run.log`, `| tee [-a] file`), or null. A background command that does
457
457
  * this leaves its task log empty, so the log is the wrong place to look and an empty
458
- * log proves nothing (X1 flash-m29).
458
+ * log proves nothing.
459
459
  *
460
460
  * Which redirect counts is decided by the command's own shell structure, from the same
461
461
  * parse every other command reader uses: a redirect written in a BACKGROUNDED statement
@@ -463,7 +463,7 @@ export function outputIsWithheldUntilExit(command) {
463
463
  * the command's. The platform's own preview launch is exactly that — the preview server is
464
464
  * started with `>/tmp/mol-preview-4173.log 2>&1 &` inside the build/serve prelude, and the
465
465
  * suite's foreground command follows it — so a reader that took the last `>` in the whole
466
- * text told a run its output went to the PREVIEW's log (X1 flash-m136 +18:31).
466
+ * text tells a run its output went to the PREVIEW's log.
467
467
  *
468
468
  * The target comes back without its shell quotes: `> '/tmp/x.log'` is `/tmp/x.log`, and
469
469
  * the callers resolve a relative one against the command's cwd.
@@ -531,8 +531,8 @@ const PRINTING_REPORTERS = new Set(['list', 'line', 'dot', 'basic', 'spec']);
531
531
  * Every background command is given this (see {@link streamingBackgroundCommand}), so a suite the
532
532
  * executor starts through its own wrapper is covered too — the wrapper passes the environment on
533
533
  * to the Playwright process it spawns. Without it the log's only failure lines are `✘` rows:
534
- * X1 xr-molecule-flash-m99 (+19:27 → +21:00, 2026-10-16) was told to `grep -n -A 20 '<test name>'`
535
- * the log, got nothing back through four greps, inferred causes from the spec prose instead, and
534
+ * A run told to `grep -n -A 20 '<test name>'` the log got nothing back through four greps,
535
+ * inferred causes from the spec prose instead, and
536
536
  * edited `app/src` while the run was still walking — which voids the run.
537
537
  */
538
538
  export const PLAYWRIGHT_FAILURES_INLINE_ENV = 'PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE';
@@ -560,7 +560,7 @@ const STATUS_EXIT_ARITHMETIC = /^[A-Za-z0-9_|&+\-*/%^<>=!~() \t]*$/;
560
560
  * `npx playwright test … > /tmp/pw.log 2>&1; echo EXIT $?` — and reading the split's last part as
561
561
  * the run withheld the reporter, the inline-failure switch and `--retries=0` from every one of
562
562
  * them: the log then carried `✘` rows and nothing else while the wait note promised each failure's
563
- * error under them (xr-molecule-flash-m173 +17:07, +39:14). Whitespace and a group's closing
563
+ * error under them. Whitespace and a group's closing
564
564
  * punctuation are not part of the step; {@link streamingBackgroundCommand} strips those before
565
565
  * asking, and strips them here too for the split it is given.
566
566
  *
@@ -570,7 +570,7 @@ const STATUS_EXIT_ARITHMETIC = /^[A-Za-z0-9_|&+\-*/%^<>=!~() \t]*$/;
570
570
  * browser half as a status step is what lets the run keep its reporter, its machine report and the
571
571
  * inline-failure switch — without it, every platform proof ran its browser suite on the request's
572
572
  * own `line` reporter, which writes no ✓/✘ mark and no per-failure error into the log the wait
573
- * reads (xr-molecule-flash-x03 +27:44).
573
+ * reads.
574
574
  *
575
575
  * Only the two shapes whose whole value is the shell's own status are read this way; anything that
576
576
  * could run — a command substitution, a backticked word, a suffix after the status — stays a step.
@@ -596,7 +596,7 @@ function isStatusOnlyStep(step) {
596
596
  * Rewrite a command about to run in the background so its log grows while it
597
597
  * runs. Two shapes left the log empty until exit, and an empty log is what
598
598
  * `wait_for_task` reads as "stuck", so working suites were reported stuck and
599
- * killed (X1 run flash-m15 lost 20 of its 60 minutes this way, 2026-10-15):
599
+ * killed (one run lost 20 of its 60 minutes this way):
600
600
  *
601
601
  * - a trailing `| tail -N`, which holds everything back until the command ends.
602
602
  * It is dropped: `wait_for_task` already returns the end of the log.
@@ -604,7 +604,7 @@ function isStatusOnlyStep(step) {
604
604
  * failing test's own error and leaves the pipeline's exit status — the filter's — as the run's.
605
605
  * Every such stage is dropped from EVERY suite run in the command, not only the last
606
606
  * ({@link stripTrailingSuiteFilters}); `wait_for_task` already returns the end of the full log,
607
- * and a run whose tally shows a failure must never report 0 (xr-molecule-flash-m101 +25:54).
607
+ * and a run whose tally shows a failure must never report 0.
608
608
  * - `playwright test` with no `--reporter`, which runs the config's reporter —
609
609
  * often `html`, which prints nothing per test. `--reporter=list` is added
610
610
  * when the playwright run is the command's last step, piped or not: it writes each result as
@@ -614,11 +614,11 @@ function isStatusOnlyStep(step) {
614
614
  *
615
615
  * The run ends up with exactly ONE reporter that prints each failure — `list`. A run that names
616
616
  * any other printing reporter has it REPLACED, never joined: two of them write every test result
617
- * into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,line` run (its
617
+ * into the log twice, which made a `--reporter=list,line` run (its
618
618
  * `line` added by this function one run earlier) report two distinct failures as "4 of this run's
619
619
  * tests have already failed the same way", and the executor stopped a 139-test walk at test 21
620
620
  * with a single cause in hand. `line` alone is no use either: it writes `[n/N] <title>` with no
621
- * ✓/✘ mark and no duration, so on X1 flash-m62 an 8.3-minute walk of one suite could never trip
621
+ * ✓/✘ mark and no duration, so an 8.3-minute walk of one suite could never trip
622
622
  * the fail-fast note. A reporter set that writes a machine format to a file (`json`, `html`,
623
623
  * `junit`) is the
624
624
  * executor's own and is left exactly as it is — so is any reporter this build does not know.
@@ -627,8 +627,8 @@ function isStatusOnlyStep(step) {
627
627
  * {@link PLAYWRIGHT_FAILURES_INLINE_ENV} (inert except for Playwright's `list` reporter), so each
628
628
  * failure's own error is in the log as that test fails rather than only at the end: a suite that is
629
629
  * still walking is exactly when the executor reads the log, and without it a running log carries
630
- * `✘` rows and nothing else (xr-molecule-flash-m99 spent ~90 s on greps of one, then fixed what it
631
- * guessed; m191's wrapper-launched run lost 6.5 minutes to the same shape).
630
+ * `✘` rows and nothing else (~90 s went to greps of one, then to fixing what was
631
+ * guessed; a wrapper-launched run lost 6.5 minutes to the same shape).
632
632
  *
633
633
  * Only the step that runs the suite is touched, and only its reporter, and only when that step
634
634
  * runs something: `npx playwright test --list` takes two seconds and writes nothing per test, so
@@ -639,9 +639,8 @@ function isStatusOnlyStep(step) {
639
639
  *
640
640
  * A command that starts the suite MORE THAN ONCE is not launched at all: every run it carries is
641
641
  * a whole walk of the same suite against the same app, and only the last step is rewritten, so the
642
- * earlier walks run unmeasured for nothing. X1 xr-molecule-flash-m157 (+14:02) chained two whole
643
- * walks of one 138-test suite with `;` — 4.5 minutes each — and the second measured nothing the
644
- * first had not. The platform refuses a second CONCURRENT run against one app and database by the
642
+ * earlier walks run unmeasured for nothing. Two whole walks of one 138-test suite chained with
643
+ * `;` — 4.5 minutes each — measured nothing the first had not. The platform refuses a second CONCURRENT run against one app and database by the
645
644
  * ONE rule ({@link suiteRunInTheWay}) — for every pair, whatever uid a run mints — and this is the
646
645
  * same hazard inside one command ({@link suiteRunStatements}). `error` then carries the refusal,
647
646
  * naming each suite step and the ONE command to send instead (the last suite step, rewritten as
@@ -652,8 +651,8 @@ function isStatusOnlyStep(step) {
652
651
  * excludes by name, and whether this command IS the platform's graded proof
653
652
  * ({@link DiagnosisRunContext}). A `--grep-invert` of exactly those exclusions is the
654
653
  * request's own scope, not a diagnosis, so it keeps the suite's retries on a deployed proof
655
- * (xr-molecule-flash-m170 +33:16) — and a proof the platform itself composed and launched
656
- * keeps them whatever its scope, targeted set included (xr-molecule-flash-l48 +26:35).
654
+ * — and a proof the platform itself composed and launched
655
+ * keeps them whatever its scope, targeted set included.
657
656
  * @param reportPath - Where this run should write Playwright's standard `json` report, when the
658
657
  * caller keeps one ({@link ./playwright-report.js}). The reporter is added to the run's own
659
658
  * reporter set, never in place of one, and its output goes to that file through
@@ -755,8 +754,8 @@ export function streamingBackgroundCommand(command, context, reportPath) {
755
754
  * (`| tee run.log`), and a statement can end on a group closer (`( … )`, `{ …; }`) or a
756
755
  * redirection (`… 2>&1`). Appending after the `)` of a subshell was a shell syntax error that
757
756
  * killed the run at once (`sh: Syntax error: word unexpected`) and pushed the model off the
758
- * platform's runner shape (xr-molecule-flash-m129 +34:29); appending after a pipe hands the
759
- * word to the filter (m102's dangling `--trace` flag, in the same shape).
757
+ * platform's runner shape; appending after a pipe hands the word to the filter (a dangling
758
+ * `--trace` flag, in the same shape).
760
759
  *
761
760
  * The suite's own segment is found wherever it sits ({@link lastSuiteRun}), because a trailing
762
761
  * status step is not another command's output: in `… > /tmp/pw.log 2>&1; echo EXIT $?` the run
@@ -928,7 +927,7 @@ export function streamingBackgroundCommand(command, context, reportPath) {
928
927
  * reporter.
929
928
  *
930
929
  * A compound command's earlier walk is not a preview: it writes the same log the executor reads,
931
- * and on X1 xr-molecule-flash-l21 (+21:27) the first 3.6-minute walk kept its own
930
+ * and there the first 3.6-minute walk keeps its own
932
931
  * `--reporter=line`, which prints `[n/N] <title>` rows with no ✓/✘ mark and no per-failure error.
933
932
  * What came back was `72 passed` and two failure NAMES; the error bodies existed only in the walk's
934
933
  * discarded log, and diagnosing them needed another run.
@@ -1137,7 +1136,7 @@ export function isSuiteRunCommand(command) {
1137
1136
  * Refuse only a command made entirely of identical suite invocations.
1138
1137
  *
1139
1138
  * Mixed shell commands execute as written. Rebuilding their prefix or suffix can lose writes,
1140
- * break heredoc delimiters or turn an && guard into unconditional work (m277). Different args
1139
+ * break heredoc delimiters or turn an && guard into unconditional work. Different args
1141
1140
  * or environments may select different suites or targets; they are not known duplicates either.
1142
1141
  * This is an optimization, so uncertainty always allows the original command.
1143
1142
  *
@@ -1197,8 +1196,8 @@ function suiteRunnerArguments(command) {
1197
1196
  * flags, and the flags that could not be placed.
1198
1197
  *
1199
1198
  * The grader walks the suite under its OWN budget, so a run whose runner config sets a different
1200
- * timeout or worker count measures a run the grader never sees (xr-molecule-flash-m169: a 60 s
1201
- * wrapper config produced a green local proof the official 15 s grader then failed). Playwright
1199
+ * timeout or worker count measures a run the grader never sees (a 60 s wrapper config produced
1200
+ * a green local proof the official 15 s grader then failed). Playwright
1202
1201
  * lets a command-line flag win over every config file, so the suite's values are put on the
1203
1202
  * runner's own arguments — written where {@link statementArgumentEnd} places them, never after a
1204
1203
  * pipe, redirect or group closer. Only the command's LAST suite step is touched: that is the run
@@ -1312,7 +1311,7 @@ export function suiteRunConfigCommand(command, config) {
1312
1311
  * Used where the platform moves a run to the background: the executor wrote the bound to fit this
1313
1312
  * tool's foreground ceiling, and in the background that ceiling no longer applies — the task is
1314
1313
  * bounded by `wait_for_task` and the turn's deadline — so the prefix only kills the walk
1315
- * ({@link withoutTimeoutWrapper}; X1 xr-molecule-flash-m143 +24:45).
1314
+ * ({@link withoutTimeoutWrapper}).
1316
1315
  *
1317
1316
  * @param command - The shell command.
1318
1317
  * @returns The rewritten command, and the largest lifted bound (null when nothing was lifted).
@@ -1325,7 +1324,7 @@ export function withoutSuiteTimeout(command) {
1325
1324
  * that runs something.
1326
1325
  *
1327
1326
  * `npx playwright test --list` matched every `\bplaywright\s+test\b` test in the platform and was
1328
- * then treated as a run: held (flash-m79's config check), backgrounded for a run that outlasts
1327
+ * then treated as a run: held (a config check), backgrounded for a run that outlasts
1329
1328
  * the tool's ceiling, and rewritten with `--reporter`. A list takes two seconds, prints its lines
1330
1329
  * and exits, so it is none of those; {@link commandRunsNoTests} is what decides.
1331
1330
  *
@@ -1378,8 +1377,7 @@ const OUTPUT_REDIRECT = /^(?:\d*|&)>>?(?!&)/;
1378
1377
  * searching the command for the re-joined text: the words are joined back with single spaces for
1379
1378
  * matching, so `command.indexOf(segment.text)` comes up empty on irregular spacing, on a grouping
1380
1379
  * brace, or on anything a heredoc body shifts — and the caller then cut at a guess or refused to
1381
- * cut at all (xr-molecule-flash-m105 +08:12, where a heredoc body turned a finished run into
1382
- * "piped" for the rest of the session).
1380
+ * cut at all (a heredoc body turned a finished run into "piped" for the rest of the session).
1383
1381
  *
1384
1382
  * A statement inside a nested `sh -c '<command>'` string is listed with null offsets: its
1385
1383
  * positions are positions in THAT string, so nothing may be spliced at them — but its separator
@@ -1456,7 +1454,7 @@ function lastSuitePipeline(command) {
1456
1454
  * the command exits with is then the following statement's. That is the right answer to "whose
1457
1455
  * exit code is this", and the wrong one to "which stages filter this run's output": a
1458
1456
  * `… --reporter=list 2>&1 | tail -5; echo done` still pipes its log through a tail, and the tail
1459
- * still throws away every failure line the run printed (xr-molecule-flash-m121 +09:14, where the
1457
+ * still throws away every failure line the run printed (where the
1460
1458
  * platform's own hold handed that shape back and the first 95-second walk's log held only the
1461
1459
  * tally).
1462
1460
  *
@@ -1473,8 +1471,8 @@ function lastSuiteRun(command) {
1473
1471
  * spec, or a Hurl run followed by a browser walk — and every one of them writes the log the
1474
1472
  * executor reads. Reading only the last one left the earlier walk's failing test's own error
1475
1473
  * behind a `| tail -5` while the LAST invocation was rewritten normally
1476
- * (xr-molecule-flash-l21 +21:27: a 3.6-minute walk returned `72 passed` with no error body when
1477
- * the driver ran `… specs 2>&1 | tail -5; echo START; … auth.spec.ts`).
1474
+ * (a 3.6-minute walk returned `72 passed` with no error body when the driver ran
1475
+ * `… specs 2>&1 | tail -5; echo START; … auth.spec.ts`).
1478
1476
  *
1479
1477
  * A command that runs no test at all is not a set of runs ({@link commandRunsNoTests}); nor is a
1480
1478
  * statement that only lists tests, which the reporter pass asks about per statement
@@ -1566,13 +1564,13 @@ function suiteFilterCut(command, located, at) {
1566
1564
  *
1567
1565
  * A filter after a runner is not "the end of the log" the executor asked for: it hides the
1568
1566
  * failing test's own error (it usually matches none of `✘|passed|failed`), and the pipeline's exit
1569
- * status becomes the FILTER's, so `… 2>&1 | grep -E "✘|passed|failed|skipped" | tail -20` came back
1570
- * `exitCode: 0` for a run with a failure in it (xr-molecule-flash-m101 +25:54). The tool returns
1567
+ * status becomes the FILTER's, so `… 2>&1 | grep -E "✘|passed|failed|skipped" | tail -20` comes
1568
+ * back `exitCode: 0` for a run with a failure in it. The tool returns
1571
1569
  * the end of the full log anyway, so the filter only costs diagnosability.
1572
1570
  *
1573
1571
  * EVERY run is stripped, not only the command's last: a compound command's earlier walk writes
1574
1572
  * the same log, and its `| tail -5` threw away the error body of a 3.6-minute Playwright walk
1575
- * whose two failure names were all the executor got back (xr-molecule-flash-l21 +21:27). Only a
1573
+ * whose two failure names were all the executor got back. Only a
1576
1574
  * run the parse places is touched, and only its own pipeline — a later statement's filters belong
1577
1575
  * to it.
1578
1576
  *
@@ -1583,12 +1581,12 @@ function suiteFilterCut(command, located, at) {
1583
1581
  *
1584
1582
  * A statement after the run's own pipeline (`; echo done`, `&& echo ok`) does not protect the
1585
1583
  * filters before it: the filter is thrown away and the trailing statement is kept, so the log the
1586
- * run writes still holds every failure (xr-molecule-flash-m121 — a `| tail -5; echo done` kept its
1584
+ * run writes still holds every failure (a `| tail -5; echo done` keeps its
1587
1585
  * tail, and a 95-second walk's log held only the tally while the platform's own hold had handed
1588
1586
  * that command back).
1589
1587
  *
1590
1588
  * The command around the dropped stages is left as written: a group they sit in keeps its closer
1591
- * (`( … | tail -25)` → `( … )`, never `( …` — xr-molecule-flash-m418 +23:31), and a stage the cut
1589
+ * (`( … | tail -25)` → `( … )`, never `( …`), and a stage the cut
1592
1590
  * would leave a closer dangling inside is not dropped at all.
1593
1591
  *
1594
1592
  * @param command - The shell command.
@@ -1630,8 +1628,8 @@ export function stripTrailingSuiteFilters(command) {
1630
1628
  * code a finished run reports is its LAST pipeline stage's, so a stage after the runner makes the
1631
1629
  * code the stage's — but only when such a stage EXISTS. The platform used to report "piped" for
1632
1630
  * any run whose statements it could not place in the text, which one heredoc body was enough to
1633
- * cause: on X1 xr-molecule-flash-m105 (+08:12) a config written in the same `exec_command` made
1634
- * the run come back with no exit code at all, and the note under it claimed a stage after the
1631
+ * cause: a config written in the same `exec_command` made the run come back with no exit code
1632
+ * at all, and the note under it claimed a stage after the
1635
1633
  * runner that was not there. The parse now places every statement (`locatedCommandSegments`), so
1636
1634
  * "unplaced" is the rare, named case rather than a guess dressed as an answer.
1637
1635
  *
@@ -1673,10 +1671,9 @@ export function suiteRunPipedAfterRunner(command) {
1673
1671
  * shares with everything else stays responsive to what the executor does next.
1674
1672
  *
1675
1673
  * A whole-suite Playwright run saturates the one container the app's Vite
1676
- * server, its API process and every other command also live in: on X1 flash-m75
1677
- * the model backgrounded a 139-test run at +36:23 and the two-word shell
1678
- * command it sent next — tick its plan file — returned nothing for 2 min 40 s,
1679
- * measured on the chat payload and on the live-stream follower independently.
1674
+ * server, its API process and every other command also live in: a backgrounded 139-test run
1675
+ * has left the two-word shell command sent next — tick its plan file — returning nothing for
1676
+ * 2 min 40 s, measured on the chat payload and on the live-stream follower independently.
1680
1677
  * The backgrounding note is right that the executor should go do other work;
1681
1678
  * what it cannot promise is that the sandbox will let it.
1682
1679
  *
@@ -1824,8 +1821,8 @@ const BLOCKED_MOL_REDIRECT = /(?:<\s*\/etc\/mol)/i;
1824
1821
  * The first check reads the commands the shell actually RUNS — the statements, a `sh -c`
1825
1822
  * string's body, and every command substitution ({@link executedCommands}) — so a `|` inside a
1826
1823
  * quoted grep alternation is data, not a pipeline: `grep -E "USER CALLS|ENV|BUTTON|passed|failed"
1827
- * app.log` prints log lines, and the `ENV` in its pattern started nothing (X0
1828
- * xr-molecule-flash-m331 blocked exactly that call as an environment dump, twice). What the
1824
+ * app.log` prints log lines, and the `ENV` in its pattern starts nothing — a text
1825
+ * match would block exactly that call as an environment dump, twice over. What the
1829
1826
  * shell DOES run stays visible through the same walk: `ls | env`, `bash -c 'env'`,
1830
1827
  * `echo "$(printenv)"` and a nested `sh -c 'x; env'` are all commands, and so is `env` itself
1831
1828
  * when nothing follows it.
@@ -1870,21 +1867,30 @@ export function checkBlockedCommand(command) {
1870
1867
  export function resolvePath(path, projectRoot) {
1871
1868
  if (path === '' || path === '/')
1872
1869
  return projectRoot;
1873
- const clean = path.replace(/\0/g, '');
1874
- const resolved = clean.startsWith('/')
1875
- ? posix.normalize(clean)
1876
- : posix.normalize(`${projectRoot}/${clean}`);
1870
+ const resolved = rootedPath(path, projectRoot);
1877
1871
  if (resolved !== projectRoot && !resolved.startsWith(projectRoot + '/'))
1878
1872
  return projectRoot;
1879
1873
  return resolved;
1880
1874
  }
1875
+ /**
1876
+ * A tool path as an absolute posix path: an absolute path normalized, a relative
1877
+ * one resolved against the project root — the one rule {@link resolvePath},
1878
+ * {@link isOutsideRoot} and {@link commandStartsWithCdInto} all compare against.
1879
+ *
1880
+ * @param path - The path the tool was given.
1881
+ * @param projectRoot - The project root.
1882
+ * @returns The normalized absolute path.
1883
+ */
1884
+ function rootedPath(path, projectRoot) {
1885
+ const clean = path.replace(/\0/g, '');
1886
+ return clean.startsWith('/') ? posix.normalize(clean) : posix.normalize(`${projectRoot}/${clean}`);
1887
+ }
1881
1888
  /**
1882
1889
  * Whether a tool path points outside the project root once normalized (an
1883
1890
  * absolute path elsewhere, or a `..` that climbs out). {@link resolvePath}
1884
1891
  * clamps such a path to the root; a file tool must refuse it instead of
1885
1892
  * answering about the root — a `list_files /tmp/x` used to return the
1886
- * workspace listing as if it were `/tmp/x` (X1 runs sonnet-m17 and sol-m05,
1887
- * 2026-10-14).
1893
+ * workspace listing as if it were `/tmp/x`.
1888
1894
  *
1889
1895
  * @param path - The path the tool was given.
1890
1896
  * @param projectRoot - The project root.
@@ -1893,12 +1899,60 @@ export function resolvePath(path, projectRoot) {
1893
1899
  export function isOutsideRoot(path, projectRoot) {
1894
1900
  if (path === '' || path === '/')
1895
1901
  return false;
1896
- const clean = path.replace(/\0/g, '');
1897
- const resolved = clean.startsWith('/')
1898
- ? posix.normalize(clean)
1899
- : posix.normalize(`${projectRoot}/${clean}`);
1902
+ const resolved = rootedPath(path, projectRoot);
1900
1903
  return resolved !== projectRoot && !resolved.startsWith(projectRoot + '/');
1901
1904
  }
1905
+ /**
1906
+ * Whether a command's FIRST statement already changes into `directory`.
1907
+ *
1908
+ * `exec_command` refuses a `cwd` outside the workspace, and that refusal tells
1909
+ * the caller to drop `cwd` and start the command with `cd <path> && `. When the
1910
+ * command ALREADY starts that way the advice describes what was just sent, so
1911
+ * the command runs as written with the backend's working directory at the root
1912
+ * instead (`{cwd: "/tmp", command: "cd /tmp && …"}` was refused with "start the command with
1913
+ * `cd /tmp && `"; the same command resent with `cwd: "/workspace"` then passes).
1914
+ *
1915
+ * Only the leading statement counts, only when the shell's own parse puts it in
1916
+ * the caller's shell — a `( cd … )` group or a `sh -c '…'` runs in its own
1917
+ * directory and moves nothing — and only when it ends the way a following
1918
+ * command needs (`&&`, `;`, a newline, or nothing after it). `cd … | cat` is a
1919
+ * pipeline, so its `cd` runs in a subshell, and `&` backgrounds it: neither
1920
+ * leaves the caller's directory changed.
1921
+ *
1922
+ * @param command - The shell command as the tool received it.
1923
+ * @param directory - The `cwd` the caller asked for.
1924
+ * @param projectRoot - The project root a relative path is resolved against.
1925
+ * @returns True when the command's first statement `cd`s into `directory`.
1926
+ */
1927
+ export function commandStartsWithCdInto(command, directory, projectRoot) {
1928
+ const first = locatedCommandSegments(command)[0];
1929
+ if (!first || first.segment === null || first.segment.program !== 'cd')
1930
+ return false;
1931
+ if ((first.shellScope?.length ?? 0) > 0)
1932
+ return false;
1933
+ const terminator = first.terminator;
1934
+ if (terminator !== '&&' && terminator !== ';' && terminator !== '\n' && terminator !== '') {
1935
+ return false;
1936
+ }
1937
+ if (first.segment.args.length !== 1)
1938
+ return false;
1939
+ const target = withoutQuotes(first.segment.args[0] ?? '');
1940
+ return rootedPath(target, projectRoot) === rootedPath(directory, projectRoot);
1941
+ }
1942
+ /**
1943
+ * A shell word with one surrounding quote pair removed (`'/tmp'` → `/tmp`,
1944
+ * `"/tmp"` → `/tmp`). A word without a matching pair is returned as written.
1945
+ *
1946
+ * @param word - One word of a parsed statement, as written.
1947
+ * @returns The word's text.
1948
+ */
1949
+ function withoutQuotes(word) {
1950
+ const quote = word[0];
1951
+ if ((quote === "'" || quote === '"') && word.length >= 2 && word.endsWith(quote)) {
1952
+ return word.slice(1, -1);
1953
+ }
1954
+ return word;
1955
+ }
1902
1956
  /**
1903
1957
  * Validate that a glob/include pattern is safe (no shell metacharacters that could
1904
1958
  * inject). Allows alphanumeric, `* ? . _ - /` and the bracket/paren glob chars `[] ()`.
@@ -1944,8 +1998,8 @@ export const BRACE_GLOB_HINT = `or one brace group of up to ${MAX_BRACE_GLOB_ALT
1944
1998
  * Neither search tool expands braces itself: `grep --include` and `find` take
1945
1999
  * the pattern as a single shell-quoted argument, so no shell ever sees it.
1946
2000
  * `find_files {pattern:"*.{ts,tsx}"}` and `search_files {include:"*.{ts,tsx}"}`
1947
- * were both refused outright (xr-molecule-flash-m199 and m189), costing a round
1948
- * trip and pushing the agent to a narrower search. The group is expanded here
2001
+ * were both refused outright, costing a round trip and pushing the agent to a narrower
2002
+ * search. The group is expanded here
1949
2003
  * instead — `search_files` passes one `--include` per alternative and
1950
2004
  * `find_files` ORs one `-name`/`-path` predicate per alternative.
1951
2005
  *
@@ -1985,8 +2039,8 @@ export function expandBraceGlob(pattern) {
1985
2039
  *
1986
2040
  * `find -name` compares a pattern against a file's BASENAME, so a pattern
1987
2041
  * containing a slash (`helpers/*`, `e2e/*.spec.ts`, `src` + `**` + `/App.tsx`)
1988
- * can never match and the tool answers `[]` (xr-molecule-flash-m77, on two
1989
- * calls in one run). Such a pattern is matched with `-path` instead: an fnmatch
2042
+ * can never match and the tool answers `[]` (twice in one run). Such a pattern is matched
2043
+ * with `-path` instead: an fnmatch
1990
2044
  * glob over the whole printed path, whose `*` crosses `/` — the rule
1991
2045
  * `.gitignore` uses, and the reason a bare `helpers/*` means "a `helpers/`
1992
2046
  * directory anywhere below the root". The pattern is anchored with a leading
@@ -2036,14 +2090,14 @@ export function findPathArgs(pattern) {
2036
2090
  * search root. Handed `/workspace/node_modules` and
2037
2091
  * `@molecule/api-resource-user/dist/*.d.ts` — a directory named outright —
2038
2092
  * `find` walked the sandbox's entire dependency tree and hit the tool's 10 s
2039
- * deadline without printing a result (xr-molecule-flash-m94: the one directory
2040
- * the model meant answered a `list_files` in 0 s). The literal prefix is that
2093
+ * deadline without printing a result (the one directory the model meant answered a `list_files`
2094
+ * in 0 s). The literal prefix is that
2041
2095
  * directory, and rooting the search at it is what keeps the call instant.
2042
2096
  *
2043
2097
  * A BARE first segment (`helpers/*`, `bonds/*`, `e2e/*.spec.ts`) is NOT one:
2044
2098
  * `helpers/*` means "a `helpers/` directory anywhere below the root", which the
2045
- * tool documents and a live run relied on (xr-molecule-flash-m77 — a path
2046
- * pattern is not anchored to one depth). Rooting those at
2099
+ * tool documents and a live run relied on (a path pattern is not anchored to one depth).
2100
+ * Rooting those at
2047
2101
  * `<root>/helpers` would answer with a SUBSET, silently: the nested
2048
2102
  * `api/src/helpers/` matches would disappear, and a missing file reads as
2049
2103
  * "no such file". So a prefix is returned only when it names a path of two or
@@ -2056,8 +2110,8 @@ export function findPathArgs(pattern) {
2056
2110
  * `node_modules` (see `findPruneArgs`), so the whole-tree form can only ever
2057
2111
  * match `<root>/@molecule/…` — the scope directory IS the directory the pattern
2058
2112
  * names. Left un-narrowed, a scoped-package pattern walked the sandbox's entire
2059
- * dependency tree and hit the 10 s deadline with no result at all
2060
- * (xr-molecule-flash-l65). A bare NAME stays a directory name at any depth:
2113
+ * dependency tree and hit the 10 s deadline with no result at all. A bare NAME stays a
2114
+ * directory name at any depth:
2061
2115
  * `helpers/*` is unchanged even under a `node_modules` root.
2062
2116
  *
2063
2117
  * @param pattern - A pattern already accepted by {@link isValidGlob}.
@@ -2094,7 +2148,7 @@ export function literalDirPrefix(pattern, root) {
2094
2148
  * `[deadline_exceeded] the operation timed out: … It can be modified by passing
2095
2149
  * 'timeoutMs' when making the request. Use '0' to disable the timeout.` — which
2096
2150
  * names an option the executor cannot set and says nothing about the search
2097
- * that died. Handing that sentence to the model is what xr-molecule-flash-m94
2151
+ * that died. Handing that sentence to the model is what a run
2098
2152
  * read after `find_files` spent 10 s walking `node_modules`; the tool answers
2099
2153
  * the deadline in its own words instead (see {@link searchTimeoutError}).
2100
2154
  *
@@ -2175,8 +2229,8 @@ export function pathArgError(path, tool) {
2175
2229
  *
2176
2230
  * The path message is the wrong reading of it: "requires a non-empty path
2177
2231
  * argument" says the call was understood and the path was left out, so the model
2178
- * retries the call it just made instead of sending the whole call again (x0
2179
- * flash-m83 +29:32: `edit_file {}`, one round lost). An empty object and a
2232
+ * retries the call it just made instead of sending the whole call again (`edit_file {}` loses
2233
+ * one round). An empty object and a
2180
2234
  * missing `path` are different failures and deserve different sentences.
2181
2235
  *
2182
2236
  * @param input - The raw tool input.
@@ -2320,9 +2374,8 @@ export const MAX_BATCH_READ_FILES = 25;
2320
2374
  * MIDDLE — the cut lands between two `"content"` values, so whole files fell
2321
2375
  * out and the note in their place said only "N characters cut here" — while
2322
2376
  * this tool's own note had promised each of those files that it "fits in one
2323
- * call, so it is not paged": X0 flash-m81 (2026-10-16) read ten e2e specs in
2324
- * one call (121,467 chars), lost the middle six, and bought them again in six
2325
- * further calls (52,908 chars).
2377
+ * call, so it is not paged": one batched read of ten e2e specs (121,467 chars) lost the
2378
+ * middle six and bought them again in six further calls (52,908 chars).
2326
2379
  *
2327
2380
  * So the CALL gets the ceiling the request gives it. Whole files are returned
2328
2381
  * up to it; the paths that did not fit are named in `note` and listed in
@@ -2357,7 +2410,7 @@ export const MAX_READ_RETURN_CHARS = MAX_READ_CALL_CHARS - READ_RESULT_ENVELOPE_
2357
2410
  /**
2358
2411
  * A file this small is returned whole on a second window of it (paging it costs a call each).
2359
2412
  * The same bound as {@link MAX_READ_RETURN_CHARS}: the windowed-read note promises that a
2360
- * file under it "fits in ONE call", and a smaller bound here broke that promise (X1 flash-m67).
2413
+ * file under it "fits in ONE call", and a smaller bound here breaks that promise.
2361
2414
  */
2362
2415
  export const SMALL_FILE_WHOLE_CHARS = MAX_READ_RETURN_CHARS;
2363
2416
  /**
@@ -2368,11 +2421,11 @@ export const SMALL_FILE_WHOLE_CHARS = MAX_READ_RETURN_CHARS;
2368
2421
  * An entry document is boilerplate the reader rarely needs in full: meta tags,
2369
2422
  * stylesheet and preload links, a `<style>` block, and a handful of `<script>`
2370
2423
  * tags. The parts an executor acts on — the body's structure, where a script is
2371
- * loaded — are all visible in an outline of one line per tag. X0 flash-m77 paid
2372
- * 63,735 chars for one such read: ~60 KB of it was the IDE's own preview bridges,
2424
+ * loaded — are all visible in an outline of one line per tag. One such read paid
2425
+ * 63,735 chars: ~60 KB of it was the IDE's own preview bridges,
2373
2426
  * and the read pushed the transcript past its context cap, so the e2e specs the
2374
2427
  * executor had just read were trimmed to summaries and it read the whole suite
2375
- * again — twice (findings, +01:23 and +02:56). The scaffold now keeps those
2428
+ * again — twice. The scaffold now keeps those
2376
2429
  * bridges in separate files, but a template's own head is still ~5-12 KB, and a
2377
2430
  * static-site or SSR entry can be far more.
2378
2431
  *
@@ -2506,7 +2559,7 @@ export function truncateMiddle(s, maxLength) {
2506
2559
  /**
2507
2560
  * A test run's tally lines (` 3 failed`, ` 71 passed (4.6m)`) the cut text lost, put back at
2508
2561
  * its end: Playwright lists every failed test after its "N failed" line, so a long list pushed
2509
- * the line itself into the cut middle and the run read as having no failures (X1 flash-m60).
2562
+ * the line itself into the cut middle and the run reads as having no failures.
2510
2563
  *
2511
2564
  * @param original - The whole output.
2512
2565
  * @param cut - The truncated output.
@@ -2621,9 +2674,8 @@ export const RUNNING_FAILURE_LINE_RE = '^[[:space:]]*(✘|✗|×|✖|✕|[0-9]+\
2621
2674
  * the failure lines so far (capped at {@link RUNNING_FAILURE_LINES}), then the end of the log.
2622
2675
  *
2623
2676
  * A Playwright `list` run prints one line per test as it finishes, so the LAST bytes of a running
2624
- * log are its least informative part — in X1 flash-m61 the last 3 000 bytes of a 139-test walk
2625
- * were the skip list, and all 61 failures were above them, which cost the run the grep it had to
2626
- * make anyway. A background run also carries each failure's own error as it fails
2677
+ * log are its least informative part — the last 3 000 bytes of a 139-test walk are the skip
2678
+ * list, and all 61 failures were above them, which costs the run the grep it has to make anyway. A background run also carries each failure's own error as it fails
2627
2679
  * ({@link PLAYWRIGHT_FAILURES_INLINE_ENV}), and {@link RUNNING_FAILURE_LINE_RE} already matches
2628
2680
  * those blocks' headers and their `Error:` lines, so this slice shows the causes too.
2629
2681
  *
@@ -2662,11 +2714,11 @@ export const RUNNING_LOG_READ_BYTES = REPEATING_CAUSE_TAIL_BYTES;
2662
2714
  * The shell command whose stdout is a bounded CONTIGUOUS read of a running task's own log: the
2663
2715
  * log's byte size, then its last {@link RUNNING_LOG_READ_BYTES} bytes.
2664
2716
  *
2665
- * {@link runningOutputSliceCommand} is the wrong read for a diagnosis, and l07 is the finding: it
2717
+ * {@link runningOutputSliceCommand} is the wrong read for a diagnosis: it
2666
2718
  * stitches the first 40 grep-selected failure lines to the last 1 000 bytes with a blank line
2667
2719
  * between, so the tail is DISJOINT from the headers above it. Read as one log, the last numbered
2668
- * header absorbs whatever the tail starts with — a clipped skipped-test title became the cause of
2669
- * a genuine failure (xr-molecule-flash-l07). A contiguous window can only ever hold real
2720
+ * header absorbs whatever the tail starts with — a clipped skipped-test title becomes the cause
2721
+ * of a genuine failure. A contiguous window can only ever hold real
2670
2722
  * neighbouring lines of one log.
2671
2723
  *
2672
2724
  * The size rides along so a caller can say when the window is only the log's end
@@ -2798,8 +2850,8 @@ function failureBody(block) {
2798
2850
  * pair of a failed assertion.
2799
2851
  *
2800
2852
  * Without this, every `TimeoutError: page.press: Timeout 5000ms exceeded.` in a log is one
2801
- * message, because that line names the ACTION (`page.press`) and never its subject. X1 flash-m84
2802
- * (2026-10-16) had four such failures in one run — one waiting on
2853
+ * message, because that line names the ACTION (`page.press`) and never its subject. Four such
2854
+ * failures in one run — one waiting on
2803
2855
  * `button:has-text("Publish Article")`, one on `.article-preview h1`, one on
2804
2856
  * `.article-preview').first().locator('.author')`, one on a generated `h1:has-text("Test
2805
2857
  * Article …")` — and the run's summary called them two causes while its mid-run note called them
@@ -2835,8 +2887,8 @@ const FAILURE_EVIDENCE_CHARS = 200;
2835
2887
  * `Expected: <n>`, and a signature built from a `waiting for` line never carries the assertion
2836
2888
  * values at all. Those values are what a fix round needs, and the run's stdout they came from is
2837
2889
  * cut in the MIDDLE by the tool-result cap, so a listed cause that does not carry them sends the
2838
- * executor back to the log — xr-molecule-flash-m102 (+12:43) made four `grep -A` round trips
2839
- * (~25 s) for the bodies of causes the note had already listed.
2890
+ * executor back to the log — four `grep -A` round trips (~25 s) for the bodies of causes the
2891
+ * note had already listed.
2840
2892
  *
2841
2893
  * @param body - The block's lines, from {@link failureBody}.
2842
2894
  * @param message - The cause's signature: a line it already carries is not repeated.
@@ -2880,7 +2932,7 @@ function failureEvidence(body, message) {
2880
2932
  * The duration is the one that matters. A suite that sets `actionTimeout: 5_000` — the X1
2881
2933
  * realworld suite does — spends `5 s` plus teardown on every "the locator never appeared"
2882
2934
  * failure, so `~7 s` is a property of the CONFIG, not of the cause, and four different missing
2883
- * elements print the same number (X1 flash-m84, 2026-10-16). Two tests waiting on the same
2935
+ * elements print the same number. Two tests waiting on the same
2884
2936
  * locator for 5 s and for 10 s wait for the same thing; two waiting on different locators do not
2885
2937
  * become one cause by timing out together.
2886
2938
  *
@@ -2914,7 +2966,7 @@ function failureBudgetMs(block) {
2914
2966
  * The cause signature with the budget it normalized away put back: `Test timeout of <n> exceeded`
2915
2967
  * becomes `Test timeout of 15000ms exceeded`. Every other `<n>` stays — those are the values that
2916
2968
  * differ between tests of one cause, while the budget is the one that does not, and it is what
2917
- * turns "a timeout" into "15 creates in 15 s" (xr-molecule-flash-m110 +20:44).
2969
+ * turns "a timeout" into "15 creates in 15 s".
2918
2970
  *
2919
2971
  * @param message - The normalized signature.
2920
2972
  * @param budgetMs - The budget the cause's first block printed.
@@ -2976,9 +3028,8 @@ function testKey(t) {
2976
3028
  * A retried failure prints twice in a `list` log — its first attempt, then the same test with
2977
3029
  * `(retry #1)` after the title — while its failure block names it without the marker. Keyed
2978
3030
  * literally, the retry row is a test no cause claims, so it fell through to the duration arm and
2979
- * was listed as a "shared duration" cause beside the proven cause it repeated: on
2980
- * xr-molecule-flash-m155 (+21:50) every retry row of the run became one of six extra causes
2981
- * duplicating the causes above them.
3031
+ * was listed as a "shared duration" cause beside the proven cause it repeated: every retry row
3032
+ * of a run became one of six extra causes duplicating the causes above them.
2982
3033
  *
2983
3034
  * @param t - A failed test, as a result line or a failure block spelled it.
2984
3035
  * @returns The key a test's retry row shares with its failure block.
@@ -3018,13 +3069,13 @@ function distinctTests(tests) {
3018
3069
  *
3019
3070
  * Either way the point is the same: a suite whose failures repeat one cause spends its whole
3020
3071
  * runtime proving that one cause, and the executor watching it can be told so at the third
3021
- * failure instead of at the last. X1 flash-m76 walked 15 tests in 4.0 minutes where 15.6 s
3072
+ * failure instead of at the last. Fifteen tests walked in 4.0 minutes where 15.6 s
3022
3073
  * each was the test timeout and one missing `navigate('/')` was the cause of all of them.
3023
3074
  *
3024
3075
  * The quorum is of DISTINCT tests — {@link REPEATING_FAILURE_MIN} of them — because a log that
3025
- * prints each failure twice (a second reporter, a retry) had X1 flash-m83 told that four of its
3026
- * tests had failed the same way when two had, so the executor stopped a 139-test walk at test 21
3027
- * with one cause in hand (2026-10-16).
3076
+ * prints each failure twice (a second reporter, a retry) has told a run that four of its
3077
+ * tests failed the same way when two had, so the executor stopped a 139-test walk at test 21
3078
+ * with one cause in hand.
3028
3079
  *
3029
3080
  * @param log - The log so far.
3030
3081
  * @returns The repeating cause, or null when no signature has {@link REPEATING_FAILURE_MIN}
@@ -3103,8 +3154,7 @@ export function repeatingFailure(log) {
3103
3154
  * A body for a test the request EXCLUDES is not that evidence: its failure does not count for
3104
3155
  * this request ({@link matchingExcludedTitles}), so a wait that handed it over as the diagnostic
3105
3156
  * spent a model round trip and 8 k of partial log on a test the same result says to ignore —
3106
- * exactly what xr-molecule-flash-h13 (+19:06) did twice while the platform's proof was still
3107
- * walking.
3157
+ * which a run has done twice while the platform's proof was still walking.
3108
3158
  *
3109
3159
  * With `finalOnly`, only a failure the run has FINISHED with counts; a pending retry waits.
3110
3160
  *
@@ -3192,10 +3242,9 @@ function failureBlockKey(block) {
3192
3242
  * retry may not have printed yet — so it waits; a command that pins `--retries=0` proves there is
3193
3243
  * no retry to wait for.
3194
3244
  *
3195
- * X1 xr-molecule-flash-l69 (+28:26): the deployed proof's yield handed back a first attempt
3196
- * (no `Retry #` separator) that then passed on its retry, and the edit it prompted cost a
3197
- * redeploy plus a whole walk. The fix is not to stop yielding — the retry-failed shape (l69's
3198
- * first walk, null-fields:71) paid off — but to yield only on this one.
3245
+ * A deployed proof's yield handed back a first attempt (no `Retry #` separator) that then
3246
+ * passed on its retry, and the edit it prompted cost a redeploy plus a whole walk. The fix is
3247
+ * not to stop yielding — the retry-failed shape paid off — but to yield only on this one.
3199
3248
  *
3200
3249
  * @param args - The log's rows and blocks, the failing test's key, and whether the command
3201
3250
  * proves the run cannot retry.
@@ -3226,8 +3275,8 @@ export const REPEATING_FAILURE_LISTED = 5;
3226
3275
  */
3227
3276
  export const TOOL_RESULT_CHARS = 8000;
3228
3277
  /**
3229
- * The characters a finished run's cause list may use of {@link TOOL_RESULT_CHARS}. The recap
3230
- * measured by xr-molecule-flash-m102 (+12:43) was `12 failures, 8 causes`: one cause line is
3278
+ * The characters a finished run's cause list may use of {@link TOOL_RESULT_CHARS}. A measured
3279
+ * recap was `12 failures, 8 causes`: one cause line is
3231
3280
  * ~200 chars and ~80 more with its two body lines, so 8 causes is ~2.3 k against the 8 k cap —
3232
3281
  * the old 5-cause cap withheld 3 causes that the same result had room for.
3233
3282
  *
@@ -3251,8 +3300,8 @@ export const FAILURE_CAUSE_TESTS_LISTED = 6;
3251
3300
  *
3252
3301
  * Used only where a COUNT of files is what the sentence needs; every note that names a
3253
3302
  * test to the executor prints the path the log wrote, because that is the path its own
3254
- * re-run hint names and the one the executor can paste back into a command (flash-m82
3255
- * read a shortened `e2e/auth.spec.ts:19` as a file the app owned, and rewrote its config
3303
+ * re-run hint names and the one the executor can paste back into a command (a shortened
3304
+ * `e2e/auth.spec.ts:19` reads as a file the app owned, and rewrites its config
3256
3305
  * over a suite path that was already right).
3257
3306
  *
3258
3307
  * @param file - A path as {@link specFile} returns it.
@@ -3318,7 +3367,7 @@ export const UNIVERSAL_CAUSE_FILES = 3;
3318
3367
  * THREE, and the number is the cost of the alternative. The old floor of 15 was chosen when the
3319
3368
  * reading was only offered at a completed run; once it STOPS the walk, every test waited for is
3320
3369
  * a test whose result is thrown away, and a blank page costs its per-test budget each time
3321
- * (m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
3370
+ * (18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
3322
3371
  * `pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
3323
3372
  * needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
3324
3373
  * no failure showing a locator RESOLVING ({@link ELEMENT_FOUND} — a blank page cannot resolve
@@ -3343,8 +3392,8 @@ const LOCATOR_WAIT = /waiting for locator\(|element\(s\) not found|toBeVisible\(
3343
3392
  * of the viewport`). A blank page cannot resolve a locator, so one of these anywhere in a run
3344
3393
  * disproves {@link emptyPageCause}.
3345
3394
  *
3346
- * xr-molecule-flash-m187 (+15:39 and +18:54) narrowed two re-runs to the tests that had just
3347
- * failed, and both were stopped as "the app is rendering NOTHING" while every block in the log
3395
+ * Two re-runs narrowed to the tests that had just failed were stopped as "the app is rendering
3396
+ * NOTHING" while every block in the log
3348
3397
  * carried `locator resolved to <i class="ion-close-circled"></i>` followed by `element is not
3349
3398
  * visible` — the page was drawn, and only an icon had no box.
3350
3399
  */
@@ -3391,10 +3440,10 @@ function pageErrorInLog(text) {
3391
3440
  *
3392
3441
  * This is ONE cause even though the signatures differ — each spec asks for its own first element
3393
3442
  * (`input[name="username"]`, `a.navbar-brand`, `h1`), so {@link repeatingFailure} cannot group
3394
- * them and {@link universalCause}'s signature test cannot see the run is over. xr-molecule-flash-
3395
- * m141 (+20:30) walked a built app that rendered nothing: 36 tests had failed, every one at its
3396
- * first locator, and the note told the run to keep walking for another ~9 minutes. xr-molecule-
3397
- * flash-m177 (+08:38 → +12:52) repeated it: 18 tests, one whole walk, on a page whose
3443
+ * them and {@link universalCause}'s signature test cannot see the run is over. One run
3444
+ * walked a built app that rendered nothing: 36 tests had failed, every one at its
3445
+ * first locator, and the note told the run to keep walking for another ~9 minutes. Another
3446
+ * repeated it: 18 tests, one whole walk, on a page whose
3398
3447
  * `PAGEERROR No 'auth-client' provider bonded` was in the log from the first one.
3399
3448
  *
3400
3449
  * @param text - The log (ANSI already stripped).
@@ -3498,8 +3547,8 @@ const PLAYWRIGHT_VALUE_FLAGS = new Set([
3498
3547
  * whole suite (or its selection cannot be read).
3499
3548
  *
3500
3549
  * {@link universalCause}'s file threshold is written for a whole-suite run, which a narrowed run
3501
- * can never reach. xr-molecule-flash-m119 (+42:08) re-ran ONE spec file against a hand-started
3502
- * `vite preview` the browser could not reach, and every test in it failed `page.goto: Timeout` —
3550
+ * can never reach. A run that re-ran ONE spec file against a hand-started `vite preview` the
3551
+ * browser could not reach had every test in it fail `page.goto: Timeout` —
3503
3552
  * but one file is below {@link UNIVERSAL_CAUSE_FILES}, so the wait sat out its entire 280 s
3504
3553
  * budget on a run that had already measured all it could.
3505
3554
  *
@@ -3548,7 +3597,7 @@ export function specFilesSelected(command) {
3548
3597
  *
3549
3598
  * A command that names `file:line` tests is a re-run of tests that already failed — the shape
3550
3599
  * {@link emptyPageCause} must not read as "the page renders nothing", because the executor is
3551
- * verifying fixes and the stop throws that verdict away (xr-molecule-flash-m187, twice).
3600
+ * verifying fixes and the stop throws that verdict away (twice).
3552
3601
  *
3553
3602
  * @param command - The run's command.
3554
3603
  * @returns The selected test references, or null when the selection is not per-test.
@@ -3606,7 +3655,7 @@ function sameTestRef(ref, where) {
3606
3655
  * threshold, and nothing has passed since its first failure. The threshold is
3607
3656
  * {@link UNIVERSAL_CAUSE_FILES} for a whole-suite run but the number of files the command
3608
3657
  * SELECTED for a narrowed one ({@link specFilesSelected}): a one-file run can never span three,
3609
- * and xr-molecule-flash-m119 (+42:08) waited out its whole budget on exactly that shape. An app
3658
+ * and a run can wait out its whole budget on exactly that shape. An app
3610
3659
  * that is not up, a 5xx on every request, or a selector that is missing on every page all look
3611
3660
  * like that, and every remaining test will fail the same way — so the run's remaining minutes
3612
3661
  * measure nothing. A CONNECTION-class cause ({@link CONNECTION_CAUSE}) needs no file span at all:
@@ -3615,8 +3664,8 @@ function sameTestRef(ref, where) {
3615
3664
  *
3616
3665
  * Anything less is NOT this: a cause that stops at one file of a multi-file run, or that leaves
3617
3666
  * other tests passing, or that shares the run with failures of another shape, leaves the run with
3618
- * something to say, and the run is the only thing that will say it. X1 flash-m83 (2026-10-16)
3619
- * stopped eight local runs at their first repeating cause over 14.5 minutes and never finished
3667
+ * something to say, and the run is the only thing that will say it. Stopping runs at their first
3668
+ * repeating cause has burned eight local runs over 14.5 minutes without finishing
3620
3669
  * one; three of its deploys each fixed a single cause that a finished run would have listed
3621
3670
  * alongside the others.
3622
3671
  *
@@ -3672,14 +3721,14 @@ export function universalCause(log, found, command) {
3672
3721
  * spec-file threshold — {@link UNIVERSAL_CAUSE_FILES}, or the files a narrowed run's own
3673
3722
  * command selects, or {@link CONNECTION_CAUSE_TESTS} tests of an app that never answered — with
3674
3723
  * nothing passing since the first. That is the
3675
- * dont-stop-runs rule. X1 flash-m83 (2026-10-16) stopped eight local runs at their first
3676
- * repeating cause in 14.5 minutes and never finished one, and a run that still has other causes
3724
+ * dont-stop-runs rule. Stopping runs at their first repeating cause has burned eight local
3725
+ * runs in 14.5 minutes without finishing one, and a run that still has other causes
3677
3726
  * left to name is the only thing that will name them.
3678
3727
  * - **A PROVEN cause** is an error repeated VERBATIM. A shared DURATION is a LEAD, never a cause:
3679
3728
  * a suite whose config sets a timeout spends that timeout on every failure that waits for
3680
3729
  * anything, so three unrelated tests timing out together look exactly like one broken run
3681
- * (`~7 s` is a property of the CONFIG, not of the cause — {@link normalizedSignature}). X1
3682
- * xr-molecule-flash-m99 (2026-10-16) was told `status: 'repeating_failure'` twice on that
3730
+ * (`~7 s` is a property of the CONFIG, not of the cause — {@link normalizedSignature}). A run
3731
+ * told `status: 'repeating_failure'` twice on that
3683
3732
  * shape — three `url-navigation.spec.ts` tests at ~16.2 s, one `timeout: 15_000` plus teardown
3684
3733
  * — read the early return as "stop waiting, edit now", and voided a run that was still walking;
3685
3734
  * the second cost a 224 s wait that ended with no result at all.
@@ -3713,12 +3762,11 @@ export function earlyReturnCause(found, universal) {
3713
3762
  * extracted by molecule-dev's `excludedTestTitles`) while a run's heading carries more of it
3714
3763
  * (`comments.spec.ts › Comments › should require login to post comment`), so containment is the
3715
3764
  * comparison. Whitespace is normalised and case is ignored: a brief may wrap the title across a
3716
- * line (`xr-molecule-flash-m44`), and the reporter's own casing never has to match the brief's.
3765
+ * line, and the reporter's own casing never has to match the brief's.
3717
3766
  *
3718
3767
  * Two readers used to answer this separately — the causes note (which therefore asked the
3719
3768
  * executor to fix a test the same result said not to) and molecule-dev's exclusion notes — and
3720
- * xr-molecule-flash-m101 paid for the gap (+12:00: a turn and an edit spent on the excluded
3721
- * test).
3769
+ * a run paid for the gap (a turn and an edit spent on the excluded test).
3722
3770
  *
3723
3771
  * @param heading - The text to match against: a test's title, its whole `file › describe › title`
3724
3772
  * path, or (for a note that only has the run's output) the run's text.
@@ -3741,7 +3789,7 @@ export function matchingExcludedTitles(heading, excluded) {
3741
3789
  *
3742
3790
  * The point is that ONE run yields EVERY cause. A Playwright report prints its failures one
3743
3791
  * after another with no statement of which of them share a cause, so a run of 19 failures is 19
3744
- * bodies to read; X1 flash-m83 (2026-10-16) spent three deploys (6.7 minutes) each fixing a
3792
+ * bodies to read; three deploys (6.7 minutes) each fixed a
3745
3793
  * single cause, two of them found by runs stopped before they walked the rest.
3746
3794
  *
3747
3795
  * The signature is the cause itself where the log carries the failures' bodies (a finished run, a
@@ -3753,8 +3801,8 @@ export function matchingExcludedTitles(heading, excluded) {
3753
3801
  * A test the request EXCLUDES by name is not a cause to fix: its failure does not count for
3754
3802
  * this request ({@link matchingExcludedTitles}), so it is dropped here rather than handed to the
3755
3803
  * executor as work — a result that names it beside "it does not count" is the contradiction
3756
- * xr-molecule-flash-m101 filed (+12:00: the model spent a turn and an edit on the excluded test,
3757
- * on a page that was passing).
3804
+ * a run filed (the model spent a turn and an edit on the excluded test, on a page that was
3805
+ * passing).
3758
3806
  *
3759
3807
  * @param log - The log of a finished or running run.
3760
3808
  * @param excluded - The test titles the request carves out, if the caller knows them.
@@ -3774,8 +3822,8 @@ const FLAKY_TALLY_LINE = /^\s*[1-9]\d*\s+flaky\b/;
3774
3822
  *
3775
3823
  * Playwright prints a numbered failure block for the FIRST attempt of a test that then passes on
3776
3824
  * its retry, and also declares the test under `N flaky` — so a reader that only looks at the
3777
- * blocks counts a flake as a cause to fix. xr-molecule-flash-m105's proof run against the deployed
3778
- * URL reported ten `waitForURL` timing flakes as "causes" of a red run (+24:54), and the executor
3825
+ * blocks counts a flake as a cause to fix. A proof run against the deployed URL reported ten
3826
+ * `waitForURL` timing flakes as "causes" of a red run, and the executor
3779
3827
  * spent its next minutes re-running them one by one until three passed untouched. The
3780
3828
  * declaration under the count is the runner's own answer, and its heading carries the same words
3781
3829
  * as the failure block's ({@link splitTitlePath} reads both the same way).
@@ -3932,7 +3980,7 @@ function groupFailures(log, excluded, pageErrors = []) {
3932
3980
  /**
3933
3981
  * The error every test of a Playwright run reports when the run loaded no base URL: `page.goto('/')`
3934
3982
  * has nothing to resolve against, so the browser refuses the navigation before any page loads.
3935
- * xr-molecule-flash-m113 (+20:34) got ten of these and a list that called them ten causes.
3983
+ * One run got ten of these and a list that called them ten causes.
3936
3984
  */
3937
3985
  const NO_BASE_URL_ERROR = /Cannot navigate to invalid URL/i;
3938
3986
  /** The browser's own `net::ERR_*`, as Playwright prints it for a document that never loaded. */
@@ -3957,7 +4005,7 @@ function everyCauseIsNavigationTimeout(causes) {
3957
4005
  * command loaded no base URL. The line names the config the run should have loaded (the caller
3958
4006
  * passes the one the platform recorded for this suite) and hands over the exact narrowed command
3959
4007
  * with it, so the next run measures the app instead of spending another six minutes on the same
3960
- * configuration mistake (xr-molecule-flash-m113 ran four such runs, 07:06:17 → 07:12:03).
4008
+ * configuration mistake (one run sent four such runs in six minutes).
3961
4009
  *
3962
4010
  * @param causes - The run's failures, all carrying the same error.
3963
4011
  * @param run - How the run was started, when the caller knows it.
@@ -4093,7 +4141,7 @@ const LANDMARK_WORD = {
4093
4141
  * The sentence for a run whose navigational elements are all missing from pages that otherwise
4094
4142
  * painted — the missing shared header the executor must check BEFORE rewriting auth.
4095
4143
  *
4096
- * xr-molecule-flash-m193 (+13:54 → +20:00): four failures on `/` — `a[href="/profile/…"]`,
4144
+ * A run had four failures on `/` — `a[href="/profile/…"]`,
4097
4145
  * `a.navbar-brand`, `nav a[href="/"]`, `nav .user-pic` — while every `error-context.md` snapshot
4098
4146
  * showed the feed, sidebar and footer with no navigation at all. `Home.tsx` imported and
4099
4147
  * re-exported `Navbar` but never rendered it, and `App.tsx` mounted `<Home />` in a bare route.
@@ -4187,7 +4235,7 @@ export function failureCausesNote(log, excluded = [], run, pageErrors = [],
4187
4235
  * Spec files whose selector-contract rows an edit broke since the last whole walk, when the
4188
4236
  * caller knows them: the narrowed re-run names them beside the failing tests. A green re-run of
4189
4237
  * the failure alone would certify a tree it never measured, and the specs selecting the
4190
- * element the edit removed were never walked (xr-molecule-flash-m187 shipped that).
4238
+ * element the edit removed were never walked (a real tree shipped that).
4191
4239
  */
4192
4240
  regressedSpecs = []) {
4193
4241
  const { causes, ignored, flaky } = groupFailures(log, excluded, pageErrors);
@@ -4303,9 +4351,9 @@ regressedSpecs = []) {
4303
4351
  * failure's two most useful body lines underneath.
4304
4352
  *
4305
4353
  * The path is the one the log itself wrote, never a shortened one: it is relative to the
4306
- * command's own cwd, so it is the form the executor can paste back (X1 flash-m82 read a shortened
4307
- * `e2e/auth.spec.ts:19` as the suite's own stale starter spec, rewrote its Playwright config over
4308
- * it and deleted the app's tests — 4 minutes of a 60-minute run on a working config).
4354
+ * command's own cwd, so it is the form the executor can paste back (a shortened
4355
+ * `e2e/auth.spec.ts:19` read as the suite's own stale starter spec, and a Playwright config
4356
+ * rewritten over it deleted the app's tests — 4 minutes of a 60-minute run on a working config).
4309
4357
  *
4310
4358
  * @param cause - The cause to write.
4311
4359
  * @param index - Its position in the recap (0-based; printed 1-based).
@@ -4343,8 +4391,8 @@ function failureCauseLine(cause, index) {
4343
4391
  * found at, and the ONE command that prints their bodies.
4344
4392
  *
4345
4393
  * Without it the old note said "… and 3 more causes" and left the executor to find them in a
4346
- * stdout whose middle the tool-result cut had already removed — xr-molecule-flash-m102 (+12:43)
4347
- * spent four `grep -A` round trips (~25 s) naming three causes the platform had withheld, and a
4394
+ * stdout whose middle the tool-result cut had already removed — four `grep -A` round trips
4395
+ * (~25 s) naming three causes the platform had withheld, and a
4348
4396
  * weaker turn reads the listed ones and skips the rest.
4349
4397
  *
4350
4398
  * @param omitted - The causes past the recap's budget.
@@ -4405,7 +4453,7 @@ function omittedCausesRecap(omitted, run, excluded) {
4405
4453
  * @param run - The run in flight: the command it was started with, as it was run, the directory
4406
4454
  * that command ran in, the config it loaded when the caller knows it, and the settings the
4407
4455
  * request required and the run left out (`env`) — the re-run carries them, so it never
4408
- * reproduces the configuration the run was measured under by mistake (m182).
4456
+ * reproduces the configuration the run was measured under by mistake.
4409
4457
  * @param locations - The failing `file:line`s to re-run, as the run's log printed them.
4410
4458
  * @param excluded - The test titles the request carves out: the re-run inverts them out
4411
4459
  * (`--grep-invert`) so it cannot report them again.
@@ -4439,18 +4487,18 @@ export function rerunOfRunningSuite(run, locations, excluded = []) {
4439
4487
  * The note names the cause early so the executor can START FIXING while the run keeps walking,
4440
4488
  * and it hands a stop command over only when {@link earlyReturnCause} says so — the SAME
4441
4489
  * predicate the wait uses to decide whether to return early, so a finding the wait keeps waiting
4442
- * on is a finding this note never asks to stop for. X1 flash-m76 (2026-10-16) spent 4.0 minutes
4490
+ * on is a finding this note never asks to stop for. One run spent 4.0 minutes
4443
4491
  * walking 15 tests whose one cause was certain at the third, 47 s in, and the note answers
4444
- * exactly that; X1 flash-m83 then stopped eight runs at their first repeating cause in 14.5
4492
+ * exactly that; another stopped eight runs at their first repeating cause in 14.5
4445
4493
  * minutes and never finished one, so the note must not say to stop when the run still has other
4446
- * causes to name. X1 xr-molecule-flash-m99 (2026-10-16) is the third shape: a shared duration is
4494
+ * causes to name. The third shape: a shared duration is
4447
4495
  * not a cause at all, so a lead reaches the executor as a note on a result it is still waiting
4448
4496
  * for — never as a verdict with a stop command in it.
4449
4497
  *
4450
4498
  * The run is STILL WALKING while this note is written — both callers produce it mid-run — so a
4451
4499
  * re-run of one of its tests is a second run against the same app and database, and the platform
4452
4500
  * refuses that ({@link suiteRunInTheWay}, the same rule its overlap hold answers with):
4453
- * xr-molecule-flash-m89 (2026-10-16) read *"Start on them now, while the run keeps walking.
4501
+ * an executor read *"Start on them now, while the run keeps walking.
4454
4502
  * `null-fields.spec.ts:53` alone reproduces the first one."* and sent exactly that re-run 23 s
4455
4503
  * later, where the hold answered *"A playwright suite is already running against this app and
4456
4504
  * database … Wait for it."* The note therefore hands over the read-and-fix to do NOW and the
@@ -4644,7 +4692,7 @@ export const PARSE_CHECKABLE = /\.(m?js|cjs|jsx|ts|tsx|mts|cts|json)$/;
4644
4692
  * Basenames TypeScript reads as JSONC: `tsconfig.json`, `tsconfig.*.json` and
4645
4693
  * the `jsconfig` counterparts. Comments and trailing commas are legal there —
4646
4694
  * `tsc` accepts them — so a strict `JSON.parse` reports a valid project config
4647
- * as broken (X0 flash-m436: a commented `tsconfig.conduit-suite.json` was
4695
+ * as broken (a commented `tsconfig.conduit-suite.json` was
4648
4696
  * answered with "every build and test fails until it does").
4649
4697
  */
4650
4698
  const TYPESCRIPT_CONFIG = /^(?:ts|js)config(?:\.[^/\\]+)?\.json$/;