@molecule/api-ai-tools 1.10.2 → 1.10.3

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-19T01:32:03.421Z
6
+ Generated: 2026-10-19T03:15:38.119Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -626,6 +626,43 @@ interface TraceReadContradiction {
626
626
  }
627
627
  ```
628
628
 
629
+ #### `TraceHiddenBox`
630
+
631
+ A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
632
+ "hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
633
+ failure snapshot carries the element's children — read here, never guessed.
634
+
635
+ ```typescript
636
+ interface TraceHiddenBox {
637
+ /** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
638
+ locator: string
639
+ /** The element's tag, lowercased (`a`). */
640
+ tag: string
641
+ /** The element's own inline style, when the snapshot carries one. */
642
+ style?: string
643
+ /**
644
+ * The `<img>` the element wraps when that image is its only element child and the element has
645
+ * no text of its own: the size the box would collapse to.
646
+ */
647
+ image?: {
648
+ /** The image's `src`, bounded. */
649
+ src?: string
650
+ /** Its `width` attribute. */
651
+ width?: string
652
+ /** Its `height` attribute. */
653
+ height?: string
654
+ /** Its inline `style`. */
655
+ style?: string
656
+ /** Its `class`. */
657
+ className?: string
658
+ /** Its `alt` text. */
659
+ alt?: string
660
+ }
661
+ /** True when the element has no text and no element children at all. */
662
+ empty?: true
663
+ }
664
+ ```
665
+
629
666
  #### `TraceArtifact`
630
667
 
631
668
  What one trace says: the test it belongs to, the requests it made, and its console lines.
@@ -715,6 +752,13 @@ interface TraceArtifact {
715
752
  * and on traces read before this field existed.
716
753
  */
717
754
  readContradiction?: TraceReadContradiction
755
+ /**
756
+ * A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
757
+ * the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
758
+ * it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
759
+ * leaving the run to probe computed styles (xr-molecule-flash-m197).
760
+ */
761
+ hiddenBox?: TraceHiddenBox
718
762
  }
719
763
  ```
720
764
 
@@ -1818,7 +1862,9 @@ the runner invocation each caller read as a run is one.
1818
1862
 
1819
1863
  A command that also runs tests is false however it is spelled: `npx playwright install &&
1820
1864
  npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
1821
- reading a real run.
1865
+ reading a real run. So is a command carrying a segment this reader cannot place (a script it
1866
+ does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
1867
+ table can be accounted for, and "only prints" is a claim about the WHOLE command.
1822
1868
 
1823
1869
  ```typescript
1824
1870
  function commandRunsNoTests(command: string): boolean
