create-harness-vibe-coding 0.8.13 → 0.8.17

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 (133) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/README-CN.md +110 -107
  3. package/README.md +93 -44
  4. package/package.json +2 -1
  5. package/src/generator.js +609 -489
  6. package/src/index.js +5 -5
  7. package/templates/common/.claude/agents/architect-manager.md +1 -0
  8. package/templates/common/.claude/agents/architect.md +2 -1
  9. package/templates/common/.claude/agents/codebase-explorer.md +1 -0
  10. package/templates/common/.claude/agents/context-master.md +2 -1
  11. package/templates/common/.claude/agents/debugger.md +1 -0
  12. package/templates/common/.claude/agents/docs-researcher.md +2 -1
  13. package/templates/common/.claude/agents/explore-manager.md +1 -0
  14. package/templates/common/.claude/agents/implement-manager.md +1 -0
  15. package/templates/common/.claude/agents/implementer.md +1 -0
  16. package/templates/common/.claude/agents/memory-master.md +2 -1
  17. package/templates/common/.claude/agents/planner.md +3 -2
  18. package/templates/common/.claude/agents/reflector.md +1 -0
  19. package/templates/common/.claude/agents/researcher.md +1 -0
  20. package/templates/common/.claude/agents/review-manager.md +1 -0
  21. package/templates/common/.claude/agents/reviewer.md +2 -1
  22. package/templates/common/.claude/agents/task-scribe.md +1 -0
  23. package/templates/common/.claude/agents/tdd-guide.md +5 -4
  24. package/templates/common/.claude/agents/test-writer.md +6 -5
  25. package/templates/common/.claude/agents/verifier.md +1 -0
  26. package/templates/common/.claude/commands/wf-auto-spark.md +16 -0
  27. package/templates/common/.claude/commands/wf-auto.md +16 -0
  28. package/templates/{optional/skills/browser-e2e/.opencode → common/.claude}/commands/wf-browser.md +3 -2
  29. package/templates/common/.claude/commands/wf-help.md +9 -5
  30. package/templates/common/.claude/commands/wf-learn.md +16 -0
  31. package/templates/common/.claude/commands/wf-max.md +20 -0
  32. package/templates/common/.claude/commands/wf-readme.md +16 -0
  33. package/templates/common/.claude/commands/wf-remove.md +16 -0
  34. package/templates/common/.claude/commands/wf-review.md +16 -0
  35. package/templates/common/.claude/commands/wf-update.md +68 -10
  36. package/templates/common/.claude/commands/wf.md +16 -0
  37. package/templates/common/.claude/rules/ecc/common.md +6 -5
  38. package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +12 -6
  39. package/templates/common/.claude/skills/tdd/SKILL.md +5 -5
  40. package/templates/common/.claude/skills/wf/SKILL.md +13 -5
  41. package/templates/common/.claude/skills/wf-agents-docs/SKILL.md +119 -0
  42. package/templates/common/.claude/skills/wf-auto/SKILL.md +27 -7
  43. package/templates/common/.claude/skills/wf-auto-spark/SKILL.md +12 -5
  44. package/templates/common/.claude/skills/wf-browser/SKILL.md +176 -0
  45. package/templates/common/.claude/skills/wf-help/SKILL.md +30 -0
  46. package/templates/common/.claude/skills/wf-learn/SKILL.md +6 -0
  47. package/templates/common/.claude/skills/wf-max/SKILL.md +43 -14
  48. package/templates/common/.claude/skills/wf-readme/SKILL.md +8 -2
  49. package/templates/common/.claude/skills/wf-remove/SKILL.md +6 -0
  50. package/templates/common/.claude/skills/wf-review/SKILL.md +10 -3
  51. package/templates/common/.claude/skills/wf-update/SKILL.md +41 -4
  52. package/templates/common/.codex/config.toml +3 -5
  53. package/templates/common/.harness-version +326 -131
  54. package/templates/common/.opencode/agents/architect-manager.md +8 -1
  55. package/templates/common/.opencode/agents/architect.md +2 -1
  56. package/templates/common/.opencode/agents/codebase-explorer.md +1 -0
  57. package/templates/common/.opencode/agents/context-master.md +2 -1
  58. package/templates/common/.opencode/agents/debugger.md +1 -0
  59. package/templates/common/.opencode/agents/docs-researcher.md +2 -1
  60. package/templates/common/.opencode/agents/explore-manager.md +11 -1
  61. package/templates/common/.opencode/agents/implement-manager.md +6 -3
  62. package/templates/common/.opencode/agents/implementer.md +1 -0
  63. package/templates/common/.opencode/agents/memory-master.md +2 -1
  64. package/templates/common/.opencode/agents/planner.md +3 -2
  65. package/templates/common/.opencode/agents/reflector.md +1 -0
  66. package/templates/common/.opencode/agents/researcher.md +1 -0
  67. package/templates/common/.opencode/agents/review-manager.md +6 -2
  68. package/templates/common/.opencode/agents/reviewer.md +2 -1
  69. package/templates/common/.opencode/agents/task-scribe.md +1 -0
  70. package/templates/common/.opencode/agents/tdd-guide.md +5 -4
  71. package/templates/common/.opencode/agents/test-writer.md +6 -5
  72. package/templates/common/.opencode/agents/verifier.md +1 -0
  73. package/templates/common/.opencode/commands/wf-auto-spark.md +3 -2
  74. package/templates/common/.opencode/commands/wf-auto.md +3 -2
  75. package/templates/common/.opencode/commands/wf-browser.md +16 -0
  76. package/templates/common/.opencode/commands/wf-help.md +9 -5
  77. package/templates/common/.opencode/commands/wf-learn.md +3 -2
  78. package/templates/common/.opencode/commands/wf-max.md +12 -6
  79. package/templates/common/.opencode/commands/wf-readme.md +3 -2
  80. package/templates/common/.opencode/commands/wf-remove.md +3 -2
  81. package/templates/common/.opencode/commands/wf-review.md +3 -2
  82. package/templates/common/.opencode/commands/wf-update.md +68 -10
  83. package/templates/common/.opencode/commands/wf.md +3 -2
  84. package/templates/common/CLAUDE.md +15 -13
  85. package/templates/common/Harness/MEMORY.md +24 -21
  86. package/templates/common/Harness/README.md +50 -47
  87. package/templates/common/Harness/ownership.manifest.json +878 -0
  88. package/templates/common/Harness/{architecture.md → project/architecture.md} +1 -1
  89. package/templates/common/Harness/research/README.md +3 -3
  90. package/templates/common/Harness/scripts/archive-tasks.mjs +12 -220
  91. package/templates/common/Harness/scripts/context-budget.mjs +95 -0
  92. package/templates/common/Harness/scripts/l2-cache-telemetry.mjs +703 -0
  93. package/templates/common/Harness/scripts/scan-clean.mjs +16 -12
  94. package/templates/common/Harness/scripts/task-state.mjs +889 -0
  95. package/templates/common/Harness/scripts/validate-harness.mjs +553 -191
  96. package/templates/common/Harness/scripts/wf-remove.mjs +61 -35
  97. package/templates/common/Harness/scripts/wf-update-check.mjs +358 -45
  98. package/templates/common/Harness/settings.json +43 -0
  99. package/templates/common/Harness/{ECC-GUIDE.md → specs/guides/ECC-GUIDE.md} +4 -4
  100. package/templates/common/Harness/{SETUP.md → specs/guides/SETUP.md} +34 -37
  101. package/templates/common/Harness/{extension.md → specs/guides/extension.md} +3 -3
  102. package/templates/common/Harness/{lifecycle.md → specs/guides/lifecycle.md} +2 -2
  103. package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +76 -0
  104. package/templates/common/Harness/{agent-workflow.md → specs/runtime/agent-workflow.md} +6 -6
  105. package/templates/common/Harness/{context-loading.md → specs/runtime/context-loading.md} +85 -21
  106. package/templates/common/Harness/{dispatch.md → specs/runtime/dispatch.md} +4 -3
  107. package/templates/common/Harness/{subagents.md → specs/runtime/subagents.md} +19 -10
  108. package/templates/common/Harness/{WF-AUTO-SPARK.md → specs/workflows/WF-AUTO-SPARK.md} +2 -2
  109. package/templates/common/Harness/{WF-AUTO.md → specs/workflows/WF-AUTO.md} +12 -7
  110. package/templates/common/Harness/{WF-KERNEL.md → specs/workflows/WF-KERNEL.md} +11 -1
  111. package/templates/common/Harness/{WF-MAX.md → specs/workflows/WF-MAX.md} +40 -7
  112. package/templates/common/Harness/specs/workflows/WF-STATE.md +141 -0
  113. package/templates/common/Harness/{WF.md → specs/workflows/WF.md} +12 -1
  114. package/templates/common/README.md +8 -6
  115. package/templates/common/memory/startup-hints.md +19 -17
  116. package/templates/common/opencode.json +1 -0
  117. package/templates/optional/catalog.json +2 -9
  118. package/templates/optional/skills/github-pr-review/.claude/skills/github-pr-review/SKILL.md +1 -1
  119. package/templates/optional/skills/python-backend/.claude/skills/python-backend/SKILL.md +1 -1
  120. package/templates/optional/skills/ts-react-frontend/.claude/skills/ts-react-frontend/SKILL.md +1 -1
  121. package/templates/optional/skills/ui-ux-review/.claude/skills/ui-ux-review/SKILL.md +1 -1
  122. package/templates/common/Harness/TASK_ARCHIVE.md +0 -56
  123. package/templates/common/Harness/WF-STATE.md +0 -83
  124. package/templates/optional/skills/browser-e2e/.claude/skills/browser-e2e/SKILL.md +0 -42
  125. package/templates/optional/skills/browser-e2e/.claude/skills/wf-browser/SKILL.md +0 -194
  126. package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +0 -119
  127. /package/templates/common/Harness/{ACCEPTANCE_PROTOCOL.md → specs/protocols/ACCEPTANCE_PROTOCOL.md} +0 -0
  128. /package/templates/common/Harness/{AGENT_ISOLATION.md → specs/protocols/AGENT_ISOLATION.md} +0 -0
  129. /package/templates/common/Harness/{DEBUG_PROTOCOL.md → specs/protocols/DEBUG_PROTOCOL.md} +0 -0
  130. /package/templates/common/Harness/{HARNESS_BRIDGE.md → specs/protocols/HARNESS_BRIDGE.md} +0 -0
  131. /package/templates/common/Harness/{MEMORY_PROTOCOL.md → specs/protocols/MEMORY_PROTOCOL.md} +0 -0
  132. /package/templates/common/Harness/{TDD-GUIDE.md → specs/protocols/TDD-GUIDE.md} +0 -0
  133. /package/templates/common/Harness/{WF-AUTO-ANGLES.md → specs/workflows/WF-AUTO-ANGLES.md} +0 -0
