@williambeto/ai-workflow 2.4.9 → 2.6.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.
Files changed (104) hide show
  1. package/bin/ai-workflow.d.ts +3 -0
  2. package/bin/ai-workflow.js +2669 -1
  3. package/bin/ai-workflow.js.map +1 -0
  4. package/chunk-2AOV2ATY.js +749 -0
  5. package/chunk-5WRI5ZAA.js +31 -0
  6. package/chunk-6EMG6FAO.js +734 -0
  7. package/chunk-UHLZX2EG.js +1827 -0
  8. package/core/index.d.ts +440 -0
  9. package/core/index.js +32 -0
  10. package/core/index.js.map +1 -0
  11. package/dist-assets/AGENTS.md +23 -6
  12. package/dist-assets/agents/atlas.md +8 -8
  13. package/dist-assets/agents/nexus.md +4 -4
  14. package/dist-assets/agents/orion.md +1 -1
  15. package/dist-assets/agents/phoenix.md +1 -1
  16. package/dist-assets/agents/sage.md +1 -1
  17. package/dist-assets/commands/implement.md +1 -1
  18. package/dist-assets/docs/QUICKSTART.md +8 -8
  19. package/dist-assets/docs/cli-reference.md +7 -7
  20. package/dist-assets/docs/compatibility/provider-usage.md +1 -9
  21. package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
  22. package/dist-assets/docs/consumer-onboarding.md +2 -2
  23. package/dist-assets/docs/design-patterns-policy.md +1 -1
  24. package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
  25. package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
  26. package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
  27. package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
  28. package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
  29. package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
  30. package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
  31. package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
  32. package/dist-assets/docs/profiles/README.md +2 -2
  33. package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
  34. package/dist-assets/docs/troubleshooting-guide.md +2 -2
  35. package/dist-assets/docs/visual-validation-guide.md +3 -3
  36. package/dist-assets/schemas/evidence.schema.json +109 -0
  37. package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
  38. package/dist-assets/templates/CLAUDE.md.template +7 -5
  39. package/dist-assets/templates/CODEX.md.template +7 -5
  40. package/evidence-validator-76ZQQYDU.js +6661 -0
  41. package/package.json +7 -64
  42. package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
  43. package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
  44. package/AGENTS.md +0 -34
  45. package/CHANGELOG.md +0 -259
  46. package/LICENSE +0 -21
  47. package/PUBLISH_MANIFEST.json +0 -34
  48. package/README.md +0 -129
  49. package/dist-assets/templates/.geminiignore.template +0 -8
  50. package/dist-assets/templates/GEMINI.md.template +0 -20
  51. package/docs/compatibility/provider-usage.md +0 -46
  52. package/docs/compatibility/runtime-matrix.md +0 -31
  53. package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
  54. package/docs/getting-started/authoring-custom-skills.md +0 -80
  55. package/docs/getting-started/quickstart.md +0 -117
  56. package/docs/getting-started/troubleshooting.md +0 -123
  57. package/docs/getting-started/upgrading-to-v2.md +0 -62
  58. package/src/adapters/index.js +0 -4
  59. package/src/adapters/platforms/antigravity.js +0 -382
  60. package/src/adapters/platforms/claude.js +0 -126
  61. package/src/adapters/platforms/codex.js +0 -113
  62. package/src/adapters/platforms/gemini.js +0 -245
  63. package/src/cli.js +0 -155
  64. package/src/commands/clean.js +0 -106
  65. package/src/commands/collect-evidence.js +0 -25
  66. package/src/commands/doctor.js +0 -202
  67. package/src/commands/execute.js +0 -382
  68. package/src/commands/init.js +0 -359
  69. package/src/commands/run.js +0 -112
  70. package/src/core/backup.js +0 -65
  71. package/src/core/completion-contract.js +0 -35
  72. package/src/core/delegation-controller.js +0 -193
  73. package/src/core/evidence/evidence-ledger.js +0 -98
  74. package/src/core/execution-planner.js +0 -62
  75. package/src/core/filesystem.js +0 -141
  76. package/src/core/finalization/finalizer.js +0 -165
  77. package/src/core/finalization/workspace-snapshot.js +0 -110
  78. package/src/core/gates/branch-gate.js +0 -134
  79. package/src/core/gates/merge-gate.js +0 -74
  80. package/src/core/handoff/handoff-engine.js +0 -104
  81. package/src/core/healing/cli-remediation-executor.js +0 -153
  82. package/src/core/healing/healer-engine.js +0 -212
  83. package/src/core/healing/runtime-remediation-executor.js +0 -136
  84. package/src/core/identity.js +0 -43
  85. package/src/core/install-plan.js +0 -34
  86. package/src/core/opencode-merge.js +0 -150
  87. package/src/core/package-assets.js +0 -141
  88. package/src/core/request-classifier.js +0 -73
  89. package/src/core/request-router.js +0 -289
  90. package/src/core/runtime/opencode-adapter.js +0 -202
  91. package/src/core/sdd/validator.js +0 -67
  92. package/src/core/statuses.js +0 -29
  93. package/src/core/symlink-layout.js +0 -93
  94. package/src/core/templates.js +0 -229
  95. package/src/core/validation/artifact-fidelity-gate.js +0 -445
  96. package/src/core/validation/canonical-finalization.js +0 -43
  97. package/src/core/validation/delivery-decision-engine.js +0 -344
  98. package/src/core/validation/evidence-collector.js +0 -245
  99. package/src/core/validation/quality-guard.js +0 -251
  100. package/src/core/validation/stack-detector.js +0 -65
  101. package/src/core/validation/validation-planner.js +0 -134
  102. package/src/core/validation/visual-verifier.js +0 -175
  103. package/src/core/workflow-profiles.js +0 -107
  104. package/src/core/workflow-state-machine.js +0 -46