@@ -2409,6 +2455,26 @@ function isCredentialExchangePath(path: string, authEndpoints?: readonly string[
2409
2455
 
2410
2456
  **Returns:** True when the path names a credential exchange.
2411
2457
 
2458
+ #### `hiddenBoxNote(items)`
2459
+
2460
+ The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
2461
+
2462
+ Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
2463
+ is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
2464
+ only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
2465
+ width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
2466
+ (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
2467
+ note said only "the cause is in the page", and the run spent over four minutes rasterising an
2468
+ SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
2469
+
2470
+ ```typescript
2471
+ function hiddenBoxNote(items: readonly TraceArtifact[]): string | null
2472
+ ```
2473
+
2474
+ - `items` — The traces {@link parseTraceArtifacts} read.
2475
+
2476
+ **Returns:** The sentence, or null when no failure carries a hidden-box reading.
2477
+
2412
2478
  #### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
2413
2479
 
2414
2480
  The note a failed suite run's result carries: what each failing test's page actually sent, and
@@ -49,7 +49,9 @@
49
49
  *
50
50
  * A command that also runs tests is false however it is spelled: `npx playwright install &&
51
51
  * npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
52
- * reading a real run.
52
+ * reading a real run. So is a command carrying a segment this reader cannot place (a script it
53
+ * does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
54
+ * table can be accounted for, and "only prints" is a claim about the WHOLE command.
53
55
  *
54
56
  * @param command - The shell command as the executor sent it.
55
57
  * @returns True when the command invokes a runner only to print.
@@ -1 +1 @@
1
- {"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA0EH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAU3D"}
1
+ {"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAiLH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB3D"}
@@ -75,6 +75,108 @@ const NO_RUN_FLAGS = {
75
75
  ],
76
76
  hurl: [/(?:^|\s)--help(?=\s|$)/, /(?:^|\s)-h(?=\s|$)/, /(?:^|\s)--version(?=\s|$)/],
77
77
  };
78
+ /**
79
+ * The programs that start no test — the shell's own bookkeeping (`cd`, `export`, `echo`), and the
80
+ * readers, printers and file commands an executor uses to look at what is already there (`cat`,
81
+ * `ls`, `cp`, `tail`, …).
82
+ *
83
+ * A segment whose program is neither a runner nor one of these is a program this reader cannot
84
+ * account for: the suite's own script (`./run-api-tests-hurl.sh`), a wrapper the executor wrote,
85
+ * anything that may itself start a suite. {@link commandRunsNoTests} answers false for a command
86
+ * carrying one — "every invocation only prints" is not a claim an unknown step can support.
87
+ * X1 xr-molecule-flash-m197 (+05:18) sent
88
+ * `hurl --version | head -2 && HOST=http://localhost:4000 ./run-api-tests-hurl.sh 2>&1 | tail -60`,
89
+ * the suite passed 13/13, and the supplied script was the one segment nobody could place: the
90
+ * whole command read as a version probe, so every "has the suite run?" reader said no for the
91
+ * rest of the run and the early deploy was held to the end of the list.
92
+ */
93
+ const NO_TEST_PROGRAMS = new Set([
94
+ '[',
95
+ '[[',
96
+ ':',
97
+ 'awk',
98
+ 'base64',
99
+ 'basename',
100
+ 'case',
101
+ 'cat',
102
+ 'cd',
103
+ 'chmod',
104
+ 'chown',
105
+ 'column',
106
+ 'comm',
107
+ 'cp',
108
+ 'cut',
109
+ 'date',
110
+ 'df',
111
+ 'diff',
112
+ 'dirname',
113
+ 'do',
114
+ 'done',
115
+ 'du',
116
+ 'echo',
117
+ 'egrep',
118
+ 'else',
119
+ 'env',
120
+ 'esac',
121
+ 'export',
122
+ 'expr',
123
+ 'false',
124
+ 'fgrep',
125
+ 'fi',
126
+ 'file',
127
+ 'fold',
128
+ 'for',
129
+ 'grep',
130
+ 'head',
131
+ 'hostname',
132
+ 'id',
133
+ 'if',
134
+ 'jq',
135
+ 'kill',
136
+ 'less',
137
+ 'ln',
138
+ 'ls',
139
+ 'md5sum',
140
+ 'mkdir',
141
+ 'mktemp',
142
+ 'more',
143
+ 'mv',
144
+ 'nl',
145
+ 'od',
146
+ 'paste',
147
+ 'printf',
148
+ 'pwd',
149
+ 'readlink',
150
+ 'realpath',
151
+ 'rev',
152
+ 'rm',
153
+ 'rmdir',
154
+ 'seq',
155
+ 'set',
156
+ 'sha256sum',
157
+ 'sleep',
158
+ 'sort',
159
+ 'stat',
160
+ 'tac',
161
+ 'tail',
162
+ 'tee',
163
+ 'test',
164
+ 'then',
165
+ 'touch',
166
+ 'tr',
167
+ 'true',
168
+ 'type',
169
+ 'uname',
170
+ 'uniq',
171
+ 'unset',
172
+ 'until',
173
+ 'wait',
174
+ 'wc',
175
+ 'which',
176
+ 'while',
177
+ 'whoami',
178
+ 'xxd',
179
+ ]);
78
180
  /**
79
181
  * A heredoc body is data a command writes, not what it runs: a `playwright test --list` inside
80
182
  * the config a command writes is not a run of this command either way. The pattern consumes the
@@ -113,7 +215,9 @@ function informational(runner, after) {
113
215
  *
114
216
  * A command that also runs tests is false however it is spelled: `npx playwright install &&
115
217
  * npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
116
- * reading a real run.
218
+ * reading a real run. So is a command carrying a segment this reader cannot place (a script it
219
+ * does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
220
+ * table can be accounted for, and "only prints" is a claim about the WHOLE command.
117
221
  *
118
222
  * @param command - The shell command as the executor sent it.
119
223
  * @returns True when the command invokes a runner only to print.
@@ -123,9 +227,16 @@ export function commandRunsNoTests(command) {
123
227
  return false;
124
228
  let invocations = 0;
125
229
  for (const segment of commandSegments(command.replace(HEREDOC, ' '))) {
126
- const runner = RUNNERS[segment.program];
127
- if (!runner)
230
+ // An own key only: `constructor` is a program name (`constructor --version`) and a
231
+ // prototype key is not a runner.
232
+ const runner = Object.hasOwn(RUNNERS, segment.program) ? RUNNERS[segment.program] : undefined;
233
+ if (runner === undefined) {
234
+ // A program this reader cannot place may start a suite of its own; only a known
235
+ // non-test program lets the command keep reading as "prints only".
236
+ if (!NO_TEST_PROGRAMS.has(segment.program))
237
+ return false;
128
238
  continue;
239
+ }
129
240
  invocations += 1;
130
241
  if (!informational(runner, segment.args.join(' ')))
131
242
  return false;
@@ -223,6 +223,39 @@ export interface TraceReadContradiction {
223
223
  /** The later GET of the same resource (`GET /api/profiles/x`). */
224
224
  read: TraceRequest;
225
225
  }
226
+ /**
227
+ * A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
228
+ * "hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
229
+ * failure snapshot carries the element's children — read here, never guessed.
230
+ */
231
+ export interface TraceHiddenBox {
232
+ /** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
233
+ locator: string;
234
+ /** The element's tag, lowercased (`a`). */
235
+ tag: string;
236
+ /** The element's own inline style, when the snapshot carries one. */
237
+ style?: string;
238
+ /**
239
+ * The `<img>` the element wraps when that image is its only element child and the element has
240
+ * no text of its own: the size the box would collapse to.
241
+ */
242
+ image?: {
243
+ /** The image's `src`, bounded. */
244
+ src?: string;
245
+ /** Its `width` attribute. */
246
+ width?: string;
247
+ /** Its `height` attribute. */
248
+ height?: string;
249
+ /** Its inline `style`. */
250
+ style?: string;
251
+ /** Its `class`. */
252
+ className?: string;
253
+ /** Its `alt` text. */
254
+ alt?: string;
255
+ };
256
+ /** True when the element has no text and no element children at all. */
257
+ empty?: true;
258
+ }
226
259
  /** What one trace says: the test it belongs to, the requests it made, and its console lines. */
227
260
  export interface TraceArtifact {
228
261
  /** The failure's spec file, relative to the run's directory, when the trace names one. */
@@ -308,6 +341,13 @@ export interface TraceArtifact {
308
341
  * and on traces read before this field existed.
309
342
  */
310
343
  readContradiction?: TraceReadContradiction;
344
+ /**
345
+ * A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
346
+ * the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
347
+ * it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
348
+ * leaving the run to probe computed styles (xr-molecule-flash-m197).
349
+ */
350
+ hiddenBox?: TraceHiddenBox;
311
351
  }
312
352
  /** How many traces one read reports, newest first. */
313
353
  export declare const MAX_TRACE_ARTIFACTS = 12;
@@ -486,6 +526,21 @@ export interface TraceRunReport {
486
526
  * @returns True when the path names a credential exchange.
487
527
  */
488
528
  export declare function isCredentialExchangePath(path: string, authEndpoints?: readonly string[]): boolean;
529
+ /**
530
+ * The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
531
+ *
532
+ * Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
533
+ * is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
534
+ * only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
535
+ * width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
536
+ * (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
537
+ * note said only "the cause is in the page", and the run spent over four minutes rasterising an
538
+ * SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
539
+ *
540
+ * @param items - The traces {@link parseTraceArtifacts} read.
541
+ * @returns The sentence, or null when no failure carries a hidden-box reading.
542
+ */
543
+ export declare function hiddenBoxNote(items: readonly TraceArtifact[]): string | null;
489
544
  /**
490
545
  * The note a failed suite run's result carries: what each failing test's page actually sent, and
491
546
  * what came back.
@@ -1 +1 @@
1
- {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
@@ -236,6 +236,109 @@ function waitedTextOf(message) {
236
236
  return quoted(/Expected(?: string)?:\\s*(["'])((?:(?!\\1).)*)\\1/)
237
237
  }
238
238
 
239
+ // The attributes of one serialized element's opening tag (\`class="a b" id="c"\`). The trace's
240
+ // snapshots carry them as an object already; the ASSERTION's error prints the tag as text.
241
+ function attrsOf(source) {
242
+ const out = {}
243
+ const pattern = /([A-Za-z_:][-A-Za-z0-9_:.]*)\\s*=\\s*("([^"]*)"|'([^']*)')/g
244
+ let match
245
+ while ((match = pattern.exec(String(source || ''))) !== null)
246
+ out[match[1].toLowerCase()] = match[3] !== undefined ? match[3] : match[4]
247
+ return out
248
+ }
249
+
250
+ // Whether a serialized subtree carries text of its own. A text node is a string; an element is
251
+ // \`[tag, attrs, …children]\`. A reference to an earlier snapshot is not followed here: the caller
252
+ // only makes a claim when every child is readable in place.
253
+ function subtreeText(node) {
254
+ if (typeof node === 'string') return node.trim() !== ''
255
+ if (!Array.isArray(node) || Array.isArray(node[0]) || typeof node[0] !== 'string') return false
256
+ for (let i = 2; i < node.length; i++) if (subtreeText(node[i])) return true
257
+ return false
258
+ }
259
+
260
+ // The serialized element children of a node, in document order (references skipped).
261
+ function elementChildren(node) {
262
+ const out = []
263
+ if (!Array.isArray(node)) return out
264
+ for (let i = 2; i < node.length; i++) {
265
+ const child = node[i]
266
+ if (typeof child === 'string' || !Array.isArray(child)) continue
267
+ if (Array.isArray(child[0]) || typeof child[0] !== 'string') continue
268
+ out.push(child)
269
+ }
270
+ return out
271
+ }
272
+
273
+ // The first element in a serialized DOM tree with this tag and (when given) id or first class.
274
+ // Playwright's snapshot serializes elements as \`[tag, attrs, …children]\`.
275
+ function findElement(node, tag, id, cls) {
276
+ if (typeof node === 'string' || !Array.isArray(node)) return null
277
+ if (Array.isArray(node[0]) || typeof node[0] !== 'string') return null
278
+ if (node[0].toLowerCase() === tag) {
279
+ const attrs = node[1] && typeof node[1] === 'object' ? node[1] : {}
280
+ const idMatch = id === '' || String(attrs.id || '') === id
281
+ const classes = String(attrs.class || attrs.className || '').split(/\\s+/)
282
+ const classMatch = id !== '' || cls === '' || classes.indexOf(cls) !== -1
283
+ if (idMatch && classMatch) return node
284
+ }
285
+ for (let i = 2; i < node.length; i++) {
286
+ const deeper = findElement(node[i], tag, id, cls)
287
+ if (deeper !== null) return deeper
288
+ }
289
+ return null
290
+ }
291
+
292
+ // A locator that RESOLVED whose box Playwright calls hidden. Playwright reports "hidden" for an
293
+ // element with a zero-size box even when it is painted, so the interesting question is why the
294
+ // box is empty — and the trace's own frame snapshot carries the element's children, the only
295
+ // evidence the run would otherwise have to probe for (xr-molecule-flash-m197: \`a.navbar-brand\`
296
+ // was 0x4 px because its only child was an <img> with no CSS size, and the note said only that
297
+ // "the cause is in the page"; the run spent 4.5 minutes rasterising an SVG to PNG).
298
+ // Only what the snapshot proves is reported: an inline display:none/visibility:hidden, a lone
299
+ // <img> child, or a textless element with no children. Anything else says nothing.
300
+ function hiddenBoxOf(message, frames) {
301
+ if (!message) return null
302
+ const text = String(message).replace(/\\u001b\\[[0-9;]*m/g, '')
303
+ if (text.indexOf('strict mode violation') !== -1) return null
304
+ if (!/Received:\\s*hidden/.test(text) && text.indexOf('element is not visible') === -1) return null
305
+ const resolved = /locator resolved to <([A-Za-z][\\w:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/.exec(text)
306
+ if (resolved === null) return null
307
+ const tag = resolved[1].toLowerCase()
308
+ const attrs = attrsOf(resolved[2])
309
+ const id = attrs.id || ''
310
+ const cls = String(attrs.class || attrs.className || '').split(/\\s+/).filter(Boolean)[0] || ''
311
+ const named = /Locator:\\s*([^\\n]+)/.exec(text)
312
+ const reading = {
313
+ locator: named !== null ? named[1].trim() : cls ? tag + '.' + cls : tag,
314
+ tag: tag,
315
+ }
316
+ const snapshot = frames.length > 0 ? frames[frames.length - 1].snapshot : null
317
+ const node = snapshot === null || snapshot === undefined ? null : findElement(snapshot, tag, id, cls)
318
+ if (node === null) return null
319
+ const own = node[1] && typeof node[1] === 'object' ? node[1] : {}
320
+ if (typeof own.style === 'string' && own.style) reading.style = own.style.slice(0, 160)
321
+ if (!subtreeText(node)) {
322
+ const children = elementChildren(node)
323
+ if (children.length === 1 && children[0][0].toLowerCase() === 'img') {
324
+ const image = children[0][1] && typeof children[0][1] === 'object' ? children[0][1] : {}
325
+ reading.image = {}
326
+ for (const key of ['src', 'width', 'height', 'style', 'alt']) {
327
+ if (typeof image[key] === 'string' && image[key])
328
+ reading.image[key] = image[key].slice(0, 120)
329
+ }
330
+ if (typeof image.class === 'string' && image.class) reading.image.className = image.class.slice(0, 120)
331
+ } else if (children.length === 0) {
332
+ reading.empty = true
333
+ }
334
+ }
335
+ const hiddenStyle = /(?:^|;)\\s*display\\s*:\\s*none|(?:^|;)\\s*visibility\\s*:\\s*hidden/.test(
336
+ typeof own.style === 'string' ? own.style : '',
337
+ )
338
+ if (!reading.image && !reading.empty && !hiddenStyle) return null
339
+ return reading
340
+ }
341
+
239
342
  // A request/response body exactly as Playwright stored it: inline on the value, or in a resource
240
343
  // entry the value names. Bounded, and binary is dropped — a JSON body is what this is for, and a
241
344
  // multipart upload would print bytes.
@@ -892,6 +995,10 @@ for (const one of found.slice(0, MAX_TRACES)) {
892
995
  }
893
996
  }
894
997
  }
998
+ // A visibility failure whose element RESOLVED but has an empty box: the snapshot's own DOM says
999
+ // what the element is made of, so the note names the cause instead of sending the run at the
1000
+ // selector (m197 — an <img> with no CSS size collapsed inside its link).
1001
+ const hiddenBox = hiddenBoxOf(failedError, frames)
895
1002
  function wire(r) {
896
1003
  return {
897
1004
  method: r.method,
@@ -1112,7 +1219,13 @@ for (const one of found.slice(0, MAX_TRACES)) {
1112
1219
  const specific = consoleErrors.filter(function (text) { return !/^Failed to load resource/.test(text) })
1113
1220
  const keptConsole = specific.concat(consoleSignals, consoleWarnings)
1114
1221
  if (specific.length === 0) keptConsole.push.apply(keptConsole, consoleErrors)
1115
- if (chosen.length === 0 && keptConsole.length === 0 && actions.length === 0) continue
1222
+ if (
1223
+ chosen.length === 0 &&
1224
+ keptConsole.length === 0 &&
1225
+ actions.length === 0 &&
1226
+ hiddenBox === null
1227
+ )
1228
+ continue
1116
1229
  process.stdout.write('TRACE ' + JSON.stringify({
1117
1230
  dir: path.relative(ROOT, path.dirname(one.file)),
1118
1231
  spec: spec,
@@ -1125,6 +1238,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
1125
1238
  requests: chosen.slice(0, MAX_REQUESTS),
1126
1239
  ...(missingText === null ? {} : { missingText: missingText }),
1127
1240
  ...(readContradiction === null ? {} : { readContradiction: readContradiction }),
1241
+ ...(hiddenBox === null ? {} : { hiddenBox: hiddenBox }),
1128
1242
  console: keptConsole.slice(0, MAX_CONSOLE).map(function (text) { return text.slice(0, 300) }),
1129
1243
  actions: actions.slice(-MAX_ACTIONS),
1130
1244
  ...(unattributed ? { unattributed: true } : {}),
@@ -1207,6 +1321,45 @@ function parseReadContradiction(value) {
1207
1321
  return null;
1208
1322
  return { field: raw.field, writeValue: raw.writeValue, readValue: raw.readValue, write, read };
1209
1323
  }
1324
+ /**
1325
+ * One `hiddenBox` out of a `TRACE` line's json, or null when the value is not one.
1326
+ *
1327
+ * The locator and tag are required; the image/empty/style detail is optional evidence, kept only
1328
+ * in the shape the script wrote it.
1329
+ *
1330
+ * @param value - The line's `hiddenBox`.
1331
+ * @returns The element the failed locator resolved to, or null.
1332
+ */
1333
+ function parseHiddenBox(value) {
1334
+ if (typeof value !== 'object' || value === null)
1335
+ return null;
1336
+ const raw = value;
1337
+ if (typeof raw.locator !== 'string' || raw.locator === '')
1338
+ return null;
1339
+ if (typeof raw.tag !== 'string' || raw.tag === '')
1340
+ return null;
1341
+ // An `image` that parses to no attributes is still the fact that the element wraps an <img>:
1342
+ // its presence is the evidence, so an empty object stays.
1343
+ const image = typeof raw.image === 'object' && raw.image !== null
1344
+ ? (() => {
1345
+ const at = raw.image;
1346
+ const out = {};
1347
+ for (const key of ['src', 'width', 'height', 'style', 'className', 'alt']) {
1348
+ const field = at[key];
1349
+ if (typeof field === 'string' && field !== '')
1350
+ out[key] = field;
1351
+ }
1352
+ return out;
1353
+ })()
1354
+ : null;
1355
+ return {
1356
+ locator: raw.locator,
1357
+ tag: raw.tag,
1358
+ ...(typeof raw.style === 'string' && raw.style ? { style: raw.style } : {}),
1359
+ ...(image === null ? {} : { image }),
1360
+ ...(raw.empty === true ? { empty: true } : {}),
1361
+ };
1362
+ }
1210
1363
  /**
1211
1364
  * One `lastAction` out of a `TRACE` line's json, or null when the value is not one.
1212
1365
  *
@@ -1254,6 +1407,7 @@ export function parseTraceArtifacts(stdout) {
1254
1407
  const lastAction = parseLastAction(raw.lastAction);
1255
1408
  const missingText = parseMissingText(raw.missingText);
1256
1409
  const readContradiction = parseReadContradiction(raw.readContradiction);
1410
+ const hiddenBox = parseHiddenBox(raw.hiddenBox);
1257
1411
  const otherContexts = Array.isArray(raw.otherContexts)
1258
1412
  ? raw.otherContexts
1259
1413
  .filter((c) => typeof c === 'object' && c !== null)
@@ -1291,6 +1445,7 @@ export function parseTraceArtifacts(stdout) {
1291
1445
  ...(raw.unattributed === true ? { unattributed: true } : {}),
1292
1446
  ...(missingText === null ? {} : { missingText }),
1293
1447
  ...(readContradiction === null ? {} : { readContradiction }),
1448
+ ...(hiddenBox === null ? {} : { hiddenBox }),
1294
1449
  });
1295
1450
  }
1296
1451
  catch (_error) {
@@ -2452,6 +2607,71 @@ function consoleLine(text) {
2452
2607
  return (`${url} answered the app's index.html, not a font — the file is missing from ` +
2453
2608
  `\`app/public/\` (or the asset import); copy/import it`);
2454
2609
  }
2610
+ /** An `<img>` as the failure snapshot recorded it, for the note's sentence. */
2611
+ function imageMarkup(image) {
2612
+ const attrs = [
2613
+ image.src === undefined ? '' : `src="${image.src}"`,
2614
+ image.alt === undefined ? '' : `alt="${image.alt}"`,
2615
+ image.width === undefined ? '' : `width="${image.width}"`,
2616
+ image.height === undefined ? '' : `height="${image.height}"`,
2617
+ image.className === undefined ? '' : `class="${image.className}"`,
2618
+ image.style === undefined ? '' : `style="${image.style}"`,
2619
+ ].filter(Boolean);
2620
+ return `<img${attrs.length > 0 ? ` ${attrs.join(' ')}` : ''}>`;
2621
+ }
2622
+ /** Whether an image's CSS (its class or inline style) declares a width or height. */
2623
+ const IMAGE_SIZE_RULE = /(?:^|;)\s*(?:width|height|max-width|max-height)\s*:/;
2624
+ const HIDING_RULE = /(?:^|;)\s*(?:display\s*:\s*none|visibility\s*:\s*hidden)/;
2625
+ /**
2626
+ * The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
2627
+ *
2628
+ * Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
2629
+ * is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
2630
+ * only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
2631
+ * width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
2632
+ * (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
2633
+ * note said only "the cause is in the page", and the run spent over four minutes rasterising an
2634
+ * SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
2635
+ *
2636
+ * @param items - The traces {@link parseTraceArtifacts} read.
2637
+ * @returns The sentence, or null when no failure carries a hidden-box reading.
2638
+ */
2639
+ export function hiddenBoxNote(items) {
2640
+ const box = items.find((item) => item.hiddenBox !== undefined)?.hiddenBox;
2641
+ if (box === undefined)
2642
+ return null;
2643
+ const element = `\`${box.locator}\``;
2644
+ if (box.image !== undefined) {
2645
+ const image = box.image;
2646
+ const shown = imageMarkup(image);
2647
+ const sized = image.className !== undefined ||
2648
+ (image.style !== undefined && IMAGE_SIZE_RULE.test(image.style));
2649
+ if (sized) {
2650
+ const rule = image.className === undefined ? 'its inline size' : `\`${image.className}\``;
2651
+ return (`${element} resolved but Playwright calls it hidden: its box has no size, and its only ` +
2652
+ `content is ${shown}. That image declares a size (${rule}), so the rule is not applying ` +
2653
+ `on this page — check that the stylesheet defining it is actually loaded (a supplied ` +
2654
+ `theme has to be linked in index.html, not only imported) and that the class name is ` +
2655
+ `spelled the way the theme defines it.`);
2656
+ }
2657
+ return (`${element} resolved but Playwright calls it hidden: the element's box is empty, and its ` +
2658
+ `only content is ${shown} — an image with no CSS size. An <img> takes its size from CSS or ` +
2659
+ `from the file; an SVG that declares only a viewBox has no intrinsic size, and a ` +
2660
+ `width/height ATTRIBUTE loses to any stylesheet rule (Tailwind's preflight, which the ` +
2661
+ `scaffold's index.css imports, sets img { max-width: 100%; height: auto }). Give the image a ` +
2662
+ `CSS width/height — a class the supplied theme already defines for it, or ` +
2663
+ `style={{ height: … }} — rather than changing the SVG or rasterising it to PNG.`);
2664
+ }
2665
+ if (box.style !== undefined && HIDING_RULE.test(box.style)) {
2666
+ return (`${element} resolved but Playwright calls it hidden: its own inline style sets ` +
2667
+ `\`${box.style}\`. Remove that declaration (or the rule that sets it) so the element has a box.`);
2668
+ }
2669
+ if (box.empty === true) {
2670
+ return (`${element} resolved but Playwright calls it hidden: the element has no text and no ` +
2671
+ `children, so its box has zero size. Give it content or an explicit width/height.`);
2672
+ }
2673
+ return null;
2674
+ }
2455
2675
  /**
2456
2676
  * The note a failed suite run's result carries: what each failing test's page actually sent, and
2457
2677
  * what came back.
@@ -2590,6 +2810,10 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
2590
2810
  const wrapped = traced.some((item) => item.requests.some((request) => request.sent !== undefined && wrappedInData(request.sent)));
2591
2811
  // The "one of these requests failing" answer is only honest when a listed request really failed.
2592
2812
  const anyFailed = traced.some((item) => item.requests.some(failedRequest));
2813
+ // A visibility failure the trace's own snapshot explains: said instead of the generic
2814
+ // "the cause is in the page" sentence, which m197 read as "go look at the page" and then spent
2815
+ // four minutes rasterising an SVG ({@link hiddenBoxNote}).
2816
+ const hiddenBox = hiddenBoxNote(traced);
2593
2817
  // m145's shape: the app's HTTP client lost its base URL, so every API call reached the page
2594
2818
  // origin without the prefix the app mounts its routes under. The prefix is READ — the caller's
2595
2819
  // client config plus what the traces show — never assumed to be `/api`.
@@ -2954,13 +3178,17 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
2954
3178
  'action sent no request at all, so there is no response to read. The page changed ' +
2955
3179
  'route and fetched nothing, which is why it still shows the previous route’s data: ' +
2956
3180
  'fix how the page reads the route, not the selector.'
2957
- : 'No listed request failed — none got a 4xx/5xx or a network error — so the cause is in the ' +
2958
- "page, not the API. Start with what the test's last action sent (the first line of each " +
2959
- 'row) and what the page did next: a request that answered (2xx included) while the page ' +
2960
- 'still did not show the state the assertion waited on is the fact to read — its status ' +
2961
- 'and response body, not the selector. If the request is not the one the test meant — an ' +
2962
- 'unfollow sent where the test clicked follow — the state the action read was stale ' +
2963
- 'before it.') +
3181
+ : hiddenBox !== null
3182
+ ? // The trace's own snapshot explains why the element Playwright resolved has no box:
3183
+ // a fact about the element, so it replaces the "look at the page" advice (m197).
3184
+ hiddenBox
3185
+ : 'No listed request failed — none got a 4xx/5xx or a network error — so the cause is in the ' +
3186
+ "page, not the API. Start with what the test's last action sent (the first line of each " +
3187
+ 'row) and what the page did next: a request that answered (2xx included) while the page ' +
3188
+ 'still did not show the state the assertion waited on is the fact to read — its status ' +
3189
+ 'and response body, not the selector. If the request is not the one the test meant — an ' +
3190
+ 'unfollow sent where the test clicked follow — the state the action read was stale ' +
3191
+ 'before it.') +
2964
3192
  prefixAdvice +
2965
3193
  (wrapped
2966
3194
  ? ' The API received a body wrapped in `data`: app-http’s `post(url, data, config)` / ' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "description": "Shared AI agent tools with backend abstraction for sandbox and local execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",