@reqvire-org/ads 0.3.23 → 0.3.24

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/dist/config.mjs CHANGED
@@ -18,7 +18,7 @@ export const defaultConfig = {
18
18
  boundaries: { styleWrites: [], componentVariables: [] },
19
19
  assets: { publicDirs: ["public/assets"], generatedPaths: ["dist", "dist-showcase", ".vite", ".ds"], generatedFiles: [], iconSource: null },
20
20
  oxlint: { plugins: [], rules: {}, overrides: [] },
21
- mcp: { showcaseUrl: null, showcaseTitle: null, apps: [], previewWatch: null, previewManifest: null, mutationChecks: null, behaviorProfiles: {}, capture: {} },
21
+ mcp: { showcaseUrl: null, showcaseTitle: null, apps: [], previewWatch: null, previewManifest: null, mutationChecks: null, mutationCheckTimeoutMs: 60000, behaviorProfiles: {}, capture: {} },
22
22
  };
23
23
 
24
24
  export function isInside(parent, file) {
@@ -120,6 +120,7 @@ export function loadConfig(directory = process.cwd()) {
120
120
  if (profile.mode === "strict" && config.framework !== "react") throw new Error(`Behavior profile ${name}: React StrictMode requires framework react; use the configured framework's lifecycle tests instead.`);
121
121
  }
122
122
  if (config.mcp.mutationChecks !== null && (!Array.isArray(config.mcp.mutationChecks) || config.mcp.mutationChecks.length > 12 || new Set(config.mcp.mutationChecks).size !== config.mcp.mutationChecks.length || config.mcp.mutationChecks.some(name => typeof name !== "string" || !/^[a-zA-Z0-9][\w:.-]*$/.test(name)))) throw new Error("mcp.mutationChecks must be null or up to 12 unique package script names.");
123
+ if (!Number.isInteger(config.mcp.mutationCheckTimeoutMs) || config.mcp.mutationCheckTimeoutMs < 100 || config.mcp.mutationCheckTimeoutMs > 300000) throw new Error("mcp.mutationCheckTimeoutMs must be an integer from 100 to 300000. This budget applies to each registered project script, separately from behavior profiles.");
123
124
  if (config.mcp.previewManifest !== null) {
124
125
  const manifest = projectPath(root, config.mcp.previewManifest, "mcp.previewManifest");
125
126
  if (!isInside(path.join(root, ".ds"), manifest) || path.extname(manifest) !== ".json") throw new Error("mcp.previewManifest must name a generated JSON manifest inside .ds/.");
@@ -109,7 +109,7 @@ function snapshot(options, destination) {
109
109
  return JSON.stringify(entries);
110
110
  }
111
111
 
112
- function checkCommand(command, args, cwd, signal, input, gate = {category:"architecture",phase:"patch-preparation"}) {
112
+ function checkCommand(command, args, cwd, signal, input, gate = {category:"architecture",phase:"patch-preparation"}, timeoutMs = 60000) {
113
113
  return new Promise((resolve, reject) => {
114
114
  if (signal?.aborted) { reject(fail("mutation-cancelled", "Mutation cancelled before validation.",undefined,evidenceFailure("runner","mutation-execution","ADS_CANCELLED"))); return; }
115
115
  const child = spawn(command, args, { cwd, shell: false, detached: process.platform !== "win32", windowsHide: true, stdio: [input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
@@ -126,13 +126,13 @@ function checkCommand(command, args, cwd, signal, input, gate = {category:"archi
126
126
  }
127
127
  const abort = () => { failure = fail("mutation-cancelled", "Mutation cancelled during validation."); kill(); };
128
128
  signal?.addEventListener("abort", abort, { once: true });
129
- const timer = setTimeout(() => { failure = fail("mutation-check", "A mutation check exceeded 60 seconds."); kill(); }, 60000);
129
+ const timer = setTimeout(() => { failure = fail("mutation-check", `${path.basename(command)} ${args.join(" ")} exceeded its ${timeoutMs} ms command budget.`); failure.code="ADS_COMMAND_TIMEOUT"; kill(); }, timeoutMs);
130
130
  for (const stream of [child.stdout, child.stderr]) stream.on("data", chunk => { output = (output + chunk.toString()).slice(-12000); });
131
131
  child.once("error", error => { failure = error; });
132
132
  child.once("exit", kill); // Also clean up descendants of a finished launcher.
133
133
  child.once("close", code => {
134
134
  clearTimeout(timer); signal?.removeEventListener("abort", abort);
135
- const detail={command:path.basename(command),args,elapsedMs:Date.now()-startedAt,exitCode:code};
135
+ const detail={command:path.basename(command),args,timeoutMs,elapsedMs:Date.now()-startedAt,exitCode:code};
136
136
  if (failure || code !== 0) {
137
137
  const error=failure??fail("mutation-check", `${path.basename(command)} ${args.join(" ")} failed (${code}).\n${output}`);
138
138
  error.diagnostic={...(error.diagnostic??{rule:"mutation-check",message:error.message}),...detail,output:output.slice(-4000),failure:evidenceFailure(failure?"runner":gate.category,gate.phase,failure?.code??(failure?"ADS_COMMAND_INTERRUPTED":"ADS_COMMAND_EXIT"),{certainty:"observed-boundary"})};
@@ -297,7 +297,7 @@ export function createMutationController(directory, { captures = createCaptureCo
297
297
  const brand = await generateBrandAssets(candidate);
298
298
  verified.push({ name: "brand assets", passed: true, skipped: !!brand.skipped });
299
299
  }
300
- for (const check of extraChecks) verified.push(await checkCommand(check.command, check.args, candidate, signal, undefined, {category:"build",phase:`project-check:${check.args[1]}`}));
300
+ for (const check of extraChecks) verified.push(await checkCommand(check.command, check.args, candidate, signal, undefined, {category:"build",phase:`project-check:${check.args[1]}`}, candidateOptions.config.mcp.mutationCheckTimeoutMs));
301
301
  boundary=evidenceFailure("runner","behavior-verification","ADS_BEHAVIOR_SETUP");
302
302
  const behaviorUnits = affectedBehaviorUnits(candidateOptions, changes.map(change => change.path));
303
303
  if (behaviorUnits.length) {