@reqvire-org/ads 0.3.15 → 0.3.16

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.
@@ -0,0 +1,37 @@
1
+ const retryDelays = [200, 800];
2
+ const networkFailure = error => error?.name === "TypeError" && /^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.)$/.test(error.message);
3
+
4
+ function pause(milliseconds, signal) {
5
+ return new Promise((resolve, reject) => {
6
+ signal?.throwIfAborted();
7
+ const finish = () => { signal?.removeEventListener("abort", abort); resolve(); };
8
+ const timer = setTimeout(finish, milliseconds);
9
+ const abort = () => { clearTimeout(timer); signal.removeEventListener("abort", abort); reject(signal.reason); };
10
+ signal?.addEventListener("abort", abort, { once: true });
11
+ });
12
+ }
13
+
14
+ /** Only resources/read is repeatable here; never wrap tool calls or assertions. */
15
+ export function resourceReader(read, { signal, onDiagnostic, wait = pause } = {}) {
16
+ return async input => {
17
+ for (let attempt = 1; ; attempt++) {
18
+ signal?.throwIfAborted();
19
+ try {
20
+ const response = await read(input);
21
+ signal?.throwIfAborted();
22
+ if (attempt > 1) onDiagnostic?.({ state: "recovered", uri: input.uri, attempt });
23
+ return response;
24
+ } catch (error) {
25
+ signal?.throwIfAborted();
26
+ if (!networkFailure(error)) throw error;
27
+ if (attempt > retryDelays.length) {
28
+ onDiagnostic?.({ state: "exhausted", uri: input.uri, attempt, message: error.message });
29
+ throw Object.assign(new Error(`MCP resource transport failed for ${input.uri} after ${attempt} attempts: ${error.message}. Inspect the host connection and browser network diagnostics; this resource-loading error does not establish a component assertion failure.`, { cause: error }), { code: "ADS_RESOURCE_TRANSPORT" });
30
+ }
31
+ const delayMs = retryDelays[attempt - 1];
32
+ onDiagnostic?.({ state: "retry", uri: input.uri, attempt, nextAttempt: attempt + 1, delayMs, message: error.message });
33
+ await wait(delayMs, signal);
34
+ }
35
+ }
36
+ };
37
+ }
@@ -51,6 +51,18 @@ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actua
51
51
  gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
52
  changes; ADS does not silently raise budgets or override the worker count.
53
53
 
54
+ If a check cannot load its MCP preview, preserve the draft and inspect the exact
55
+ resource URI and failed browser requests before changing component code. The ADS
56
+ showcase retries a fetch transport failure on `resources/read` at most twice
57
+ (200/800 ms backoff); recovered startup reads remain visible in
58
+ `startupDiagnostics`. It never retries tool calls, assertions or complete checks,
59
+ and persistent failures still block application. HTTP/protocol/content errors,
60
+ integrity failures and cancellation do not retry. External hosts own their
61
+ transport behavior. Chrome's `ERR_NETWORK_CHANGED` does not prove an ADS restart
62
+ or identify Docker/VPN churn: correlate failure timestamps with live interface
63
+ events, Chrome network logs and server availability. Do not weaken checks or
64
+ rewrite a valid API to hide infrastructure failures.
65
+
54
66
  Named barrel imports can narrow affected checks when the project accurately
55
67
  declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
68
  or unsupported policies and dynamic/wildcard dependencies stay conservative.
@@ -51,6 +51,18 @@ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actua
51
51
  gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
52
  changes; ADS does not silently raise budgets or override the worker count.
53
53
 
54
+ If a check cannot load its MCP preview, preserve the draft and inspect the exact
55
+ resource URI and failed browser requests before changing component code. The ADS
56
+ showcase retries a fetch transport failure on `resources/read` at most twice
57
+ (200/800 ms backoff); recovered startup reads remain visible in
58
+ `startupDiagnostics`. It never retries tool calls, assertions or complete checks,
59
+ and persistent failures still block application. HTTP/protocol/content errors,
60
+ integrity failures and cancellation do not retry. External hosts own their
61
+ transport behavior. Chrome's `ERR_NETWORK_CHANGED` does not prove an ADS restart
62
+ or identify Docker/VPN churn: correlate failure timestamps with live interface
63
+ events, Chrome network logs and server availability. Do not weaken checks or
64
+ rewrite a valid API to hide infrastructure failures.
65
+
54
66
  Named barrel imports can narrow affected checks when the project accurately
55
67
  declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
68
  or unsupported policies and dynamic/wildcard dependencies stay conservative.
