@reqvire-org/ads 0.3.16 → 0.3.18

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 (59) hide show
  1. package/README.md +3 -0
  2. package/dist/atomic-composition.mjs +1 -1
  3. package/dist/check.mjs +43 -11
  4. package/dist/evidence.mjs +18 -0
  5. package/dist/investigations.mjs +101 -0
  6. package/dist/linaria/preserve-values.cjs +21 -2
  7. package/dist/mcp/behavior-pages.mjs +36 -0
  8. package/dist/mcp/context.mjs +3 -1
  9. package/dist/mcp/execution-results.mjs +146 -0
  10. package/dist/mcp/mutations.mjs +34 -17
  11. package/dist/mcp/preview-widget.bundle.js +12 -12
  12. package/dist/mcp/resource-loader.js +3 -3
  13. package/dist/mcp/result-pages.mjs +87 -0
  14. package/dist/mcp/server.mjs +67 -30
  15. package/dist/property-registrations.mjs +39 -0
  16. package/dist/scenario-selectors.mjs +140 -0
  17. package/dist/scenario-trace.mjs +1 -1
  18. package/dist/scoped-tokens.mjs +6 -0
  19. package/dist/selector-match.mjs +94 -0
  20. package/dist/showcase/client.bundle.js +2 -2
  21. package/dist/skills/ads-break-ui/SKILL.md +8 -3
  22. package/dist/skills/ads-break-ui/references/atomic-styles.md +48 -0
  23. package/dist/skills/ads-break-ui/references/captures.md +6 -1
  24. package/dist/skills/ads-break-ui/references/evidence.md +120 -0
  25. package/dist/skills/ads-break-ui/references/guarded-editing.md +8 -5
  26. package/dist/skills/ads-break-ui/references/scenarios.md +32 -0
  27. package/dist/skills/ads-designer/SKILL.md +8 -3
  28. package/dist/skills/ads-designer/references/atomic-styles.md +48 -0
  29. package/dist/skills/ads-designer/references/captures.md +6 -1
  30. package/dist/skills/ads-designer/references/evidence.md +120 -0
  31. package/dist/skills/ads-designer/references/guarded-editing.md +8 -5
  32. package/dist/skills/ads-designer/references/scenarios.md +32 -0
  33. package/dist/skills/ads-import/SKILL.md +8 -3
  34. package/dist/skills/ads-import/references/atomic-styles.md +48 -0
  35. package/dist/skills/ads-import/references/captures.md +6 -1
  36. package/dist/skills/ads-import/references/evidence.md +120 -0
  37. package/dist/skills/ads-import/references/guarded-editing.md +8 -5
  38. package/dist/skills/ads-import/references/scenarios.md +32 -0
  39. package/dist/skills/ads-migrate/SKILL.md +8 -3
  40. package/dist/skills/ads-migrate/references/atomic-styles.md +48 -0
  41. package/dist/skills/ads-migrate/references/captures.md +6 -1
  42. package/dist/skills/ads-migrate/references/evidence.md +120 -0
  43. package/dist/skills/ads-migrate/references/guarded-editing.md +8 -5
  44. package/dist/skills/ads-migrate/references/scenarios.md +32 -0
  45. package/dist/skills/ads-motion/SKILL.md +8 -3
  46. package/dist/skills/ads-motion/references/atomic-styles.md +48 -0
  47. package/dist/skills/ads-motion/references/captures.md +6 -1
  48. package/dist/skills/ads-motion/references/evidence.md +120 -0
  49. package/dist/skills/ads-motion/references/guarded-editing.md +8 -5
  50. package/dist/skills/ads-motion/references/scenarios.md +32 -0
  51. package/dist/verification/browser.mjs +22 -7
  52. package/dist/verification/playwright-reporter.cjs +23 -5
  53. package/dist/verification.mjs +31 -9
  54. package/docs/ADS.md +10 -0
  55. package/docs/ARCHITECTURE.md +47 -3
  56. package/docs/BEHAVIORS.md +30 -0
  57. package/docs/CHANGELOG.md +30 -0
  58. package/docs/MCP.md +66 -7
  59. package/package.json +2 -1
@@ -15,6 +15,7 @@ import { createCaptureController, captureSelections } from "../capture.mjs";
15
15
  import { previewSourceRevision } from "../preview-inputs.mjs";
16
16
  import { inspectScenarios } from "../scenarios.mjs";
17
17
  import { checkUnitCandidate, checkUnitFiles, inspectUnits, validateUnitId } from "../units.mjs";
18
+ import { acceptance, failure as evidenceFailure } from "../evidence.mjs";
18
19
 
