@williambeto/ai-workflow 2.9.0 → 2.9.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [unreleased]
2
2
 
3
+ ## [2.9.1] - 2026-07-15
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - Accept persisted Playwright snapshots from the scoped delivery as valid UI
8
+ evidence, regardless of their test-directory location
9
+ - Recognize screenshots generated under `.evidence/visual/` and evaluate them
10
+ in the same `collect-evidence` execution that captured them
11
+ - Avoid repeating `NOT_DELIVERED` as a second blocking diagnostic when an
12
+ earlier validation or quality gate already explains the failure
13
+
14
+ ### 🧪 Testing
15
+
16
+ - Add regression coverage for Playwright snapshot discovery, generated visual
17
+ evidence ordering, missing required screenshots, and non-duplicated finalizer
18
+ diagnostics
19
+
3
20
  ## [2.9.0] - 2026-07-15
4
21
 
5
22
  ### 🚀 Features
@@ -34,7 +34,7 @@ import {
34
34
  QualityGuard,
35
35
  ValidationPlanner,
36
36
  parseDeliveryOutcomeClaim
37
- } from "../chunk-GLYX3HEY.js";
37
+ } from "../chunk-PP42PB7M.js";
38
38
  import {
39
39
  DeliveryDecisionEngine
40
40
  } from "../chunk-H7GIKXFO.js";
@@ -3697,7 +3697,7 @@ var commandMap = {
3697
3697
  });
3698
3698
  },
