create-harness-vibe-coding 0.8.5 → 0.8.7

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 (39) hide show
  1. package/README.md +20 -4
  2. package/package.json +1 -1
  3. package/src/generator.js +30 -11
  4. package/src/index.js +129 -5
  5. package/templates/common/.claude/agents/reflector.md +35 -0
  6. package/templates/common/.claude/agents/verifier.md +5 -3
  7. package/templates/common/.claude/commands/wf-help.md +1 -2
  8. package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +10 -4
  9. package/templates/common/.claude/skills/wf/SKILL.md +7 -3
  10. package/templates/common/.claude/skills/wf-auto/SKILL.md +59 -107
  11. package/templates/common/.claude/skills/wf-auto-spark/SKILL.md +19 -17
  12. package/templates/common/.claude/skills/wf-max/SKILL.md +40 -21
  13. package/templates/common/.claude/skills/wf-update/SKILL.md +9 -4
  14. package/templates/common/.codex/config.toml +5 -0
  15. package/templates/common/.harness-version +36 -34
  16. package/templates/common/AGENTS.md +26 -25
  17. package/templates/common/CLAUDE.md +10 -9
  18. package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +12 -4
  19. package/templates/common/Harness/README.md +12 -13
  20. package/templates/common/Harness/WF-AUTO-SPARK.md +18 -1
  21. package/templates/common/Harness/WF-AUTO.md +518 -492
  22. package/templates/common/Harness/WF-MAX.md +284 -232
  23. package/templates/common/Harness/WF.md +47 -29
  24. package/templates/common/Harness/agent-workflow.md +108 -76
  25. package/templates/common/Harness/dispatch.md +96 -95
  26. package/templates/common/Harness/extension.md +1 -1
  27. package/templates/common/Harness/subagents.md +78 -56
  28. package/templates/common/Harness/tasks/_template/ARTIFACTS.md +1 -1
  29. package/templates/common/Harness/tasks/_template/NOTES.md +1 -1
  30. package/templates/common/Harness/tasks/_template/PLAN.md +53 -60
  31. package/templates/common/Harness/tasks/_template/PROGRESS.md +26 -29
  32. package/templates/common/MEMORY.md +26 -29
  33. package/templates/common/SETUP.md +29 -22
  34. package/templates/common/scripts/scan-clean.mjs +80 -41
  35. package/templates/common/scripts/validate-harness.mjs +101 -31
  36. package/templates/common/scripts/wf-remove.mjs +279 -278
  37. package/templates/common/scripts/wf-update-check.mjs +395 -195
  38. package/templates/optional/skills/browser-e2e/.claude/skills/wf-browser/SKILL.md +1 -1
  39. package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +57 -21
@@ -20,37 +20,39 @@ import { resolve, dirname, sep, join } from 'path';
20
20
  import { fileURLToPath } from 'url';
21
21
  import { createInterface } from 'readline';
22
22
 
23
- const __dirname = dirname(fileURLToPath(import.meta.url));
24
- const ROOT = process.env.WF_ROOT ? resolve(process.env.WF_ROOT) : resolve(__dirname, '..', '..');
25
- const VERSION_FILE = resolve(ROOT, 'Harness', '.harness-version');
26
- const SOURCE_BASE = 'https://raw.githubusercontent.com/zingspark/create-harness-vibe-coding/main/templates/common/';
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ const ROOT = process.env.WF_ROOT ? resolve(process.env.WF_ROOT) : resolve(__dirname, '..', '..');
25
+ const VERSION_FILE = resolve(ROOT, 'Harness', '.harness-version');
26
+ const DEFAULT_SOURCE_BASE = 'https://raw.githubusercontent.com/zingspark/create-harness-vibe-coding/main/templates/common/';
27
27
 
28
28
  // ── Classification constants ────────────────────────────────────────
29
29
 
30
30
  /** Files we NEVER delete, even if dead. */