19
20
  export const mutationLimits = { units: 8, files: 32, patchBytes: 256 * 1024, patchFileBytes: 16 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, readChars: 12000, snapshotFiles: 20000, snapshotBytes: 128 * 1024 * 1024 };
20
21
  const digest = bytes => `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
@@ -22,8 +23,8 @@ const ignored = new Set([".git", ".ds", "node_modules", ".vite", "coverage", ".p
22
23
  const textFile = file => /\.(?:[cm]?[jt]sx?|css|html|jsonc?|md|txt|svg)$/.test(file);
23
24
  const setupDotfile = file => /^\.(?:oxlintrc|linariarc|babelrc)(?:\.json)?$/.test(file);
24
25
 
25
- function fail(rule, message, file) {
26
- return Object.assign(new Error(message), { diagnostic: { rule, message, ...(file ? { file } : {}) } });
26
+ function fail(rule, message, file, boundary = evidenceFailure("architecture","mutation-policy",rule)) {
27
+ return Object.assign(new Error(message), { diagnostic: { rule, message, failure:boundary, ...(file ? { file } : {}) } });
27
28
  }
28
29
 
29
30
  function address(options, file) {
@@ -108,12 +109,13 @@ function snapshot(options, destination) {
108
109
  return JSON.stringify(entries);
109
110
  }
110
111
 
111
- function checkCommand(command, args, cwd, signal, input) {
112
+ function checkCommand(command, args, cwd, signal, input, gate = {category:"architecture",phase:"patch-preparation"}) {
112
113
  return new Promise((resolve, reject) => {
113
- if (signal?.aborted) { reject(fail("mutation-cancelled", "Mutation cancelled before validation.")); return; }
114
+ if (signal?.aborted) { reject(fail("mutation-cancelled", "Mutation cancelled before validation.",undefined,evidenceFailure("runner","mutation-execution","ADS_CANCELLED"))); return; }
114
115
  const child = spawn(command, args, { cwd, shell: false, detached: process.platform !== "win32", windowsHide: true, stdio: [input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
115
116
  env: { ...process.env, PATH: [path.join(cwd, "node_modules/.bin"), path.dirname(process.execPath), process.env.PATH ?? ""].join(path.delimiter) } });
116
117
  let output = "", failure;
118
+ const startedAt=Date.now();
117
119
  if (input !== undefined) { child.stdin.on("error", () => {}); child.stdin.end(input); }
118
120
  function kill() {
119
121
  if (!child.pid) return;
@@ -130,13 +132,23 @@ function checkCommand(command, args, cwd, signal, input) {
130
132
  child.once("exit", kill); // Also clean up descendants of a finished launcher.
131
133
  child.once("close", code => {
132
134
  clearTimeout(timer); signal?.removeEventListener("abort", abort);
133
- if (failure) reject(failure);
134
- else if (code !== 0) reject(fail("mutation-check", `${path.basename(command)} ${args.join(" ")} failed (${code}).\n${output}`));
135
- else resolve({ command: path.basename(command), args, passed: true });
135
+ const detail={command:path.basename(command),args,elapsedMs:Date.now()-startedAt,exitCode:code};
136
+ if (failure || code !== 0) {
137
+ const error=failure??fail("mutation-check", `${path.basename(command)} ${args.join(" ")} failed (${code}).\n${output}`);
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"})};
139
+ reject(error);
140
+ } else resolve({ ...detail,phase:gate.phase, passed: true });
136
141
  });
137
142
  });
138
143
  }
139
144
 
145
+ function mutationEvidence(result, candidateRevision) {
146
+ const diagnostics=(result.diagnostics??[]).map(diagnostic=>({...diagnostic,failure:diagnostic.failure??evidenceFailure("architecture","mutation-policy",diagnostic.rule)}));
147
+ const failed=diagnostics[0];
148
+ const accepted=acceptance("candidate-validation",result.ok,(result.checks??[]).filter(check=>check.passed&&!check.skipped).map(check=>check.name??[check.command,...(check.args??[])].join(" ")),["unregistered-checks","unselected-behavior-checks","post-application-preview-freshness","visual-parity","accessibility-audit"]);
149
+ return {...result,...(result.diagnostics?{diagnostics}:{}),acceptance:accepted,investigation:{target:"candidate",candidateRevision:candidateRevision??null,...(failed?{failure:failed.failure,failingStep:failed.failure.phase,source:failed.file?{file:failed.file,line:failed.line,column:failed.column}:null,...(failed.command?{command:failed.command,args:failed.args,exitCode:failed.exitCode,output:failed.output}:{}),message:failed.message}:{}),acceptance:accepted}};
150
+ }
151
+
140
152
  function checks(options) {
141
153
  const context = projectContext(options);
142
154
  const pkg = JSON.parse(readFileSync(path.join(options.root, "package.json"), "utf8"));
@@ -175,11 +187,12 @@ export function createMutationController(directory, { captures = createCaptureCo
175
187
  startLine: source.content.slice(0, offset).split("\n").length };
176
188
  },
177
189
  async apply({ unit, patch, patchFile, patches, dryRun = false, screenshot = false, captures: selections }, signal, notifyProgress) {
178
- if (busy) return { ok: false, applied: false, diagnostics: [{ rule: "mutation-busy", message: "Another mutation is validating; retry after it finishes." }] };
190
+ if (busy) return mutationEvidence({ ok: false, applied: false, diagnostics: [{ rule: "mutation-busy", message: "Another mutation is validating; retry after it finishes.",failure:evidenceFailure("runner","mutation-execution","ADS_MUTATION_BUSY") }] });
179
191
  busy = true;
180
192
  active = new AbortController();
181
193
  signal = signal ? AbortSignal.any([signal, active.signal]) : active.signal;
182
- let temporary;
194
+ let temporary, candidateRevision;
195
+ let boundary=evidenceFailure("runner","mutation-execution","ADS_MUTATION_EXCEPTION",{certainty:"unclassified"});
183
196
  const verified = [];
184
197
  try {
185
198
  const options = loadConfig(root);
@@ -264,8 +277,9 @@ export function createMutationController(directory, { captures = createCaptureCo
264
277
  const candidateOptions = loadConfig(candidate);
265
278
  const extraChecks = checks(candidateOptions);
266
279
  const expected = snapshot(candidateOptions);
280
+ candidateRevision=digest(Buffer.from(expected));
267
281
  const result = checkProject(candidate, { trackingRoot: root });
268
- if (!result.ok) return lastResult = { ok: false, applied: false, diagnostics: result.diagnostics.slice(0, 50), total: result.diagnostics.length, checks: verified };
282
+ if (!result.ok) return lastResult = mutationEvidence({ ok: false, applied: false, diagnostics: result.diagnostics, total: result.diagnostics.length, checks: verified },candidateRevision);
269
283
  verified.push({ name: "ads check", passed: true });
270
284
  const lintConfig = generateLintConfig(candidate).file;
271
285
  const require = createRequire(path.join(candidate, "package.json"));
@@ -276,30 +290,33 @@ export function createMutationController(directory, { captures = createCaptureCo
276
290
  if (configuration) for (const file of inspectUnits(candidateOptions).units.find(item => item.id === "configuration").files) {
277
291
  if (/\.[cm]?[jt]sx?$/.test(file) && !lintPaths.some(directory => isInside(path.resolve(candidate, directory), path.resolve(candidate, file)))) lintPaths.push(file);
278
292
  }
279
- await checkCommand(process.execPath, [executable, "--deny-warnings", "--config", lintConfig, ...lintPaths], candidate, signal);
293
+ await checkCommand(process.execPath, [executable, "--deny-warnings", "--config", lintConfig, ...lintPaths], candidate, signal, undefined, {category:"architecture",phase:"registered-oxlint"});
280
294
  verified.push({ name: "registered Oxlint rules", passed: true });
281
295
  if (assets || configuration) {
296
+ boundary=evidenceFailure("build","brand-assets","ADS_ASSET_BUILD");
282
297
  const brand = await generateBrandAssets(candidate);
283
298
  verified.push({ name: "brand assets", passed: true, skipped: !!brand.skipped });
284
299
  }
285
- for (const check of extraChecks) verified.push(await checkCommand(check.command, check.args, candidate, signal));
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]}`}));
301
+ boundary=evidenceFailure("runner","behavior-verification","ADS_BEHAVIOR_SETUP");
286
302
  const behaviorUnits = affectedBehaviorUnits(candidateOptions, changes.map(change => change.path));