@@ -51,6 +51,18 @@ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actua
51
51
  gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
52
  changes; ADS does not silently raise budgets or override the worker count.
53
53
 
54
+ If a check cannot load its MCP preview, preserve the draft and inspect the exact
55
+ resource URI and failed browser requests before changing component code. The ADS
56
+ showcase retries a fetch transport failure on `resources/read` at most twice
57
+ (200/800 ms backoff); recovered startup reads remain visible in
58
+ `startupDiagnostics`. It never retries tool calls, assertions or complete checks,
59
+ and persistent failures still block application. HTTP/protocol/content errors,
60
+ integrity failures and cancellation do not retry. External hosts own their
61
+ transport behavior. Chrome's `ERR_NETWORK_CHANGED` does not prove an ADS restart
62
+ or identify Docker/VPN churn: correlate failure timestamps with live interface
63
+ events, Chrome network logs and server availability. Do not weaken checks or
64
+ rewrite a valid API to hide infrastructure failures.
65
+
54
66
  Named barrel imports can narrow affected checks when the project accurately
55
67
  declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
68
  or unsupported policies and dynamic/wildcard dependencies stay conservative.
@@ -51,6 +51,18 @@ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actua
51
51
  gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
52
  changes; ADS does not silently raise budgets or override the worker count.
53
53
 
54
+ If a check cannot load its MCP preview, preserve the draft and inspect the exact
55
+ resource URI and failed browser requests before changing component code. The ADS
56
+ showcase retries a fetch transport failure on `resources/read` at most twice
57
+ (200/800 ms backoff); recovered startup reads remain visible in
58
+ `startupDiagnostics`. It never retries tool calls, assertions or complete checks,
59
+ and persistent failures still block application. HTTP/protocol/content errors,
60
+ integrity failures and cancellation do not retry. External hosts own their
61
+ transport behavior. Chrome's `ERR_NETWORK_CHANGED` does not prove an ADS restart
62
+ or identify Docker/VPN churn: correlate failure timestamps with live interface
63
+ events, Chrome network logs and server availability. Do not weaken checks or
64
+ rewrite a valid API to hide infrastructure failures.
65
+
54
66
  Named barrel imports can narrow affected checks when the project accurately
55
67
  declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
68
  or unsupported policies and dynamic/wildcard dependencies stay conservative.
@@ -51,6 +51,18 @@ blanket sleeps. Fake clocks suit timer-only guarantees; keep real time for actua
51
51
  gesture/motion/layout guarantees. Project settings belong in guarded configuration
52
52
  changes; ADS does not silently raise budgets or override the worker count.
53
53
 
54
+ If a check cannot load its MCP preview, preserve the draft and inspect the exact
55
+ resource URI and failed browser requests before changing component code. The ADS
56
+ showcase retries a fetch transport failure on `resources/read` at most twice
57
+ (200/800 ms backoff); recovered startup reads remain visible in
58
+ `startupDiagnostics`. It never retries tool calls, assertions or complete checks,
59
+ and persistent failures still block application. HTTP/protocol/content errors,
60
+ integrity failures and cancellation do not retry. External hosts own their
61
+ transport behavior. Chrome's `ERR_NETWORK_CHANGED` does not prove an ADS restart
62
+ or identify Docker/VPN churn: correlate failure timestamps with live interface
63
+ events, Chrome network logs and server availability. Do not weaken checks or
64
+ rewrite a valid API to hide infrastructure failures.
65
+
54
66
  Named barrel imports can narrow affected checks when the project accurately
55
67
  declares package `sideEffects`; retain CSS/global effects in that policy. Missing
56
68
  or unsupported policies and dynamic/wildcard dependencies stay conservative.