31
- const PRESERVE_PATTERNS = [
32
- /^Harness\/PROGRESS\.md$/,
33
- /^Harness\/tasks\//,
34
- /^Harness\/memory\//,
35
- /^Harness\/research\/PRD\.md$/,
36
- /^Harness\/research\/research-results\.md$/,
37
- /^Harness\/architecture\.md$/,
38
- /^README\.md$/,
39
- /^\.gitignore$/,
40
- /^package\.json$/,
41
- /^package-lock\.json$/,
42
- ];
31
+ const PRESERVE_PATTERNS = [
32
+ /^Harness\/PROGRESS\.md$/,
33
+ /^Harness\/tasks\//,
34
+ /^Harness\/memory\//,
35
+ /^Harness\/research\/PRD\.md$/,
36
+ /^Harness\/research\/research-results\.md$/,
37
+ /^Harness\/architecture\.md$/,
38
+ /^tests\/\.gitkeep$/,
39
+ /^README\.md$/,
40
+ /^\.gitignore$/,
41
+ /^package\.json$/,
42
+ /^package-lock\.json$/,
43
+ ];
43
44
 
44
45
  /** Directories scanned for orphan files (untracked by either checksum set). */
45
46
  const FRAMEWORK_DIRS = [
46
47
  '.claude/agents',
48
+ '.claude/commands',
47
49
  '.claude/skills',
48
50
  '.claude/rules',
49
51
  '.agents/skills',
50
- '.codex',
51
- 'Harness/scripts',
52
- 'Harness/workflows',
53
- ];
52
+ '.codex',
53
+ 'Harness/scripts',
54
+ 'Harness/workflows',
55
+ ];
54
56
 
55
57
  // ── Helpers ──────────────────────────────────────────────────────────
56
58
 
@@ -67,25 +69,60 @@ function safePath(file) {
67
69
  }
68
70
 
69
71
  /** Canonical normalization for classification matching. */
70
- function canonicalPath(file) {
71
- return file.replace(/\\/g, '/').replace(/\/+/g, '/');
72
- }
72
+ function canonicalPath(file) {
73
+ return file.replace(/\\/g, '/').replace(/\/+/g, '/');
74
+ }
75
+
76
+ function readFlagValue(args, flagName) {
77
+ const idx = args.indexOf(flagName);
78
+ if (idx === -1) return null;
79
+ const value = args[idx + 1];
80
+ if (!value || value.startsWith('--')) return null;
81
+ return value;
82
+ }
83
+
84
+ function normalizeSourceBase(sourceBase) {
85
+ return sourceBase.endsWith('/') ? sourceBase : sourceBase + '/';
86
+ }
73
87
 
74
88
  /** Check whether a canonical path matches any PRESERVE pattern. */
