harness-everything 0.25.1 → 0.26.0

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 (32) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +23 -0
  4. package/ci/mechanism-35-verification-contract.test.js +416 -0
  5. package/ci/mutation-check.js +2 -0
  6. package/ci/mutation-loader.js +4 -0
  7. package/docs/audit.md +2 -0
  8. package/harness-everything/SKILL.md +2 -2
  9. package/harness-everything/scripts/verify-gate.js +223 -75
  10. package/hooks/scripts/lib/workflow-isolation.js +3 -4
  11. package/hooks/scripts/state-persist.js +1 -1
  12. package/opencode-plugin/plugin.json +1 -1
  13. package/package.json +1 -1
  14. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  15. package/plugins/harness-everything/hooks/scripts/lib/workflow-isolation.js +3 -4
  16. package/plugins/harness-everything/hooks/scripts/state-persist.js +1 -1
  17. package/plugins/harness-everything/plugin.json +1 -1
  18. package/plugins/harness-everything/scripts/lib/verification-contract.js +357 -0
  19. package/plugins/harness-everything/skills/harness-everything/SKILL.md +2 -2
  20. package/plugins/harness-everything/skills/harness-everything/scripts/verify-gate.js +223 -75
  21. package/plugins/harness-everything/skills/verification-loop/SKILL.md +15 -23
  22. package/plugins/harness-everything/skills/verification-loop/references/project-verification-contract.md +112 -0
  23. package/plugins/harness-everything/skills/verification-loop/references/verification-phases.md +4 -3
  24. package/plugins/harness-everything/skills/verification-loop/references/verify-contract.schema.json +32 -0
  25. package/plugins/harness-everything/skills/verification-loop/references/verify-result.schema.json +65 -0
  26. package/sbom.cdx.json +7 -7
  27. package/scripts/lib/verification-contract.js +357 -0
  28. package/verification-loop/SKILL.md +15 -23
  29. package/verification-loop/references/project-verification-contract.md +112 -0
  30. package/verification-loop/references/verification-phases.md +4 -3
  31. package/verification-loop/references/verify-contract.schema.json +32 -0
  32. package/verification-loop/references/verify-result.schema.json +65 -0
@@ -1,100 +1,248 @@
1
1
  #!/usr/bin/env node
2
+ 'use strict';
3
+
2
4
  /**
3
- * Verification Gatekeeper
4
- * Runs the target project's own mechanical checks (lint/test scripts from the
5
- * nearest package.json) and refuses completion (Exit 1) when any of them fail.
5
+ * Harness project verification gate.
6
6
  *
7
- * Escape hatches, both deliberate:
8
- * - `.verify-fail.tmp` in cwd forces a failure - the hermetic self-regression
9
- * suite uses it to prove the blocking mechanism without needing a real
10
- * broken project.
11
- * - HARNESS_SKIP_PROJECT_CHECKS=1 skips the project-script phase (the tmp-file
12
- * check above still applies). The gate sets it for its own children so a
13
- * project whose test suite itself drives the Harness runtime (like this repo) cannot
14
- * recurse back into the gate.
7
+ * Contract: .harness/verify.json
8
+ * Discovery: read-only candidates; legacy package lint/test remains auto-run.
9
+ * UNCHECKED_* is non-blocking control flow and is never passing-test evidence.
15
10
  */
16
- const { execSync } = require('child_process');
17
11
  const fs = require('fs');
18
12
  const path = require('path');
13
+ const { spawnSync } = require('child_process');
19
14
 