@@ -27,7 +27,11 @@ export async function openPreviewScenario(page, url, scenario, { timeout = 30000
27
27
  while (Date.now() < end) {
28
28
  for (const frame of page.frames().filter(frame => frame !== page.mainFrame())) {
29
29
  const state = await frame.evaluate(() => ({ state: document.documentElement.dataset.adsPreviewState, id: document.documentElement.dataset.adsScenario, message: document.getElementById("ads-preview-status")?.textContent })).catch(() => ({}));
30
- if (state.id === scenario && state.state === "ready") return frame;
30
+ if (state.id === scenario && state.state === "ready") {
31
+ const retries = await page.evaluate(() => Number(document.documentElement.dataset.adsResourceRetries ?? 0)).catch(() => 0);
32
+ if (retries > 0) console.warn(JSON.stringify({ adsPreviewDiagnostic: { kind: "resource-read-recovery", scenario, retries, failedRequests: failures } }));
33
+ return frame;
34
+ }
31
35
  if (state.id === scenario && state.state === "error") throw new Error(state.message || "The MCP App failed before verification.");
32
36
  }
33
37
  const error = await page.evaluate(() => document.querySelector('#status[data-error="true"]')?.textContent).catch(() => null);
@@ -8,6 +8,8 @@ module.exports = class AdsBehaviorReporter {
8
8
  this.cases = [];
9
9
  this.errors = [];
10
10
  this.active = new Map();
11
+ this.diagnostics = new Map();
12
+ this.diagnosticBuffers = new Map();
11
13
  this.startedAt = Date.now();
12
14
  this.totalTests = 0;
13
15
  }
@@ -22,9 +24,27 @@ module.exports = class AdsBehaviorReporter {
22
24
  }
23
25
  onTestEnd(test, result) {
24
26
  this.active.delete(test.id);
25
- for (const entry of this.entries(test)) this.cases.push({ ...entry, status: result.status === "passed" && test.expectedStatus === "passed" ? "passed" : ["skipped", "interrupted"].includes(result.status) ? "skipped" : "failed", durationMs: result.duration, workerIndex: result.workerIndex, retry: result.retry, completedAt: new Date().toISOString(), message: result.errors?.map(error => error.message || error.stack || error.value || "The browser runner reported an error without details.").join("\n").slice(0, 1000) });
27
+ const diagnostics = this.diagnostics.get(test.id);
28
+ for (const entry of this.entries(test)) this.cases.push({ ...entry, status: result.status === "passed" && test.expectedStatus === "passed" ? "passed" : ["skipped", "interrupted"].includes(result.status) ? "skipped" : "failed", durationMs: result.duration, workerIndex: result.workerIndex, retry: result.retry, completedAt: new Date().toISOString(), ...(diagnostics?.length ? { diagnostics } : {}), message: result.errors?.map(error => error.message || error.stack || error.value || "The browser runner reported an error without details.").join("\n").slice(0, 1000) });
29
+ this.diagnostics.delete(test.id);
30
+ this.diagnosticBuffers.delete(test.id);
26
31
  this.publish();
27
32
  }
33
+ onStdErr(chunk, test) {
34
+ if (!test) return;
35
+ const lines = ((this.diagnosticBuffers.get(test.id) ?? "") + String(chunk)).split("\n");
36
+ this.diagnosticBuffers.set(test.id, lines.pop().slice(-4000));
37
+ for (const line of lines) {
38
+ if (line.length > 4000) continue;
39
+ try {
40
+ const diagnostic = JSON.parse(line).adsPreviewDiagnostic;
41
+ if (diagnostic?.kind !== "resource-read-recovery" || typeof diagnostic.scenario !== "string" || !Number.isSafeInteger(diagnostic.retries) || diagnostic.retries < 1) continue;
42
+ const collected = this.diagnostics.get(test.id) ?? [];
43
+ if (collected.length < 5) collected.push({ kind: diagnostic.kind, scenario: diagnostic.scenario.slice(0, 300), retries: diagnostic.retries, failedRequests: (Array.isArray(diagnostic.failedRequests) ? diagnostic.failedRequests : []).slice(0, 5).map(value => String(value).slice(0, 500)) });
44
+ this.diagnostics.set(test.id, collected);
45
+ } catch { /* Ordinary test output is not an ADS startup diagnostic. */ }
46
+ }
47
+ }
28
48
  onError(error) {
29
49
  if (this.errors.length < 5) this.errors.push(String(error.message || error.stack || error.value || "The browser runner reported a suite error without details.").slice(0, 4000));
30
50
  this.publish();
@@ -310,6 +310,7 @@ export function createVerificationController(root, { reportRoot = root, onProgre
310
310
  results.push({ unit: check.unit, check: check.id, file: check.file, case: check.case, profile: profileName, status, current, revision: check.inputRevision, runId, completedAt: new Date().toISOString(),
311
311
  previewRevision: profile.mode === "browser" ? host?.previewRevision() ?? readPreviewManifest(options)?.revision ?? null : null,
312
312
  ...(matches.length ? { reportedStatus, durationMs: matches.reduce((sum, item) => sum + (Number.isFinite(item.durationMs) ? Math.max(0, item.durationMs) : 0), 0) } : {}),
313
+ ...(matches.some(item => item.diagnostics?.length) ? { startupDiagnostics: matches.flatMap(item => Array.isArray(item.diagnostics) ? item.diagnostics.slice(0, 5) : []).slice(0, 10) } : {}),
313
314
  ...(runnerFailure ? { runnerExitCode: run.code } : {}),
314
315
  execution: progress.cases.find(item => item.unit === check.unit && item.check === check.id)?.state,
315
316
  message: [matches.filter(item => item.status !== "passed").map(item => String(item.message || `Case reported ${item.status} without a diagnostic.`).slice(0, 2000)).join("\n"), runnerError ? `${runnerError.message} This check ${matches.length ? `reported ${reportedStatus}` : `was ${progress.cases.find(item => item.unit === check.unit && item.check === check.id)?.state ?? "not reported"}`}; the complete run did not pass.` : !matches.length ? "The selected case did not execute/report. Missing and skipped cases do not pass." : runnerFailure ? `This case reported ${reportedStatus}, but the complete run did not pass. ${runnerFailure}` : ""].filter(Boolean).join("\n"), report: slash(path.relative(reportRoot, report)) });
@@ -229,6 +229,15 @@ scenarios unmounts the previous view while keeping unchanged dependencies cached
229
229
  Persistence is a host lifecycle choice; a new external widget loads the same
230
230
  resources independently. Resource sharing is scoped to an App instance.
231
231
 
232
+ The showcase's resource bridge can recover a transient fetch failure with at most
233
+ three read attempts and bounded backoff. This applies only to `resources/read`;
234
+ tool calls, assertions and whole checks are never replayed. Cancellation,
235
+ HTTP/protocol/content errors and integrity failures remain failures. Browser
236
+ diagnostics record retries, and verification retains recovered startup reads in
237
+ its case results. Persistent transport failures still block a mutation. External
238
+ hosts own their transport recovery; the reusable App's integrity checks remain
239
+ the same.
240
+
232
241
  Managed graph builders publish into a private session directory beside the
233
242
  configured manifest. Logical scenario IDs and resource URIs stay unchanged;
234
243
  standalone builds retain the configured output and cannot overwrite a live
package/docs/BEHAVIORS.md CHANGED
@@ -169,8 +169,23 @@ pass, even if all reported cases claim success. Results retain `reportedStatus`
169
169
  and `runnerExitCode` and explain the overall failure. Suite-level browser errors and
170
170
  bounded runner output appear in diagnostics. `openBehaviorScenario` includes the
171
171
  scenario ID and failed browser requests in startup failures; App resource errors
172
- identify the resource URI. Failed assertions and startup errors are not retried
173
- automatically. Fix or investigate the failure before explicitly verifying again.
172
+ identify the resource URI. The ADS showcase retries only `resources/read` fetch
173
+ transport failures, with at most three attempts and 200/800 ms backoff. Recovered
174
+ startup reads appear in `startupDiagnostics` with retry counts and failed browser
175
+ requests; each retry is also logged in the browser console. This does not retry a
176
+ tool call, assertion, check or complete startup. HTTP/protocol/content errors,
177
+ integrity failures and cancellations do not retry. Persistent loading failures
178
+ still fail the check and block mutations. External hosts control their own
179
+ transport recovery.
180
+
181
+ `net::ERR_NETWORK_CHANGED` reports Chrome's network-change error, not proof that
182
+ the MCP endpoint restarted or that a particular interface changed. To investigate
183
+ an intermittent failure, correlate the saved case timestamps with Chrome's
184
+ network log, server availability and live interface monitoring (for example,
185
+ `ip -ts monitor link address route` on Linux). Interfaces being present, or stable
186
+ after a failure, does not establish the cause. Preserve the draft and original
187
+ diagnostic; do not change a component API, reference example or verification
188
+ policy to compensate for a resource-loading failure.
174
189
 
175
190
  The reporter atomically publishes partial snapshots at test start/end. While a
176
191
  run is active, `ads_status.behaviors.progress` exposes the profile, runner budget,
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.16
4
+
5
+ - Recover transient showcase MCP resource fetch failures with bounded read-only
6
+ retries. Keep tool calls, assertions, checks and non-transport failures outside
7
+ recovery; expose recovered startup reads in verification diagnostics and keep
8
+ persistent loading failures blocking. Verify interrupted resource delivery and
9
+ failed delivery against a dummy App, without changing project verification policy.
10
+
3
11
  ## 0.3.15
4
12
 
5
13
  - Keep fragment links inside inline preview documents by supplying a local
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reqvire-org/ads",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
4
4
  "type": "module",
5
5
  "description": "A design system your agent can see and shape. Your components. Your look. Your code.",
6
6
  "repository": {