openspec-playwright 0.3.7 → 0.3.10

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.
@@ -3,9 +3,21 @@
3
3
 
4
4
  <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
5
 
6
- ### Apr 7, 2026
6
+ ### Mar 27, 2026
7
7
 
8
8
  | ID | Time | T | Title | Read |
9
9
  |----|------|---|-------|------|
10
- | #5762 | 11:14 AM | 🔵 | SKILL.md Already Documents fillAndVerify Pattern | ~214 |
10
+ | #5635 | 3:00 PM | | Simplified skill input instructions | ~201 |
11
+ | #5634 | " | 🟣 | Improved change selection logic in openspec-e2e skill | ~200 |
12
+ | #5633 | " | 🔵 | openspec-e2e SKILL.md implements three-agent Playwright pipeline | ~252 |
13
+ | #5511 | 11:22 AM | ✅ | Extended guardrails to protect auth files from overwriting | ~181 |
14
+ | #5510 | 11:21 AM | 🟣 | Enhanced SKILL.md with Playwright auth project configuration | ~228 |
15
+ | #5427 | 10:05 AM | ✅ | Committed style fixes aligning SKILL.md with OpenSpec template standard | ~281 |
16
+
17
+ ### Apr 16, 2026
18
+
19
+ | ID | Time | T | Title | Read |
20
+ |----|------|---|-------|------|
21
+ | #5997 | 9:59 AM | 🔵 | openspec-e2e skill uses openspec-pw CLI for parallel exploration | ~279 |
22
+ | #5996 | " | 🔵 | openspec-e2e skill handles missing auth gracefully | ~172 |
11
23
  </claude-mem-context>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: openspec-e2e
3
- description: Run Playwright E2E verification for an OpenSpec change. Use when the user wants to validate that the implementation works end-to-end by running Playwright tests generated from the specs.
3
+ description: "Run Playwright E2E verification. ONLY invoke when user explicitly says '/opsx:e2e' or 'run E2E tests'. Do NOT auto-trigger from explore/propose/apply/verify/continue stages E2E is a separate workflow requiring explicit user request."
4
4
  license: MIT
5
5
  compatibility: Requires openspec CLI, Playwright (with browsers installed), /browse (gstack, for exploration), and @playwright/mcp (globally installed via `claude mcp add playwright npx @playwright/mcp@latest`, for test execution + Healer).
6
6
  metadata:
@@ -449,6 +449,65 @@ After writing `app-exploration.md`, extract **project-level shared knowledge** a
449
449
 
450
450
  Append only new/changed items — preserve existing content.
451
451
 
452
+ ### 4.5. Vision Check (Optional, VLM-powered)
453
+
454
+ If Ollama with a vision model is available, analyze screenshots for layout anomalies before proceeding to test generation. This catches UI issues early — before tests fail.
455
+
456
+ **Prerequisite**: Run `openspec-pw doctor` to check Vision Check availability:
457
+ ```
458
+ ─── Vision Check ───
459
+ ✓ ollama: http://localhost:11434 (qwen2.5-vl)
460
+ ```
461
+
462
+ If Vision Check shows `⚠ disabled` or `⚠ not reachable` → skip this step entirely. Vision Check is optional and should not block exploration.
463
+
464
+ **Configuration** (in order of priority):
465
+ 1. Environment variables: `OLLAMA_URL`, `OLLAMA_VISION_MODEL`
466
+ 2. `tests/playwright/app-knowledge.md` → **Vision Check Config** section
467
+
468
+ If no configuration is found, vision check is disabled.
469
+
470
+ **Run vision check** after exploration is complete:
471
+
472
+ ```bash
473
+ openspec-pw vision-check --screenshots "__screenshots__/*.png"
474
+ ```
475
+
476
+ **Flags**:
477
+ - `--screenshots <glob>` — Screenshot paths (required)
478
+ - `--parallel <n>` — Concurrent Ollama requests (default: 4)
479
+ - `--severity <levels>` — Filter by severity: `blocking,warning,minor`
480
+ - `--output <path>` — Write JSON results to file
481
+ - `--dry-run` — List screenshots without analyzing
482
+ - `--json` — Output JSON format
483
+
484
+ **Exit codes**:
485
+ - `0` = Check completed (with or without anomalies)
486
+ - `1` = Ollama not available → skip vision check, continue workflow
487
+ - `2` = Configuration disabled → skip vision check, continue workflow
488
+
489
+ **Anomaly types detected**:
490
+
491
+ | Type | Description | Example |
492
+ | --- | --- | --- |
493
+ | `obscured` | Interactive element covered by another element | Submit button hidden behind modal |
494
+ | `crowded` | Elements too close together, hard to distinguish | Form fields with <8px spacing |
495
+ | `overflowed` | Content clipped by container | Truncated text without ellipsis |
496
+
497
+ **Output handling**:
498
+
499
+ 1. **JSON output** (with `--json`): Parse anomalies array for programmatic use
500
+ 2. **Auto-append**: Without `--json`, anomalies are appended to `app-exploration.md` → **Visual Anomalies** table
501
+ 3. **Decision table**:
502
+
503
+ | Anomaly Severity | Action |
504
+ | --- | --- |
505
+ | `blocking` | Flag route with `⚠️ Layout`, consider skipping test generation until fixed |
506
+ | `warning` | Log to Visual Anomalies table, continue test generation |
507
+ | `minor` | Log for reference, no action required |
508
+
509
+ **Graceful degradation**: If Ollama is unavailable or times out → log warning, skip vision check, continue to Step 5. Vision check should never block the E2E workflow.
510
+
452
511
  #### 4.7. After exploration