20
- console.log("=== šŸ›”ļø Harness Verification Gate ===");
21
-
22
- // Phase 1: explicit failure injection (test/demo hook).
23
- if (fs.existsSync('.verify-fail.tmp')) {
24
- console.error("āŒ Verification FAILED: .verify-fail.tmp present (injected failure).");
25
- console.error("Please fix the code and run this gate again.");
26
- process.exit(1);
15
+ function loadVerificationModel() {
16
+ for (const candidate of [
17
+ path.resolve(__dirname, '../../scripts/lib/verification-contract.js'),
18
+ path.resolve(__dirname, '../../../scripts/lib/verification-contract.js'),
19
+ ]) {
20
+ if (fs.existsSync(candidate)) return require(candidate);
21
+ }
22
+ throw new Error('verification-contract runtime is missing from this Harness distribution');
27
23
  }
28
24
 
29
- if (process.env.HARNESS_SKIP_PROJECT_CHECKS === '1') {
30
- console.log("ā„¹ļø HARNESS_SKIP_PROJECT_CHECKS=1 - project scripts skipped (recursion guard / hermetic test mode).");
31
- console.log("āœ… Verification SUCCESS (injection check only).");
32
- process.exit(0);
25
+ const {
26
+ detectRoot,
27
+ enumerateSubmoduleRoots,
28
+ findGitRoot,
29
+ readContract,
30
+ } = loadVerificationModel();
31
+
32
+ const jsonMode = process.argv.includes('--json');
33
+ const cwd = path.resolve(process.cwd());
34
+ const repoRoot = findGitRoot(cwd);
35
+
36
+ function emptyReport(status, reason) {
37
+ return {
38
+ status,
39
+ reason,
40
+ repoRoot,
41
+ roots: [],
42
+ checks: [],
43
+ candidates: [],
44
+ };
33
45
  }
34
46
 
35
- // Phase 2: locate the nearest package.json, walking up from cwd but never
36
- // above the enclosing git repository (same scoping rule as tier-router.js).
37
- function findPackageDir() {
38
- let dir = path.resolve(process.cwd());
39
- while (true) {
40
- if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
41
- if (fs.existsSync(path.join(dir, '.git'))) return null; // repo root reached, none found
42
- const parent = path.dirname(dir);
43
- if (parent === dir) return null;
44
- dir = parent;
47
+ function emit(report) {
48
+ if (jsonMode) {
49
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
50
+ return;
51
+ }
52
+
53
+ console.log('=== šŸ›”ļø Harness Verification Gate ===');
54
+ console.log(`Status: ${report.status}`);
55
+ console.log(`Repository: ${report.repoRoot}`);
56
+ if (report.reason) console.log(`Reason: ${report.reason}`);
57
+
58
+ for (const root of report.roots || []) {
59
+ console.log(`\n[${root.status}] ${root.path}`);
60
+ if (root.reason) console.log(` ${root.reason}`);
61
+ for (const check of root.checks || []) {
62
+ const exit = check.exitCode === null ? 'timeout/error' : check.exitCode;
63
+ console.log(` check ${check.id}: ${check.run} (exit ${exit}, ${check.durationMs}ms)`);
64
+ if (check.stdout) process.stdout.write(check.stdout.endsWith('\n') ? check.stdout : check.stdout + '\n');
65
+ if (check.stderr) process.stderr.write(check.stderr.endsWith('\n') ? check.stderr : check.stderr + '\n');
66
+ }
67
+ for (const candidate of root.candidates || []) {
68
+ console.log(` candidate [${candidate.source}]: ${candidate.run}`);
69
+ }
45
70
  }
71
+
72
+ if (report.status === 'UNCHECKED_NO_CHECKS' || report.status === 'UNCHECKED_DISCOVERED') {
73
+ console.log('\nExit code is 0 (non-blocking), but NO COMPLETE AUTOMATED EVIDENCE was produced.');
74
+ console.log('PROHIBITED: Do NOT cite this run as proof that tests pass. Perform the listed or project-declared verification first.');
75
+ } else if (report.status === 'PASSED') {
76
+ console.log('\nāœ… Verification PASSED. Objective project checks completed successfully.');
77
+ } else if (report.status === 'FAILED') {
78
+ console.error('\nāŒ Verification FAILED. Fix the reported check/contract error and run this gate again.');
79
+ } else if (report.status === 'SKIPPED') {
80
+ console.log('ā„¹ļø Project checks skipped by HARNESS_SKIP_PROJECT_CHECKS=1.');
81
+ }
82
+ }
83
+
84
+ function finish(report) {
85
+ emit(report);
86
+ process.exit(report.status === 'FAILED' ? 1 : 0);
87
+ }
88
+
89
+ function statusRank(status) {
90
+ return {
91
+ PASSED: 0,
92
+ UNCHECKED_NO_CHECKS: 1,
93
+ UNCHECKED_DISCOVERED: 2,
94
+ FAILED: 3,
95
+ }[status] ?? 0;
96
+ }
97
+
98
+ function aggregateStatus(roots) {
99
+ if (!roots.length) return 'UNCHECKED_NO_CHECKS';
100
+ return roots.reduce((worst, root) => statusRank(root.status) > statusRank(worst) ? root.status : worst, 'PASSED');
46
101
  }
47
102
 
48
- function detectRunner(dir) {
49
- if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm';
50
- if (fs.existsSync(path.join(dir, 'yarn.lock'))) return 'yarn';
51
- if (fs.existsSync(path.join(dir, 'bun.lockb')) || fs.existsSync(path.join(dir, 'bun.lock'))) return 'bun';
52
- return 'npm';
103
+ function executeCheck(check) {
104
+ const started = Date.now();
105
+ const result = spawnSync(check.run, {
106
+ cwd: check.cwd,
107
+ shell: true,
108
+ encoding: 'utf8',
109
+ windowsHide: true,
110
+ timeout: check.timeoutSec * 1000,
111
+ env: { ...process.env, HARNESS_SKIP_PROJECT_CHECKS: '1' },
112
+ maxBuffer: 16 * 1024 * 1024,
113
+ });
114
+ const durationMs = Date.now() - started;
115
+ const timedOut = !!(result.error && result.error.code === 'ETIMEDOUT');
116
+ const exitCode = typeof result.status === 'number' ? result.status : null;
117
+ return {
118
+ id: check.id,
119
+ run: check.run,
120
+ cwd: check.cwd,
121
+ source: check.source,
122
+ exitCode,
123
+ durationMs,
124
+ timedOut,
125
+ signal: result.signal || null,
126
+ stdout: result.stdout || '',
127
+ stderr: result.stderr || '',
128
+ passed: !timedOut && exitCode === 0,
129
+ };
53
130
  }
54
131
 
55
- const pkgDir = findPackageDir();
56
- let scripts = {};
57
- if (pkgDir) {
58
- try {
59
- scripts = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8')).scripts || {};
60
- } catch (err) {
61
- console.error(`āŒ Verification FAILED: package.json at ${pkgDir} is unreadable/invalid JSON (${err.message}).`);
62
- process.exit(1);
132
+ function publicCheck(check) {
133
+ return {
134
+ id: check.id,
135
+ run: check.run,
136
+ cwd: check.cwd,
137
+ source: check.source,
138
+ exitCode: check.exitCode,
139
+ durationMs: check.durationMs,
140
+ ...(check.timedOut ? { timedOut: true } : {}),
141
+ ...(check.signal ? { signal: check.signal } : {}),
142
+ ...(!jsonMode && check.stdout ? { stdout: check.stdout } : {}),
143
+ ...(!jsonMode && check.stderr ? { stderr: check.stderr } : {}),
144
+ };
145
+ }
146
+
147
+ function candidateView(candidate) {
148
+ return {
149
+ source: candidate.source,
150
+ run: candidate.run,
151
+ cwd: candidate.cwd,
152
+ };
153
+ }
154
+
155
+ function rootReason(status, checks, candidates) {
156
+ if (status === 'FAILED') {
157
+ const failed = checks.find(check => !check.passed);
158
+ if (!failed) return 'verification failed';
159
+ return failed.timedOut
160
+ ? `check ${failed.id} timed out after ${failed.durationMs}ms`
161
+ : `check ${failed.id} exited ${failed.exitCode}`;
63
162
  }
163
+ if (status === 'UNCHECKED_DISCOVERED') return `${candidates.length} verification candidate(s) detected but not executed`;
164
+ if (status === 'UNCHECKED_NO_CHECKS') return 'no project verification checks were declared or detected';
165
+ return `${checks.length} check(s) passed`;
64
166
  }
65
167
 
66
- // npm-init's placeholder ("no test specified" + exit 1) is absence of a test
67
- // suite, not a failing one - running it would block every fresh project.
68
- const runnable = ['lint', 'test'].filter(name =>
69
- typeof scripts[name] === 'string' &&
70
- scripts[name].trim() !== '' &&
71
- !/no test specified/i.test(scripts[name])
72
- );
73
-
74
- if (!pkgDir || runnable.length === 0) {
75
- console.log("āš ļø Verification UNCHECKED: No automated lint/test scripts detected in package.json.");
76
- console.log(" Exit code is 0 (non-blocking), but NO AUTOMATED EVIDENCE was produced.");
77
- console.log(" PROHIBITED: Do NOT cite this run as proof that tests pass. Perform manual or alternative verification first.");
78
- process.exit(0);
168
+ function contractPlan(contract) {
169
+ const rootResults = contract.roots.map(root => ({
170
+ path: root.path,
171
+ status: 'UNCHECKED_NO_CHECKS',
172
+ reason: '',
173
+ checks: [],
174
+ candidates: [],
175
+ }));
176
+ const byPath = new Map(rootResults.map(root => [path.resolve(root.path), root]));
177
+
178
+ for (const check of contract.checks) {
179
+ const executed = executeCheck(check);
180
+ const root = byPath.get(path.resolve(check.root));
181
+ root.checks.push(executed);
182
+ }
183
+
184
+ for (const root of rootResults) {
185
+ root.status = root.checks.length === 0
186
+ ? 'UNCHECKED_NO_CHECKS'
187
+ : root.checks.some(check => !check.passed) ? 'FAILED' : 'PASSED';
188
+ root.reason = rootReason(root.status, root.checks, root.candidates);
189
+ }
190
+ return rootResults;
79
191
  }
80
192
 
81
- const runner = detectRunner(pkgDir);
82
- for (const name of runnable) {
83
- console.log(`\nā–¶ Running "${runner} run ${name}" in ${pkgDir} ...`);
84
- try {
85
- execSync(`${runner} run ${name}`, {
86
- cwd: pkgDir,
87
- stdio: 'inherit',
88
- timeout: 10 * 60 * 1000,
89
- env: { ...process.env, HARNESS_SKIP_PROJECT_CHECKS: '1' }
193
+ function discoveredPlan() {
194
+ const roots = [];
195
+ for (const rootPath of enumerateSubmoduleRoots(repoRoot)) {
196
+ const detected = detectRoot(rootPath);
197
+ const executed = detected.checks.map(executeCheck);
198
+ const candidates = detected.candidates;
199
+ const status = executed.some(check => !check.passed)
200
+ ? 'FAILED'
201
+ : candidates.length > 0 ? 'UNCHECKED_DISCOVERED'
202
+ : executed.length > 0 ? 'PASSED' : 'UNCHECKED_NO_CHECKS';
203
+ roots.push({
204
+ path: rootPath,
205
+ status,
206
+ reason: rootReason(status, executed, candidates),
207
+ checks: executed,
208
+ candidates,
90
209
  });
91
- console.log(`āœ… ${name} passed.`);
92
- } catch (err) {
93
- console.error(`\nāŒ Verification FAILED: "${runner} run ${name}" exited non-zero${err.signal ? ` (signal ${err.signal})` : ''}.`);
94
- console.error("Fix the failures above and run this gate again.");
95
- process.exit(1);
96
210
  }
211
+ return roots;
97
212
  }
98
213
 
99
- console.log("\nāœ… Verification SUCCESS. You may now complete the task.");
100
- process.exit(0);
214
+ function finalize(roots) {
215
+ const status = aggregateStatus(roots);
216
+ const failingRoot = roots.find(root => root.status === status);
217
+ const report = {
218
+ status,
219
+ reason: failingRoot ? failingRoot.reason : '',
220
+ repoRoot,
221
+ roots: roots.map(root => ({
222
+ path: root.path,
223
+ status: root.status,
224
+ reason: root.reason,
225
+ checks: root.checks.map(publicCheck),
226
+ candidates: root.candidates.map(candidateView),
227
+ })),
228
+ checks: roots.flatMap(root => root.checks.map(publicCheck)),
229
+ candidates: roots.flatMap(root => root.candidates.map(candidateView)),
230
+ };
231
+ finish(report);
232
+ }
233
+
234
+ try {
235
+ // Injection remains first and cannot be bypassed by the recursion guard.
236
+ if (fs.existsSync(path.join(cwd, '.verify-fail.tmp'))) {
237
+ finish(emptyReport('FAILED', '.verify-fail.tmp present (injected failure)'));
238
+ }
239
+
240
+ if (process.env.HARNESS_SKIP_PROJECT_CHECKS === '1') {
241
+ finish(emptyReport('SKIPPED', 'HARNESS_SKIP_PROJECT_CHECKS=1 recursion guard'));
242
+ }
243
+
244
+ const contract = readContract(repoRoot);
245
+ finalize(contract ? contractPlan(contract) : discoveredPlan());
246
+ } catch (error) {
247
+ finish(emptyReport('FAILED', error && error.message ? error.message : String(error)));
248
+ }
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const crypto = require('crypto');
5
5
  const path = require('path');
6
6
  const { spawnSync } = require('child_process');
7
+ const { isVerificationCommand } = require('../../../scripts/lib/verification-contract');
7
8
 
8
9
  function gitRaw(cwd, args, options = {}) {
9
10
  const result = spawnSync('git', args, {
@@ -192,10 +193,8 @@ function linkedWorktree(cwd, boundRoot) {
192
193
  // Unsupported shell syntax still fails closed.
193
194
  const DANGEROUS_FLAG_RE = /\s(?:--(?:output|ext-diff|textconv|exec|pre|pre-glob|pager|open|batch|filters)|-[xoO])(?:\b|=)|\s-(?:exec|execdir|delete|fprint|fprintf)\b/i;
194
195
  const READ_ONLY_PREFIX_RE = /^(?:git\s+(?:status|rev-parse|diff|log|show|ls-files|check-ignore)(?:\s|$)|git\s+branch\s+--show-current$|git\s+worktree\s+list(?:\s|$)|gh\s+(?:issue|pr)\s+(?:view|list)(?:\s|$)|gh\s+repo\s+view(?:\s|$)|gh\s+auth\s+status(?:\s|$)|(?:pwd|ls|dir|cat|type|head|tail|wc|stat|rg|grep|echo|Get-Location|Get-ChildItem|Get-Content|Select-String|Test-Path)(?:\s|$))/i;
195
- const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
196
-
197
- function isVerificationShell(command) {
198
- return VERIFY_COMMAND_RE.test(String(command || ''));
196
+ function isVerificationShell(command, options = {}) {
197
+ return isVerificationCommand(command, options);
199
198
  }
200
199
 
201
200
  function isReadOnlySegment(segment) {
@@ -43,7 +43,7 @@ function processState(payload) {
43
43
  if (isShell) {
44
44
  const commandClass = classifyShell(command);
45
45
  if (commandClass !== 'read-only' && commandClass !== 'worktree-setup') state.lastEditAt = Date.now();
46
- if (!isFailed && isVerificationShell(command)) { state.lastVerifyAt = Date.now(); state.lastVerifyExitCode = exitCode ?? null; }
46
+ if (!isFailed && isVerificationShell(command, { cwd: cwdOf(payload, root), root })) { state.lastVerifyAt = Date.now(); state.lastVerifyExitCode = exitCode ?? null; }
47
47
  }
48
48
  fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
49
49
  observeTool(payload);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-enforcement",
3
3
  "description": "Repository inventory for the opencode enforcement plugin and its standalone helper",
4
- "version": "0.25.1",
4
+ "version": "0.26.0",
5
5
  "runtime": "index.mjs",
6
6
  "hooks": {
7
7
  "verify": "hooks/verify.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-everything",
3
- "version": "0.25.1",
3
+ "version": "0.26.0",
4
4
  "description": "Harness OS: An Orchestrated Agent Operating System for AI-driven Development",
5
5
  "main": "harness-everything/scripts/bootstrap.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-everything",
3
- "version": "0.25.1",
3
+ "version": "0.26.0",
4
4
  "description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
5
5
  "author": {
6
6
  "name": "Miya Daniel",
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const crypto = require('crypto');
5
5
  const path = require('path');
6
6
  const { spawnSync } = require('child_process');
7
+ const { isVerificationCommand } = require('../../../scripts/lib/verification-contract');
7
8
 
8
9
  function gitRaw(cwd, args, options = {}) {
9
10
  const result = spawnSync('git', args, {
@@ -192,10 +193,8 @@ function linkedWorktree(cwd, boundRoot) {
192
193
  // Unsupported shell syntax still fails closed.
193
194
  const DANGEROUS_FLAG_RE = /\s(?:--(?:output|ext-diff|textconv|exec|pre|pre-glob|pager|open|batch|filters)|-[xoO])(?:\b|=)|\s-(?:exec|execdir|delete|fprint|fprintf)\b/i;
194
195
  const READ_ONLY_PREFIX_RE = /^(?:git\s+(?:status|rev-parse|diff|log|show|ls-files|check-ignore)(?:\s|$)|git\s+branch\s+--show-current$|git\s+worktree\s+list(?:\s|$)|gh\s+(?:issue|pr)\s+(?:view|list)(?:\s|$)|gh\s+repo\s+view(?:\s|$)|gh\s+auth\s+status(?:\s|$)|(?:pwd|ls|dir|cat|type|head|tail|wc|stat|rg|grep|echo|Get-Location|Get-ChildItem|Get-Content|Select-String|Test-Path)(?:\s|$))/i;
195
- const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
196
-
197
- function isVerificationShell(command) {
198
- return VERIFY_COMMAND_RE.test(String(command || ''));
196
+ function isVerificationShell(command, options = {}) {
197
+ return isVerificationCommand(command, options);
199
198
  }
200
199
 
201
200
  function isReadOnlySegment(segment) {
@@ -43,7 +43,7 @@ function processState(payload) {
43
43
  if (isShell) {
44
44
  const commandClass = classifyShell(command);
45
45
  if (commandClass !== 'read-only' && commandClass !== 'worktree-setup') state.lastEditAt = Date.now();
46
- if (!isFailed && isVerificationShell(command)) { state.lastVerifyAt = Date.now(); state.lastVerifyExitCode = exitCode ?? null; }
46
+ if (!isFailed && isVerificationShell(command, { cwd: cwdOf(payload, root), root })) { state.lastVerifyAt = Date.now(); state.lastVerifyExitCode = exitCode ?? null; }
47
47
  }
48
48
  fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8');
49
49
  observeTool(payload);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "harness-everything",
4
- "version": "0.25.1",
4
+ "version": "0.26.0",
5
5
  "description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
6
6
  "author": {
7
7
  "name": "Miya Daniel",