75
- function isPreserved(canonical) {
76
- for (const p of PRESERVE_PATTERNS) {
77
- if (p.test(canonical)) return true;
78
- }
79
- return false;
80
- }
89
+ function isPreserved(canonical) {
90
+ for (const p of PRESERVE_PATTERNS) {
91
+ if (p.test(canonical)) return true;
92
+ }
93
+ return false;
94
+ }
95
+
96
+ function selectedOptionIds(localVersion) {
97
+ return new Set(
98
+ Array.isArray(localVersion?.options)
99
+ ? localVersion.options.map(String).filter(Boolean)
100
+ : [],
101
+ );
102
+ }
103
+
104
+ function optionalSkillFromSource(source) {
105
+ const normalized = canonicalPath(source || '');
106
+ const match = normalized.match(/^skills\/([^/]+)\//);
107
+ return match ? match[1] : null;
108
+ }
109
+
110
+ function isInstalledOptionalFile(file, localVersion) {
111
+ const skillId = optionalSkillFromSource(localVersion?.sources?.[file]);
112
+ return Boolean(skillId && selectedOptionIds(localVersion).has(skillId));
113
+ }
81
114
 
82
115
  /** Detect template placeholders in remote content. */
83
116
  function isTemplate(raw) {
84
117
  return /\{\{[a-zA-Z]+\}\}/.test(raw);
85
118
  }
86
119
 
87
- async function fetchRemote(url, timeoutMs = 30000) {
88
- const controller = new AbortController();
120
+ async function fetchRemote(url, timeoutMs = 30000) {
121
+ if (url.startsWith('file://')) {
122
+ return readFileSync(fileURLToPath(url), 'utf-8');
123
+ }
124
+
125
+ const controller = new AbortController();
89
126
  const timer = setTimeout(() => controller.abort(), timeoutMs);
90
127
  try {
91
128
  const res = await fetch(url, { signal: controller.signal });
@@ -217,9 +254,10 @@ function removeEmptyParents(fileAbsPath) {
217
254
 
218
255
  async function main() {
219
256
  const args = process.argv.slice(2);
220
- const clean = args.includes('--clean');
221
- const yes = args.includes('--yes');
222
- const jsonOut = args.includes('--json');
257
+ const clean = args.includes('--clean');
258
+ const yes = args.includes('--yes');
259
+ const jsonOut = args.includes('--json');
260
+ const sourceBase = normalizeSourceBase(readFlagValue(args, '--source-base') || process.env.WF_SOURCE_BASE || DEFAULT_SOURCE_BASE);
223
261
 
224
262
  // 1. Read local state
225
263
  if (!existsSync(VERSION_FILE)) {
@@ -248,7 +286,7 @@ async function main() {
248
286
  // 2. Fetch remote version file
249
287
  let remoteVersion;
250
288
  try {
251
- const raw = await fetchRemote(SOURCE_BASE + '.harness-version');
289
+ const raw = await fetchRemote(sourceBase + '.harness-version');
252
290
  if (isTemplate(raw)) {
253
291
  if (jsonOut) {
254
292
  console.log(JSON.stringify({ status: 'error', message: 'Remote .harness-version is a template (contains {{placeholders}}). Cannot determine dead files.' }));
@@ -275,12 +313,13 @@ async function main() {
275
313
  // 3. Classify dead files
276
314
  // DEAD = in local checksums but NOT in remote checksums, AND NOT preserved
277
315
  const deadFiles = [];
278
- for (const file of Object.keys(localChecksums)) {
279
- const canonical = canonicalPath(file);
280
- if (remoteChecksums[file]) continue; // still in remote, not dead
281
- if (isPreserved(canonical)) continue; // user data, never dead
282
- deadFiles.push({ file, reason: 'not in remote template' });
283
- }
316
+ for (const file of Object.keys(localChecksums)) {
317
+ const canonical = canonicalPath(file);
318
+ if (remoteChecksums[file]) continue; // still in remote, not dead
319
+ if (isPreserved(canonical)) continue; // user data, never dead
320
+ if (isInstalledOptionalFile(file, localVersion)) continue; // installed optional workflow/skill
321
+ deadFiles.push({ file, reason: 'not in remote template' });
322
+ }
284
323
 
285
324
  // 4. Find orphan files
286
325
  const orphanFiles = findOrphanFiles(localChecksums, remoteChecksums);
@@ -22,10 +22,11 @@ const commonAgents = [
22
22
  'architect',
23
23
  'test-writer',
24
24
  'implementer',
25
- 'debugger',
26
- 'reviewer',
27
- 'verifier',
28
- 'memory-master',
25
+ 'debugger',
26
+ 'reviewer',
27
+ 'verifier',
28
+ 'reflector',
29
+ 'memory-master',
29
30
  'context-master',
30
31
  'explore-manager',
31
32
  'architect-manager',
@@ -214,18 +215,33 @@ function registeredSkillFiles(...texts) {
214
215
  return [...files].sort();
215
216
  }
216
217
 
217
- function registeredWorkflowFiles(...texts) {
218
- const files = new Set();
219
- const pattern = /(?:Harness\/)?workflows\/([A-Za-z0-9._-]+\.md)/g;
218
+ function registeredWorkflowFiles(...texts) {
219
+ const files = new Set();
220
+ const pattern = /(?:Harness\/)?workflows\/([A-Za-z0-9._-]+\.md)/g;
220
221
 
221
222
  for (const text of texts) {
222
223
  for (const match of text.matchAll(pattern)) {
223
224
  files.add(`Harness/workflows/${match[1]}`);
224
225
  }
225
226
  }
226
-
227
- return [...files].sort();
228
- }
227
+
228
+ return [...files].sort();
229
+ }
230
+
231
+ function listedWorkflowCommands(...texts) {
232
+ const commands = new Set();
233
+ const pattern = /`\/(wf(?:-[a-z0-9]+)?)(?:\s+[^`]*)?`/g;
234
+
235
+ for (const text of texts) {
236
+ for (const match of text.matchAll(pattern)) {
237
+ const command = `/${match[1]}`;
238
+ if (command === '/wf-help') continue;
239
+ commands.add(command);
240
+ }
241
+ }
242
+
243
+ return [...commands].sort();
244
+ }
229
245
 
230
246
  for (const rel of required) {
231
247
  if (!fs.existsSync(path.join(root, rel))) {
@@ -380,13 +396,25 @@ for (const workflow of listMarkdownFiles('Harness/workflows')) {
380
396
  }
381
397
  }
382
398
 
383
- for (const skillFile of registeredSkillFiles(docsReadme, memory)) {
384
- if (!fs.existsSync(path.join(root, skillFile))) {
385
- errors.push(`registered skill file is missing: ${skillFile}`);
386
- }
387
- }
388
-
389
- for (const workflowFile of registeredWorkflowFiles(docsReadme, memory)) {
399
+ for (const skillFile of registeredSkillFiles(docsReadme, memory)) {
400
+ if (!fs.existsSync(path.join(root, skillFile))) {
401
+ errors.push(`registered skill file is missing: ${skillFile}`);
402
+ }
403
+ }
404
+
405
+ for (const command of listedWorkflowCommands(docsReadme, read('.claude/commands/wf-help.md'))) {
406
+ const skill = command.slice(1);
407
+ const claudeSkill = `.claude/skills/${skill}/SKILL.md`;
408
+ const codexSkill = `.agents/skills/${skill}/SKILL.md`;
409
+ if (!fs.existsSync(path.join(root, claudeSkill))) {
410
+ errors.push(`command docs list missing Claude skill: ${command} -> ${claudeSkill}`);
411
+ }
412
+ if (!fs.existsSync(path.join(root, codexSkill))) {
413
+ errors.push(`command docs list missing Codex skill: ${command} -> ${codexSkill}`);
414
+ }
415
+ }
416
+
417
+ for (const workflowFile of registeredWorkflowFiles(docsReadme, memory)) {
390
418
  if (!fs.existsSync(path.join(root, workflowFile))) {
391
419
  // Optional workflows (browser-e2e, ts-react-frontend, etc.) may not be installed
392
420
  continue;
@@ -478,13 +506,23 @@ for (const agent of commonAgents) {
478
506
 
479
507
  requireText('Harness/extension.md', 'Skills should extend the harness');
480
508
  requireText('Harness/agent-workflow.md', 'Harness/tasks/<task-id>/PROGRESS.md');
509
+ requireText('CLAUDE.md', 'Keep task records compact', 'compact task record startup rule');
510
+ requireText('Harness/README.md', 'Task records are compact by default', 'compact task record router rule');
511
+ requireText('Harness/agent-workflow.md', 'AC record size', 'compact AC record size rule');
512
+ requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'Default to 1-3 concise ACs', 'compact AC default');
513
+ requireText('Harness/WF.md', 'Do not paste command logs or subagent transcripts', 'compact heartbeat rule');
514
+ requireText('Harness/tasks/_template/PLAN.md', 'Compact task record', 'compact task PLAN template');
515
+ requireText('Harness/tasks/_template/PLAN.md', 'Default: keep 1-3 concise ACs', 'compact task PLAN AC guidance');
516
+ requireText('Harness/tasks/_template/PROGRESS.md', 'Compact heartbeat', 'compact task PROGRESS template');
481
517
  requireText('Harness/agent-workflow.md', 'TDD-GUIDE.md', 'agent workflow loads TDD guide');
482
518
  requireText('Harness/agent-workflow.md', 'real user-path test', 'agent workflow real user path requirement');
483
519
  requireText('Harness/research/README.md', 'research-results.md');
484
520
  requireText('Harness/WF.md', 'Ralph-style harness loop', 'WF loop description');
485
521
  requireText('Harness/WF.md', 'Heartbeat Protocol', 'heartbeat protocol');
486
- requireText('Harness/WF.md', 'WF mode requires multi-subagent orchestration by default', 'WF multi-subagent default');
487
- requireText('Harness/WF.md', 'Explicit `/wf`, `$wf`, `wf mode`, `workflow mode`, or `wk mode` MUST use at least 3 distinct role passes', 'explicit WF/WK role-pass minimum');
522
+ requireText('Harness/WF.md', 'WF mode requires the complete role chain by default', 'WF complete role chain default');
523
+ requireText('Harness/WF.md', 'MUST cover every mandatory role class before closeout', 'explicit WF/WK complete role chain');
524
+ requireText('Harness/WF.md', 'Independent Validation', 'WF independent validation role');
525
+ requireText('Harness/WF.md', 'reflector', 'WF reflector gate');
488
526
  requireText('Harness/WF.md', '.claude/agents/', 'WF built-in agent roster path');
489
527
  requireText('Harness/WF.md', 'Collaboration decision tree', 'WF decision tree');
490
528
  requireText('Harness/WF.md', 'Harness/tasks/', 'WF task directory reference');
@@ -502,23 +540,43 @@ requireText('Harness/subagents.md', '## Source Attribution', 'subagent source at
502
540
  requireText('Harness/subagents.md', 'npx skills find', 'find-skills discovery attribution');
503
541
  requireText('Harness/subagents.md', 'superpowers:dispatching-parallel-agents', 'parallel-agent source attribution');
504
542
  requireText('Harness/subagents.md', 'superpowers:subagent-driven-development', 'subagent-driven source attribution');
505
- requireText('Harness/subagents.md', '## Built-in Agent Roster', 'built-in agent roster');
506
- requireText('Harness/subagents.md', '## WF Default Fan-Out', 'WF default fan-out');
507
- requireText('Harness/subagents.md', 'concrete conditions', 'subagent decision tree');
508
- requireText('Harness/subagents.md', 'parallel planner/researcher/docs-researcher/architect subagents', 'WF roster orchestration shape');
509
- requireText('Harness/subagents.md', '## Efficiency Ladder', 'subagent efficiency ladder');
510
- requireText('Harness/subagents.md', '## Review Gates', 'subagent review gates');
543
+ requireText('Harness/subagents.md', '## Built-in Agent Roster', 'built-in agent roster');
544
+ requireText('Harness/subagents.md', '## WF Default Fan-Out', 'WF default fan-out');
545
+ requireText('Harness/subagents.md', 'concrete conditions', 'subagent decision tree');
546
+ requireText('Harness/subagents.md', 'parallel planner/researcher/docs-researcher/architect subagents', 'WF roster orchestration shape');
547
+ requireText('Harness/subagents.md', '`reflector`', 'reflector agent roster');
548
+ requireText('Harness/subagents.md', 'reflector PASS', 'reflector review gate');
549
+ requireText('Harness/subagents.md', '## Efficiency Ladder', 'subagent efficiency ladder');
550
+ requireText('Harness/subagents.md', '## Review Gates', 'subagent review gates');
511
551
  requireText('Harness/architecture.md', '## 2. Interface Decoupling', 'architecture interface decoupling');
512
552
  requireText('Harness/architecture.md', '## 3. State Design', 'architecture state design');
513
553
  requireText('Harness/architecture.md', 'Avoid speculative abstraction', 'anti-overengineering architecture rule');
514
554
  requireText('CLAUDE.md', 'Use explicit interfaces or state models only when they protect a real boundary', 'CLAUDE interface/state simplicity rule');
515
555
  requireText('CLAUDE.md', '/wf-update', 'wf update startup instruction');
516
556
  requireText('Harness/README.md', 'Need harness update', 'update routing row');
517
- requireText('Harness/WF-MAX.md', 'three-layer architecture', 'WF-MAX three-layer architecture');
518
- requireText('Harness/WF-MAX.md', 'agent role', 'WF-MAX agent role separation');
519
- requireText('Harness/WF-MAX.md', 'write-set coloring', 'WF-MAX coloring algorithm');
520
- requireText('Harness/WF-MAX.md', 'wave dispatch', 'WF-MAX wave dispatch');
521
- requireText('Harness/README.md', '/wf-max', 'wf max router alias');
557
+ requireText('Harness/WF-MAX.md', 'three-layer architecture', 'WF-MAX three-layer architecture');
558
+ requireText('Harness/WF-MAX.md', 'agent role', 'WF-MAX agent role separation');
559
+ requireText('Harness/WF-MAX.md', 'write-set coloring', 'WF-MAX coloring algorithm');
560
+ requireText('Harness/WF-MAX.md', 'wave dispatch', 'WF-MAX wave dispatch');
561
+ requireText('Harness/WF-MAX.md', 'strict superset', 'WF-MAX strict superset');
562
+ requireText('Harness/WF-MAX.md', 'cross-CLI overflow', 'WF-MAX cross-CLI overflow');
563
+ requireText('Harness/WF-MAX.md', 'claude -p', 'WF-MAX Claude CLI overflow');
564
+ requireText('Harness/WF-MAX.md', 'codex exec', 'WF-MAX Codex CLI overflow');
565
+ requireText('Harness/WF-MAX.md', 'agents.max_threads', 'WF-MAX Codex max_threads config');
566
+ requireText('Harness/WF-MAX.md', 'agents.max_depth', 'WF-MAX Codex max_depth config');
567
+ requireText('Harness/WF-MAX.md', 'max_threads = 12', 'WF-MAX Codex scaffold max_threads default');
568
+ requireText('Harness/WF-MAX.md', 'max_depth = 1', 'WF-MAX Codex scaffold max_depth default');
569
+ requireText('Harness/WF-MAX.md', 'ask the user before raising `agents.max_threads`', 'WF-MAX asks before raising Codex thread cap');
570
+ requireText('Harness/WF-MAX.md', 'Close completed agents', 'WF-MAX close completed agents before overflow');
571
+ requireText('Harness/WF-MAX.md', 'Codex++', 'WF-MAX forbids relying on Codex++ as stable capacity');
572
+ requireText('.claude/skills/wf-max/SKILL.md', 'agents.max_threads', 'wf-max skill Codex thread config');
573
+ requireText('.claude/skills/wf-max/SKILL.md', 'max_threads = 12', 'wf-max skill Codex scaffold max_threads default');
574
+ requireText('.claude/skills/wf-max/SKILL.md', 'ask the user before raising', 'wf-max skill asks before raising Codex thread cap');
575
+ requireText('.claude/skills/wf-max/SKILL.md', 'Codex++', 'wf-max skill forbids Codex++ capacity assumption');
576
+ requireText('.codex/config.toml', '[agents]', 'Codex agents config table');
577
+ requireText('.codex/config.toml', 'max_threads = 12', 'Codex scaffold max_threads default');
578
+ requireText('.codex/config.toml', 'max_depth = 1', 'Codex scaffold max_depth default');
579
+ requireText('Harness/README.md', '/wf-max', 'wf max router alias');
522
580
  requireText('Harness/README.md', 'WF-MAX.md', 'WF-MAX router reference');
523
581
  requireText('Harness/README.md', 'ACCEPTANCE_PROTOCOL.md', 'acceptance protocol router reference');
524
582
  requireText('Harness/README.md', 'TDD-GUIDE.md', 'TDD guide router reference');
@@ -529,6 +587,12 @@ requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'Syntax-only checks', 'syntax-only
529
587
  requireText('Harness/AGENT_ISOLATION.md', 'implementer', 'agent isolation implementer rule');
530
588
  requireText('Harness/HARNESS_BRIDGE.md', 'Network Trace Collector', 'harness bridge network trace collector');
531
589
  requireText('Harness/WF-AUTO.md', 'Intent Checkpoint', 'wf-auto intent checkpoint');
590
+ requireText('Harness/WF-AUTO.md', 'Inherited WF/WF-MAX Constraints', 'wf-auto inherited WF/WF-MAX constraints');
591
+ requireText('Harness/WF-AUTO.md', 'Mini PRD -> AC IDs -> test/validation plan -> implementer -> verifier', 'wf-auto per-cycle WF chain');
592
+ requireText('Harness/WF-AUTO.md', 'reflector PASS', 'wf-auto reflector gate');
593
+ requireText('Harness/WF-AUTO-SPARK.md', 'Inherited Execution Chain', 'wf-auto-spark inherited execution chain');
594
+ requireText('Harness/WF-AUTO-SPARK.md', 'External spark search replaces discovery only', 'wf-auto-spark discovery-only inheritance');
595
+ requireText('Harness/WF-AUTO-SPARK.md', 'reflector PASS', 'wf-auto-spark reflector gate');
532
596
  requireText('Harness/MEMORY_PROTOCOL.md', 'Scenario Memory Hints', 'memory protocol scenario hints');
533
597
  requireText('Harness/MEMORY_PROTOCOL.md', 'WF closeout', 'memory protocol WF closeout injection row');
534
598
  requireText('Harness/MEMORY_PROTOCOL.md', 'memory-master owns writes', 'memory protocol memory write ownership');
@@ -536,7 +600,7 @@ requireText('Harness/TDD-GUIDE.md', 'Browser/UI Acceptance TDD Gate', 'browser U
536
600
  requireText('Harness/TDD-GUIDE.md', 'syntax-only', 'syntax-only acceptance prohibition');
537
601
  requireText('Harness/TDD-GUIDE.md', 'Playwright/CDP', 'Playwright/CDP acceptance requirement');
538
602
  requireText('Harness/TDD-GUIDE.md', 'AC-by-AC result matrix', 'AC-by-AC TDD evidence matrix');
539
- requireText('Harness/tasks/_template/PLAN.md', 'User Action / API Request', 'task template user action/API test-plan column');
603
+ requireText('Harness/tasks/_template/PLAN.md', 'Expanded evidence required when triggered', 'task template expanded evidence trigger');
540
604
  requireText('Harness/templates/TEST_PLAN.template.md', 'syntax-only checks', 'test plan template syntax-only prohibition');
541
605
  requireText('.claude/skills/tdd/SKILL.md', 'Harness/ACCEPTANCE_PROTOCOL.md', 'tdd skill loads acceptance protocol');
542
606
  requireText('.claude/skills/tdd/SKILL.md', 'Harness/HARNESS_BRIDGE.md', 'tdd skill loads harness bridge');
@@ -549,6 +613,7 @@ requireText('.claude/agents/tdd-guide.md', 'real user actions', 'tdd-guide real
549
613
  requireText('.claude/agents/test-writer.md', 'Harness/ACCEPTANCE_PROTOCOL.md', 'test-writer loads acceptance protocol');
550
614
  requireText('.claude/agents/test-writer.md', 'real user-path test', 'test-writer real user path requirement');
551
615
  requireText('.claude/agents/test-writer.md', 'network URL, method, payload', 'test-writer network assertion requirement');
616
+ requireText('.claude/agents/reflector.md', 'PASS, RETURN_TO_DEBUG, or BLOCKED', 'reflector verdict contract');
552
617
  requireText('Harness/DEBUG_PROTOCOL.md', 'Layer Classification', 'debug layer classification');
553
618
  requireText('Harness/MEMORY_PROTOCOL.md', 'AC ID', 'memory AC traceability');
554
619
  requireText('Harness/subagents.md', 'Max parallelism', 'subagents max parallelism row');
@@ -568,12 +633,17 @@ requireText('Harness/README.md', 'no runtime hook state', 'README no-hook runtim
568
633
  requireText('Harness/WF-AUTO.md', 'WF-AUTO Hook Exception', 'wf-auto-only hook exception');
569
634
  requireText('Harness/WF-AUTO.md', 'only `/wf-auto` may use a runtime hook', 'wf-auto-only hook boundary');
570
635
  forbidText('CLAUDE.md', 'Enforced by hooks', 'WF-MAX hook enforcement claim');
636
+ forbidText('Harness/WF.md', 'MUST use at least 3 distinct role passes', 'old WF role-pass minimum');
637
+ forbidText('Harness/WF.md', 'at least three distinct role passes', 'old WF role-pass wording');
638
+ forbidText('Harness/dispatch.md', 'requires ≥3 distinct subagents', 'old dispatch WF role minimum');
571
639
  forbidText('CLAUDE.md', 'WF-MAX hooks', 'WF-MAX hook enforcement claim');
572
640
  forbidText('Harness/README.md', 'PreToolUse hook', 'WF-MAX PreToolUse hook claim');
573
641
  forbidText('Harness/README.md', 'SessionStart hook', 'WF-MAX SessionStart hook claim');
574
642
  forbidText('Harness/README.md', 'hook-managed', 'hook-managed runtime claim');
575
643
  forbidText('Harness/README.md', 'HOOK_PROTOCOL.md', 'removed hook protocol reference');
576
644
  forbidText('Harness/WF-AUTO.md', 'Hook-Assisted Long Loop', 'wf-auto hook loop section');
645
+ forbidText('Harness/dispatch.md', 'removes the cap entirely', 'unbounded runtime-cap claim');
646
+ forbidText('Harness/WF-MAX.md', 'no hard agent cap; recursion governed', 'unbounded agent-cap claim');
577
647
  forbidText('Harness/MEMORY_PROTOCOL.md', 'Hooks may', 'hook-triggered memory claim');
578
648
  forbidText('.claude/settings.json', 'wf-mode-hook.mjs', 'Claude WF hook command registration');
579
649
  forbidText('.codex/hooks.json', 'wf-mode-hook.mjs', 'Codex WF hook command registration');