287
303
  if (behaviorUnits.length) {
288
304
  const verification = createVerificationController(candidate, { reportRoot: root, onProgress: value => { progress = { stage: "behavior", ...value }; notifyProgress?.(progress); } });
289
305
  let evidence;
290
306
  try { evidence = await verification.run({ units: behaviorUnits, persist: false }, signal); }
291
307
  finally { await verification.close(); }
292
- if (!evidence.ok) return lastResult = { ok: false, applied: false, diagnostics: [...evidence.diagnostics, ...evidence.results.filter(item => item.status !== "passed").map(item => ({ rule: "behavior-check", message: `${item.unit}#${item.check}: ${item.status}. ${item.message}` }))].slice(0, 50), checks: verified, behavior: evidence };
308
+ if (!evidence.ok) return lastResult = mutationEvidence({ ok: false, applied: false, diagnostics: [...evidence.results.filter(item => item.status !== "passed").map(item => ({ rule: "behavior-check", failure:item.failure, message: `${item.unit}#${item.check}: ${item.status}. ${item.message}` })),...evidence.diagnostics], checks: verified, behavior: evidence },candidateRevision);
293
309
  verified.push({ name: "registered behavior checks", passed: true, results: evidence.results });
294
310
  }
295
311
  let screenshots;
296
312
  if (screenshot) {
313
+ boundary=evidenceFailure("runner","capture","ADS_CAPTURE_FAILED");
297
314
  progress = { stage: "capture", total: selections.length, completed: 0, active: [], profile: "screenshots", elapsedMs: 0 };
298
315
  notifyProgress?.(progress);
299
316
  const proposal = digest(Buffer.from(JSON.stringify(changes.map(change => [change.path, change.content === null ? null : digest(Buffer.from(change.content))]))));
300
317
  screenshots = await captures.run({ captures: selections, candidateRoot: candidate, proposal, publish: false }, signal, value => { progress = value; notifyProgress?.(value); });
301
318
  }