453
512
 
454
513
  Pass `app-exploration.md` to:
package/README.md CHANGED
@@ -35,9 +35,11 @@ Claude Code — E2E workflow is driven by SKILL.md using /browse (exploration) +
35
35
  ```bash
36
36
  openspec-pw init # Initialize integration (one-time setup)
37
37
  openspec-pw update # Update CLI and commands to latest version
38
- openspec-pw doctor # Check prerequisites
38
+ openspec-pw doctor # Check prerequisites (including Vision Check)
39
39
  openspec-pw audit # Audit tests for orphaned specs and issues
40
40
  openspec-pw migrate # Migrate old test files to new structure
41
+ openspec-pw explore # Explore routes in parallel with Playwright
42
+ openspec-pw vision-check # Analyze screenshots for layout anomalies (Ollama VLM)
41
43
  openspec-pw uninstall # Remove integration from the project
42
44
  ```
43
45
 
package/README.zh-CN.md CHANGED
@@ -45,9 +45,11 @@ Claude Code — E2E 工作流由 SKILL.md 驱动,使用 /browse(探索)+ P
45
45
  ```bash
46
46
  openspec-pw init # 初始化集成(一次性设置)
47
47
  openspec-pw update # 更新 CLI 和命令到最新版本
48
- openspec-pw doctor # 检查前置条件
48
+ openspec-pw doctor # 检查前置条件(含 Vision Check)
49
49
  openspec-pw audit # 检查测试文件是否有孤儿文件和配置问题
50
50
  openspec-pw migrate # 迁移旧测试文件到新目录结构
51
+ openspec-pw explore # 并行探索路由
52
+ openspec-pw vision-check # 使用 Ollama VLM 检测布局问题
51
53
  openspec-pw uninstall # 移除项目中的集成
52
54
  ```
53
55
 
@@ -2,6 +2,7 @@ import { existsSync } from "fs";
2
2
  import { join } from "path";
3
3
  import { execSync } from "child_process";
4
4
  import chalk from "chalk";
5
+ import { loadOllamaConfig, checkOllamaHealth } from "../utils/ollama.js";
5
6
  export async function doctor(options = {}) {
6
7
  const checks = [];
7
8
  const projectRoot = process.cwd();
@@ -105,7 +106,22 @@ export async function doctor(options = {}) {
105
106
  ok: hasSeed,
106
107
  message: hasSeed ? "found" : "not found (optional)",
107
108
  });
