create-harness-vibe-coding 0.8.3 → 0.8.5
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-CN.md +2 -2
- package/README.md +2 -3
- package/package.json +1 -2
- package/src/generator.js +4 -5
- package/templates/common/.claude/agents/context-master.md +3 -2
- package/templates/common/.claude/agents/memory-master.md +3 -2
- package/templates/common/.claude/agents/tdd-guide.md +45 -19
- package/templates/common/.claude/agents/test-writer.md +52 -38
- package/templates/common/.claude/commands/wf-help.md +21 -0
- package/templates/common/.claude/rules/ecc/common.md +1 -1
- package/templates/common/.claude/settings.json +33 -90
- package/templates/common/.claude/skills/tdd/SKILL.md +18 -11
- package/templates/common/.claude/skills/wf-auto/SKILL.md +1 -1
- package/templates/common/.claude/skills/wf-remove/SKILL.md +26 -13
- package/templates/common/.codex/config.toml +0 -2
- package/templates/common/.harness-version +57 -39
- package/templates/common/AGENTS.md +7 -6
- package/templates/common/CLAUDE.md +3 -4
- package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +170 -0
- package/templates/common/Harness/AGENT_ISOLATION.md +73 -0
- package/templates/common/Harness/DEBUG_PROTOCOL.md +64 -0
- package/templates/common/Harness/ECC-GUIDE.md +4 -4
- package/templates/common/Harness/HARNESS_BRIDGE.md +128 -0
- package/templates/common/Harness/MEMORY_PROTOCOL.md +114 -0
- package/templates/common/Harness/README.md +31 -16
- package/templates/common/Harness/TDD-GUIDE.md +138 -56
- package/templates/common/Harness/WF-AUTO.md +77 -16
- package/templates/common/Harness/WF-MAX.md +81 -22
- package/templates/common/Harness/WF.md +38 -18
- package/templates/common/Harness/agent-workflow.md +47 -22
- package/templates/common/Harness/context-loading.md +33 -32
- package/templates/common/Harness/dispatch.md +10 -4
- package/templates/common/Harness/extension.md +3 -3
- package/templates/common/Harness/lifecycle.md +19 -13
- package/templates/common/Harness/research/PRD.md +18 -9
- package/templates/common/Harness/subagents.md +41 -14
- package/templates/common/Harness/tasks/_template/NAMING.md +2 -2
- package/templates/common/Harness/tasks/_template/PLAN.md +53 -11
- package/templates/common/Harness/templates/ACCEPTANCE.template.md +20 -0
- package/templates/common/Harness/templates/API_CONTRACT.template.md +40 -0
- package/templates/common/Harness/templates/PLAYWRIGHT_SPEC.template.ts +21 -0
- package/templates/common/Harness/templates/PRD.template.md +45 -0
- package/templates/common/Harness/templates/TEST_PLAN.template.md +14 -0
- package/templates/common/Harness/templates/UI_CONTRACT.template.md +11 -0
- package/templates/common/Harness/templates/VALIDATION_REPORT.template.md +20 -0
- package/templates/common/MEMORY.md +14 -3
- package/templates/common/README.md +1 -1
- package/templates/common/SETUP.md +24 -20
- package/templates/common/scripts/validate-harness.mjs +137 -41
- package/templates/common/scripts/wf-remove.mjs +123 -56
- package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +11 -3
- package/templates/optional/skills/ts-react-frontend/Harness/workflows/ts-react-frontend.md +1 -1
- package/templates/common/.codex/hooks.json +0 -59
- package/templates/common/scripts/wf-mode-hook.mjs +0 -642
- package/templates/common/scripts/wf-statusline.ps1 +0 -62
- package/templates/common/scripts/wf-statusline.sh +0 -67
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* wf-remove.mjs
|
|
3
|
+
* wf-remove.mjs �?Safely remove Harness framework files.
|
|
4
4
|
*
|
|
5
5
|
* Classifies all Harness-owned files into:
|
|
6
|
-
* SAFE
|
|
7
|
-
* MODIFIED
|
|
8
|
-
* USER
|
|
6
|
+
* SAFE �?framework files matching stored checksums �?auto-remove
|
|
7
|
+
* MODIFIED �?framework files edited by user �?MUST confirm
|
|
8
|
+
* USER �?user data files �?NEVER remove
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
11
|
* node Harness/scripts/wf-remove.mjs # dry-run: show plan
|
|
@@ -23,12 +23,12 @@ import { resolve, dirname, join, sep } from 'path';
|
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { createInterface } from 'readline';
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const SCRIPT_FILE = fileURLToPath(import.meta.url);
|
|
27
|
+
const __dirname = dirname(SCRIPT_FILE);
|
|
28
|
+
const SCRIPT_ROOT = resolve(__dirname, '..', '..');
|
|
27
29
|
const ROOT = process.env.WF_ROOT
|
|
28
30
|
? resolve(process.env.WF_ROOT)
|
|
29
|
-
:
|
|
30
|
-
? process.cwd()
|
|
31
|
-
: resolve(__dirname, '..', '..'));
|
|
31
|
+
: SCRIPT_ROOT;
|
|
32
32
|
const VERSION_FILE = resolve(ROOT, 'Harness', '.harness-version');
|
|
33
33
|
|
|
34
34
|
// ── Classification ─────────────────────────────────────────────────
|
|
@@ -45,7 +45,7 @@ const HARNESS_PREFIXES = [
|
|
|
45
45
|
'tests/.gitkeep',
|
|
46
46
|
];
|
|
47
47
|
|
|
48
|
-
/** Files the user owns
|
|
48
|
+
/** Files the user owns �?NEVER remove. Consistent with wf-update-check PRESERVE_PATTERNS. */
|
|
49
49
|
const USER_DATA_PATTERNS = [
|
|
50
50
|
/^Harness\/PROGRESS\.md$/,
|
|
51
51
|
/^Harness\/tasks\//,
|
|
@@ -97,17 +97,71 @@ function isPurgeableHarnessData(file, { keepTasks }) {
|
|
|
97
97
|
if (keepTasks && /^Harness\/tasks\//.test(file) && !isFrameworkTaskFile(file)) return false;
|
|
98
98
|
return PURGEABLE_HARNESS_DATA_PATTERNS.some(p => p.test(file));
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
/** Framework files that should always exist (not part of removal). */
|
|
102
|
-
const KEEP_FRAMEWORK = new Set([
|
|
103
|
-
'Harness/.harness-version',
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
|
|
101
|
+
/** Framework files that should always exist (not part of removal). */
|
|
102
|
+
const KEEP_FRAMEWORK = new Set([
|
|
103
|
+
'Harness/.harness-version',
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
const BUILT_IN_AGENT_NAMES = [
|
|
107
|
+
'architect-manager',
|
|
108
|
+
'architect',
|
|
109
|
+
'context-master',
|
|
110
|
+
'debugger',
|
|
111
|
+
'docs-researcher',
|
|
112
|
+
'explore-manager',
|
|
113
|
+
'implement-manager',
|
|
114
|
+
'implementer',
|
|
115
|
+
'memory-master',
|
|
116
|
+
'planner',
|
|
117
|
+
'researcher',
|
|
118
|
+
'review-manager',
|
|
119
|
+
'reviewer',
|
|
120
|
+
'tdd-guide',
|
|
121
|
+
'test-writer',
|
|
122
|
+
'verifier',
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const BUILT_IN_SKILL_NAMES = [
|
|
126
|
+
'browser-e2e',
|
|
127
|
+
'github-pr-review',
|
|
128
|
+
'python-backend',
|
|
129
|
+
'subagent-orchestrator',
|
|
130
|
+
'tdd',
|
|
131
|
+
'ts-react-frontend',
|
|
132
|
+
'ui-ux-review',
|
|
133
|
+
'wf',
|
|
134
|
+
'wf-auto',
|
|
135
|
+
'wf-auto-spark',
|
|
136
|
+
'wf-browser',
|
|
137
|
+
'wf-learn',
|
|
138
|
+
'wf-max',
|
|
139
|
+
'wf-readme',
|
|
140
|
+
'wf-remove',
|
|
141
|
+
'wf-review',
|
|
142
|
+
'wf-update',
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const KNOWN_FRAMEWORK_FILES = new Set([
|
|
146
|
+
...BUILT_IN_AGENT_NAMES.map(name => `.claude/agents/${name}.md`),
|
|
147
|
+
...BUILT_IN_SKILL_NAMES.flatMap(name => [
|
|
148
|
+
`.claude/skills/${name}/SKILL.md`,
|
|
149
|
+
`.agents/skills/${name}/SKILL.md`,
|
|
150
|
+
]),
|
|
151
|
+
'.claude/commands/wf-help.md',
|
|
152
|
+
'.claude/rules/ecc/common.md',
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
function isKnownFrameworkFile(file) {
|
|
156
|
+
return KNOWN_FRAMEWORK_FILES.has(file);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Directories to clean up if empty after file removal. */
|
|
160
|
+
const CLEANUP_DIRS = [
|
|
108
161
|
'.claude/agents',
|
|
109
162
|
'.claude/skills/wf-auto',
|
|
110
163
|
'.claude/skills/wf-browser',
|
|
164
|
+
'.claude/skills/tdd',
|
|
111
165
|
'.claude/skills/wf',
|
|
112
166
|
'.claude/skills/wf-learn',
|
|
113
167
|
'.claude/skills/wf-max',
|
|
@@ -118,6 +172,7 @@ const CLEANUP_DIRS = [
|
|
|
118
172
|
'.claude/skills/subagent-orchestrator',
|
|
119
173
|
'.agents/skills/wf-auto',
|
|
120
174
|
'.agents/skills/wf-browser',
|
|
175
|
+
'.agents/skills/tdd',
|
|
121
176
|
'.agents/skills/wf',
|
|
122
177
|
'.agents/skills/wf-learn',
|
|
123
178
|
'.agents/skills/wf-max',
|
|
@@ -249,7 +304,7 @@ function removeEmptyDirs(startDir) {
|
|
|
249
304
|
|
|
250
305
|
async function askUser(question) {
|
|
251
306
|
if (!process.stdin.isTTY) {
|
|
252
|
-
console.log(' (non-interactive
|
|
307
|
+
console.log(' (non-interactive �?defaulting to KEEP)');
|
|
253
308
|
return 'k';
|
|
254
309
|
}
|
|
255
310
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -298,10 +353,13 @@ async function main() {
|
|
|
298
353
|
addFilesUnder(allFiles, dir, scanIssues);
|
|
299
354
|
}
|
|
300
355
|
|
|
301
|
-
// Add framework files that might not be in checksums (newer additions)
|
|
356
|
+
// Add framework files that might not be in checksums (newer additions)
|
|
302
357
|
const extraPatterns = [
|
|
358
|
+
...KNOWN_FRAMEWORK_FILES,
|
|
303
359
|
'.claude/skills/wf/SKILL.md',
|
|
304
360
|
'.agents/skills/wf/SKILL.md',
|
|
361
|
+
'.claude/skills/tdd/SKILL.md',
|
|
362
|
+
'.agents/skills/tdd/SKILL.md',
|
|
305
363
|
'.claude/skills/wf-auto/SKILL.md',
|
|
306
364
|
'.agents/skills/wf-auto/SKILL.md',
|
|
307
365
|
'Harness/WF-AUTO.md',
|
|
@@ -319,9 +377,9 @@ async function main() {
|
|
|
319
377
|
}
|
|
320
378
|
|
|
321
379
|
// 2. Classify every file
|
|
322
|
-
const safe = []; // SAFE
|
|
323
|
-
const modified = []; // MODIFIED
|
|
324
|
-
const user = []; // USER
|
|
380
|
+
const safe = []; // SAFE �?matches checksum, auto-remove
|
|
381
|
+
const modified = []; // MODIFIED �?user edited, must confirm
|
|
382
|
+
const user = []; // USER �?never remove
|
|
325
383
|
const skipped = []; // File not on disk, traversal rejected, or framework-keep
|
|
326
384
|
const purge = []; // Explicitly requested Harness user-data removal
|
|
327
385
|
skipped.push(...scanIssues);
|
|
@@ -343,13 +401,13 @@ async function main() {
|
|
|
343
401
|
});
|
|
344
402
|
continue;
|
|
345
403
|
}
|
|
346
|
-
user.push({ file, reason: 'user data
|
|
404
|
+
user.push({ file, reason: 'user data �?NEVER removed' });
|
|
347
405
|
continue;
|
|
348
406
|
}
|
|
349
407
|
|
|
350
408
|
// Only allow deletion of files under harness-owned prefixes
|
|
351
409
|
if (!isHarnessOwned(canonical)) {
|
|
352
|
-
user.push({ file, reason: 'outside harness prefix
|
|
410
|
+
user.push({ file, reason: 'outside harness prefix �?NEVER removed' });
|
|
353
411
|
continue;
|
|
354
412
|
}
|
|
355
413
|
|
|
@@ -362,31 +420,40 @@ async function main() {
|
|
|
362
420
|
skipped.push({ file, reason: 'not on disk' });
|
|
363
421
|
continue;
|
|
364
422
|
}
|
|
365
|
-
|
|
366
|
-
const currentHash = sha256File(diskPath);
|
|
367
|
-
const storedHash = storedChecksums[file];
|
|
368
|
-
|
|
369
|
-
if (!storedHash) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
423
|
+
|
|
424
|
+
const currentHash = sha256File(diskPath);
|
|
425
|
+
const storedHash = storedChecksums[file];
|
|
426
|
+
|
|
427
|
+
if (!storedHash) {
|
|
428
|
+
if (isKnownFrameworkFile(canonical)) {
|
|
429
|
+
safe.push({
|
|
430
|
+
file,
|
|
431
|
+
currentHash,
|
|
432
|
+
storedHash: currentHash,
|
|
433
|
+
reason: 'known framework file missing from legacy checksums',
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
modified.push({ file, currentHash, storedHash: 'none', reason: 'not in checksums' });
|
|
437
|
+
}
|
|
438
|
+
} else if (currentHash === storedHash) {
|
|
439
|
+
safe.push({ file, currentHash, storedHash });
|
|
440
|
+
} else {
|
|
374
441
|
modified.push({ file, currentHash, storedHash, reason: 'user modified' });
|
|
375
442
|
}
|
|
376
443
|
}
|
|
377
444
|
|
|
378
|
-
|
|
445
|
+
// Also check for .claude/settings.json (might have user permissions)
|
|
379
446
|
const settingsFile = resolve(ROOT, '.claude', 'settings.json');
|
|
380
447
|
if (existsSync(settingsFile)) {
|
|
381
448
|
const settingsHash = sha256File(settingsFile);
|
|
382
449
|
const storedSettingsHash = storedChecksums['.claude/settings.json'];
|
|
383
450
|
if (storedSettingsHash && settingsHash !== storedSettingsHash) {
|
|
384
|
-
// User modified settings
|
|
451
|
+
// User modified settings �?move to modified
|
|
385
452
|
// Remove from safe if it was there
|
|
386
453
|
const idx = safe.findIndex(s => s.file === '.claude/settings.json');
|
|
387
454
|
if (idx >= 0) {
|
|
388
455
|
safe.splice(idx, 1);
|
|
389
|
-
modified.push({ file: '.claude/settings.json', currentHash: settingsHash, storedHash: storedSettingsHash, reason: 'user modified settings (may contain
|
|
456
|
+
modified.push({ file: '.claude/settings.json', currentHash: settingsHash, storedHash: storedSettingsHash, reason: 'user modified settings (may contain permissions)' });
|
|
390
457
|
}
|
|
391
458
|
}
|
|
392
459
|
}
|
|
@@ -425,16 +492,16 @@ async function main() {
|
|
|
425
492
|
return;
|
|
426
493
|
}
|
|
427
494
|
|
|
428
|
-
console.log('\n🧹 WF-REMOVE
|
|
495
|
+
console.log('\n🧹 WF-REMOVE �?Harness framework removal plan\n');
|
|
429
496
|
|
|
430
497
|
if (safe.length > 0) {
|
|
431
|
-
console.log(
|
|
432
|
-
for (const s of safe) console.log(`
|
|
498
|
+
console.log(`�?SAFE (${safe.length} files �?auto-remove, unmodified framework):`);
|
|
499
|
+
for (const s of safe) console.log(` �?${s.file}`);
|
|
433
500
|
console.log('');
|
|
434
501
|
}
|
|
435
502
|
|
|
436
503
|
if (modified.length > 0) {
|
|
437
|
-
console.log(
|
|
504
|
+
console.log(`�?MODIFIED (${modified.length} files �?REQUIRE CONFIRMATION):`);
|
|
438
505
|
for (const m of modified) {
|
|
439
506
|
console.log(` ? ${m.file} [${m.reason}]`);
|
|
440
507
|
}
|
|
@@ -442,8 +509,8 @@ async function main() {
|
|
|
442
509
|
}
|
|
443
510
|
|
|
444
511
|
if (user.length > 0) {
|
|
445
|
-
console.log(`🔒 USER DATA (${user.length} files
|
|
446
|
-
for (const u of user) console.log(`
|
|
512
|
+
console.log(`🔒 USER DATA (${user.length} files �?NEVER removed):`);
|
|
513
|
+
for (const u of user) console.log(` �?${u.file} [${u.reason}]`);
|
|
447
514
|
console.log('');
|
|
448
515
|
}
|
|
449
516
|
|
|
@@ -460,17 +527,17 @@ async function main() {
|
|
|
460
527
|
return;
|
|
461
528
|
}
|
|
462
529
|
|
|
463
|
-
// 4. Apply
|
|
530
|
+
// 4. Apply �?remove SAFE files automatically (rehash before unlink)
|
|
464
531
|
let safeRemoved = 0;
|
|
465
532
|
let safeFailed = 0;
|
|
466
533
|
for (const s of safe) {
|
|
467
534
|
const diskPath = safePath(s.file);
|
|
468
|
-
if (!diskPath) { console.error(`
|
|
535
|
+
if (!diskPath) { console.error(` �?Traversal rejected: ${s.file}`); safeFailed++; continue; }
|
|
469
536
|
if (!existsSync(diskPath)) continue;
|
|
470
537
|
// Re-verify hash hasn't changed since classification
|
|
471
538
|
const currentHash = sha256File(diskPath);
|
|
472
539
|
if (currentHash !== s.storedHash) {
|
|
473
|
-
console.log(`
|
|
540
|
+
console.log(` �?Skipped (modified since classification): ${s.file}`);
|
|
474
541
|
safeFailed++;
|
|
475
542
|
continue;
|
|
476
543
|
}
|
|
@@ -478,13 +545,13 @@ async function main() {
|
|
|
478
545
|
unlinkSync(diskPath);
|
|
479
546
|
safeRemoved++;
|
|
480
547
|
} catch (e) {
|
|
481
|
-
console.error(`
|
|
548
|
+
console.error(` �?Failed to remove: ${s.file} �?${e.message}`);
|
|
482
549
|
safeFailed++;
|
|
483
550
|
}
|
|
484
551
|
}
|
|
485
|
-
console.log(
|
|
552
|
+
console.log(`�?Removed ${safeRemoved} safe files.`);
|
|
486
553
|
|
|
487
|
-
// 5. Handle MODIFIED files
|
|
554
|
+
// 5. Handle MODIFIED files �?prompt user
|
|
488
555
|
let modifiedRemoved = 0;
|
|
489
556
|
let modifiedKept = 0;
|
|
490
557
|
let modifiedFailed = 0;
|
|
@@ -504,7 +571,7 @@ async function main() {
|
|
|
504
571
|
}
|
|
505
572
|
if (yes) {
|
|
506
573
|
// Non-interactive mode: skip all modified
|
|
507
|
-
console.log(`
|
|
574
|
+
console.log(` �?Skipped (modified): ${m.file}`);
|
|
508
575
|
modifiedKept++;
|
|
509
576
|
continue;
|
|
510
577
|
}
|
|
@@ -516,17 +583,17 @@ async function main() {
|
|
|
516
583
|
const answer = await askUser(' Choose [d/k]: ');
|
|
517
584
|
if (answer === 'd' || answer === 'delete') {
|
|
518
585
|
const diskPath = safePath(m.file);
|
|
519
|
-
if (!diskPath) { console.error(`
|
|
586
|
+
if (!diskPath) { console.error(` �?Traversal rejected: ${m.file}`); modifiedFailed++; continue; }
|
|
520
587
|
try {
|
|
521
588
|
unlinkSync(diskPath);
|
|
522
|
-
console.log(`
|
|
589
|
+
console.log(` �?Deleted: ${m.file}`);
|
|
523
590
|
modifiedRemoved++;
|
|
524
591
|
} catch (e) {
|
|
525
|
-
console.error(`
|
|
592
|
+
console.error(` �?Failed: ${m.file} �?${e.message}`);
|
|
526
593
|
modifiedFailed++;
|
|
527
594
|
}
|
|
528
595
|
} else {
|
|
529
|
-
console.log(`
|
|
596
|
+
console.log(` �?Kept: ${m.file}`);
|
|
530
597
|
modifiedKept++;
|
|
531
598
|
}
|
|
532
599
|
}
|
|
@@ -587,7 +654,7 @@ async function main() {
|
|
|
587
654
|
}
|
|
588
655
|
} else if (!existsSync(harPath) || (existsSync(harPath) && readdirSync(harPath).filter(f => f !== '.harness-version').length === 0)) {
|
|
589
656
|
unlinkSync(VERSION_FILE);
|
|
590
|
-
console.log('
|
|
657
|
+
console.log('�?Removed .harness-version (Harness directory empty).');
|
|
591
658
|
}
|
|
592
659
|
}
|
|
593
660
|
|
|
@@ -606,7 +673,7 @@ async function main() {
|
|
|
606
673
|
if (pruned > 0) {
|
|
607
674
|
versionData.checksums = checksums;
|
|
608
675
|
writeFileSync(VERSION_FILE, JSON.stringify(versionData, null, 2) + '\n', 'utf-8');
|
|
609
|
-
console.log(
|
|
676
|
+
console.log(`�?Pruned ${pruned} stale checksum(s) from .harness-version.`);
|
|
610
677
|
}
|
|
611
678
|
}
|
|
612
679
|
|
|
@@ -620,7 +687,7 @@ async function main() {
|
|
|
620
687
|
// Strip the harness section: everything from "## 1. Harness Binding" to the next "## "
|
|
621
688
|
const cleaned = content.replace(/## 1\. Harness Binding[\s\S]*?(?=## 2\.)/, '');
|
|
622
689
|
writeFileSync(claudePath, cleaned, 'utf-8');
|
|
623
|
-
console.log('
|
|
690
|
+
console.log('�?Stripped Harness binding section from CLAUDE.md.');
|
|
624
691
|
}
|
|
625
692
|
}
|
|
626
693
|
}
|
|
@@ -17,7 +17,7 @@ Browser evidence in this project follows the contract:
|
|
|
17
17
|
2. **CLI mode is preferred for deterministic steps** — use `browser-use open/state/click/screenshot` for predictable flows
|
|
18
18
|
3. **Agent mode is for dynamic exploration** — use Browser Use Agent API when the page structure is unknown or changing
|
|
19
19
|
4. **Evidence goes to the task directory** — `Harness/tasks/<task-id>/evidence/*.png`
|
|
20
|
-
5. **Stable UI selector contract** — Stable accessible labels/roles and stable test
|
|
20
|
+
5. **Stable UI selector contract** — Stable accessible labels/roles and stable test selectors such as `data-testid` are required for critical UI controls and states: inputs, buttons, filters, rows, empty/error/loading states.
|
|
21
21
|
|
|
22
22
|
## Chrome DevTools / CDP / MCP Checklist
|
|
23
23
|
|
|
@@ -30,7 +30,14 @@ Browser evidence in this project follows the contract:
|
|
|
30
30
|
- [ ] Capture screenshot, trace, video, or result artifact paths
|
|
31
31
|
- [ ] Clean up any dev server or browser processes
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
### Harness Bridge
|
|
34
|
+
|
|
35
|
+
For frontend-backend flows, use `Harness/HARNESS_BRIDGE.md` to validate UI
|
|
36
|
+
selectors, API payloads, seeded data, runtime state probes, and CDP/network
|
|
37
|
+
traces. Browser validation must produce an AC-by-AC result matrix when the task
|
|
38
|
+
has acceptance criteria.
|
|
39
|
+
|
|
40
|
+
## Quick Install
|
|
34
41
|
|
|
35
42
|
```bash
|
|
36
43
|
# One-time setup
|
|
@@ -58,7 +65,8 @@ If `browser-use` is not installed, fall back to:
|
|
|
58
65
|
|
|
59
66
|
## Integration Points
|
|
60
67
|
|
|
61
|
-
- **WF mode**: when browser-visible changes are made, follow the evidence contract in `Harness/WF.md#Browser And API Evidence`
|
|
68
|
+
- **WF mode**: when browser-visible changes are made, follow the evidence contract in `Harness/WF.md#Browser And API Evidence`
|
|
69
|
+
- **Harness Bridge**: use `Harness/HARNESS_BRIDGE.md` for UI contract, API contract, seeded test data, runtime state probes, and network trace collection.
|
|
62
70
|
- **wf-browser**: Claude Code uses `.claude/skills/wf-browser/SKILL.md`; Codex uses `.agents/skills/wf-browser/SKILL.md`. Both load this workflow.
|
|
63
71
|
- **MEMORY.md**: registered as optional workflow skill
|
|
64
72
|
- **README.md**: routing table row "Browser E2E testing or automation" → browser-e2e
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- Real-browser smoke or screenshot evidence for user-visible changes.
|
|
8
8
|
- Console/runtime error check result for the changed screen or flow.
|
|
9
9
|
- Notes on responsive behavior and important interaction states.
|
|
10
|
-
- Stable accessible labels/roles and stable test
|
|
10
|
+
- Stable accessible labels/roles and stable test selectors such as `data-testid` are required for critical UI controls and states: inputs, buttons, filters, rows, empty/error/loading states.
|
|
11
11
|
|
|
12
12
|
Typecheck, build, and unit tests are necessary signals but are not enough for user-visible React changes. Before claiming UI acceptance, load the app in a real browser by Playwright, Chrome DevTools/CDP, or a documented manual run and capture screenshot/trace/console evidence.
|
|
13
13
|
For TS/React UI work, capture the selector contract in the feature doc before implementation so tests and manual checks can target stable selectors instead of component internals or brittle DOM paths.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hooks": {
|
|
3
|
-
"SessionStart": [
|
|
4
|
-
{
|
|
5
|
-
"matcher": "",
|
|
6
|
-
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
"type": "command",
|
|
9
|
-
"command": "node Harness/scripts/wf-mode-hook.mjs"
|
|
10
|
-
}
|
|
11
|
-
]
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"UserPromptSubmit": [
|
|
15
|
-
{
|
|
16
|
-
"matcher": "",
|
|
17
|
-
"hooks": [
|
|
18
|
-
{
|
|
19
|
-
"type": "command",
|
|
20
|
-
"command": "node Harness/scripts/wf-mode-hook.mjs"
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"PreToolUse": [
|
|
26
|
-
{
|
|
27
|
-
"matcher": "Edit|Write|MultiEdit|Bash",
|
|
28
|
-
"hooks": [
|
|
29
|
-
{
|
|
30
|
-
"type": "command",
|
|
31
|
-
"command": "node Harness/scripts/wf-mode-hook.mjs"
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
"PostToolUse": [
|
|
37
|
-
{
|
|
38
|
-
"matcher": "",
|
|
39
|
-
"hooks": [
|
|
40
|
-
{
|
|
41
|
-
"type": "command",
|
|
42
|
-
"command": "node Harness/scripts/wf-mode-hook.mjs"
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
"Stop": [
|
|
48
|
-
{
|
|
49
|
-
"matcher": "",
|
|
50
|
-
"hooks": [
|
|
51
|
-
{
|
|
52
|
-
"type": "command",
|
|
53
|
-
"command": "node Harness/scripts/wf-mode-hook.mjs"
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
}
|
|
59
|
-
}
|