3699
3699
  validate: async (_, flags) => {
3700
- const { runValidate } = await import("../validate-DDKN6ORS.js");
3700
+ const { runValidate } = await import("../validate-7G3HBE2K.js");
3701
3701
  await runValidate({
3702
3702
  cwd: process.cwd(),
3703
3703
  taskSlug: flags.taskSlug,
@@ -25,6 +25,9 @@ function parseStatusLine(line) {
25
25
  function isManagedChangePath(file = "") {
26
26
  return /(^|\/)(node_modules|vendor|dist|coverage|\.cache)(\/|$)/.test(normalizePath(file));
27
27
  }
28
+ function isScreenshotPath(file = "") {
29
+ return /\.(png|jpe?g|webp)$/i.test(normalizePath(file));
30
+ }
28
31
  async function exists(target) {
29
32
  try {
30
33
  await fs.access(target);
@@ -163,17 +166,22 @@ var QualityGuard = class {
163
166
  }
164
167
  async verifyUiEvidence() {
165
168
  if (!this.isUiTask()) return { status: "NOT_RUN", reason: "Non-UI task." };
166
- const candidates = ["docs", "screenshots", "evidence"];
167
- const found = [];
169
+ const candidates = ["docs", "screenshots", "evidence", ".evidence"];
170
+ const found = /* @__PURE__ */ new Set();
171
+ for (const file of this.getScopedChangedFiles()) {
172
+ const normalized = normalizePath(file);
173
+ if (!isScreenshotPath(normalized) || path.isAbsolute(normalized) || normalized.startsWith("../")) continue;
174
+ if (await exists(path.join(this.cwd, normalized))) found.add(normalized);
175
+ }
168
176
  for (const dir of candidates) {
169
177
  const root = path.join(this.cwd, dir);
170
178
  if (!await exists(root)) continue;
171
179
  const entries = await fs.readdir(root, { recursive: true }).catch(() => []);
172
180
  for (const entry of entries) {
173
- if (/\.(png|jpe?g|webp)$/i.test(String(entry))) found.push(normalizePath(path.join(dir, String(entry))));
181
+ if (isScreenshotPath(String(entry))) found.add(normalizePath(path.join(dir, String(entry))));
174
182
  }
175
183
  }
176
- if (found.length) return { status: "PASS", screenshots: found.slice(0, 20) };
184
+ if (found.size) return { status: "PASS", screenshots: [...found].sort().slice(0, 20) };
177
185
  if (this.evidencePolicy === "required") {
178
186
  return { status: "FAIL_QUALITY_GATE", reason: "Required evidence for a high-risk or release UI change does not include a persisted screenshot." };
179
187
  }
@@ -673,6 +681,26 @@ var EvidenceCollector = class {
673
681
  const implementationFilesPresent = qualityGuard.isImplementationTask();
674
682
  const executableBehavior = await qualityGuard.hasExecutableBehaviorChanges(this.deliveryChangedFiles || void 0);
675
683
  const results = tasks.map((task) => this.runTask(task));
684
+ let visualEvidence = null;
685
+ if (this.visualDist) {
686
+ console.log(`
687
+ --- Capturing Visual Evidence ---`);
688
+ console.log(`Serving ${this.visualDist} on port ${this.port}...`);
689
+ try {
690
+ const verifier = new VisualVerifier({ cwd: this.cwd, visualDist: this.visualDist, port: this.port });
691
+ const screenshots = await verifier.captureScreenshots();
692
+ visualEvidence = {
693
+ desktop: path3.relative(this.cwd, screenshots[0]),
694
+ mobile: path3.relative(this.cwd, screenshots[1])
695
+ };
696
+ console.log(`Visual evidence captured successfully:`);
697
+ console.log(`- Desktop: ${visualEvidence.desktop}`);
698
+ console.log(`- Mobile: ${visualEvidence.mobile}`);
699
+ } catch (err) {
700
+ console.error(`Error capturing visual evidence: ${err.message}`);
701
+ throw err;
702
+ }
703
+ }
676
704
  const policyValidation = await qualityGuard.verify();
677
705
  const behaviorTests = results.filter((result) => result.kind === "test");
678
706
  const passingBehaviorTest = behaviorTests.some((result) => result.status === "PASS");
@@ -745,7 +773,8 @@ var EvidenceCollector = class {
745
773
  deliveryDecision: deliveryDecision.decision,
746
774
  claimedOutcome: this.claimedOutcome
747
775
  });
748
- if (["NEEDS_CLARIFICATION", "NOT_DELIVERED"].includes(deliveryOutcome) && ["PASS", "PASS_WITH_NOTES"].includes(overallStatus)) {
776
+ const deliveryOutcomeBlocks = ["NEEDS_CLARIFICATION", "NOT_DELIVERED"].includes(deliveryOutcome) && ["PASS", "PASS_WITH_NOTES"].includes(overallStatus);
777
+ if (deliveryOutcomeBlocks) {
749
778
  overallStatus = "BLOCKED";
750
779
  }
751
780
  const limitations = [];
@@ -811,7 +840,7 @@ var EvidenceCollector = class {
811
840
  "satisfy the stated decision requirement before finalizing"
812
841
  ));
813
842
  }
814
- if (["NEEDS_CLARIFICATION", "NOT_DELIVERED"].includes(deliveryOutcome)) {
843
+ if (deliveryOutcomeBlocks) {
815
844
  limitations.push(blockingDiagnostic(
816
845
  "delivery-outcome",
817
846
  `the resolved outcome is ${deliveryOutcome}`,
@@ -820,26 +849,6 @@ var EvidenceCollector = class {
820
849
  ));
821
850
  }
822
851
  const uniqueLimitations = [...new Set(limitations)];
823
- let visualEvidence = null;
824
- if (this.visualDist) {
825
- console.log(`
826
- --- Capturing Visual Evidence ---`);
827
- console.log(`Serving ${this.visualDist} on port ${this.port}...`);
828
- try {
829
- const verifier = new VisualVerifier({ cwd: this.cwd, visualDist: this.visualDist, port: this.port });
830
- const screenshots = await verifier.captureScreenshots();
831
- visualEvidence = {
832
- desktop: path3.relative(this.cwd, screenshots[0]),
833
- mobile: path3.relative(this.cwd, screenshots[1])
834
- };
835
- console.log(`Visual evidence captured successfully:`);
836
- console.log(`- Desktop: ${visualEvidence.desktop}`);
837
- console.log(`- Mobile: ${visualEvidence.mobile}`);
838
- } catch (err) {
839
- console.error(`Error capturing visual evidence: ${err.message}`);
840
- throw err;
841
- }
842
- }
843
852
  const evidence = {
844
853
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
845
854
  taskSlug: this.taskSlug,
@@ -1053,4 +1062,4 @@ export {
1053
1062
  EvidenceCollector,
1054
1063
  ValidationPlanner
1055
1064
  };
1056
- //# sourceMappingURL=chunk-GLYX3HEY.js.map
1065
+ //# sourceMappingURL=chunk-PP42PB7M.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williambeto/ai-workflow",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "AI Workflow Kit — OpenCode-first software delivery workflow with agents, commands, skills, validation, and evidence",
5
5
  "license": "MIT",
6
6
  "author": "José Willams",
@@ -3,7 +3,7 @@ import {
3
3
  QualityGuard,
4
4
  ValidationPlanner,
5
5
  VisualVerifier
6
- } from "./chunk-GLYX3HEY.js";
6
+ } from "./chunk-PP42PB7M.js";
7
7
  import "./chunk-H7GIKXFO.js";
8
8
  import "./chunk-5WRI5ZAA.js";
9
9
 
@@ -86,4 +86,4 @@ async function runValidate({
86
86
  export {
87
87
  runValidate
88
88
  };
89
- //# sourceMappingURL=validate-DDKN6ORS.js.map
89
+ //# sourceMappingURL=validate-7G3HBE2K.js.map