108
- const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test").length === 0;
109
+ // Vision Check (Ollama)
110
+ const ollamaConfig = loadOllamaConfig(projectRoot);
111
+ let visionOk = false;
112
+ let visionMessage = "disabled";
113
+ if (ollamaConfig.enabled) {
114
+ const health = await checkOllamaHealth(ollamaConfig);
115
+ visionOk = health.ok;
116
+ visionMessage = health.message;
117
+ }
118
+ checks.push({
119
+ category: "Vision Check",
120
+ name: "ollama",
121
+ ok: visionOk,
122
+ message: visionMessage,
123
+ });
124
+ const allOk = checks.filter((c) => !c.ok && c.category !== "Seed Test" && c.category !== "Vision Check").length === 0;
109
125
  if (options.json) {
110
126
  console.log(JSON.stringify({ ok: allOk, checks }, null, 2));
111
127
  if (!allOk)
@@ -123,7 +139,7 @@ export async function doctor(options = {}) {
123
139
  if (check.ok) {
124
140
  console.log(chalk.green(` ✓ ${check.name}: ${check.message}`));
125
141
  }
126
- else if (check.category === "Seed Test") {
142
+ else if (check.category === "Seed Test" || check.category === "Vision Check") {
127
143
  console.log(chalk.yellow(` ⚠ ${check.name}: ${check.message}`));
128
144
  }
129
145
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
3
+ *
4
+ * Usage:
5
+ * openspec-pw vision-check --screenshots "__screenshots__/*.png"
6
+ * openspec-pw vision-check --screenshots "shot1.png,shot2.png" --parallel 2
7
+ *
8
+ * Exit codes:
9
+ * 0 = Check completed (with or without anomalies)
10
+ * 1 = Ollama not available (skipped)
11
+ * 2 = Configuration missing or invalid (skipped)
12
+ */
13
+ export interface VisionCheckOptions {
14
+ screenshots: string;
15
+ config?: string;
16
+ parallel?: number;
17
+ output?: string;
18
+ dryRun?: boolean;
19
+ severity?: string;
20
+ json?: boolean;
21
+ }
22
+ /**
23
+ * Main vision check command.
24
+ */
25
+ export declare function visionCheck(options: VisionCheckOptions): Promise<void>;
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
3
+ *
4
+ * Usage:
5
+ * openspec-pw vision-check --screenshots "__screenshots__/*.png"
6
+ * openspec-pw vision-check --screenshots "shot1.png,shot2.png" --parallel 2
7
+ *
8
+ * Exit codes:
9
+ * 0 = Check completed (with or without anomalies)
10
+ * 1 = Ollama not available (skipped)
11
+ * 2 = Configuration missing or invalid (skipped)
12
+ */
13
+ import { existsSync, readFileSync, writeFileSync } from "fs";
14
+ import { join, basename } from "path";
15
+ import { glob } from "glob";
16
+ import chalk from "chalk";
17
+ import { loadOllamaConfig, checkOllamaHealth, batchAnalyzeScreenshots, } from "../utils/ollama.js";
18
+ /**
19
+ * Extract route name from screenshot path.
20
+ * e.g., "__screenshots__/dashboard.png" → "/dashboard"
21
+ * "__screenshots__/settings-profile.png" → "/settings/profile"
22
+ */
23
+ function extractRouteFromPath(screenshotPath) {
24
+ const name = basename(screenshotPath, ".png");
25
+ // Convert dashes back to slashes, handle root
26
+ if (name === "index" || name === "home" || name === "root") {
27
+ return "/";
28
+ }
29
+ return "/" + name.replace(/-/g, "/");
30
+ }
31
+ /**
32
+ * Resolve screenshot paths from glob or comma-separated list.
33
+ */
34
+ async function resolveScreenshots(pattern) {
35
+ // Check if comma-separated list
36
+ if (pattern.includes(",") && !pattern.includes("*")) {
37
+ return pattern.split(",").map((p) => ({
38
+ path: p.trim(),
39
+ route: extractRouteFromPath(p.trim()),
40
+ }));
41
+ }
42
+ // Glob pattern
43
+ const paths = await glob(pattern, { absolute: true });
44
+ return paths.map((p) => ({
45
+ path: p,
46
+ route: extractRouteFromPath(p),
47
+ }));
48
+ }
49
+ /**
50
+ * Filter anomalies by severity level.
51
+ */
52
+ function filterBySeverity(anomalies, severityFilter) {
53
+ const levels = severityFilter.toLowerCase().split(",").map((s) => s.trim());
54
+ return anomalies.filter((a) => levels.includes(a.severity));
55
+ }
56
+ /**
57
+ * Append anomalies to app-exploration.md Visual Anomalies section.
58
+ */
59
+ function appendToExploration(projectRoot, anomalies) {
60
+ const explorationPath = join(projectRoot, "tests", "playwright", "app-exploration.md");
61
+ if (!existsSync(explorationPath)) {
62
+ console.error(chalk.yellow(`Warning: ${explorationPath} not found, skipping write`));
63
+ return;
64
+ }
65
+ let content = readFileSync(explorationPath, "utf-8");
66
+ // Find or create Visual Anomalies section
67
+ const sectionHeader = "## Visual Anomalies";
68
+ const tableHeader = `| Route | Element | Type | Position | Severity | Description |
69
+ | --- | --- | --- | --- | --- | --- |`;
70
+ if (!content.includes(sectionHeader)) {
71
+ // Add section before "## Next Steps" or at the end
72
+ const nextStepsIndex = content.indexOf("## Next Steps");
73
+ if (nextStepsIndex !== -1) {
74
+ content =
75
+ content.slice(0, nextStepsIndex) +
76
+ `${sectionHeader}\n\n${tableHeader}\n\n` +
77
+ content.slice(nextStepsIndex);
78
+ }
79
+ else {
80
+ content += `\n\n${sectionHeader}\n\n${tableHeader}\n`;
81
+ }
82
+ }
83
+ // Append new anomalies (de-duplicate by route + element + type)
84
+ const existingRows = content.match(/\| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \| [^|]+ \|/g) || [];
85
+ const existingKeys = new Set(existingRows.map((row) => {
86
+ const cells = row.split("|").map((c) => c.trim()).filter(Boolean);
87
+ return `${cells[0]}:${cells[1]}:${cells[2]}`;
88
+ }));
89
+ const newRows = [];
90
+ for (const a of anomalies) {
91
+ const key = `${a.route}:${a.element}:${a.type}`;
92
+ if (!existingKeys.has(key)) {
93
+ newRows.push(`| ${a.route} | ${a.element} | ${a.type} | ${a.position} | ${a.severity} | ${a.description} |`);
94
+ existingKeys.add(key);
95
+ }
96
+ }
97
+ if (newRows.length > 0) {
98
+ // Insert after table header
99
+ const headerIndex = content.indexOf(tableHeader);
100
+ if (headerIndex !== -1) {
101
+ const insertPoint = headerIndex + tableHeader.length;
102
+ content =
103
+ content.slice(0, insertPoint) +
104
+ "\n" +
105
+ newRows.join("\n") +
106
+ content.slice(insertPoint);
107
+ }
108
+ writeFileSync(explorationPath, content, "utf-8");
109
+ console.log(chalk.green(` ✓ Appended ${newRows.length} anomalies to app-exploration.md`));
110
+ }
111
+ }
112
+ /**
113
+ * Main vision check command.
114
+ */
115
+ export async function visionCheck(options) {
116
+ const projectRoot = process.cwd();
117
+ // 1. Load configuration
118
+ const config = loadOllamaConfig(projectRoot);
119
+ if (!config.enabled) {
120
+ const result = {
121
+ processed: 0,
122
+ anomalies: [],
123
+ skipped: [],
124
+ skipReason: "disabled",
125
+ ollamaUrl: config.url,
126
+ model: config.model,
127
+ };
128
+ if (options.json) {
129
+ console.log(JSON.stringify(result, null, 2));
130
+ }
131
+ else {
132
+ console.log(chalk.yellow("Vision check disabled in config"));
133
+ }
134
+ process.exit(2);
135
+ }
136
+ // 2. Dry run: list files only
137
+ if (options.dryRun) {
138
+ const screenshots = await resolveScreenshots(options.screenshots);
139
+ console.log(chalk.blue("\n📷 Screenshots to analyze (dry run):\n"));
140
+ for (const s of screenshots) {
141
+ console.log(` ${s.route} → ${s.path}`);
142
+ }
143
+ console.log(chalk.gray(`\nTotal: ${screenshots.length} files`));
144
+ console.log(chalk.gray(`Ollama: ${config.url} (${config.model})`));
145
+ return;
146
+ }
147
+ // 3. Health check
148
+ const health = await checkOllamaHealth(config);
149
+ if (!health.ok) {
150
+ const result = {
151
+ processed: 0,
152
+ anomalies: [],
153
+ skipped: [],
154
+ skipReason: health.message,
155
+ ollamaUrl: config.url,
156
+ model: config.model,
157
+ };
158
+ if (options.json) {
159
+ console.log(JSON.stringify(result, null, 2));
160
+ }
161
+ else {
162
+ console.log(chalk.yellow(`Vision check skipped: ${health.message}`));
163
+ }
164
+ process.exit(1);
165
+ }
166
+ // 4. Resolve screenshots
167
+ const screenshots = await resolveScreenshots(options.screenshots);
168
+ if (screenshots.length === 0) {
169
+ console.log(chalk.yellow("No screenshots found matching pattern"));
170
+ process.exit(0);
171
+ }
172
+ if (!options.json) {
173
+ console.log(chalk.blue(`\n🔍 Analyzing ${screenshots.length} screenshots...\n`));
174
+ }
175
+ // 5. Batch analyze
176
+ const concurrency = options.parallel || 4;
177
+ const result = await batchAnalyzeScreenshots(config, screenshots, concurrency);
178
+ // 6. Filter by severity if specified
179
+ if (options.severity) {
180
+ result.anomalies = filterBySeverity(result.anomalies, options.severity);
181
+ }
182
+ // 7. Output results
183
+ if (options.json) {
184
+ console.log(JSON.stringify(result, null, 2));
185
+ }
186
+ else {
187
+ // Text output
188
+ console.log(chalk.blue("─── Vision Check Results ───\n"));
189
+ console.log(` Processed: ${result.processed}/${screenshots.length}`);
190
+ console.log(` Anomalies: ${result.anomalies.length}`);
191
+ if (result.skipped.length > 0) {
192
+ console.log(chalk.yellow(` Skipped: ${result.skipped.length}`));
193
+ }
194
+ if (result.anomalies.length > 0) {
195
+ console.log(chalk.blue("\n─── Anomalies ───\n"));
196
+ for (const a of result.anomalies) {
197
+ const severityColor = a.severity === "blocking"
198
+ ? chalk.red
199
+ : a.severity === "warning"
200
+ ? chalk.yellow
201
+ : chalk.gray;
202
+ console.log(` ${severityColor(`[${a.severity}]`)} ${a.route}`);
203
+ console.log(` Element: ${a.element}`);
204
+ console.log(` Type: ${a.type} (${a.position})`);
205
+ console.log(` ${a.description}\n`);
206
+ }
207
+ }
208
+ else {
209
+ console.log(chalk.green("\n ✓ No layout anomalies detected\n"));
210
+ }
211
+ }
212
+ // 8. Append to app-exploration.md if anomalies found
213
+ if (result.anomalies.length > 0 && !options.json) {
214
+ appendToExploration(projectRoot, result.anomalies);
215
+ }
216
+ // 9. Write to output file if specified
217
+ if (options.output) {
218
+ writeFileSync(options.output, JSON.stringify(result, null, 2), "utf-8");
219
+ if (!options.json) {
220
+ console.log(chalk.green(` ✓ Results written to ${options.output}`));
221
+ }
222
+ }
223
+ }
224
+ //# sourceMappingURL=visionCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visionCheck.js","sourceRoot":"","sources":["../../src/commands/visionCheck.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { migrate } from "./commands/migrate.js";
10
10
  import { uninstall } from "./commands/uninstall.js";
11
11
  import { audit } from "./commands/audit.js";
12
12
  import { explore } from "./commands/explore.js";
13
+ import { visionCheck } from "./commands/visionCheck.js";
13
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
15
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
15
16
  const program = new Command();
@@ -74,5 +75,18 @@ program
74
75
  .action(async (opts) => {
75
76
  await explore(opts);
76
77
  });
78
+ program
79
+ .command("vision-check")
80
+ .description("Analyze screenshots for layout anomalies using Ollama VLM")
81
+ .requiredOption("-s, --screenshots <pattern>", "Screenshot path(s): glob pattern or comma-separated list")
82
+ .option("-c, --config <path>", "Config file path (default: app-knowledge.md)")
83
+ .option("-p, --parallel <n>", "Number of parallel requests to Ollama", (v) => parseInt(v, 10), 4)
84
+ .option("-o, --output <path>", "Write JSON results to file")
85
+ .option("--severity <levels>", "Filter by severity: blocking,warning,minor")
86
+ .option("-n, --dry-run", "List screenshots without analyzing")
87
+ .option("--json", "Output results as JSON")
88
+ .action(async (opts) => {
89
+ await visionCheck(opts);
90
+ });
77
91
  program.parse();
78
92
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAC1D,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CACV,yEAAyE,CAC1E;KACA,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,SAAS,CAAC;KAC/D,MAAM,CAAC,UAAU,EAAE,mCAAmC,CAAC;KACvD,MAAM,CAAC,QAAQ,EAAE,qDAAqD,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,uCAAuC,CAAC;KAC5D,MAAM,CAAC,IAAI,CAAC,CAAC;AAEhB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,qDAAqD,CAAC;KAClE,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC;KACrC,MAAM,CAAC,YAAY,EAAE,2BAA2B,CAAC;KACjD,MAAM,CAAC,MAAM,CAAC,CAAC;AAElB,OAAO;KACJ,OAAO,CAAC,mBAAmB,CAAC;KAC5B,WAAW,CAAC,iDAAiD,CAAC;KAC9D,MAAM,CACL,sBAAsB,EACtB,+CAA+C,CAChD;KACA,MAAM,CAAC,yBAAyB,EAAE,yBAAyB,EAAE,KAAK,CAAC;KACnE,MAAM,CAAC,QAAQ,EAAE,wBAAwB,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,iCAAiC,CAAC;KACjE,MAAM,CAAC,SAAS,EAAE,sCAAsC,CAAC;KACzD,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KAC5F,MAAM,CAAC,gBAAgB,EAAE,oCAAoC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACjG,MAAM,CAAC,cAAc,EAAE,sCAAsC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACjG,MAAM,CAAC,YAAY,EAAE,0BAA0B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KACnF,MAAM,CAAC,iBAAiB,EAAE,6BAA6B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC;KAC3F,MAAM,CAAC,UAAU,EAAE,kDAAkD,CAAC;KACtE,MAAM,CAAC,oBAAoB,EAAE,kDAAkD,CAAC;KAChF,MAAM,CAAC,GAAG,CAAC,CAAC;AAEf,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CACV,2FAA2F,CAC5F;KACA,MAAM,CACL,eAAe,EACf,kDAAkD,CACnD;KACA,MAAM,CAAC,aAAa,EAAE,8CAA8C,CAAC;KACrE,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CACV,uEAAuE,CACxE;KACA,MAAM,CAAC,SAAS,CAAC,CAAC;AAErB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mEAAmE,CAAC;KAChF,MAAM,CAAC,KAAK,CAAC,CAAC;AAEjB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CACL,oBAAoB,EACpB,4BAA4B,EAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EACtB,CAAC,CACF;KACA,MAAM,CACL,eAAe,EACf,6CAA6C,CAC9C;KACA,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,2DAA2D,CAAC;KACxE,cAAc,CACb,6BAA6B,EAC7B,0DAA0D,CAC3D;KACA,MAAM,CACL,qBAAqB,EACrB,8CAA8C,CAC/C;KACA,MAAM,CACL,oBAAoB,EACpB,uCAAuC,EACvC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EACtB,CAAC,CACF;KACA,MAAM,CACL,qBAAqB,EACrB,4BAA4B,CAC7B;KACA,MAAM,CACL,qBAAqB,EACrB,4CAA4C,CAC7C;KACA,MAAM,CACL,eAAe,EACf,oCAAoC,CACrC;KACA,MAAM,CACL,QAAQ,EACR,wBAAwB,CACzB;KACA,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Ollama API utilities for vision model integration.
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Function parameters
6
+ * 2. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
7
+ * 3. app-knowledge.md Vision Check Config section
8
+ *
9
+ * If no configuration is found, vision check is disabled.
10
+ */
11
+ export interface OllamaConfig {
12
+ url: string;
13
+ model: string;
14
+ enabled: boolean;
15
+ }
16
+ export interface VisionAnomaly {
17
+ route: string;
18
+ screenshot: string;
19
+ element: string;
20
+ type: "obscured" | "crowded" | "overflowed";
21
+ position: string;
22
+ severity: "blocking" | "warning" | "minor";
23
+ description: string;
24
+ }
25
+ export interface VisionCheckResult {
26
+ processed: number;
27
+ anomalies: VisionAnomaly[];
28
+ skipped: string[];
29
+ skipReason?: string;
30
+ ollamaUrl: string;
31
+ model: string;
32
+ }
33
+ export interface OllamaGenerateResponse {
34
+ response: string;
35
+ done: boolean;
36
+ }
37
+ /**
38
+ * Load Ollama config with priority:
39
+ * 1. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
40
+ * 2. app-knowledge.md Vision Check Config section
41
+ *
42
+ * If no configuration is found, returns enabled=false.
43
+ */
44
+ export declare function loadOllamaConfig(projectRoot?: string): OllamaConfig;
45
+ /**
46
+ * Check if Ollama is running and the vision model is available.
47
+ * Returns { ok, message } for doctor output.
48
+ */
49
+ export declare function checkOllamaHealth(config: OllamaConfig): Promise<{
50
+ ok: boolean;
51
+ message: string;
52
+ }>;
53
+ /**
54
+ * Analyze a single screenshot with the vision model.
55
+ */
56
+ export declare function analyzeScreenshot(config: OllamaConfig, screenshotPath: string, route: string): Promise<VisionAnomaly[]>;
57
+ /**
58
+ * Batch analyze multiple screenshots with concurrency control.
59
+ */
60
+ export declare function batchAnalyzeScreenshots(config: OllamaConfig, screenshots: Array<{
61
+ path: string;
62
+ route: string;
63
+ }>, concurrency?: number): Promise<VisionCheckResult>;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Ollama API utilities for vision model integration.
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Function parameters
6
+ * 2. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
7
+ * 3. app-knowledge.md Vision Check Config section
8
+ *
9
+ * If no configuration is found, vision check is disabled.
10
+ */
11
+ import { existsSync, readFileSync } from "fs";
12
+ import { join } from "path";
13
+ // ── Config loading ────────────────────────────────────────────────────────────
14
+ /**
15
+ * Load Ollama config from app-knowledge.md.
16
+ * Parses the "## Vision Check Config" section.
17
+ */
18
+ function loadFromAppKnowledge(projectRoot) {
19
+ const appKnowledgePath = join(projectRoot, "tests", "playwright", "app-knowledge.md");
20
+ if (!existsSync(appKnowledgePath))
21
+ return {};
22
+ try {
23
+ const content = readFileSync(appKnowledgePath, "utf-8");
24
+ const match = content.match(/## Vision Check Config\s*\n([\s\S]*?)(?=\n##|\n---|\z)/);
25
+ if (!match)
26
+ return {};
27
+ const section = match[1];
28
+ const config = {};
29
+ const urlMatch = section.match(/ollama_url:\s*(.+)/);
30
+ if (urlMatch)
31
+ config.url = urlMatch[1].trim();
32
+ const modelMatch = section.match(/vision_model:\s*(.+)/);
33
+ if (modelMatch)
34
+ config.model = modelMatch[1].trim();
35
+ const enabledMatch = section.match(/vision_enabled:\s*(.+)/);
36
+ if (enabledMatch)
37
+ config.enabled = enabledMatch[1].trim() === "true";
38
+ return config;
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ }
44
+ /**
45
+ * Load Ollama config with priority:
46
+ * 1. Environment variables (OLLAMA_URL, OLLAMA_VISION_MODEL)
47
+ * 2. app-knowledge.md Vision Check Config section
48
+ *
49
+ * If no configuration is found, returns enabled=false.
50
+ */
51
+ export function loadOllamaConfig(projectRoot = process.cwd()) {
52
+ const appKnowledge = loadFromAppKnowledge(projectRoot);
53
+ // Environment variables override app-knowledge.md
54
+ const url = process.env.OLLAMA_URL || appKnowledge.url;
55
+ const model = process.env.OLLAMA_VISION_MODEL || appKnowledge.model;
56
+ // If no url or model configured, disable vision check
57
+ if (!url || !model) {
58
+ return { url: "", model: "", enabled: false };
59
+ }
60
+ // Enabled: default true unless explicitly disabled
61
+ let enabled = true;
62
+ if (process.env.OLLAMA_VISION_ENABLED !== undefined) {
63
+ enabled = process.env.OLLAMA_VISION_ENABLED === "true";
64
+ }
65
+ else if (appKnowledge.enabled !== undefined) {
66
+ enabled = appKnowledge.enabled;
67
+ }
68
+ return { url, model, enabled };
69
+ }
70
+ // ── Health check ──────────────────────────────────────────────────────────────
71
+ /**
72
+ * Check if Ollama is running and the vision model is available.
73
+ * Returns { ok, message } for doctor output.
74
+ */
75
+ export async function checkOllamaHealth(config) {
76
+ if (!config.enabled) {
77
+ return { ok: false, message: "disabled in config" };
78
+ }
79
+ try {
80
+ // Check Ollama server
81
+ const controller = new AbortController();
82
+ const timeout = setTimeout(() => controller.abort(), 5000);
83
+ const response = await fetch(`${config.url}/api/tags`, {
84
+ signal: controller.signal,
85
+ });
86
+ clearTimeout(timeout);
87
+ if (!response.ok) {
88
+ return { ok: false, message: `server error: ${response.status}` };
89
+ }
90
+ const data = (await response.json());
91
+ const models = data.models || [];
92
+ const modelNames = models.map((m) => m.name);
93
+ // Check if vision model exists (exact match or prefix match)
94
+ const hasModel = modelNames.some((name) => name === config.model ||
95
+ name.startsWith(`${config.model}:`) ||
96
+ name === `${config.model}:latest`);
97
+ if (!hasModel) {
98
+ return {
99
+ ok: false,
100
+ message: `model "${config.model}" not found (available: ${modelNames.slice(0, 3).join(", ")}...)`,
101
+ };
102
+ }
103
+ return { ok: true, message: `${config.url} (${config.model})` };
104
+ }
105
+ catch (err) {
106
+ const error = err;
107
+ if (error.name === "AbortError") {
108
+ return { ok: false, message: "connection timeout" };
109
+ }
110
+ return { ok: false, message: `not reachable: ${error.message}` };
111
+ }
112
+ }
113
+ // ── Vision analysis ───────────────────────────────────────────────────────────
114
+ const VISION_PROMPT = `你是一个专业的网页UI测试员。请分析这张截图,检测以下问题:
115
+
116
+ 1. 遮挡:可交互元素(按钮、表单、链接)是否被其他元素遮挡、覆盖或压住,导致无法正常点击?
117
+ 2. 拥挤:多个元素是否间距过窄、文字重叠、边界模糊,导致难以区分或误操作?
118
+ 3. 溢出:内容是否被容器裁剪,导致关键信息不可见?
119
+
120
+ 对于每个问题,请返回:
121
+ - element: 元素名称/描述
122
+ - type: 问题类型(obscured / crowded / overflowed)
123
+ - position: 位置(用屏幕区域描述:左上/右上/中/左下/右下)
124
+ - severity: 严重程度(blocking / warning / minor)
125
+ - description: 问题描述
126
+
127
+ 如果一切正常,返回空的 anomalies 数组。
128
+
129
+ 请严格以JSON格式回答,不要包含任何其他文字:
130
+ {
131
+ "anomalies": []
132
+ }`;
133
+ /**
134
+ * Analyze a single screenshot with the vision model.
135
+ */
136
+ export async function analyzeScreenshot(config, screenshotPath, route) {
137
+ if (!existsSync(screenshotPath)) {
138
+ throw new Error(`Screenshot not found: ${screenshotPath}`);
139
+ }
140
+ const imageBase64 = readFileSync(screenshotPath).toString("base64");
141
+ const response = await fetch(`${config.url}/api/generate`, {
142
+ method: "POST",
143
+ headers: { "Content-Type": "application/json" },
144
+ body: JSON.stringify({
145
+ model: config.model,
146
+ prompt: VISION_PROMPT,
147
+ images: [imageBase64],
148
+ stream: false,
149
+ }),
150
+ });
151
+ if (!response.ok) {
152
+ throw new Error(`Ollama API error: ${response.status}`);
153
+ }
154
+ const data = (await response.json());
155
+ const responseText = data.response || "";
156
+ // Parse JSON from response (may have markdown code block)
157
+ let jsonStr = responseText;
158
+ const jsonMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/);
159
+ if (jsonMatch) {
160
+ jsonStr = jsonMatch[1];
161
+ }
162
+ try {
163
+ const parsed = JSON.parse(jsonStr.trim());
164
+ const anomalies = parsed.anomalies || [];
165
+ // Enrich with route and screenshot path
166
+ return anomalies.map((a) => ({
167
+ ...a,
168
+ route,
169
+ screenshot: screenshotPath,
170
+ }));
171
+ }
172
+ catch {
173
+ // If JSON parse fails, return empty (graceful degradation)
174
+ console.error(`Warning: Could not parse vision response for ${route}`);
175
+ return [];
176
+ }
177
+ }
178
+ /**
179
+ * Batch analyze multiple screenshots with concurrency control.
180
+ */
181
+ export async function batchAnalyzeScreenshots(config, screenshots, concurrency = 4) {
182
+ const result = {
183
+ processed: 0,
184
+ anomalies: [],
185
+ skipped: [],
186
+ ollamaUrl: config.url,
187
+ model: config.model,
188
+ };
189
+ // Process in batches
190
+ for (let i = 0; i < screenshots.length; i += concurrency) {
191
+ const batch = screenshots.slice(i, i + concurrency);
192
+ const promises = batch.map(async ({ path, route }) => {
193
+ try {
194
+ const anomalies = await analyzeScreenshot(config, path, route);
195
+ result.processed++;
196
+ result.anomalies.push(...anomalies);
197
+ }
198
+ catch (err) {
199
+ const error = err;
200
+ console.error(`Warning: Skipped ${route}: ${error.message}`);
201
+ result.skipped.push(path);
202
+ }
203
+ });
204
+ await Promise.allSettled(promises);
205
+ }
206
+ return result;
207
+ }
208
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/utils/ollama.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,8 @@
8
8
 
9
9
  本规范适用于 OpenSpec + openspec-playwright 项目(Claude Code 作为开发工具)。
10
10
 
11
+ **项目规范**:动手前读 `openspec/config.yaml`(技术栈、结构、约定、约束等),无内容则忽略。
12
+
11
13
  E2E 工作流前提(由用户确保,非 AI 操作):
12
14
  - gstack:`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`(提供 `/browse` 探索 + `/qa` 浏览器验证)
13
15
  - OpenSpec CLI:`npm install -g @fission-ai/openspec && openspec init`(提供变更管理能力)
@@ -28,7 +30,7 @@ E2E 工作流前提(由用户确保,非 AI 操作):
28
30
  ## 3. 上下文管理
29
31
  **文件读取完整**:超过 500 行的文件,不要假设单次读取覆盖完整内容——根据需要分次读取或编辑前重新读取完整文件。超过 10 条消息后,编辑任何文件前强制重新读取。
30
32
 
31
- **OpenSpec 阶段隔离**:`specs/playwright/`、`tests/playwright/`(seed 除外)和 `test-plan.md` 由 `/opsx:e2e` 显式触发,不由 explore/propose/continue/apply/verify 等阶段自动推断。E2E 工作流是独立的。
33
+ **OpenSpec 阶段隔离**:所有阶段均由用户手动触发,不自动进入下一阶段。`/opsx:explore`、`/opsx:propose`、`/opsx:apply`、`/opsx:verify`、`/opsx:e2e` 均需用户明确调用。禁止在同一阶段内触发其他阶段(如 explore 阶段不能调用 applyverify 阶段不能调用 e2e)。
32
34
 
33
35
  **重构前清死代码**:未使用的 import/export/prop/console.log 先删掉,单独提交,再做重构。
34
36
 
@@ -51,11 +53,11 @@ E2E 工作流前提(由用户确保,非 AI 操作):
51
53
  ```
52
54
  1. 探索与提案
53
55
  2. 产品与架构评审(按需触发)
54
- 3. 设计审查 → /plan-design-review + /frontend-design
56
+ 3. 设计审查 → /plan-design-review
55
57
  4. 实现 → /opsx:apply
56
58
  5. 自审 → /opsx:verify
57
59
  6. E2E 测试 → /opsx:e2e <change-name> → /browse 探索 + /qa 验证
58
- 7. 发布 → /ship 或 /land-and-deploy
60
+ 7. 发布 → 用户手动 /ship 或 /land-and-deploy
59
61
  8. 迭代回顾 → /retro
60
62
  ```
61
63
 
@@ -74,8 +76,8 @@ E2E 工作流前提(由用户确保,非 AI 操作):
74
76
 
75
77
  **5. 自审**:`/opsx:verify` 自审实现代码,确保质量。
76
78
 
77
- **6. E2E 测试**:`/opsx:e2e <change-name>` 生成 Playwright 测试 → `/browse` 探索真实 DOM → Healer 自动修复 → `/qa` 真实浏览器验证。E2E 通过后进入发布环节。
79
+ **6. E2E 测试**:`/opsx:e2e <change-name>` 生成 Playwright 测试 → `/browse` 探索真实 DOM → Healer 自动修复 → `/qa` 真实浏览器验证。E2E 通过后,由用户决定发布时机。
78
80
 
79
- **7. 发布**:`/ship`、`/land-and-deploy` 或 `/canary`。内部项目可能直接部署,无需走 PR 机制。
81
+ **7. 发布**:由用户手动触发 `/ship`、`/land-and-deploy` 或 `/canary`。内部项目可能直接部署,无需走 PR 机制。
80
82
 
81
83
  **8. 迭代回顾**:`/retro`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.3.7",
3
+ "version": "0.3.10",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,9 +19,11 @@
19
19
  "release": "npm version patch && node scripts/bump-docs.js && npm run build && git add docs/index.html && git push && git push --tags && npm publish"
20
20
  },
21
21
  "dependencies": {
22
+ "@types/glob": "^8.1.0",
22
23
  "@types/tar": "^6.1.13",
23
24
  "chalk": "^5.3.0",
24
25
  "commander": "^12.1.0",
26
+ "glob": "^13.0.6",
25
27
  "playwright": "^1.50.0",
26
28
  "tar": "^7.5.13"
27
29
  },
@@ -3,5 +3,20 @@
3
3
 
4
4
  <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
5
 
6
- *No recent activity*
6
+ ### Mar 27, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #5522 | 11:25 AM | 🔵 | Playwright auth setup template supports API and UI login | ~261 |
11
+ | #5514 | 11:22 AM | 🟣 | Created credentials.yaml template for E2E test authentication | ~227 |
12
+ | #5513 | " | 🟣 | Created auth.setup.ts template for Playwright Test Agents | ~276 |
13
+ | #5390 | 9:48 AM | 🔄 | Refactored openspec-playwright from CLI to setup tool + Claude Code skill | ~209 |
14
+ | #5375 | 9:44 AM | 🟣 | Created Playwright seed test template | ~224 |
15
+
16
+ ### Apr 16, 2026
17
+
18
+ | ID | Time | T | Title | Read |
19
+ |----|------|---|-------|------|
20
+ | #6002 | 10:06 AM | 🔵 | openspec-e2e provides seed test template with anti-patterns documented | ~299 |
21
+ | #5998 | 9:59 AM | 🔵 | OpenSpec Playwright auth.setup.ts uses session reuse pattern | ~202 |
7
22
  </claude-mem-context>
@@ -58,6 +58,18 @@ BASE_URL: <from env or seed.spec.ts>
58
58
  |-------|-------|-------|
59
59
  | | | |
60
60
 
61
+ ## Visual Anomalies
62
+
63
+ Auto-populated by `openspec-pw vision-check`. Layout issues detected via Ollama VLM.
64
+
65
+ | Route | Element | Type | Position | Severity | Description |
66
+ | --- | --- | --- | --- | --- | --- |
67
+ | | | | | | |
68
+
69
+ > **Type legend**: `obscured` (element covered) | `crowded` (spacing too narrow) | `overflowed` (content clipped)
70
+ >
71
+ > **Severity**: `blocking` (cannot interact) | `warning` (usability issue) | `minor` (cosmetic)
72
+
61
73
  ## Next Steps
62
74
 
63
75
  After exploration, pass this file to Step 5 (Planner) and Step 6 (Generator).
@@ -101,6 +101,22 @@ Persists assertion repairs (typos, spec drift) across sessions.
101
101
 
102
102
  ---
103
103
 
104
+ ## Vision Check Config
105
+
106
+ Ollama VLM configuration for layout anomaly detection. See `openspec-pw doctor` for status.
107
+
108
+ ```
109
+ ollama_url:
110
+ vision_model:
111
+ vision_enabled: false
112
+ ```
113
+
114
+ > **Note**: Vision check is disabled by default. Configure `ollama_url` and `vision_model` to enable.
115
+ > Environment variables (`OLLAMA_URL`, `OLLAMA_VISION_MODEL`) override these values.
116
+ > Run `openspec-pw vision-check --dry-run` to verify configuration.
117
+
118
+ ---
119
+
104
120
  ## Changelog
105
121
 
106
122
  | Date | Change | By |
@@ -1,13 +0,0 @@
1
- <claude-mem-context>
2
- # Recent Activity
3
-
4
- <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
-
6
- ### Apr 7, 2026
7
-
8
- | ID | Time | T | Title | Read |
9
- |----|------|---|-------|------|
10
- | #5756 | 11:10 AM | ✅ | BasePage Input Methods Enhanced with Framework Sync | ~226 |
11
- | #5747 | 10:58 AM | ⚖️ | Input Reactivity Fix Strategy | ~265 |
12
- | #5744 | 10:57 AM | 🔵 | BasePage Pattern for Playwright Testing | ~253 |
13
- </claude-mem-context>