@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.
- package/README.md +3 -0
- package/dist/atomic-composition.mjs +1 -1
- package/dist/check.mjs +43 -11
- package/dist/evidence.mjs +18 -0
- package/dist/investigations.mjs +101 -0
- package/dist/linaria/preserve-values.cjs +21 -2
- package/dist/mcp/behavior-pages.mjs +36 -0
- package/dist/mcp/context.mjs +3 -1
- package/dist/mcp/execution-results.mjs +146 -0
- package/dist/mcp/mutations.mjs +34 -17
- package/dist/mcp/preview-widget.bundle.js +12 -12
- package/dist/mcp/resource-loader.js +3 -3
- package/dist/mcp/result-pages.mjs +87 -0
- package/dist/mcp/server.mjs +67 -30
- package/dist/property-registrations.mjs +39 -0
- package/dist/scenario-selectors.mjs +140 -0
- package/dist/scenario-trace.mjs +1 -1
- package/dist/scoped-tokens.mjs +6 -0
- package/dist/selector-match.mjs +94 -0
- package/dist/showcase/client.bundle.js +2 -2
- package/dist/skills/ads-break-ui/SKILL.md +8 -3
- package/dist/skills/ads-break-ui/references/atomic-styles.md +48 -0
- package/dist/skills/ads-break-ui/references/captures.md +6 -1
- package/dist/skills/ads-break-ui/references/evidence.md +120 -0
- package/dist/skills/ads-break-ui/references/guarded-editing.md +8 -5
- package/dist/skills/ads-break-ui/references/scenarios.md +32 -0
- package/dist/skills/ads-designer/SKILL.md +8 -3
- package/dist/skills/ads-designer/references/atomic-styles.md +48 -0
- package/dist/skills/ads-designer/references/captures.md +6 -1
- package/dist/skills/ads-designer/references/evidence.md +120 -0
- package/dist/skills/ads-designer/references/guarded-editing.md +8 -5
- package/dist/skills/ads-designer/references/scenarios.md +32 -0
- package/dist/skills/ads-import/SKILL.md +8 -3
- package/dist/skills/ads-import/references/atomic-styles.md +48 -0
- package/dist/skills/ads-import/references/captures.md +6 -1
- package/dist/skills/ads-import/references/evidence.md +120 -0
- package/dist/skills/ads-import/references/guarded-editing.md +8 -5
- package/dist/skills/ads-import/references/scenarios.md +32 -0
- package/dist/skills/ads-migrate/SKILL.md +8 -3
- package/dist/skills/ads-migrate/references/atomic-styles.md +48 -0
- package/dist/skills/ads-migrate/references/captures.md +6 -1
- package/dist/skills/ads-migrate/references/evidence.md +120 -0
- package/dist/skills/ads-migrate/references/guarded-editing.md +8 -5
- package/dist/skills/ads-migrate/references/scenarios.md +32 -0
- package/dist/skills/ads-motion/SKILL.md +8 -3
- package/dist/skills/ads-motion/references/atomic-styles.md +48 -0
- package/dist/skills/ads-motion/references/captures.md +6 -1
- package/dist/skills/ads-motion/references/evidence.md +120 -0
- package/dist/skills/ads-motion/references/guarded-editing.md +8 -5
- package/dist/skills/ads-motion/references/scenarios.md +32 -0
- package/dist/verification/browser.mjs +22 -7
- package/dist/verification/playwright-reporter.cjs +23 -5
- package/dist/verification.mjs +31 -9
- package/docs/ADS.md +10 -0
- package/docs/ARCHITECTURE.md +47 -3
- package/docs/BEHAVIORS.md +30 -0
- package/docs/CHANGELOG.md +30 -0
- package/docs/MCP.md +66 -7
- package/package.json +2 -1
package/dist/mcp/mutations.mjs
CHANGED
|
@@ -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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
};
|