@@ -1,445 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- export class ArtifactFidelityGate {
5
- constructor({ cwd = process.cwd(), deliveryClass, userRequest, explicitApprovals } = {}) {
6
- this.cwd = cwd;
7
- this.deliveryClass = deliveryClass;
8
- this.userRequest = userRequest;
9
- this.explicitApprovals = explicitApprovals;
10
- }
11
-
12
- async verify(changedFiles = []) {
13
- if (changedFiles.length === 0) {
14
- return { passed: true };
15
- }
16
-
17
- if (this.deliveryClass === "framework-native-frontend") {
18
- for (const file of changedFiles) {
19
- const ext = path.extname(file).toLowerCase();
20
-
21
- if (ext === ".html" || ext === ".htm") {
22
- const basename = path.basename(file).toLowerCase();
23
- const isSpaEntry = basename === "index.html" &&
24
- (file === "index.html" || file === "public/index.html" || file === "src/index.html");
25
- if (!isSpaEntry) {
26
- return {
27
- passed: false,
28
- reason: `Fidelity Violation: standalone HTML file '${file}' is not permitted in a framework-native frontend project. UI must be implemented within framework components (e.g. React/Vue/Next/Nuxt files) instead of static prototypes.`
29
- };
30
- }
31
-
32
- try {
33
- const content = await fs.readFile(path.join(this.cwd, file), "utf8");
34
- if (content.includes("unpkg.com") || content.includes("cdnjs.cloudflare.com") || content.includes("cdn.jsdelivr.net")) {
35
- if (content.includes("vue") || content.includes("react")) {
36
- return {
37
- passed: false,
38
- reason: `Fidelity Violation: SPA entry point '${file}' imports framework library via CDN scripts. Framework dependencies must be installed and resolved locally via npm/package.json.`
39
- };
40
- }
41
- }
42
- } catch {
43
- // ignore
44
- }
45
- }
46
- }
47
- }
48
-
49
- if (this.deliveryClass === "wordpress-php") {
50
- for (const file of changedFiles) {
51
- const ext = path.extname(file).toLowerCase();
52
- if (ext === ".html" || ext === ".htm") {
53
- return {
54
- passed: false,
55
- reason: `Fidelity Violation: standalone HTML file '${file}' is not permitted in a WordPress project. UI templates must be implemented in PHP files within theme or plugin structures.`
56
- };
57
- }
58
- }
59
- }
60
-
61
- const frontendViolations = await this.verifyFrontendFidelity(changedFiles);
62
- if (frontendViolations && frontendViolations.length > 0) {
63
- return {
64
- passed: false,
65
- reason: frontendViolations.join("; ")
66
- };
67
- }
68
-
69
- return { passed: true };
70
- }
71
-
72
- async verifyRequestFidelity(arg) {
73
- let userRequest = this.userRequest || "";
74
- let deliveryDecision = this.deliveryDecision || {};
75
- let projectContext = this.projectContext || {};
76
- let changedFiles = [];
77
- let explicitApprovals = this.explicitApprovals || [];
78
-
79
- if (Array.isArray(arg)) {
80
- changedFiles = arg;
81
- } else if (arg && typeof arg === "object") {
82
- userRequest = arg.userRequest || userRequest;
83
- deliveryDecision = arg.deliveryDecision || deliveryDecision;
84
- projectContext = arg.projectContext || projectContext;
85
- changedFiles = arg.changedFiles || changedFiles;
86
- explicitApprovals = arg.explicitApprovals || explicitApprovals;
87
- }
88
-
89
- const blockingDecisions = new Set([
90
- "BLOCK_STACK_CONFLICT",
91
- "BLOCK_UNCLEAR_SCOPE",
92
- "BLOCK_UNSUPPORTED_CONTEXT",
93
- "REQUIRE_EXPLICIT_AUTHORIZATION",
94
- "RELEASE_OR_DEPLOY_REQUIRES_APPROVAL"
95
- ]);
96
-
97
- if (blockingDecisions.has(deliveryDecision?.decision)) {
98
- const filtered = changedFiles.filter(file =>
99
- !file.startsWith(".ai-workflow/") &&
100
- !file.startsWith(".git/") &&
101
- file !== "EVIDENCE.json" &&
102
- file !== "EVIDENCE.temp.json"
103
- );
104
- return {
105
- status: "BLOCKED",
106
- passed: false,
107
- reason: `Delivery decision is blocking: ${deliveryDecision.decision}`,
108
- decision: deliveryDecision.decision,
109
- checkedFiles: filtered,
110
- violations: [`Blocking delivery decision: ${deliveryDecision.decision}`]
111
- };
112
- }
113
-
114
- const filteredFiles = changedFiles.filter(file =>
115
- !file.startsWith(".ai-workflow/") &&
116
- !file.startsWith(".git/") &&
117
- file !== "EVIDENCE.json" &&
118
- file !== "EVIDENCE.temp.json"
119
- );
120
-
121
- const req = userRequest.toLowerCase();
122
- const isReadOnly = /(inspect|audit|read-only|dependencies)/.test(req);
123
- const isDocsOnly = /(document|doc|readme)/.test(req) && !req.includes("code");
124
- const isRelease = /(release|publish|deploy)/.test(req);
125
-
126
- const violations = [];
127
-
128
- if (isReadOnly && filteredFiles.length > 0) {
129
- violations.push("Read-only audit task mutated repository files mismatch.");
130
- }
131
-
132
- if (isDocsOnly) {
133
- const codeChanged = filteredFiles.some(file =>
134
- !file.endsWith(".md") && !file.endsWith(".txt") && !file.startsWith("docs/")
135
- );
136
- if (codeChanged) {
137
- violations.push("Documentation-only task mutated codebase files without justification mismatch.");
138
- }
139
- }
140
-
141
- if (isRelease) {
142
- const approved = explicitApprovals.includes("npm-publish") || explicitApprovals.includes("deploy-production");
143
- if (!approved) {
144
- violations.push("Release/deploy task proceeds without explicit approval token mismatch.");
145
- }
146
- }
147
-
148
- const reqReact = /(react|next)/.test(req);
149
- const reqVue = /(vue|nuxt)/.test(req);
150
- const reqBackend = /(laravel|django|rails|express|nest|fastify|flask)/.test(req);
151
- const reqWp = /(wordpress|wp)/.test(req);
152
- const reqCli = /(cli|command)/.test(req);
153
-
154
- if (reqReact && this.deliveryClass === "standalone-web" && !/prototype/.test(req)) {
155
- violations.push("React framework page requested, but a standalone HTML/CSS substitute mismatch was delivered instead.");
156
- }
157
- if (reqVue && this.deliveryClass === "standalone-web" && !/prototype/.test(req)) {
158
- violations.push("Vue framework page requested, but a standalone HTML/CSS substitute mismatch was delivered instead.");
159
- }
160
-
161
- if (reqBackend && filteredFiles.length > 0) {
162
- const isLaravel = /\blaravel\b/i.test(req);
163
- const isDjango = /\bdjango\b/i.test(req);
164
- const isExpressNestFastify = /\b(express|nest|fastify)\b/i.test(req);
165
- const isRails = /\brails\b/i.test(req);
166
-
167
- if (isLaravel) {
168
- const isLaravelArtifact = filteredFiles.some(file =>
169
- file === "composer.json" ||
170
- file === "artisan" ||
171
- file.startsWith("routes/") ||
172
- file.startsWith("app/Http/Controllers/") ||
173
- file.startsWith("app/Models/") ||
174
- file.startsWith("app/Services/") ||
175
- file.startsWith("tests/Feature/") ||
176
- file.startsWith("tests/Unit/") ||
177
- /(^|\/)(routes\/.*\.php|app\/Http\/Controllers\/.*\.php|app\/Models\/.*\.php|app\/Services\/.*\.php|tests\/Feature\/.*\.php|tests\/Unit\/.*\.php|artisan|composer\.json)$/.test(file)
178
- );
179
- if (!isLaravelArtifact) {
180
- violations.push("Laravel endpoint/scaffold requested, but no valid Laravel files/structure mismatch was delivered.");
181
- }
182
- } else if (isDjango) {
183
- const isDjangoArtifact = filteredFiles.some(file =>
184
- file === "manage.py" ||
185
- file === "requirements.txt" ||
186
- file === "pyproject.toml" ||
187
- file.endsWith("settings.py") ||
188
- file.endsWith("urls.py") ||
189
- file.endsWith("views.py") ||
190
- file.endsWith("models.py") ||
191
- file.endsWith("tests.py") ||
192
- /(^|\/)(manage\.py|settings\.py|urls\.py|views\.py|models\.py|tests\.py|requirements\.txt|pyproject\.toml)$/.test(file)
193
- );
194
- if (!isDjangoArtifact) {
195
- violations.push("Django backend requested, but no valid Django files/structure mismatch was delivered.");
196
- }
197
- } else if (isExpressNestFastify) {
198
- const isExpressNestFastifyArtifact = filteredFiles.some(file =>
199
- file === "package.json" ||
200
- file.startsWith("src/routes/") ||
201
- file.startsWith("src/controllers/") ||
202
- file.startsWith("src/services/") ||
203
- file.startsWith("tests/") ||
204
- /(^|\/)(package\.json|src\/routes\/.*|src\/controllers\/.*|src\/services\/.*|tests\/.*|server\.[a-z]+|app\.[a-z]+|main\.[a-z]+)$/.test(file)
205
- );
206
- if (!isExpressNestFastifyArtifact) {
207
- violations.push("Express/Fastify/Nest backend requested, but no valid Node.js server files/structure mismatch was delivered.");
208
- }
209
- } else if (isRails) {
210
- const isRailsArtifact = filteredFiles.some(file =>
211
- file === "Gemfile" ||
212
- file === "config/routes.rb" ||
213
- file.startsWith("app/controllers/") ||
214
- file.startsWith("app/models/") ||
215
- file.startsWith("app/services/") ||
216
- file.startsWith("test/") ||
217
- file.startsWith("spec/") ||
218
- /(^|\/)(Gemfile|config\/routes\.rb|app\/controllers\/.*|app\/models\/.*|app\/services\/.*|test\/.*|spec\/.*)$/.test(file)
219
- );
220
- if (!isRailsArtifact) {
221
- violations.push("Ruby on Rails backend requested, but no valid Rails files/structure mismatch was delivered.");
222
- }
223
- } else {
224
- const isGenericBackendArtifact = filteredFiles.some(file =>
225
- !file.includes("plain_script.py") &&
226
- (
227
- file.startsWith("app/") ||
228
- file.startsWith("routes/") ||
229
- file.startsWith("config/") ||
230
- file.startsWith("src/controllers/") ||
231
- file.startsWith("src/routes/") ||
232
- file.startsWith("src/services/") ||
233
- file.startsWith("backend/") ||
234
- file.startsWith("server/") ||
235
- file.startsWith("api/") ||
236
- file.endsWith(".py") ||
237
- file.endsWith(".php") ||
238
- file.endsWith(".rb") ||
239
- file.endsWith(".go")
240
- )
241
- );
242
- if (!isGenericBackendArtifact) {
243
- violations.push("Backend endpoint requested, but wrong backend artifact structure mismatch was delivered.");
244
- }
245
- }
246
- }
247
-
248
- if (reqCli && filteredFiles.length > 0) {
249
- const isCliArtifact = filteredFiles.some(file =>
250
- file.startsWith("bin/") || file.startsWith("src/commands/")
251
- );
252
- if (!isCliArtifact) {
253
- violations.push("CLI command requested, but a loose script outside the executable bin/commands configuration mismatch was delivered.");
254
- }
255
- }
256
-
257
- if (reqWp && filteredFiles.length > 0) {
258
- let isWpArtifact = false;
259
- for (const file of filteredFiles) {
260
- const basename = path.basename(file);
261
- const ext = path.extname(file).toLowerCase();
262
-
263
- if (file.startsWith("wp-content/") || file === "wp-config.php") {
264
- isWpArtifact = true;
265
- break;
266
- }
267
-
268
- if (basename === "functions.php" ||
269
- basename === "front-page.php" ||
270
- basename === "single.php" ||
271
- basename === "page.php" ||
272
- basename === "archive.php" ||
273
- basename === "theme.json" ||
274
- file.startsWith("template-parts/") ||
275
- file.includes("/template-parts/")) {
276
- isWpArtifact = true;
277
- break;
278
- }
279
- if (basename === "style.css") {
280
- const hasHeader = await hasWpThemeHeader(this.cwd, file);
281
- if (hasHeader) {
282
- isWpArtifact = true;
283
- break;
284
- }
285
- }
286
- if (basename === "index.php") {
287
- isWpArtifact = true;
288
- break;
289
- }
290
-
291
- if (basename === "uninstall.php" ||
292
- basename === "composer.json" ||
293
- file.startsWith("includes/") || file.includes("/includes/") ||
294
- file.startsWith("admin/") || file.includes("/admin/") ||
295
- file.startsWith("public/") || file.includes("/public/") ||
296
- file.startsWith("src/") || file.includes("/src/")) {
297
- isWpArtifact = true;
298
- break;
299
- }
300
- if (ext === ".php") {
301
- const hasHeader = await hasWpPluginHeader(this.cwd, file);
302
- if (hasHeader) {
303
- isWpArtifact = true;
304
- break;
305
- }
306
- }
307
- }
308
-
309
- const hasHtml = filteredFiles.some(file => path.extname(file).toLowerCase() === ".html" || path.extname(file).toLowerCase() === ".htm");
310
-
311
- if (!isWpArtifact || hasHtml) {
312
- violations.push("WordPress plugin or theme requested, but files were delivered outside the WordPress structure or contained standalone HTML.");
313
- }
314
- }
315
-
316
- const frontendViolations = await this.verifyFrontendFidelity(filteredFiles);
317
- if (frontendViolations && frontendViolations.length > 0) {
318
- violations.push(...frontendViolations);
319
- }
320
-
321
- const status = violations.length > 0 ? "BLOCKED" : "PASS";
322
- return {
323
- status,
324
- passed: status === "PASS",
325
- reason: violations.length > 0 ? `Fidelity violations: ${violations.join("; ")}` : "Verification passed",
326
- decision: deliveryDecision.decision || this.deliveryClass,
327
- checkedFiles: filteredFiles,
328
- violations
329
- };
330
- }
331
-
332
- async verifyFrontendFidelity(changedFiles) {
333
- const skillPaths = [
334
- path.join(this.cwd, ".agents/skills/frontend-design-system/SKILL.md"),
335
- path.join(this.cwd, ".agents/skills/frontend_design_system/SKILL.md"),
336
- path.join(this.cwd, ".ai-workflow/opencode/skills/frontend-design-system/SKILL.md"),
337
- path.join(this.cwd, ".ai-workflow/opencode/skills/frontend_design_system/SKILL.md"),
338
- path.join(this.cwd, "opencode/skills/frontend-design-system/SKILL.md"),
339
- path.join(this.cwd, "opencode/skills/frontend_design_system/SKILL.md")
340
- ];
341
-
342
- let hasDesignSystemSkill = false;
343
- for (const p of skillPaths) {
344
- const exists = await fs.access(p).then(() => true).catch(() => false);
345
- if (exists) {
346
- hasDesignSystemSkill = true;
347
- break;
348
- }
349
- }
350
-
351
- if (!hasDesignSystemSkill) {
352
- return null;
353
- }
354
-
355
- const uiFiles = changedFiles.filter((file) =>
356
- /\.(tsx|jsx|vue|html|css|scss|less|blade\.php)$/.test(file)
357
- );
358
- if (uiFiles.length === 0) return null;
359
-
360
- const violations = [];
361
- let cssContentCombined = "";
362
- let htmlContentCombined = "";
363
- let styleAttrsCount = 0;
364
-
365
- for (const file of uiFiles) {
366
- try {
367
- const fullPath = path.join(this.cwd, file);
368
- const content = await fs.readFile(fullPath, "utf8");
369
-
370
- if (/\.(css|scss|less|vue|tsx|jsx)$/.test(file)) {
371
- cssContentCombined += content;
372
- }
373
- if (/\.(html|vue|tsx|jsx|blade\.php)$/.test(file)) {
374
- htmlContentCombined += content;
375
-
376
- // Match inline style attributes
377
- const inlineStyles = content.match(/style\s*=\s*["']/g) || [];
378
- styleAttrsCount += inlineStyles.length;
379
-
380
- // CDN Checks
381
- if (/(unpkg\.com|cdnjs\.cloudflare\.com|cdn\.jsdelivr\.net|tailwindcss|bootstrap)/i.test(content)) {
382
- const isApproved = (this.explicitApprovals || []).some(app =>
383
- /cdn-styles|tailwind|bootstrap/i.test(app)
384
- );
385
- if (!isApproved) {
386
- violations.push(`Fidelity Violation: standalone external CDN style/library dependency detected in '${file}' without explicit authorization.`);
387
- }
388
- }
389
- }
390
- } catch {
391
- // ignore
392
- }
393
- }
394
-
395
- // 1. Minimum CSS tokens check
396
- // Option D: only enforce when CSS volume is substantial (>200 chars of effective rules)
397
- // and bypass is available via explicit approval token 'no-css-vars' or 'css-vars-bypass'.
398
- const hasRules = cssContentCombined.includes("{");
399
- const cssVarsApproved = (this.explicitApprovals || []).some(app =>
400
- /no-css-vars|css-vars-bypass/i.test(app)
401
- );
402
- const cssRulesContent = cssContentCombined.replace(/\/\*[\s\S]*?\*\//g, "").trim();
403
- const isSubstantialCss = cssRulesContent.length > 200;
404
- const hasCssVars = /(--[a-zA-Z0-9_-]+\s*:|var\(--[a-zA-Z0-9_-]+\))/g.test(cssContentCombined);
405
- if (hasRules && isSubstantialCss && !hasCssVars && !cssVarsApproved) {
406
- violations.push("Fidelity Violation: Design System token mismatch. No CSS custom properties (variables) detected or used in changed UI stylesheets. Add explicit approval 'no-css-vars' to bypass this check for simple or legacy stylesheets.");
407
- }
408
-
409
- // 2. Excessive style="..." inline styles check
410
- if (styleAttrsCount > 5) {
411
- violations.push(`Fidelity Violation: excessive inline styles detected (${styleAttrsCount} style="..." declarations). Use CSS classes or utility classes instead.`);
412
- }
413
-
414
- // 3. Main HTML semantic tags check
415
- if (htmlContentCombined.length > 0) {
416
- const hasSemanticTags = /<(header|main|footer|nav|section)\b/i.test(htmlContentCombined);
417
- const hasH1 = /<h1\b/i.test(htmlContentCombined);
418
- if (!hasSemanticTags && !hasH1) {
419
- violations.push("Fidelity Violation: HTML layout structure lacks basic semantic elements (<header>, <main>, <footer>, <nav>, <section>) or <h1> tag.");
420
- }
421
- }
422
-
423
- return violations;
424
- }
425
- }
426
-
427
- async function hasWpThemeHeader(cwd, file) {
428
- try {
429
- const fullPath = path.join(cwd, file);
430
- const content = await fs.readFile(fullPath, "utf8");
431
- return /\bTheme Name\s*:/i.test(content) || /\bTheme URI\s*:/i.test(content);
432
- } catch {
433
- return false;
434
- }
435
- }
436
-
437
- async function hasWpPluginHeader(cwd, file) {
438
- try {
439
- const fullPath = path.join(cwd, file);
440
- const content = await fs.readFile(fullPath, "utf8");
441
- return /\bPlugin Name\s*:/i.test(content) || /\bDescription\s*:/i.test(content);
442
- } catch {
443
- return false;
444
- }
445
- }
@@ -1,43 +0,0 @@
1
- export const DELIVERY_SUMMARY_FIELDS = Object.freeze([
2
- "Status",
3
- "Branch",
4
- "Changes",
5
- "Validation",
6
- "Known limitations"
7
- ]);
8
-
9
- export function buildDeliverySummary({ evidence }) {
10
- const commands = (evidence?.commands || []).map((item) => `${item.command || item.name}: ${item.status}`).join("; ") || "No validation commands recorded";
11
- return {
12
- Status: evidence?.status || "BLOCKED",
13
- Branch: evidence?.branch || "unknown",
14
- Changes: (evidence?.changedFiles || []).join(", ") || "No changed files recorded",
15
- Validation: commands,
16
- "Known limitations": (evidence?.limitations || []).join("; ") || "None recorded"
17
- };
18
- }
19
-
20
- export function validateDeliverySummary(summary) {
21
- const missing = DELIVERY_SUMMARY_FIELDS.filter((field) => !String(summary?.[field] || "").trim());
22
- const status = String(summary?.Status || "").trim();
23
- const invalid = [];
24
- if (!["COMPLETED", "COMPLETED_WITH_NOTES", "BLOCKED"].includes(status)) invalid.push("Status");
25
- if (status !== "BLOCKED" && /FAIL|BLOCKED/.test(String(summary?.Validation || ""))) invalid.push("successful status with failed validation");
26
- return { status: missing.length || invalid.length ? "FAIL" : "PASS", missing, invalid };
27
- }
28
-
29
- export function formatDeliverySummary(summary) {
30
- return DELIVERY_SUMMARY_FIELDS.map((field) => `${field}: ${summary?.[field] || "NOT_RECORDED"}`).join("\n");
31
- }
32
-
33
- // Backward-compatible aliases for integrations that imported the previous API.
34
- export const CANONICAL_FINALIZATION_FIELDS = DELIVERY_SUMMARY_FIELDS;
35
- export const buildCanonicalFinalization = ({ collectorStatus = "BLOCKED", branchRecovery = "unknown" } = {}) => ({
36
- Status: collectorStatus === "PASS" ? "COMPLETED" : collectorStatus === "PASS_WITH_NOTES" ? "COMPLETED_WITH_NOTES" : "BLOCKED",
37
- Branch: branchRecovery,
38
- Changes: "See observed diff",
39
- Validation: collectorStatus,
40
- "Known limitations": "See evidence"
41
- });
42
- export const validateCanonicalFinalization = validateDeliverySummary;
43
- export const formatCanonicalFinalization = formatDeliverySummary;