302
- if (signal?.aborted) throw fail("mutation-cancelled", "Mutation cancelled before application.");
319
+ if (signal?.aborted) throw fail("mutation-cancelled", "Mutation cancelled before application.",undefined,evidenceFailure("runner","mutation-execution","ADS_CANCELLED"));
303
320
  if (snapshot(candidateOptions) !== expected) throw fail("mutation-check", "A validation command changed source files. Use checks without source-writing side effects.");
304
321
  if (snapshot(loadConfig(root)) !== before) throw fail("mutation-conflict", "Project inputs changed during validation; no changes were applied. Read current source and retry.");
305
322
  for (const change of changes) address(options, change.path);
@@ -336,11 +353,11 @@ export function createMutationController(directory, { captures = createCaptureCo
336
353
  const warnings = [];
337
354
  if (!dryRun && (assets || configuration)) { try { await generateBrandAssets(root); } catch (error) { warnings.push({ rule: "mutation-assets", message: `Source was applied; generated brand output needs recovery: ${error.message}` }); } }
338
355
  if (!dryRun && captures.store.fingerprint()) { try { captures.store.promote(previewSourceRevision(loadConfig(root)), inspectScenarios(loadConfig(root)).scenarios); } catch (error) { warnings.push({ rule: "mutation-capture", message: `Source was applied; capture promotion needs recovery: ${error.message}` }); } }
339
- return lastResult = { ok: true, ...(patches ? {units,transaction:true} : {unit}), applied: !dryRun, changes: changes.map(change => ({ path: change.path, operation: change.content === null ? "delete" : change.expectedDigest === null ? "create" : "update", digest: change.content === null ? null : digest(Buffer.from(change.content)) })), checks: verified, ...(result.advisories?.length ? { advisories: result.advisories.slice(0, 50), advisoryCount: result.advisories.length } : {}), ...(warnings.length ? { warnings } : {}),
356
+ return lastResult = mutationEvidence({ ok: true, ...(patches ? {units,transaction:true} : {unit}), applied: !dryRun, changes: changes.map(change => ({ path: change.path, operation: change.content === null ? "delete" : change.expectedDigest === null ? "create" : "update", digest: change.content === null ? null : digest(Buffer.from(change.content)) })), checks: verified, ...(result.advisories?.length ? { advisories: result.advisories, advisoryCount: result.advisories.length } : {}), ...(warnings.length ? { warnings } : {}),
340
357
  ...(screenshots ? { screenshots } : {}),
341
- preview: "Source validation passed. Inspect the managed preview build and rendered scenario after applying; validation is not visual verification." };
358
+ preview: "Source validation passed. Inspect the managed preview build and rendered scenario after applying; validation is not visual verification." },candidateRevision);
342
359
  } catch (error) {
343
- return lastResult = { ok: false, applied: false, diagnostics: [error.diagnostic ?? { rule: "mutation-error", message: error.message }], checks: verified };
360
+ return lastResult = mutationEvidence({ ok: false, applied: false, diagnostics: [error.diagnostic ?? { rule: "mutation-error", message: error.message,failure:error.failure??boundary }], checks: verified },candidateRevision);
344
361
  } finally { try { if (temporary) rmSync(temporary, { recursive: true, force: true }); } finally { busy = false; active = undefined; progress = undefined; } }
345
362
  },
346
363
  };