@@ -38,29 +38,39 @@ const commonAgents = [
38
38
 
39
39
  const commonSkills = [
40
40
  'wf',
41
+ 'wf-help',
41
42
  'tdd',
42
43
  'wf-update',
43
44
  'wf-max',
44
45
  'wf-review',
45
46
  'wf-learn',
47
+ 'wf-browser',
46
48
  'subagent-orchestrator',
47
49
  'wf-readme',
50
+ 'wf-agents-docs',
48
51
  'wf-remove',
49
52
  'wf-auto',
50
53
  'wf-auto-spark',
51
54
  ];
52
55
 
53
- const opencodeWorkflowCommands = [
56
+ const workflowCommands = [
54
57
  'wf',
55
58
  'wf-max',
56
59
  'wf-auto',
57
60
  'wf-auto-spark',
58
61
  'wf-learn',
59
62
  'wf-review',
63
+ 'wf-browser',
60
64
  'wf-readme',
61
65
  'wf-remove',
62
66
  ];
63
67
 
68
+ const opencodeWorkflowCommands = workflowCommands;
69
+
70
+ const cacheDisciplinedSkills = commonSkills.filter(skill => (
71
+ skill === 'subagent-orchestrator' || (skill.startsWith('wf') && skill !== 'wf-help')
72
+ ));
73
+
64
74
  const memoryFiles = [
65
75
  'Harness/memory/startup-hints.md',
66
76
  'Harness/memory/routes.md',
@@ -74,13 +84,13 @@ const required = [
74
84
  'CLAUDE.md',
75
85
  'README.md',
76
86
  'Harness/MEMORY.md',
77
- 'Harness/WF.md',
78
- 'Harness/WF-MAX.md',
79
- 'Harness/ACCEPTANCE_PROTOCOL.md',
80
- 'Harness/AGENT_ISOLATION.md',
81
- 'Harness/HARNESS_BRIDGE.md',
82
- 'Harness/DEBUG_PROTOCOL.md',
83
- 'Harness/MEMORY_PROTOCOL.md',
87
+ 'Harness/specs/workflows/WF.md',
88
+ 'Harness/specs/workflows/WF-MAX.md',
89
+ 'Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md',
90
+ 'Harness/specs/protocols/AGENT_ISOLATION.md',
91
+ 'Harness/specs/protocols/HARNESS_BRIDGE.md',
92
+ 'Harness/specs/protocols/DEBUG_PROTOCOL.md',
93
+ 'Harness/specs/protocols/MEMORY_PROTOCOL.md',
84
94
  'Harness/templates/PRD.template.md',
85
95
  'Harness/templates/ACCEPTANCE.template.md',
86
96
  'Harness/templates/UI_CONTRACT.template.md',
@@ -90,10 +100,12 @@ const required = [
90
100
  'Harness/templates/VALIDATION_REPORT.template.md',
91
101
  ...memoryFiles,
92
102
  '.codex/config.toml',
103
+ '.codex/hooks.json',
93
104
  'opencode.json',
94
105
  '.claude/settings.json',
95
106
  '.claude/commands/wf-help.md',
96
107
  '.claude/commands/wf-update.md',
108
+ ...workflowCommands.map(command => `.claude/commands/${command}.md`),
97
109
  '.opencode/commands/wf-help.md',
98
110
  '.opencode/commands/wf-update.md',
99
111
  ...opencodeWorkflowCommands.map(command => `.opencode/commands/${command}.md`),
@@ -104,24 +116,30 @@ const required = [
104
116
  ...commonSkills.map(skill => `.claude/skills/${skill}/SKILL.md`),
105
117
  ...commonSkills.map(skill => `.agents/skills/${skill}/SKILL.md`),
106
118
  'Harness/README.md',
119
+ 'Harness/settings.json',
120
+ 'Harness/specs/guides/SETUP.md',
107
121
  'Harness/PROGRESS.md',
108
- 'Harness/lifecycle.md',
109
- 'Harness/subagents.md',
110
- 'Harness/dispatch.md',
111
- 'Harness/extension.md',
112
- 'Harness/context-loading.md',
113
- 'Harness/WF-KERNEL.md',
114
- 'Harness/agent-workflow.md',
115
- 'Harness/architecture.md',
122
+ 'Harness/specs/guides/lifecycle.md',
123
+ 'Harness/specs/runtime/subagents.md',
124
+ 'Harness/specs/runtime/dispatch.md',
125
+ 'Harness/specs/guides/extension.md',
126
+ 'Harness/specs/runtime/context-loading.md',
127
+ 'Harness/ownership.manifest.json',
128
+ 'Harness/specs/workflows/WF-KERNEL.md',
129
+ 'Harness/specs/runtime/agent-workflow.md',
130
+ 'Harness/project/architecture.md',
116
131
  'Harness/research/README.md',
117
132
  'Harness/research/research-results.md',
118
133
  'Harness/research/PRD.md',
134
+ 'Harness/scripts/context-budget.mjs',
135
+ 'Harness/scripts/l2-cache-telemetry.mjs',
119
136
  'Harness/scripts/wf-update-check.mjs',
120
137
  'Harness/scripts/wf-remove.mjs',
121
138
  'Harness/scripts/scan-clean.mjs',
139
+ 'Harness/scripts/task-state.mjs',
122
140
  'Harness/scripts/archive-tasks.mjs',
123
- 'Harness/WF-STATE.md',
124
- 'Harness/TASK_ARCHIVE.md',
141
+ 'Harness/specs/workflows/WF-STATE.md',
142
+ 'Harness/specs/protocols/TASK_ARCHIVE.md',
125
143
  'Harness/.harness-version',
126
144
  ];
127
145
 
@@ -129,7 +147,7 @@ const projectFacts = [
129
147
  'Harness/PROGRESS.md',
130
148
  'Harness/research/PRD.md',
131
149
  'Harness/research/research-results.md',
132
- 'Harness/architecture.md',
150
+ 'Harness/project/architecture.md',
133
151
  ];
134
152
 
135
153
  const contextPacks = [
@@ -149,9 +167,34 @@ const contextPacks = [
149
167
 
150
168
  const durableCommunicationDocs = [
151
169
  'Harness/README.md',
152
- 'Harness/subagents.md',
153
- 'Harness/dispatch.md',
170
+ 'Harness/specs/runtime/subagents.md',
171
+ 'Harness/specs/runtime/dispatch.md',
172
+ 'Harness/specs/runtime/context-loading.md',
173
+ ];
174
+
175
+ const legacyRootSpecDocs = [
176
+ 'Harness/ACCEPTANCE_PROTOCOL.md',
177
+ 'Harness/AGENT_ISOLATION.md',
178
+ 'Harness/DEBUG_PROTOCOL.md',
179
+ 'Harness/ECC-GUIDE.md',
180
+ 'Harness/HARNESS_BRIDGE.md',
181
+ 'Harness/MEMORY_PROTOCOL.md',
182
+ 'Harness/SETUP.md',
183
+ 'Harness/TASK_ARCHIVE.md',
184
+ 'Harness/TDD-GUIDE.md',
185
+ 'Harness/WF-AUTO-ANGLES.md',
186
+ 'Harness/WF-AUTO-SPARK.md',
187
+ 'Harness/WF-AUTO.md',
188
+ 'Harness/WF-KERNEL.md',
189
+ 'Harness/WF-MAX.md',
190
+ 'Harness/WF-STATE.md',
191
+ 'Harness/WF.md',
192
+ 'Harness/agent-workflow.md',
154
193
  'Harness/context-loading.md',
194
+ 'Harness/dispatch.md',
195
+ 'Harness/extension.md',
196
+ 'Harness/lifecycle.md',
197
+ 'Harness/subagents.md',
155
198
  ];
156
199
 
157
200
  const errors = [];
@@ -171,6 +214,19 @@ function forbidText(rel, text, label = text) {
171
214
  if (body && body.includes(text)) errors.push(`${rel} contains forbidden ${label}`);
172
215
  }
173
216
 
217
+ function activeToml(rel) {
218
+ return read(rel)
219
+ .split(/\r?\n/)
220
+ .map(line => line.trim())
221
+ .filter(line => line && !line.startsWith('#'))
222
+ .join('\n');
223
+ }
224
+
225
+ function forbidActiveToml(rel, pattern, label) {
226
+ const body = activeToml(rel);
227
+ if (body && pattern.test(body)) errors.push(`${rel} contains active forbidden ${label}`);
228
+ }
229
+
174
230
  function frontmatterField(text, field) {
175
231
  const match = text.match(new RegExp(`^${field}:\\s*(.+)$`, 'm'));
176
232
  return match ? match[1].trim() : '';
@@ -199,6 +255,28 @@ function listMarkdownFiles(rel) {
199
255
  const TASK_NAME_RE = /^task-[a-z]+(-[a-z0-9]+){1,4}$/;
200
256
  const TASK_NAME_MAX = 46; // "task-" (5) + ≤40 chars body + 1 safety = 46
201
257
  const TASK_RESERVED = new Set(['_template', 'auto', '_archive']);
258
+ const TASK_SAFE_ARCHIVE_STATUSES = new Set(['complete', 'verified', 'archived', 'abandoned', 'obsolete', 'done', 'closed', 'closeout']);
259
+ const TASK_NEVER_ARCHIVE_STATUSES = new Set(['active', 'blocked', 'in_progress', 'running', 'pending', 'needs-user-decision']);
260
+ const TASK_PHASE_ALIASES = new Map([
261
+ ['implementation', 'implement'],
262
+ ['build', 'implement'],
263
+ ['validation', 'verify'],
264
+ ['complete', 'verified'],
265
+ ['done', 'verified'],
266
+ ['closed', 'closeout'],
267
+ ]);
268
+ const TASK_STATUS_ALIASES = new Map([
269
+ ['in-progress', 'in_progress'],
270
+ ['inprogress', 'in_progress'],
271
+ ['needs_user_decision', 'needs-user-decision'],
272
+ ['needs-user', 'needs-user-decision'],
273
+ ]);
274
+ const TASK_VALID_PHASES = new Set([
275
+ 'intake', 'clarify', 'requirements', 'prd', 'acceptance', 'plan', 'explore',
276
+ 'implement', 'verify', 'review', 'fix', 'reflect', 'closeout', 'blocked',
277
+ 'archived', 'verified',
278
+ ]);
279
+ const TASK_VALID_STATUSES = new Set([...TASK_SAFE_ARCHIVE_STATUSES, ...TASK_NEVER_ARCHIVE_STATUSES, 'skipped', 'failed']);
202
280
 
203
281
  function validateTaskName(name, strict) {
204
282
  if (TASK_RESERVED.has(name)) return null;
@@ -215,6 +293,57 @@ function validateTaskName(name, strict) {
215
293
  return null;
216
294
  }
217
295
 
296
+ function normalizeTaskToken(value, validSet, aliasMap) {
297
+ if (value === null || value === undefined) return '';
298
+ const raw = String(value).trim().toLowerCase();
299
+ if (!raw) return '';
300
+ const compact = raw.replace(/\s+/g, '-');
301
+ const direct = aliasMap.get(compact) || compact;
302
+ if (validSet.has(direct)) return direct;
303
+ const tokens = raw
304
+ .replace(/[`*_()[\]{}:]/g, ' ')
305
+ .replace(/[^a-z0-9_-]+/g, ' ')
306
+ .split(/\s+/)
307
+ .map(token => token.trim())
308
+ .filter(Boolean);
309
+ for (const token of tokens) {
310
+ const canonical = aliasMap.get(token) || token;
311
+ if (validSet.has(canonical)) return canonical;
312
+ }
313
+ return '';
314
+ }
315
+
316
+ function normalizeTaskStatus(value) {
317
+ return normalizeTaskToken(value, TASK_VALID_STATUSES, TASK_STATUS_ALIASES);
318
+ }
319
+
320
+ function normalizeTaskPhase(value) {
321
+ return normalizeTaskToken(value, TASK_VALID_PHASES, TASK_PHASE_ALIASES);
322
+ }
323
+
324
+ function taskStateIssue(message) {
325
+ if (strict) errors.push(message);
326
+ else console.warn(`Warning: ${message}`);
327
+ }
328
+
329
+ function parseRootTaskProgress(text) {
330
+ const activeMatch = text.match(/## Active Task\s*\r?\n+(?:\s*\r?\n)?\s*-\s*([^\r\n]+)/);
331
+ const rawActive = activeMatch ? activeMatch[1].trim() : '';
332
+ const activeTask = rawActive && !/^none$/i.test(rawActive) ? rawActive : null;
333
+ const rows = [];
334
+ const taskIndexMatch = text.match(/## Task Index\s*\r?\n([\s\S]*?)(?=\r?\n## |\s*$)/);
335
+ if (taskIndexMatch) {
336
+ for (const line of taskIndexMatch[1].split(/\r?\n/)) {
337
+ const trimmed = line.trim();
338
+ if (!trimmed.startsWith('|')) continue;
339
+ if (/^\|\s*-+/.test(trimmed) || /^\|\s*ID\s*\|/i.test(trimmed)) continue;
340
+ const cells = trimmed.split('|').slice(1, -1).map(cell => cell.trim());
341
+ if (cells.length >= 4 && cells[0]) rows.push({ id: cells[0], phase: cells[2] });
342
+ }
343
+ }
344
+ return { activeTask, rows };
345
+ }
346
+
218
347
  function unresolvedTemplatePlaceholders(text) {
219
348
  const placeholders = [];
220
349
  const pattern = /\{\{([^{}\r\n]+)\}\}/g;
@@ -262,7 +391,6 @@ function listedWorkflowCommands(...texts) {
262
391
  for (const match of text.matchAll(pattern)) {
263
392
  const command = `/${match[1]}`;
264
393
  if (command === '/wf-help') continue;
265
- if (command === '/wf-browser') continue; // optional workflow, not a required skill
266
394
  commands.add(command);
267
395
  }
268
396
  }
@@ -276,6 +404,27 @@ for (const rel of required) {
276
404
  }
277
405
  }
278
406
 
407
+ for (const rel of legacyRootSpecDocs) {
408
+ if (fs.existsSync(path.join(root, rel))) {
409
+ errors.push(`legacy root Harness spec doc should be migrated to Harness/specs/**: ${rel}`);
410
+ }
411
+ }
412
+
413
+ const manifestText = read('Harness/ownership.manifest.json');
414
+ if (manifestText) {
415
+ try {
416
+ const manifest = JSON.parse(manifestText);
417
+ for (const entry of manifest.frameworkOwned || []) {
418
+ if (!entry || typeof entry.path !== 'string') continue;
419
+ if (!fs.existsSync(path.join(root, ...entry.path.split('/')))) {
420
+ errors.push(`ownership manifest frameworkOwned file missing: ${entry.path}`);
421
+ }
422
+ }
423
+ } catch (err) {
424
+ errors.push(`Harness/ownership.manifest.json is not valid JSON: ${err.message}`);
425
+ }
426
+ }
427
+
279
428
  const removedHookArtifacts = [
280
429
  'Harness/HOOK_PROTOCOL.md',
281
430
  'Harness/scripts/wf-mode-hook.mjs',
@@ -328,7 +477,7 @@ for (const taskDir of taskDirs) {
328
477
  // M2: MCP-as-Worker fake-compliance guard.
329
478
  // Task capsules MUST NOT record mcp__codex.codex_implement / mcp__claude.claude_implement
330
479
  // as Worker execution. Historical do-not-repeat references are allowed only when the
331
- // file is marked ANTI-PATTERN. See Harness/WF-MAX.md "Worker Channel Degradation & Independence".
480
+ // file is marked ANTI-PATTERN. See Harness/specs/workflows/WF-MAX.md "Worker Channel Degradation & Independence".
332
481
  const MCP_AS_WORKER_RE = /mcp__(?:codex)\.codex_implement|mcp__(?:claude)\.claude_implement/;
333
482
  for (const taskDir of taskDirs) {
334
483
  if (!taskDir.startsWith('task-')) continue;
@@ -372,13 +521,13 @@ for (const rel of durableCommunicationDocs) {
372
521
  requireText(rel, 'chat/subagent transcript state is non-authoritative', 'non-authoritative transcript invariant');
373
522
  }
374
523
 
375
- requireText('Harness/MEMORY_PROTOCOL.md', 'L1/L2/L3 Memory Architecture', 'L2/L3 memory architecture section');
376
- requireText('Harness/MEMORY_PROTOCOL.md', 'Memory Candidate Detection', 'memory candidate detection section');
377
- requireText('Harness/MEMORY_PROTOCOL.md', 'explicit user preference', 'explicit user preference immediate write rule');
378
- requireText('Harness/MEMORY_PROTOCOL.md', 'Memory Routing (L3)', 'memory routing section');
379
- requireText('Harness/MEMORY_PROTOCOL.md', 'Scenario pack', 'route scoring scenario pack');
380
- requireText('Harness/MEMORY_PROTOCOL.md', 'same tool or command pattern fails 3+ times', 'tool reflection trigger');
381
- requireText('Harness/MEMORY_PROTOCOL.md', 'user corrects the same assumption or preference 2+ times', 'user correction reflection trigger');
524
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'L1/L2/L3 Memory Architecture', 'L2/L3 memory architecture section');
525
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'Memory Candidate Detection', 'memory candidate detection section');
526
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'explicit user preference', 'explicit user preference immediate write rule');
527
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'Memory Routing (L3)', 'memory routing section');
528
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'Scenario pack', 'route scoring scenario pack');
529
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'same tool or command pattern fails 3+ times', 'tool reflection trigger');
530
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'user corrects the same assumption or preference 2+ times', 'user correction reflection trigger');
382
531
  requireText('CLAUDE.md', 'startup-hints.md', 'CLAUDE startup-hints routing');
383
532
  requireText('.claude/rules/ecc/common.md', 'startup-hints.md', 'ECC startup-hints routing');
384
533
  requireText('.claude/rules/ecc/common.md', 'memory candidates', 'ECC memory candidate detection');
@@ -398,20 +547,57 @@ requireText('Harness/memory/tool-usage-reflections.md', 'supersedes', 'tool refl
398
547
  requireText('Harness/memory/agent-lessons-patterns.md', 'supersedes', 'agent lessons template date rule');
399
548
  requireText('Harness/MEMORY.md', 'startup-hints.md', 'MEMORY.md startup-hints registration');
400
549
  requireText('Harness/MEMORY.md', 'Memory routes', 'MEMORY.md routes.md registration');
550
+ requireText('Harness/MEMORY.md', '../.claude/commands/wf-update.md', 'MEMORY.md wf-update direct command registration');
401
551
  requireText('CLAUDE.md', 'If `Harness/` exists, this repository is governed by the Harness contract', 'Harness binding contract');
402
552
  requireText('CLAUDE.md', 'memory and resource router', 'memory/resource router');
403
553
  requireText('CLAUDE.md', '## 5a. Low-Noise Progress', 'low-noise progress section');
554
+ requireText('CLAUDE.md', "Match the user's language for all user-facing prose", 'user-facing language match rule');
404
555
  requireText('CLAUDE.md', 'Keep intermediate user updates to 1-2 short sentences', 'low-noise intermediate update rule');
405
556
  requireText('.claude/rules/ecc/common.md', '## Low-Noise Progress', 'ECC low-noise progress section');
557
+ requireText('.claude/rules/ecc/common.md', "Match the user's language for user-facing prose", 'ECC user-facing language match rule');
406
558
  requireText('.claude/rules/ecc/common.md', 'Do not recap plans, paste logs, or narrate obvious file reads', 'ECC low-noise no-recap rule');
559
+ requireText('.claude/rules/ecc/common.md', 'excluding `/wf-help`, `$wf-help`, `/skills wf-help`, `/wf-update`, `$wf-update`, and `/skills wf-update`', 'ECC direct command exemption');
407
560
  requireText('Harness/README.md', 'Load By Task', 'Harness task router');
408
- requireText('CLAUDE.md', 'Harness/SETUP.md` is a bootstrap-only document', 'setup bootstrap-only contract');
561
+ requireText('Harness/README.md', 'Need context/cache/token efficiency', 'cache/token router row');
562
+ requireText('Harness/specs/runtime/context-loading.md', 'Context Tiers', 'context tier load budget section');
563
+ requireText('Harness/specs/runtime/context-loading.md', 'automatic route profiles, not user-selected modes', 'context tiers are automatic route profiles');
564
+ requireText('Harness/specs/runtime/context-loading.md', 'Budgets are regression guards, not exclusion rules', 'context budgets do not block required files');
565
+ requireText('Harness/specs/runtime/context-loading.md', 'Do not skip required rules', 'context budget correctness priority');
566
+ requireText('Harness/specs/runtime/context-loading.md', 'Escalation rule', 'context-loading targeted escalation rule');
567
+ requireText('Harness/specs/runtime/context-loading.md', 'Thin startup', 'thin startup context tier');
568
+ requireText('Harness/specs/runtime/context-loading.md', 'Routed skill/doc', 'routed skill/doc lazy tier');
569
+ requireText('Harness/specs/runtime/context-loading.md', 'Cache-First Context Contract', 'cache-first context contract');
570
+ requireText('Harness/specs/runtime/context-loading.md', 'Cache Validation Levels', 'cache validation levels');
571
+ requireText('Harness/specs/runtime/context-loading.md', 'Do not claim real cache hits', 'real cache telemetry boundary');
572
+ requireText('Harness/specs/runtime/context-loading.md', 'cached_tokens', 'OpenAI cache telemetry field');
573
+ requireText('Harness/specs/runtime/context-loading.md', 'cache_read_input_tokens', 'Anthropic cache telemetry field');
574
+ requireText('Harness/specs/workflows/WF.md', 'Cache Discipline', 'WF cache discipline');
575
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Cache-First Context Contract', 'WF-KERNEL cache-first contract');
576
+ requireText('Harness/specs/runtime/dispatch.md', 'Cache-first dispatch', 'dispatch cache-first discipline');
577
+ requireText('Harness/specs/runtime/subagents.md', 'Cache-first discipline', 'subagents cache-first discipline');
578
+ forbidText('CLAUDE.md', 'Harness/specs/guides/SETUP.md', 'CLAUDE.md SETUP reference');
409
579
  forbidText('CLAUDE.md', 'follow it before normal project work', 'installed-project SETUP hot-path routing');
410
- requireText('Harness/SETUP.md', 'Harness/MEMORY_PROTOCOL.md', 'setup memory protocol reference');
580
+ requireText('Harness/specs/guides/SETUP.md', 'Harness/specs/protocols/MEMORY_PROTOCOL.md', 'setup memory protocol reference');
581
+ requireText('Harness/specs/guides/SETUP.md', 'no startup dependency on this setup reference', 'SETUP startup boundary');
582
+ forbidText('Harness/specs/guides/SETUP.md', 'bootstrap contract line', 'stale SETUP-to-CLAUDE bootstrap contract');
583
+ forbidText('Harness/specs/runtime/context-loading.md', 'Always keep:', 'ambiguous always-load context rule');
584
+ requireText('Harness/scripts/context-budget.mjs', 'thin-startup', 'context-budget thin startup route');
585
+ requireText('Harness/scripts/context-budget.mjs', 'cache-diagnostics-route', 'context-budget cache diagnostics route');
586
+ requireText('Harness/scripts/context-budget.mjs', 'not runtime exclusion rules', 'context-budget non-exclusion guard');
587
+ requireText('Harness/scripts/context-budget.mjs', 'approxTokens', 'context-budget approximate token output');
588
+ requireText('Harness/specs/runtime/context-loading.md', 'Harness/scripts/l2-cache-telemetry.mjs', 'L2 telemetry script reference');
589
+ requireText('Harness/README.md', 'scripts/l2-cache-telemetry.mjs', 'Harness README L2 telemetry script route');
590
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', 'cache_read_input_tokens', 'L2 telemetry cache-read field');
591
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', 'cache_creation_input_tokens', 'L2 telemetry cache-creation field');
592
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', 'claimGate', 'L2 telemetry claim gate');
593
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', '--strict-mcp-config', 'L2 telemetry strict MCP isolation');
594
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', '--max-budget-usd', 'L2 telemetry per-turn budget');
595
+ requireText('Harness/scripts/l2-cache-telemetry.mjs', '--resume', 'L2 telemetry session resume');
596
+ forbidText('Harness/scripts/l2-cache-telemetry.mjs', '--bare', 'L2 telemetry bare mode');
411
597
  requireText('Harness/README.md', 'subagent', 'subagent orchestrator entry trigger');
412
598
  requireText('Harness/README.md', 'PROGRESS.md', 'PROGRESS global task index');
413
599
  requireText('CLAUDE.md', 'Harness/tasks/', 'task capsule directory reference');
414
- requireText('Harness/subagents.md', 'Subagents are readers and reporters', 'subagent state committer rule');
600
+ requireText('Harness/specs/runtime/subagents.md', 'Subagents are readers and reporters', 'subagent state committer rule');
415
601
  for (const heading of ['## 2. Think Before Coding', '## 3. Simplicity First', '## 4. Surgical Changes', '## 5. Goal-Driven Execution']) {
416
602
  requireText('CLAUDE.md', heading, `Karpathy-style rule heading: ${heading}`);
417
603
  }
@@ -426,21 +612,21 @@ if (progress) {
426
612
 
427
613
  // Legacy PLAN.md deprecation check (removed — stub only)
428
614
 
429
- const dispatch = read('Harness/dispatch.md');
615
+ const dispatch = read('Harness/specs/runtime/dispatch.md');
430
616
  if (dispatch) {
431
617
  for (const agent of commonAgents) {
432
- if (!dispatch.includes(`\`${agent}\``)) errors.push(`Harness/dispatch.md missing common agent: ${agent}`);
618
+ if (!dispatch.includes(`\`${agent}\``)) errors.push(`Harness/specs/runtime/dispatch.md missing common agent: ${agent}`);
433
619
  }
434
- if (!dispatch.includes('## Handoff Format')) errors.push('Harness/dispatch.md missing heading: ## Handoff Format');
620
+ if (!dispatch.includes('## Handoff Format')) errors.push('Harness/specs/runtime/dispatch.md missing heading: ## Handoff Format');
435
621
  }
436
622
 
437
- const contextLoading = read('Harness/context-loading.md');
623
+ const contextLoading = read('Harness/specs/runtime/context-loading.md');
438
624
  if (contextLoading) {
439
625
  if (!contextLoading.includes('Harness/README.md` is the primary Harness documentation router')) {
440
- errors.push('Harness/context-loading.md must declare Harness/README.md as the primary Harness documentation router');
626
+ errors.push('Harness/specs/runtime/context-loading.md must declare Harness/README.md as the primary Harness documentation router');
441
627
  }
442
628
  for (const pack of contextPacks) {
443
- if (!contextLoading.includes(pack)) errors.push(`Harness/context-loading.md missing subagent pack: ${pack}`);
629
+ if (!contextLoading.includes(pack)) errors.push(`Harness/specs/runtime/context-loading.md missing subagent pack: ${pack}`);
444
630
  }
445
631
  }
446
632
 
@@ -489,7 +675,7 @@ for (const command of listedWorkflowCommands(docsReadme, read('.claude/commands/
489
675
 
490
676
  for (const workflowFile of registeredWorkflowFiles(docsReadme, memory)) {
491
677
  if (!fs.existsSync(path.join(root, workflowFile))) {
492
- // Optional workflows (browser-e2e, ts-react-frontend, etc.) may not be installed
678
+ // Optional workflows (ts-react-frontend, ui-ux-review, etc.) may not be installed
493
679
  continue;
494
680
  }
495
681
  }
@@ -509,9 +695,14 @@ function requireUiSelectorContract(rel) {
509
695
  }
510
696
  }
511
697
 
512
- requireUiSelectorContract('Harness/workflows/browser-e2e.md');
698
+ requireUiSelectorContract('.claude/skills/wf-browser/SKILL.md');
699
+ requireUiSelectorContract('.agents/skills/wf-browser/SKILL.md');
513
700
  requireUiSelectorContract('Harness/workflows/ts-react-frontend.md');
514
701
 
702
+ if (fs.existsSync(path.join(root, 'Harness/workflows/browser-e2e.md'))) {
703
+ errors.push('retired optional workflow must not exist: Harness/workflows/browser-e2e.md');
704
+ }
705
+
515
706
  for (const skill of commonSkills) {
516
707
  const rel = `.claude/skills/${skill}/SKILL.md`;
517
708
  const text = read(rel);
@@ -632,183 +823,243 @@ for (const claudeRel of listMarkdownFiles('.claude/commands')) {
632
823
  }
633
824
  }
634
825
 
635
- requireText('Harness/extension.md', 'Skills should extend the harness');
636
- requireText('Harness/agent-workflow.md', 'Harness/tasks/<task-id>/PROGRESS.md');
826
+ requireText('Harness/specs/guides/extension.md', 'Skills should extend the harness');
827
+ requireText('Harness/specs/runtime/agent-workflow.md', 'Harness/tasks/<task-id>/PROGRESS.md');
637
828
  requireText('Harness/README.md', 'Task records are compact by default', 'compact task record router rule');
638
- requireText('Harness/agent-workflow.md', 'AC record size', 'compact AC record size rule');
639
- requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'Default to 1-3 concise ACs', 'compact AC default');
640
- requireText('Harness/WF.md', 'task-scribe', 'compact heartbeat rule');
829
+ requireText('Harness/specs/runtime/agent-workflow.md', 'AC record size', 'compact AC record size rule');
830
+ requireText('Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'Default to 1-3 concise ACs', 'compact AC default');
831
+ requireText('Harness/specs/workflows/WF.md', 'task-scribe', 'compact heartbeat rule');
641
832
  requireText('Harness/tasks/_template/PLAN.md', 'Compact task record', 'compact task PLAN template');
642
833
  requireText('Harness/tasks/_template/PLAN.md', 'Default: keep 1-3 concise ACs', 'compact task PLAN AC guidance');
643
834
  requireText('Harness/tasks/_template/PROGRESS.md', 'Compact heartbeat', 'compact task PROGRESS template');
644
- requireText('Harness/agent-workflow.md', 'TDD-GUIDE.md', 'agent workflow loads TDD guide');
645
- requireText('Harness/agent-workflow.md', 'real user-path test', 'agent workflow real user path requirement');
835
+ requireText('Harness/specs/runtime/agent-workflow.md', 'TDD-GUIDE.md', 'agent workflow loads TDD guide');
836
+ requireText('Harness/specs/runtime/agent-workflow.md', 'real user-path test', 'agent workflow real user path requirement');
646
837
  requireText('Harness/research/README.md', 'research-results.md');
647
- requireText('Harness/WF.md', 'Orchestration Loop', 'WF orchestration loop');
648
- requireText('Harness/WF.md', 'WF-KERNEL.md', 'WF references WF-KERNEL');
649
- requireText('Harness/WF.md', 'Cross-review and reflector NOT mandatory', 'WF-Light no mandatory cross-review');
650
- requireText('Harness/WF.md', '`.claude/agents/`', 'WF agent roster path');
651
- requireText('Harness/WF.md', 'Harness/tasks/', 'WF task directory reference');
838
+ requireText('Harness/specs/workflows/WF.md', 'Orchestration Loop', 'WF orchestration loop');
839
+ requireText('Harness/specs/workflows/WF.md', 'WF-KERNEL.md', 'WF references WF-KERNEL');
840
+ requireText('Harness/specs/workflows/WF.md', 'Cross-review and reflector NOT mandatory', 'WF-Light no mandatory cross-review');
841
+ requireText('Harness/specs/workflows/WF.md', '`.claude/agents/`', 'WF agent roster path');
842
+ requireText('Harness/specs/workflows/WF.md', 'Harness/tasks/', 'WF task directory reference');
843
+ const taskIdConvention = '`task-<verb>-<noun>[-detail]`';
844
+ requireText('Harness/specs/workflows/WF.md', taskIdConvention, 'WF task-id naming convention');
845
+ requireText('Harness/specs/workflows/WF-STATE.md', taskIdConvention, 'WF-STATE task-id naming convention');
846
+ requireText('Harness/specs/workflows/WF-MAX.md', taskIdConvention, 'WF-MAX task-id naming convention');
652
847
  requireText('Harness/README.md', 'explicit WF token', 'WF explicit contract');
653
848
  requireText('Harness/README.md', 'tier-gated acceptance', 'WF tiered acceptance router output');
654
- requireText('.claude/skills/wf/SKILL.md', 'Harness/WF.md', 'wf skill loads core WF doc');
849
+ requireText('.claude/skills/wf/SKILL.md', 'Harness/specs/workflows/WF.md', 'wf skill loads core WF doc');
655
850
  requireText('.claude/skills/wf/SKILL.md', 'Tier-aware acceptance', 'wf skill tier-aware acceptance');
656
- requireText('.agents/skills/wf/SKILL.md', 'Harness/WF.md', 'Codex wf skill loads core WF doc');
657
- requireText('.claude/skills/subagent-orchestrator/SKILL.md', 'Harness/subagents.md', 'subagent-orchestrator loads subagents doc');
851
+ requireText('.claude/skills/wf/SKILL.md', taskIdConvention, 'wf skill task-id naming convention');
852
+ requireText('.agents/skills/wf/SKILL.md', 'Harness/specs/workflows/WF.md', 'Codex wf skill loads core WF doc');
853
+ requireText('.agents/skills/wf/SKILL.md', taskIdConvention, 'Codex wf skill task-id naming convention');
854
+ requireText('.claude/skills/subagent-orchestrator/SKILL.md', 'Harness/specs/runtime/subagents.md', 'subagent-orchestrator loads subagents doc');
658
855
  requireText('.claude/skills/subagent-orchestrator/SKILL.md', '.claude/agents/', 'subagent-orchestrator built-in agent roster path');
659
856
  requireText('.claude/skills/subagent-orchestrator/SKILL.md', 'WF/WF-MAX requires tier-specific', 'subagent-orchestrator WF tier-specific contract');
660
857
  requireText('.claude/skills/wf-readme/SKILL.md', 'README.md', 'wf-readme loads README');
661
- requireText('.claude/skills/wf-readme/SKILL.md', 'Harness/architecture.md', 'wf-readme links architecture docs');
662
- requireText('Harness/subagents.md', '## Source Attribution', 'subagent source attribution');
663
- requireText('Harness/subagents.md', 'npx skills find', 'find-skills discovery attribution');
664
- requireText('Harness/subagents.md', 'superpowers:dispatching-parallel-agents', 'parallel-agent source attribution');
665
- requireText('Harness/subagents.md', 'superpowers:subagent-driven-development', 'subagent-driven source attribution');
666
- requireText('Harness/subagents.md', '## Built-in Agent Roster', 'built-in agent roster');
667
- requireText('Harness/subagents.md', '## WF Default Fan-Out', 'WF default fan-out');
668
- requireText('Harness/subagents.md', 'concrete conditions', 'subagent decision tree');
669
- requireText('Harness/subagents.md', 'parallel planner/researcher/docs-researcher/architect subagents', 'WF roster orchestration shape');
670
- requireText('Harness/subagents.md', '`reflector`', 'reflector agent roster');
671
- requireText('Harness/subagents.md', 'reflector PASS', 'reflector review gate');
672
- requireText('Harness/subagents.md', '## Efficiency Ladder', 'subagent efficiency ladder');
673
- requireText('Harness/subagents.md', '## Review Gates', 'subagent review gates');
674
- requireText('Harness/architecture.md', '## 2. Interface Decoupling', 'architecture interface decoupling');
675
- requireText('Harness/architecture.md', '## 3. State Design', 'architecture state design');
676
- requireText('Harness/architecture.md', 'Avoid speculative abstraction', 'anti-overengineering architecture rule');
858
+ requireText('.claude/skills/wf-readme/SKILL.md', 'Harness/project/architecture.md', 'wf-readme links architecture docs');
859
+ requireText('Harness/specs/runtime/subagents.md', '## Source Attribution', 'subagent source attribution');
860
+ requireText('Harness/specs/runtime/subagents.md', 'npx skills find', 'find-skills discovery attribution');
861
+ requireText('Harness/specs/runtime/subagents.md', 'superpowers:dispatching-parallel-agents', 'parallel-agent source attribution');
862
+ requireText('Harness/specs/runtime/subagents.md', 'superpowers:subagent-driven-development', 'subagent-driven source attribution');
863
+ requireText('Harness/specs/runtime/subagents.md', '## Built-in Agent Roster', 'built-in agent roster');
864
+ requireText('Harness/specs/runtime/subagents.md', '## WF Default Fan-Out', 'WF default fan-out');
865
+ requireText('Harness/specs/runtime/subagents.md', 'concrete conditions', 'subagent decision tree');
866
+ requireText('Harness/specs/runtime/subagents.md', 'parallel planner/researcher/docs-researcher/architect subagents', 'WF roster orchestration shape');
867
+ requireText('Harness/specs/runtime/subagents.md', '`reflector`', 'reflector agent roster');
868
+ requireText('Harness/specs/runtime/subagents.md', 'reflector PASS', 'reflector review gate');
869
+ requireText('Harness/specs/runtime/subagents.md', '## Efficiency Ladder', 'subagent efficiency ladder');
870
+ requireText('Harness/specs/runtime/subagents.md', '## Review Gates', 'subagent review gates');
871
+ requireText('Harness/project/architecture.md', '## 2. Interface Decoupling', 'architecture interface decoupling');
872
+ requireText('Harness/project/architecture.md', '## 3. State Design', 'architecture state design');
873
+ requireText('Harness/project/architecture.md', 'Avoid speculative abstraction', 'anti-overengineering architecture rule');
677
874
  requireText('CLAUDE.md', 'Use explicit interfaces or state models only when they protect a real boundary', 'CLAUDE interface/state simplicity rule');
678
875
  requireText('Harness/README.md', '/wf-update', 'wf update startup instruction');
679
876
  requireText('Harness/README.md', 'Need harness update', 'update routing row');
680
- requireText('Harness/WF-MAX.md', 'three-layer architecture', 'WF-MAX three-layer architecture');
681
- requireText('Harness/WF-MAX.md', 'dispatch permissions', 'WF-MAX role separation');
682
- requireText('Harness/WF-MAX.md', 'file_claim', 'WF-MAX coloring algorithm');
683
- requireText('Harness/WF-MAX.md', 'CEO → Manager → Worker', 'WF-MAX wave dispatch');
684
- requireText('Harness/WF-MAX.md', 'inherits the full', 'WF-MAX strict superset');
685
- requireText('Harness/WF-MAX.md', 'Cross-CLI', 'WF-MAX cross-CLI overflow');
686
- requireText('Harness/WF-MAX.md', 'claude -p', 'WF-MAX Claude CLI overflow');
687
- requireText('Harness/WF-MAX.md', 'codex exec', 'WF-MAX Codex CLI overflow');
688
- requireText('Harness/WF-MAX.md', 'agents.max_threads', 'WF-MAX Codex max_threads config');
689
- requireText('Harness/WF-MAX.md', 'agents.max_depth', 'WF-MAX Codex max_depth config');
690
- requireText('Harness/WF-MAX.md', 'max_threads = 12', 'WF-MAX Codex scaffold max_threads default');
691
- requireText('Harness/WF-MAX.md', 'max_depth = 1', 'WF-MAX Codex scaffold max_depth default');
692
- requireText('Harness/WF-MAX.md', 'Ask the user before raising', 'WF-MAX asks before raising Codex thread cap');
693
- requireText('Harness/WF-MAX.md', 'Close completed agents', 'WF-MAX close completed agents before overflow');
694
- requireText('Harness/WF-MAX.md', 'Codex++', 'WF-MAX forbids relying on Codex++ as stable capacity');
695
- requireText('.claude/skills/wf-max/SKILL.md', 'agents.max_threads', 'wf-max skill Codex thread config');
696
- requireText('.claude/skills/wf-max/SKILL.md', 'max_threads = 12', 'wf-max skill Codex scaffold max_threads default');
877
+ requireText('Harness/specs/workflows/WF-MAX.md', 'three-layer architecture', 'WF-MAX three-layer architecture');
878
+ requireText('Harness/specs/workflows/WF-MAX.md', 'dispatch permissions', 'WF-MAX role separation');
879
+ requireText('Harness/specs/workflows/WF-MAX.md', 'file_claim', 'WF-MAX coloring algorithm');
880
+ requireText('Harness/specs/workflows/WF-MAX.md', 'CEO → Manager → Worker', 'WF-MAX wave dispatch');
881
+ requireText('Harness/specs/workflows/WF-MAX.md', 'inherits the full', 'WF-MAX strict superset');
882
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Cross-CLI', 'WF-MAX cross-CLI overflow');
883
+ requireText('Harness/specs/workflows/WF-MAX.md', 'claude -p', 'WF-MAX Claude CLI overflow');
884
+ requireText('Harness/specs/workflows/WF-MAX.md', 'codex exec', 'WF-MAX Codex CLI overflow');
885
+ requireText('Harness/specs/workflows/WF-MAX.md', 'agents.max_threads', 'WF-MAX Codex legacy max_threads alias');
886
+ requireText('Harness/specs/workflows/WF-MAX.md', 'max_concurrent_threads_per_session', 'WF-MAX Codex concurrent thread config');
887
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Mandatory Fan-Out Contract', 'WF-MAX mandatory fan-out attempt contract');
888
+ requireText('Harness/specs/workflows/WF-MAX.md', 'fanoutAttempted: true', 'WF-MAX records fan-out attempt');
889
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Runtime Capacity Map', 'WF-MAX runtime capacity map');
890
+ requireText('Harness/specs/workflows/WF-MAX.md', 'CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION', 'WF-MAX Claude Code subagent cap docs');
891
+ requireText('Harness/specs/workflows/WF-MAX.md', 'CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS', 'WF-MAX Claude Code concurrent subagent cap docs');
892
+ requireText('Harness/specs/workflows/WF-MAX.md', 'CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH', 'WF-MAX Claude Code spawn-depth cap docs');
893
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Do not scaffold Codex scalar agent caps', 'WF-MAX Codex config compatibility guard');
894
+ requireText('Harness/specs/workflows/WF-MAX.md', 'subagent_depth = 2', 'WF-MAX OpenCode subagent depth default');
895
+ requireText('Harness/specs/workflows/WF-MAX.md', 'asks the user before any project/global Codex config change', 'WF-MAX asks before changing Codex config');
896
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Close completed agents', 'WF-MAX close completed agents before overflow');
897
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Codex++', 'WF-MAX forbids relying on Codex++ as stable capacity');
898
+ requireText('Harness/specs/workflows/WF-MAX.md', 'does not authorize CEO source edits', 'WF-MAX useful-degrade CEO source-edit boundary');
899
+ requireText('Harness/specs/workflows/WF-MAX.md', '## Cross-Task Decisions', 'WF-MAX PROGRESS heading preservation');
900
+ requireText('.claude/skills/wf-max/SKILL.md', 'agents.max_concurrent_threads_per_session', 'wf-max skill Codex thread config');
901
+ requireText('.claude/skills/wf-max/SKILL.md', 'agents.max_threads', 'wf-max skill Codex legacy alias');
902
+ requireText('.claude/skills/wf-max/SKILL.md', 'Do not scaffold scalar `[agents]` caps', 'wf-max skill Codex config compatibility guard');
903
+ requireText('.claude/skills/wf-max/SKILL.md', 'CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION', 'wf-max skill Claude Code subagent cap');
904
+ requireText('.claude/skills/wf-max/SKILL.md', 'CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS', 'wf-max skill Claude Code concurrent subagent cap');
905
+ requireText('.claude/skills/wf-max/SKILL.md', 'CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH', 'wf-max skill Claude Code spawn-depth cap');
906
+ requireText('.claude/skills/wf-max/SKILL.md', 'subagent_depth = 2', 'wf-max skill OpenCode subagent depth default');
697
907
  requireText('.claude/skills/wf-max/SKILL.md', 'ask the user before raising', 'wf-max skill asks before raising Codex thread cap');
698
908
  requireText('.claude/skills/wf-max/SKILL.md', 'Codex++', 'wf-max skill forbids Codex++ capacity assumption');
699
- requireText('.codex/config.toml', '[agents]', 'Codex agents config table');
700
- requireText('.codex/config.toml', 'max_threads = 12', 'Codex scaffold max_threads default');
701
- requireText('.codex/config.toml', 'max_depth = 1', 'Codex scaffold max_depth default');
909
+ requireText('.claude/skills/wf-max/SKILL.md', taskIdConvention, 'wf-max skill task-id naming convention');
910
+ requireText('.agents/skills/wf-max/SKILL.md', taskIdConvention, 'Codex wf-max skill task-id naming convention');
911
+ requireText('.claude/skills/wf-max/SKILL.md', 'does not authorize CEO source edits', 'wf-max skill useful-degrade CEO source-edit boundary');
912
+ requireText('.claude/skills/wf-max/SKILL.md', '## Cross-Task Decisions', 'wf-max skill PROGRESS heading preservation');
913
+ requireText('.codex/config.toml', 'intentionally avoids WF-MAX capacity defaults', 'Codex config compatibility note');
914
+ forbidActiveToml('.codex/config.toml', /^\[agents\]$/m, 'Codex project config agents table');
915
+ forbidActiveToml('.codex/config.toml', /^\s*max_concurrent_threads_per_session\s*=/m, 'Codex project config concurrent thread scalar');
916
+ forbidActiveToml('.codex/config.toml', /^\s*max_depth\s*=/m, 'Codex project config max_depth scalar');
917
+ forbidActiveToml('.codex/config.toml', /^\s*max_threads\s*=/m, 'Codex project config legacy max_threads scalar');
702
918
  requireText('opencode.json', '"$schema": "https://opencode.ai/config.json"', 'OpenCode config schema');
703
919
  requireText('opencode.json', '.claude/rules/ecc/common.md', 'OpenCode instructions reference to ECC rules');
920
+ requireText('opencode.json', '"subagent_depth": 2', 'OpenCode WF-MAX subagent nesting depth');
921
+ requireText('.opencode/commands/wf-max.md', 'MUST attempt native runtime subagent fan-out', 'OpenCode wf-max command fan-out attempt');
922
+ requireText('.opencode/commands/wf-max.md', 'subagent_depth >= 2', 'OpenCode wf-max command subagent depth requirement');
923
+ requireText('.opencode/commands/wf-max.md', 'fanoutAttempted: true', 'OpenCode wf-max command records fan-out attempt');
924
+ requireText('.opencode/agents/explore-manager.md', 'task:', 'OpenCode explore-manager task permission');
925
+ requireText('.opencode/agents/explore-manager.md', '"codebase-explorer": allow', 'OpenCode explore-manager child allowlist');
926
+ requireText('.opencode/agents/explore-manager.md', '"git status*": allow', 'OpenCode explore-manager read-only git status');
927
+ requireText('.opencode/agents/explore-manager.md', '"git diff*": allow', 'OpenCode explore-manager read-only git diff');
928
+ forbidText('.opencode/agents/explore-manager.md', '"git *": allow', 'OpenCode explore-manager broad git permission');
929
+ requireText('.opencode/agents/architect-manager.md', '"architect": allow', 'OpenCode architect-manager child allowlist');
930
+ requireText('.opencode/agents/implement-manager.md', '"implementer": allow', 'OpenCode implement-manager child allowlist');
931
+ forbidText('.opencode/agents/implement-manager.md', '"node *": allow', 'OpenCode implement-manager broad node permission');
932
+ forbidText('.opencode/agents/implement-manager.md', '"npm *": allow', 'OpenCode implement-manager broad npm permission');
933
+ requireText('.opencode/agents/review-manager.md', '"reviewer": allow', 'OpenCode review-manager child allowlist');
934
+ forbidText('.opencode/agents/review-manager.md', '"node *": allow', 'OpenCode review-manager broad node permission');
704
935
  requireText('Harness/README.md', '/wf-max', 'wf max router alias');
705
936
  requireText('Harness/README.md', 'WF-MAX.md', 'WF-MAX router reference');
706
937
  requireText('Harness/README.md', 'ACCEPTANCE_PROTOCOL.md', 'acceptance protocol router reference');
707
938
  requireText('Harness/README.md', 'TDD-GUIDE.md', 'TDD guide router reference');
708
- requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'PRD-GATE', 'acceptance PRD gate');
709
- requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'AC-GATE', 'acceptance AC gate');
710
- requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'Acceptance Result', 'acceptance result matrix');
711
- requireText('Harness/ACCEPTANCE_PROTOCOL.md', 'Syntax-only checks', 'syntax-only checks are not browser acceptance evidence');
712
- requireText('Harness/AGENT_ISOLATION.md', 'implementer', 'agent isolation implementer rule');
713
- requireText('Harness/HARNESS_BRIDGE.md', 'Network Trace Collector', 'harness bridge network trace collector');
714
- requireText('Harness/WF-AUTO.md', 'Intent Checkpoint', 'wf-auto intent checkpoint');
715
- requireText('Harness/WF-AUTO.md', 'Adaptive Coverage Exhaustion Gate', 'wf-auto adaptive coverage exhaustion gate');
716
- requireText('Harness/WF-AUTO.md', 'dynamic high-risk obligations', 'wf-auto dynamic high-risk obligations');
717
- requireText('Harness/WF-AUTO.md', 'two different confirmation strategies', 'wf-auto two different confirmation strategies');
718
- requireText('Harness/WF-AUTO-ANGLES.md', 'Selection algorithm', 'wf-auto adaptive angle selection');
719
- requireText('Harness/WF-AUTO-ANGLES.md', 'Dynamic obligations', 'wf-auto dynamic obligations');
720
- requireText('Harness/WF-AUTO-ANGLES.md', 'Common probe recipes', 'wf-auto common probe recipes');
721
- forbidText('Harness/WF-AUTO.md', 'All 8 exhausted', 'stale eight-angle exhaustion rule');
722
- forbidText('Harness/WF-AUTO.md', '3 consecutive all-exhausted rounds', 'stale three-consecutive-all-exhausted stop rule');
723
- forbidText('Harness/WF-AUTO.md', '8-Angle Exhaustion Gate', 'stale eight-angle exhaustion gate name');
724
- forbidText('Harness/WF-AUTO-SPARK.md', '8 parallel external searches', 'stale eight-spark fixed-count search');
725
- requireText('Harness/WF-AUTO.md', 'Inherited WF/WF-MAX Constraints', 'wf-auto inherited WF/WF-MAX constraints');
726
- requireText('Harness/WF-AUTO.md', 'Mini PRD -> AC IDs -> test/validation plan -> implementer -> verifier', 'wf-auto per-cycle WF chain');
727
- requireText('Harness/WF-AUTO.md', 'reflector PASS', 'wf-auto reflector gate');
728
- requireText('Harness/WF-AUTO-SPARK.md', 'Inherited Execution Chain', 'wf-auto-spark inherited execution chain');
729
- requireText('Harness/WF-AUTO-SPARK.md', 'External spark search replaces discovery only', 'wf-auto-spark discovery-only inheritance');
730
- requireText('Harness/WF-AUTO-SPARK.md', 'reflector PASS', 'wf-auto-spark reflector gate');
731
- requireText('Harness/WF-AUTO-SPARK.md', 'task-scribe formats task-state writes', 'wf-auto-spark task-scribe recorder delegation');
732
- requireText('Harness/WF-AUTO-SPARK.md', 'searchFallback: ceo-direct', 'wf-auto-spark search fallback');
733
- requireText('Harness/WF-AUTO-SPARK.md', 'pre-implementation triage', 'wf-auto-spark reflector escalation');
734
- requireText('Harness/WF-AUTO-SPARK.md', 'Literal anti-pattern matching', 'wf-auto-spark anti-pattern invariant guard');
735
- requireText('Harness/WF-AUTO-SPARK.md', 'node scripts/build-version.mjs --check', 'wf-auto-spark checksum drift guard');
736
- requireText('Harness/WF-MAX.md', 'process-file delegation is the default', 'WF-MAX task-scribe process-file delegation default');
737
- requireText('Harness/MEMORY_PROTOCOL.md', 'Scenario Memory Hints', 'memory protocol scenario hints');
738
- requireText('Harness/MEMORY_PROTOCOL.md', 'WF closeout', 'memory protocol WF closeout injection row');
739
- requireText('Harness/MEMORY_PROTOCOL.md', 'memory-master owns writes', 'memory protocol memory write ownership');
740
- requireText('Harness/TDD-GUIDE.md', 'Browser/UI Acceptance TDD Gate', 'browser UI acceptance TDD gate');
741
- requireText('Harness/TDD-GUIDE.md', 'syntax-only', 'syntax-only acceptance prohibition');
742
- requireText('Harness/TDD-GUIDE.md', 'Playwright/CDP', 'Playwright/CDP acceptance requirement');
743
- requireText('Harness/TDD-GUIDE.md', 'AC-by-AC result matrix', 'AC-by-AC TDD evidence matrix');
939
+ requireText('Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'PRD-GATE', 'acceptance PRD gate');
940
+ requireText('Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'AC-GATE', 'acceptance AC gate');
941
+ requireText('Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'Acceptance Result', 'acceptance result matrix');
942
+ requireText('Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'Syntax-only checks', 'syntax-only checks are not browser acceptance evidence');
943
+ requireText('Harness/specs/protocols/AGENT_ISOLATION.md', 'implementer', 'agent isolation implementer rule');
944
+ requireText('Harness/specs/protocols/HARNESS_BRIDGE.md', 'Network Trace Collector', 'harness bridge network trace collector');
945
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Intent Checkpoint', 'wf-auto intent checkpoint');
946
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Adaptive Coverage Exhaustion Gate', 'wf-auto adaptive coverage exhaustion gate');
947
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'dynamic high-risk obligations', 'wf-auto dynamic high-risk obligations');
948
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'two different confirmation strategies', 'wf-auto two different confirmation strategies');
949
+ requireText('Harness/specs/workflows/WF-AUTO-ANGLES.md', 'Selection algorithm', 'wf-auto adaptive angle selection');
950
+ requireText('Harness/specs/workflows/WF-AUTO-ANGLES.md', 'Dynamic obligations', 'wf-auto dynamic obligations');
951
+ requireText('Harness/specs/workflows/WF-AUTO-ANGLES.md', 'Common probe recipes', 'wf-auto common probe recipes');
952
+ forbidText('Harness/specs/workflows/WF-AUTO.md', 'All 8 exhausted', 'stale eight-angle exhaustion rule');
953
+ forbidText('Harness/specs/workflows/WF-AUTO.md', '3 consecutive all-exhausted rounds', 'stale three-consecutive-all-exhausted stop rule');
954
+ forbidText('Harness/specs/workflows/WF-AUTO.md', '8-Angle Exhaustion Gate', 'stale eight-angle exhaustion gate name');
955
+ forbidText('Harness/specs/workflows/WF-AUTO-SPARK.md', '8 parallel external searches', 'stale eight-spark fixed-count search');
956
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Inherited WF/WF-MAX Constraints', 'wf-auto inherited WF/WF-MAX constraints');
957
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Mini PRD -> AC IDs -> test/validation plan -> implementer -> verifier', 'wf-auto per-cycle WF chain');
958
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'reflector PASS', 'wf-auto reflector gate');
959
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Manual or benchmark-driven single-cycle', 'wf-auto bounded single-cycle tick contract');
960
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Harness/tasks/auto/PLAN.md', 'wf-auto bounded tick PLAN record');
961
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Harness/tasks/auto/PROGRESS.md', 'wf-auto bounded tick PROGRESS record');
962
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'Inherited Execution Chain', 'wf-auto-spark inherited execution chain');
963
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'External spark search replaces discovery only', 'wf-auto-spark discovery-only inheritance');
964
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'reflector PASS', 'wf-auto-spark reflector gate');
965
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'task-scribe formats task-state writes', 'wf-auto-spark task-scribe recorder delegation');
966
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'searchFallback: ceo-direct', 'wf-auto-spark search fallback');
967
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'pre-implementation triage', 'wf-auto-spark reflector escalation');
968
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'Literal anti-pattern matching', 'wf-auto-spark anti-pattern invariant guard');
969
+ requireText('Harness/specs/workflows/WF-AUTO-SPARK.md', 'node scripts/build-version.mjs --check', 'wf-auto-spark checksum drift guard');
970
+ requireText('Harness/specs/workflows/WF-MAX.md', 'process-file delegation is the default', 'WF-MAX task-scribe process-file delegation default');
971
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'Scenario Memory Hints', 'memory protocol scenario hints');
972
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'WF closeout', 'memory protocol WF closeout injection row');
973
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'memory-master owns writes', 'memory protocol memory write ownership');
974
+ requireText('Harness/specs/protocols/TDD-GUIDE.md', 'Browser/UI Acceptance TDD Gate', 'browser UI acceptance TDD gate');
975
+ requireText('Harness/specs/protocols/TDD-GUIDE.md', 'syntax-only', 'syntax-only acceptance prohibition');
976
+ requireText('Harness/specs/protocols/TDD-GUIDE.md', 'Playwright/CDP', 'Playwright/CDP acceptance requirement');
977
+ requireText('Harness/specs/protocols/TDD-GUIDE.md', 'AC-by-AC result matrix', 'AC-by-AC TDD evidence matrix');
744
978
  requireText('Harness/tasks/_template/PLAN.md', 'Expanded evidence required when triggered', 'task template expanded evidence trigger');
745
979
  requireText('Harness/templates/TEST_PLAN.template.md', 'syntax-only checks', 'test plan template syntax-only prohibition');
746
- requireText('.claude/skills/tdd/SKILL.md', 'Harness/ACCEPTANCE_PROTOCOL.md', 'tdd skill loads acceptance protocol');
747
- requireText('.claude/skills/tdd/SKILL.md', 'Harness/HARNESS_BRIDGE.md', 'tdd skill loads harness bridge');
980
+ requireText('.claude/skills/tdd/SKILL.md', 'Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'tdd skill loads acceptance protocol');
981
+ requireText('.claude/skills/tdd/SKILL.md', 'Harness/specs/protocols/HARNESS_BRIDGE.md', 'tdd skill loads harness bridge');
748
982
  requireText('.claude/skills/tdd/SKILL.md', 'No syntax-only acceptance', 'tdd skill forbids syntax-only acceptance');
749
983
  requireText('.claude/skills/wf-remove/SKILL.md', 'User-facing removal is the slash/skill command', 'wf-remove slash command is user-facing');
750
984
  requireText('.claude/skills/wf-remove/SKILL.md', 'agent-internal execution steps', 'wf-remove script commands are agent-internal');
751
985
  requireText('.claude/skills/wf-remove/SKILL.md', 'verify residual discovery folders', 'wf-remove residual discovery verification');
986
+ forbidText('Harness/scripts/wf-remove.mjs', '\uFFFD', 'replacement character in wf-remove output');
987
+ for (const marker of ['codebase-explorer', 'task-scribe', 'wf-agents-docs', 'wf-auto-spark']) {
988
+ requireText('Harness/scripts/wf-remove.mjs', marker, `wf-remove built-in registry includes ${marker}`);
989
+ }
752
990
  requireText('.claude/skills/wf-review/SKILL.md', 'opencode run --agent reviewer', 'wf-review OpenCode peer CLI path');
753
991
  requireText('.claude/skills/wf-review/SKILL.md', 'Role: reviewer', 'wf-review installed reviewer role fallback');
754
992
  requireText('.claude/skills/wf-review/SKILL.md', 'The main agent is the controller', 'wf-review controller final authority');
993
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'claude -p --output-format json', 'wf-agents-docs Claude JSON CLI path');
994
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'codex exec --json', 'wf-agents-docs Codex JSONL CLI path');
995
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'opencode run --format json', 'wf-agents-docs OpenCode JSON CLI path');
996
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'cache_read_input_tokens', 'wf-agents-docs Claude cache telemetry field');
997
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'Do not trust exit code alone', 'wf-agents-docs PowerShell wrapper guard');
998
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'Evidence-Packet Review Pattern', 'wf-agents-docs evidence-packet review method');
999
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'No Scratch-File Rule', 'wf-agents-docs scratch-file guard');
1000
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'Subagent Output Contract', 'wf-agents-docs subagent output contract');
1001
+ requireText('.claude/skills/wf-agents-docs/SKILL.md', 'Do not write CLI probe output under `%TEMP%`', 'wf-agents-docs temp pollution guard');
1002
+ requireText('Harness/README.md', 'Need peer CLI automation docs', 'Harness router peer CLI automation docs row');
755
1003
  requireText('.opencode/commands/wf-review.md', 'peer-review contract', 'OpenCode wf-review wrapper peer-review contract');
756
- requireText('Harness/subagents.md', 'For `/wf-review`, use the installed `reviewer` role', 'subagents wf-review role fallback');
1004
+ requireText('Harness/specs/runtime/subagents.md', 'For `/wf-review`, use the installed `reviewer` role', 'subagents wf-review role fallback');
757
1005
  requireText('.claude/agents/tdd-guide.md', 'Browser Acceptance Rules', 'tdd-guide browser acceptance rules');
758
1006
  requireText('.claude/agents/tdd-guide.md', 'real user actions', 'tdd-guide real user action requirement');
759
- requireText('.claude/agents/test-writer.md', 'Harness/ACCEPTANCE_PROTOCOL.md', 'test-writer loads acceptance protocol');
1007
+ requireText('.claude/agents/test-writer.md', 'Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md', 'test-writer loads acceptance protocol');
760
1008
  requireText('.claude/agents/test-writer.md', 'real user-path test', 'test-writer real user path requirement');
761
1009
  requireText('.claude/agents/test-writer.md', 'network URL, method, payload', 'test-writer network assertion requirement');
762
1010
  requireText('.claude/agents/reflector.md', 'PASS, RETURN_TO_DEBUG, or BLOCKED', 'reflector verdict contract');
763
- requireText('Harness/DEBUG_PROTOCOL.md', 'Layer Classification', 'debug layer classification');
764
- requireText('Harness/MEMORY_PROTOCOL.md', 'AC ID', 'memory AC traceability');
765
- requireText('Harness/subagents.md', 'Max parallelism', 'subagents max parallelism row');
766
- requireText('Harness/dispatch.md', 'Concurrency group', 'dispatch concurrency group field');
767
- requireText('Harness/dispatch.md', 'File claim', 'dispatch file claim field');
1011
+ requireText('Harness/specs/protocols/DEBUG_PROTOCOL.md', 'Layer Classification', 'debug layer classification');
1012
+ requireText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'AC ID', 'memory AC traceability');
1013
+ requireText('Harness/specs/runtime/subagents.md', 'Max parallelism', 'subagents max parallelism row');
1014
+ requireText('Harness/specs/runtime/dispatch.md', 'Concurrency group', 'dispatch concurrency group field');
1015
+ requireText('Harness/specs/runtime/dispatch.md', 'File claim', 'dispatch file claim field');
768
1016
  requireText('CLAUDE.md', '/wf-max', 'wf max startup instruction');
769
1017
  requireText('.claude/commands/wf-help.md', 'Do not invoke a skill', 'wf-help direct command boundary');
770
1018
  requireText('.claude/commands/wf-help.md', '| `/wf-help` |', 'wf-help command row');
771
1019
  requireText('.claude/commands/wf-help.md', '| `/wf-max <task>` |', 'wf-help wf-max row');
772
1020
  requireText('.claude/commands/wf-help.md', '| `/wf-auto` |', 'wf-help wf-auto row');
773
1021
  requireText('.claude/commands/wf-help.md', '| `/wf-readme <task>` |', 'wf-help wf-readme row');
1022
+ forbidText('.claude/commands/wf-help.md', '.opencode/skills/', 'wf-help nonexistent OpenCode skills directory claim');
1023
+ forbidText('.opencode/commands/wf-help.md', '.opencode/skills/', 'OpenCode wf-help nonexistent skills directory claim');
774
1024
  requireText('Harness/README.md', '## Direct Commands', 'direct commands section');
775
1025
  requireText('Harness/README.md', '.claude/commands/wf-help.md', 'wf-help router reference');
776
- requireText('Harness/WF-MAX.md', 'three-layer architecture', 'CLAUDE.md three-layer role architecture');
777
- requireText('Harness/README.md', 'no runtime hook state', 'CLAUDE no-hook role enforcement statement');
778
- requireText('Harness/README.md', 'no runtime hook state', 'README no-hook runtime statement');
779
- requireText('Harness/WF-AUTO.md', 'WF-AUTO Hook Exception', 'wf-auto-only hook exception');
780
- requireText('Harness/WF-AUTO.md', 'only `/wf-auto` may use a runtime hook', 'wf-auto-only hook boundary');
1026
+ requireText('Harness/specs/workflows/WF-MAX.md', 'three-layer architecture', 'CLAUDE.md three-layer role architecture');
1027
+ requireText('Harness/README.md', 'role enforcement has no runtime hook state', 'CLAUDE no-hook role enforcement statement');
1028
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'Runtime Hook Boundaries', 'runtime hook boundaries');
1029
+ requireText('Harness/specs/workflows/WF-AUTO.md', 'only `/wf-auto` may use a runtime hook to drive auto-optimization', 'wf-auto-only hook boundary');
781
1030
  forbidText('CLAUDE.md', 'Enforced by hooks', 'WF-MAX hook enforcement claim');
782
- forbidText('Harness/WF.md', 'MUST use at least 3 distinct role passes', 'old WF role-pass minimum');
783
- forbidText('Harness/WF.md', 'at least three distinct role passes', 'old WF role-pass wording');
784
- forbidText('Harness/dispatch.md', 'requires ≥3 distinct subagents', 'old dispatch WF role minimum');
1031
+ forbidText('Harness/specs/workflows/WF.md', 'MUST use at least 3 distinct role passes', 'old WF role-pass minimum');
1032
+ forbidText('Harness/specs/workflows/WF.md', 'at least three distinct role passes', 'old WF role-pass wording');
1033
+ forbidText('Harness/specs/runtime/dispatch.md', 'requires ≥3 distinct subagents', 'old dispatch WF role minimum');
785
1034
  forbidText('CLAUDE.md', 'WF-MAX hooks', 'WF-MAX hook enforcement claim');
786
1035
  forbidText('Harness/README.md', 'PreToolUse hook', 'WF-MAX PreToolUse hook claim');
787
1036
  forbidText('Harness/README.md', 'SessionStart hook', 'WF-MAX SessionStart hook claim');
788
1037
  forbidText('Harness/README.md', 'hook-managed', 'hook-managed runtime claim');
789
1038
  forbidText('Harness/README.md', 'HOOK_PROTOCOL.md', 'removed hook protocol reference');
790
- forbidText('Harness/WF-AUTO.md', 'Hook-Assisted Long Loop', 'wf-auto hook loop section');
791
- forbidText('Harness/dispatch.md', 'removes the cap entirely', 'unbounded runtime-cap claim');
792
- forbidText('Harness/WF-MAX.md', 'no hard agent cap; recursion governed', 'unbounded agent-cap claim');
793
- forbidText('Harness/MEMORY_PROTOCOL.md', 'Hooks may', 'hook-triggered memory claim');
1039
+ forbidText('Harness/specs/workflows/WF-AUTO.md', 'Hook-Assisted Long Loop', 'wf-auto hook loop section');
1040
+ forbidText('Harness/specs/runtime/dispatch.md', 'removes the cap entirely', 'unbounded runtime-cap claim');
1041
+ forbidText('Harness/specs/workflows/WF-MAX.md', 'no hard agent cap; recursion governed', 'unbounded agent-cap claim');
1042
+ forbidText('Harness/specs/protocols/MEMORY_PROTOCOL.md', 'Hooks may', 'hook-triggered memory claim');
794
1043
  forbidText('.claude/settings.json', 'wf-mode-hook.mjs', 'Claude WF hook command registration');
795
1044
  forbidText('.codex/hooks.json', 'wf-mode-hook.mjs', 'Codex WF hook command registration');
796
1045
  const codexHookConfig = read('.codex/hooks.json');
797
1046
  const claudeSettings = read('.claude/settings.json');
798
1047
  requireText('.codex/hooks.json', '"SessionStart"', 'Codex startup-only update hook');
799
1048
  requireText('.claude/settings.json', '"SessionStart"', 'Claude startup-only update hook');
800
- forbidText('.codex/hooks.json', 'UserPromptSubmit', 'Codex turn-by-turn update hook');
1049
+ forbidText('.codex/hooks.json', 'UserPromptSubmit', 'Codex turn-by-turn status hook');
1050
+ forbidText('.codex/hooks.json', '"Stop"', 'Codex stop status hook');
1051
+ forbidText('.codex/hooks.json', 'wf-status.mjs', 'removed Codex WF status script');
801
1052
  forbidText('.claude/settings.json', 'UserPromptSubmit', 'Claude turn-by-turn update hook');
802
1053
  requireText('.opencode/plugins/harness-wf-status.mjs', 'opencode.startup', 'OpenCode startup-only update check');
803
1054
  forbidText('.opencode/plugins/harness-wf-status.mjs', "'chat.message'", 'OpenCode turn-by-turn update hook');
804
- if (codexHookConfig && !codexHookConfig.includes('wf-auto')) {
805
- errors.push('.codex/hooks.json may only exist for a wf-auto hook configuration');
1055
+ if (codexHookConfig && !codexHookConfig.includes('wf-auto-update-prompt.mjs')) {
1056
+ errors.push('.codex/hooks.json missing startup wf-auto update check');
806
1057
  }
807
1058
  if (claudeSettings.includes('"hooks"') && !claudeSettings.includes('wf-auto')) {
808
1059
  errors.push('.claude/settings.json hooks may only be used for wf-auto');
809
1060
  }
810
- if (read('.codex/config.toml').includes('hooks = true') && !codexHookConfig.includes('wf-auto')) {
811
- errors.push('.codex/config.toml may enable hooks only with a wf-auto hook configuration');
1061
+ if (read('.codex/config.toml').includes('hooks = true') && !codexHookConfig.includes('wf-auto-update-prompt.mjs')) {
1062
+ errors.push('.codex/config.toml may enable hooks only with a Harness hook configuration');
812
1063
  }
813
1064
 
814
1065
  // AGENTS.md must be a thin shim — no Harness/WF-MAX/command table content
@@ -821,24 +1072,65 @@ requireText('AGENTS.md', 'compatibility entry', 'AGENTS.md Codex shim purpose');
821
1072
  requireText('AGENTS.md', 'single source', 'AGENTS.md CLAUDE.md as single source');
822
1073
 
823
1074
  // Implicit WF trigger phrases forbidden in active runtime docs and SETUP
824
- for (const rel of ['Harness/README.md', 'Harness/WF.md', 'Harness/context-loading.md', 'Harness/subagents.md', 'Harness/SETUP.md']) {
1075
+ for (const rel of ['Harness/README.md', 'Harness/specs/workflows/WF.md', 'Harness/specs/runtime/context-loading.md', 'Harness/specs/runtime/subagents.md', 'Harness/specs/guides/SETUP.md']) {
825
1076
  forbidText(rel, 'workflow mode', 'implicit WF trigger: workflow mode');
826
1077
  forbidText(rel, 'wk mode', 'implicit WF trigger: wk mode');
827
1078
  }
828
1079
  // SETUP must not require old WF complete-role-chain contract
829
- requireText('Harness/SETUP.md', 'explicit WF entry only', 'SETUP explicit WF entry contract');
830
- requireText('Harness/SETUP.md', 'WF-Light', 'SETUP WF-Light tier reference');
831
- requireText('Harness/SETUP.md', 'WF-Max-Useful', 'SETUP WF-Max-Useful tier reference');
1080
+ requireText('Harness/specs/guides/SETUP.md', 'explicit WF entry only', 'SETUP explicit WF entry contract');
1081
+ requireText('Harness/specs/guides/SETUP.md', 'WF-Light', 'SETUP WF-Light tier reference');
1082
+ requireText('Harness/specs/guides/SETUP.md', 'WF-Max-Useful', 'SETUP WF-Max-Useful tier reference');
832
1083
 
833
1084
  // Memory Preflight markers required in workflow skill adapters
834
- for (const skill of ['wf', 'wf-max', 'subagent-orchestrator']) {
1085
+ for (const skill of ['wf', 'wf-max', 'wf-auto', 'subagent-orchestrator']) {
835
1086
  requireText(`.claude/skills/${skill}/SKILL.md`, 'Memory Preflight', `${skill} skill Memory Preflight section`);
836
1087
  }
1088
+ for (const skill of cacheDisciplinedSkills) {
1089
+ requireText(`.claude/skills/${skill}/SKILL.md`, 'Cache Discipline', `${skill} skill cache discipline`);
1090
+ }
1091
+ requireText('.claude/skills/wf-auto/SKILL.md', 'bounded test tick', 'wf-auto skill bounded tick auto capsule rule');
1092
+ requireText('.claude/skills/wf-auto/SKILL.md', 'missing auto capsule evidence', 'wf-auto skill missing auto capsule failure rule');
1093
+ requireText('.claude/skills/wf-auto/SKILL.md', 'Harness/tasks/auto/PLAN.md', 'wf-auto skill PLAN record');
1094
+ requireText('.claude/skills/wf-auto/SKILL.md', 'Harness/tasks/auto/PROGRESS.md', 'wf-auto skill PROGRESS record');
1095
+ requireText('.claude/skills/wf-auto/SKILL.md', 'evidence ledger path/summary', 'wf-auto skill return evidence ledger path');
1096
+ if (fs.existsSync(path.join(root, '.claude/skills/wf-browser/SKILL.md'))) {
1097
+ requireText('.claude/skills/wf-browser/SKILL.md', 'Cache Discipline', 'wf-browser skill cache discipline');
1098
+ requireText('.claude/skills/wf-browser/SKILL.md', 'Browser Evidence Contract', 'wf-browser browser evidence contract');
1099
+ requireText('.claude/skills/wf-browser/SKILL.md', 'Controllable UI Contract', 'wf-browser controllable UI contract');
1100
+ requireText('.claude/skills/wf-browser/SKILL.md', 'Browser Use CLI', 'wf-browser current Browser Use CLI guidance');
1101
+ requireText('.claude/skills/wf-browser/SKILL.md', 'browser-use --doctor', 'wf-browser Browser Use doctor command');
1102
+ requireText('.claude/skills/wf-browser/SKILL.md', 'browser-use skill', 'wf-browser Browser Use skill command');
1103
+ requireText('.claude/skills/wf-browser/SKILL.md', 'new_tab("https://example.com")', 'wf-browser Browser Use script-style example');
1104
+ requireText('.claude/skills/wf-browser/SKILL.md', 'capture_screenshot', 'wf-browser Browser Use screenshot helper');
1105
+ requireText('.claude/skills/wf-browser/SKILL.md', 'old `browser-use open/state/click/screenshot/input/wait` subcommands are removed', 'wf-browser removed old Browser Use subcommands');
1106
+ requireText('.claude/skills/wf-browser/SKILL.md', 'data-testid', 'wf-browser data-testid guidance');
1107
+ requireText('.claude/skills/wf-browser/SKILL.md', 'accessible labels/roles', 'wf-browser accessible selector guidance');
1108
+ requireText('.claude/skills/wf-browser/SKILL.md', 'inputs, buttons, filters, rows, empty/error/loading states', 'wf-browser controllable UI coverage targets');
1109
+ }
837
1110
 
838
1111
  // Direct command checks
839
1112
  requireText('Harness/README.md', '/wf-update', 'wf-update direct command reference');
840
1113
  requireText('.claude/commands/wf-update.md', 'Do not invoke a skill', 'wf-update direct command boundary');
1114
+ for (const rel of ['.claude/commands/wf-update.md', '.opencode/commands/wf-update.md']) {
1115
+ requireText(rel, '## Cache Discipline', `${rel} cache discipline`);
1116
+ requireText(rel, 'agent.safeApplyCommand', `${rel} safe apply step`);
1117
+ requireText(rel, '--apply-safe', `${rel} apply-safe command`);
1118
+ requireText(rel, 'agent.aiMergeRequired', `${rel} AI merge step`);
1119
+ requireText(rel, '--accept-local', `${rel} accept-local decision`);
1120
+ requireText(rel, '--accept-merged', `${rel} accept-merged decision`);
1121
+ requireText(rel, '--accept-template', `${rel} accept-template decision`);
1122
+ requireText(rel, '--finalize', `${rel} finalize command`);
1123
+ requireText(rel, 'strict `--apply` only when', `${rel} strict apply boundary`);
1124
+ requireText(rel, '## Return', `${rel} return contract`);
1125
+ requireText(rel, 'agent.releaseHighlights', `${rel} release highlights summary`);
1126
+ requireText(rel, 'releaseNotes.highlights', `${rel} release notes fallback`);
1127
+ }
1128
+ requireText('Harness/scripts/wf-update-check.mjs', 'releaseHighlights', 'wf-update-check release highlights metadata');
1129
+ requireText('Harness/scripts/wf-update-check.mjs', 'updateReportRequired', 'wf-update-check user update report requirement');
841
1130
  requireText('.claude/commands/wf-help.md', 'direct command', 'wf-help wf-update direct command classification');
1131
+ requireText('.claude/commands/wf-help.md', '$wf-help', 'wf-help Codex compatibility usage');
1132
+ requireText('.claude/commands/wf-help.md', '/wf-browser', 'wf-help built-in browser workflow row');
1133
+ requireText('.opencode/commands/wf-help.md', '/wf-browser', 'OpenCode wf-help built-in browser workflow row');
842
1134
 
843
1135
  // wf-update skill must NOT claim Claude Code /wf-update as a skill invocation
844
1136
  // (allow mentions in the description/body that say "direct command" — those are correct)
@@ -853,6 +1145,10 @@ for (const command of opencodeWorkflowCommands) {
853
1145
  requireText(`.opencode/commands/${command}.md`, 'workflow command', `OpenCode ${command} workflow command classification`);
854
1146
  requireText(`.opencode/commands/${command}.md`, `.claude/skills/${command}/SKILL.md`, `OpenCode ${command} wrapper skill routing`);
855
1147
  requireText(`.opencode/commands/${command}.md`, 'Harness/MEMORY.md', `OpenCode ${command} wrapper router load`);
1148
+ requireText(`.opencode/commands/${command}.md`, 'Cache-First Context Contract', `OpenCode ${command} cache-first routing`);
1149
+ }
1150
+ if (fs.existsSync(path.join(root, '.opencode/commands/wf-browser.md'))) {
1151
+ requireText('.opencode/commands/wf-browser.md', 'Cache-First Context Contract', 'OpenCode wf-browser cache-first routing');
856
1152
  }
857
1153
 
858
1154
  // wf-max adapter must stay tier-aware, not the old unconditional contract
@@ -866,31 +1162,33 @@ requireText('.claude/agents/codebase-explorer.md', 'model: haiku', 'codebase-exp
866
1162
  requireText('.claude/agents/codebase-explorer.md', 'read-only', 'codebase-explorer read-only contract');
867
1163
 
868
1164
  // WF-KERNEL.md required invariants
869
- requireText('Harness/WF-KERNEL.md', 'Ready-Queue', 'WF-KERNEL ready-queue section');
870
- requireText('Harness/WF-KERNEL.md', 'Role / Model Matrix', 'WF-KERNEL role-model matrix');
871
- requireText('Harness/WF-KERNEL.md', 'Dispatch Packet', 'WF-KERNEL dispatch packet format');
872
- requireText('Harness/WF-KERNEL.md', 'Task Type', 'WF-KERNEL task type routing');
873
- requireText('Harness/WF-KERNEL.md', 'small-fast', 'WF-KERNEL small-fast tier mapping');
1165
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Ready-Queue', 'WF-KERNEL ready-queue section');
1166
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Role / Model Matrix', 'WF-KERNEL role-model matrix');
1167
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Dispatch Packet', 'WF-KERNEL dispatch packet format');
1168
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Task Type', 'WF-KERNEL task type routing');
1169
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'small-fast', 'WF-KERNEL small-fast tier mapping');
874
1170
 
875
1171
  // WF variants reference WF-KERNEL
876
- requireText('Harness/WF.md', 'WF-KERNEL.md', 'WF.md references WF-KERNEL');
877
- requireText('Harness/WF-MAX.md', 'WF-KERNEL.md', 'WF-MAX.md references WF-KERNEL');
878
- requireText('Harness/WF-MAX.md', 'probe-worker-channels.mjs', 'WF-MAX references worker-channel probe (M3 anti-regression)');
1172
+ requireText('Harness/specs/workflows/WF.md', 'WF-KERNEL.md', 'WF.md references WF-KERNEL');
1173
+ requireText('Harness/specs/workflows/WF-MAX.md', 'WF-KERNEL.md', 'WF-MAX.md references WF-KERNEL');
1174
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Use proven channels only', 'WF-MAX proven-channel degradation rule');
1175
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Evidence-Packet', 'WF-MAX evidence-packet channel record');
1176
+ requireText('Harness/specs/workflows/WF-MAX.md', 'Do not create ad hoc probe scripts', 'WF-MAX no ad hoc probe scripts');
879
1177
 
880
1178
  // Tier-aware acceptance: WF-Light must NOT require global cross-review/reflector
881
- requireText('Harness/WF.md', 'Cross-review and reflector NOT mandatory', 'WF.md WF-Light no mandatory cross-review');
882
- requireText('Harness/WF-KERNEL.md', 'Cross-review and reflector are NOT mandatory', 'WF-KERNEL WF-Light no mandatory cross-review');
1179
+ requireText('Harness/specs/workflows/WF.md', 'Cross-review and reflector NOT mandatory', 'WF.md WF-Light no mandatory cross-review');
1180
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'Cross-review and reflector are NOT mandatory', 'WF-KERNEL WF-Light no mandatory cross-review');
883
1181
 
884
1182
  // WF-MAX: maximum safe fan-out, not unconditional
885
- requireText('Harness/WF-MAX.md', 'maximum safe fan-out', 'WF-MAX maximum safe fan-out');
886
- requireText('Harness/WF-MAX.md', 'WF-Max-Useful', 'WF-MAX useful fan-out mode');
887
- requireText('Harness/WF-MAX.md', 'WF-Max-Strict', 'WF-MAX strict override mode');
1183
+ requireText('Harness/specs/workflows/WF-MAX.md', 'maximum safe fan-out', 'WF-MAX maximum safe fan-out');
1184
+ requireText('Harness/specs/workflows/WF-MAX.md', 'WF-Max-Useful', 'WF-MAX useful fan-out mode');
1185
+ requireText('Harness/specs/workflows/WF-MAX.md', 'WF-Max-Strict', 'WF-MAX strict override mode');
888
1186
 
889
1187
  // task-scribe is task-state write exception
890
- requireText('Harness/subagents.md', 'task-scribe', 'subagents.md task-scribe registration');
891
- requireText('Harness/dispatch.md', 'task-scribe', 'dispatch.md task-scribe roster');
892
- requireText('Harness/WF-KERNEL.md', 'task-scribe', 'WF-KERNEL task-scribe ownership');
893
- forbidText('Harness/WF-KERNEL.md', 'Only the main agent writes task', 'old single-writer rule');
1188
+ requireText('Harness/specs/runtime/subagents.md', 'task-scribe', 'subagents.md task-scribe registration');
1189
+ requireText('Harness/specs/runtime/dispatch.md', 'task-scribe', 'dispatch.md task-scribe roster');
1190
+ requireText('Harness/specs/workflows/WF-KERNEL.md', 'task-scribe', 'WF-KERNEL task-scribe ownership');
1191
+ forbidText('Harness/specs/workflows/WF-KERNEL.md', 'Only the main agent writes task', 'old single-writer rule');
894
1192
 
895
1193
  // Old WF contract must not return to hot-path docs:
896
1194
  // /wf is WF-KERNEL tiered orchestration, not a default complete role chain;
@@ -899,16 +1197,16 @@ const hotPathDocs = [
899
1197
  'CLAUDE.md',
900
1198
  'Harness/MEMORY.md',
901
1199
  'Harness/README.md',
902
- 'Harness/WF.md',
903
- 'Harness/WF-MAX.md',
904
- 'Harness/WF-AUTO.md',
905
- 'Harness/WF-AUTO-SPARK.md',
906
- 'Harness/WF-KERNEL.md',
907
- 'Harness/agent-workflow.md',
908
- 'Harness/dispatch.md',
909
- 'Harness/subagents.md',
910
- 'Harness/context-loading.md',
911
- 'Harness/ACCEPTANCE_PROTOCOL.md',
1200
+ 'Harness/specs/workflows/WF.md',
1201
+ 'Harness/specs/workflows/WF-MAX.md',
1202
+ 'Harness/specs/workflows/WF-AUTO.md',
1203
+ 'Harness/specs/workflows/WF-AUTO-SPARK.md',
1204
+ 'Harness/specs/workflows/WF-KERNEL.md',
1205
+ 'Harness/specs/runtime/agent-workflow.md',
1206
+ 'Harness/specs/runtime/dispatch.md',
1207
+ 'Harness/specs/runtime/subagents.md',
1208
+ 'Harness/specs/runtime/context-loading.md',
1209
+ 'Harness/specs/protocols/ACCEPTANCE_PROTOCOL.md',
912
1210
  ];
913
1211
  for (const rel of hotPathDocs) {
914
1212
  forbidText(rel, 'complete role chain mandatory', 'old /wf default complete-role-chain contract');
@@ -919,16 +1217,80 @@ for (const rel of hotPathDocs) {
919
1217
  }
920
1218
 
921
1219
  // WF-STATE.md and TASK_ARCHIVE.md must be reachable from the entry router
922
- requireText('CLAUDE.md', 'Harness/WF-STATE.md', 'CLAUDE.md WF-STATE routing');
923
- requireText('CLAUDE.md', 'Harness/TASK_ARCHIVE.md', 'CLAUDE.md TASK_ARCHIVE routing');
1220
+ requireText('CLAUDE.md', 'Harness/specs/workflows/WF-STATE.md', 'CLAUDE.md WF-STATE routing');
1221
+ requireText('CLAUDE.md', 'Harness/specs/protocols/TASK_ARCHIVE.md', 'CLAUDE.md TASK_ARCHIVE routing');
924
1222
  requireText('Harness/README.md', 'WF-STATE.md', 'README WF-STATE routing');
925
- requireText('Harness/TASK_ARCHIVE.md', 'archive-tasks.mjs', 'TASK_ARCHIVE script reference');
1223
+ requireText('Harness/specs/protocols/TASK_ARCHIVE.md', 'archive-tasks.mjs', 'TASK_ARCHIVE script reference');
1224
+ requireText('Harness/specs/workflows/WF-STATE.md', 'task-state.mjs', 'WF-STATE task-state script contract');
1225
+ requireText('Harness/specs/protocols/TASK_ARCHIVE.md', 'task-state.mjs', 'TASK_ARCHIVE task-state script reference');
1226
+
1227
+ // Task-state consistency: prompt text is not authoritative for machine state.
1228
+ const rootTaskProgress = parseRootTaskProgress(read('Harness/PROGRESS.md'));
1229
+ const outerTaskSet = new Set(taskDirs.filter(name => !TASK_RESERVED.has(name) && !name.startsWith('_')));
1230
+ const rootTaskRows = new Set(rootTaskProgress.rows.map(row => row.id));
1231
+
1232
+ if (rootTaskProgress.activeTask && !outerTaskSet.has(rootTaskProgress.activeTask)) {
1233
+ taskStateIssue(`Harness/PROGRESS.md Active Task "${rootTaskProgress.activeTask}" does not exist under Harness/tasks/`);
1234
+ }
1235
+
1236
+ for (const row of rootTaskProgress.rows) {
1237
+ if (!outerTaskSet.has(row.id)) {
1238
+ taskStateIssue(`Harness/PROGRESS.md Task Index row has no outer task directory: ${row.id}`);
1239
+ }
1240
+ if (row.phase && !normalizeTaskPhase(row.phase)) {
1241
+ taskStateIssue(`Harness/PROGRESS.md Task Index row has unknown phase for ${row.id}: ${row.phase}`);
1242
+ }
1243
+ }
1244
+
1245
+ const activeStateTasks = [];
1246
+ for (const taskDir of outerTaskSet) {
1247
+ if (!rootTaskRows.has(taskDir)) {
1248
+ taskStateIssue(`Harness/tasks/${taskDir}/ is missing from Harness/PROGRESS.md Task Index`);
1249
+ }
1250
+
1251
+ const statePath = path.join(root, 'Harness', 'tasks', taskDir, 'STATE.json');
1252
+ if (!fs.existsSync(statePath)) {
1253
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json is missing; run node Harness/scripts/task-state.mjs reconcile --apply`);
1254
+ continue;
1255
+ }
1256
+
1257
+ let state;
1258
+ try {
1259
+ state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
1260
+ } catch (err) {
1261
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json is invalid JSON: ${err.message}`);
1262
+ continue;
1263
+ }
1264
+
1265
+ if (state.taskId && state.taskId !== taskDir) {
1266
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json taskId "${state.taskId}" does not match directory`);
1267
+ }
1268
+ const status = normalizeTaskStatus(state.status);
1269
+ const phase = normalizeTaskPhase(state.phase);
1270
+ if (state.status && !status) {
1271
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json has unknown status "${state.status}"`);
1272
+ }
1273
+ if (state.phase && !phase) {
1274
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json has unknown phase "${state.phase}"`);
1275
+ }
1276
+ if (status === 'active') activeStateTasks.push(taskDir);
1277
+ if (status === 'active' && taskDir !== rootTaskProgress.activeTask) {
1278
+ taskStateIssue(`Harness/tasks/${taskDir}/STATE.json is active but Harness/PROGRESS.md Active Task is ${rootTaskProgress.activeTask || 'None'}; run node Harness/scripts/task-state.mjs reconcile --apply`);
1279
+ }
1280
+ if (taskDir === rootTaskProgress.activeTask && status && status !== 'active') {
1281
+ taskStateIssue(`Harness/PROGRESS.md Active Task points to ${taskDir}, but STATE.json status is "${status}"; run node Harness/scripts/task-state.mjs reconcile --apply`);
1282
+ }
1283
+ }
1284
+
1285
+ if (activeStateTasks.length > 1) {
1286
+ taskStateIssue(`Multiple STATE.json files are active: ${activeStateTasks.join(', ')}; run node Harness/scripts/task-state.mjs reconcile --apply`);
1287
+ }
926
1288
 
927
- // Outer task capsule cap: keep Harness/tasks/ lean (see Harness/TASK_ARCHIVE.md)
1289
+ // Outer task capsule cap: keep Harness/tasks/ lean (see Harness/specs/protocols/TASK_ARCHIVE.md)
928
1290
  const OUTER_TASK_CAP = 5;
929
1291
  const outerTasks = taskDirs.filter(name => !TASK_RESERVED.has(name) && !name.startsWith('_'));
930
1292
  if (outerTasks.length > OUTER_TASK_CAP) {
931
- const capMsg = `Harness/tasks/ has ${outerTasks.length} outer task capsules (cap ${OUTER_TASK_CAP}); archive completed tasks with node Harness/scripts/archive-tasks.mjs --apply (see Harness/TASK_ARCHIVE.md)`;
1293
+ const capMsg = `Harness/tasks/ has ${outerTasks.length} outer task capsules (cap ${OUTER_TASK_CAP}); archive completed tasks with node Harness/scripts/task-state.mjs archive --apply (compat: node Harness/scripts/archive-tasks.mjs --apply; see Harness/specs/protocols/TASK_ARCHIVE.md)`;
932
1294
  if (strict) errors.push(capMsg);
933
1295
  else console.warn(`Warning: ${capMsg}`);
934
1296
  }