harness-everything 0.12.1 → 0.12.2

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.
@@ -10,7 +10,7 @@
10
10
  "name": "harness-everything",
11
11
  "source": "./",
12
12
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
13
- "version": "0.12.1",
13
+ "version": "0.12.2",
14
14
  "author": {
15
15
  "name": "Miya Daniel"
16
16
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-everything",
3
3
  "description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
4
- "version": "0.12.1",
4
+ "version": "0.12.2",
5
5
  "author": {
6
6
  "name": "Miya Daniel"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.12.2](https://github.com/dyphn1/Harness-everything/compare/v0.12.1...v0.12.2) (2026-09-19)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **workflow:** admit observation-only verification at limit ([08b859e](https://github.com/dyphn1/Harness-everything/commit/08b859ef27c04a07d9a0a6faeeed52f3da986378))
10
+ * **workflow:** admit observation-only verification at limit ([8924a3c](https://github.com/dyphn1/Harness-everything/commit/8924a3cc80455592accbe81e3f2df0f4479a2d8b))
11
+ * **workflow:** allow verification probe at iteration limit ([7ce511e](https://github.com/dyphn1/Harness-everything/commit/7ce511e29ca368371d996f4cbeff02298d97434c))
12
+ * **workflow:** allow verification probe at iteration limit ([07e6ed0](https://github.com/dyphn1/Harness-everything/commit/07e6ed0b369e38f274ac099af590b12373b1ad1c))
13
+
5
14
  ## [0.12.1](https://github.com/dyphn1/Harness-everything/compare/v0.12.0...v0.12.1) (2026-09-19)
6
15
 
7
16
  ### Bug Fixes
@@ -203,6 +203,58 @@ try {
203
203
  git(['init']); fs.writeFileSync(path.join(repo, 'README.md'), 'fixture\n'); git(['add', '.']);
204
204
  git(['-c', 'user.name=Harness Test', '-c', 'user.email=harness@example.invalid', 'commit', '-m', 'fixture']);
205
205
  git(['worktree', 'add', linked, '-b', 'isolated']);
206
+
207
+ // #164: the mutation budget is an edit budget, not a reason to make the
208
+ // final edit unverifiable. At the exact limit a verification-shaped shell
209
+ // gets observation-only admission. No-effect verification may complete the
210
+ // workflow; a verification command that mutates still exhausts after Post.
211
+ const finalVerify = fixtureSession('workflow-final-iteration-verify');
212
+ check(finalVerify.route('Fix this checkout bug with a regression test').status === 0 &&
213
+ finalVerify.control('start').status === 0,
214
+ '#164 final-iteration verification fixture starts Tier 2');
215
+ const finalVerifyLimit = read(finalVerify.file).workflowPlan.limits.maxIterations;
216
+ for (let i = 0; i < finalVerifyLimit; i++) {
217
+ check(finalVerify.gate('Write', { file_path: path.join(repo, 'final-verify-' + i + '.txt') }).status === 0,
218
+ '#164 confirmed mutation fills iteration budget ' + (i + 1) + '/' + finalVerifyLimit);
219
+ }
220
+ check(read(finalVerify.file).budget.counters.iterations === finalVerifyLimit,
221
+ '#164 fixture reaches the exact mutation limit while still running');
222
+ const finalVerifyId = 'toolu_final_iteration_verify';
223
+ check(finalVerify.gate('Bash', { command: 'node run-test.js' }, { tool_use_id: finalVerifyId }).status === 0,
224
+ '#164 no-effect verification shell is admitted at the exact mutation limit');
225
+ check(finalVerify.persist('Bash', { command: 'node run-test.js' }, { stdout: 'tests passed' },
226
+ { tool_use_id: finalVerifyId }).status === 0,
227
+ '#164 no-effect verification settles without exhausting the budget');
228
+ check(read(finalVerify.file).budget.counters.iterations === finalVerifyLimit &&
229
+ read(finalVerify.file).state === 'running',
230
+ '#164 no-effect verification preserves the full mutation count and running state');
231
+ check(finalVerify.stop().status === 0 && read(finalVerify.file).state === 'satisfied',
232
+ '#164 verification after the final allowed edit can satisfy Stop');
233
+
234
+ const finalMutator = fixtureSession('workflow-final-iteration-mutator');
235
+ check(finalMutator.route('Fix this checkout bug with a regression test').status === 0 &&
236
+ finalMutator.control('start').status === 0,
237
+ '#164 at-limit mutator fixture starts Tier 2');
238
+ const finalMutatorLimit = read(finalMutator.file).workflowPlan.limits.maxIterations;
239
+ for (let i = 0; i < finalMutatorLimit; i++) {
240
+ check(finalMutator.gate('Write', { file_path: path.join(repo, 'final-mutator-' + i + '.txt') }).status === 0,
241
+ '#164 mutator fixture fills iteration budget ' + (i + 1) + '/' + finalMutatorLimit);
242
+ }
243
+ const finalMutatorId = 'toolu_final_iteration_mutator';
244
+ check(finalMutator.gate('Bash', { command: 'node run-test-and-mutate.js' }, { tool_use_id: finalMutatorId }).status === 0,
245
+ '#164 verification-shaped command receives observation-only admission at limit');
246
+ const finalOverflowTarget = path.join(repo, 'final-iteration-overflow.txt');
247
+ fs.writeFileSync(finalOverflowTarget, 'mutation beyond limit\n');
248
+ check(finalMutator.persist('Bash', { command: 'node run-test-and-mutate.js' }, { stdout: 'changed' },
249
+ { tool_use_id: finalMutatorId }).status === 2,
250
+ '#164 at-limit verification-shaped command that mutates is rejected on observation');
251
+ const finalMutatorState = read(finalMutator.file);
252
+ check(finalMutatorState.state === 'blocked' &&
253
+ finalMutatorState.budget?.state === 'budget-exhausted' &&
254
+ finalMutatorState.budget?.reasonCode === 'iteration-budget-exhausted',
255
+ '#164 observed mutation beyond the limit leaves an auditable blocked workflow');
256
+ fs.unlinkSync(finalOverflowTarget);
257
+
206
258
  const taskNotification = '<task-notification><summary>Background command "Run full test suite" failed with exit code 1</summary></task-notification>';
207
259
  const unboundNotification = node('harness-everything/scripts/kernel-router.js', { cwd: repo, prompt: taskNotification });
208
260
  check(unboundNotification.status === 0 &&
@@ -135,6 +135,11 @@ function linkedWorktree(cwd, boundRoot) {
135
135
  // Unsupported shell syntax still fails closed.
136
136
  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;
137
137
  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;
138
+ const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
139
+
140
+ function isVerificationShell(command) {
141
+ return VERIFY_COMMAND_RE.test(String(command || ''));
142
+ }
138
143
 
139
144
  function isReadOnlySegment(segment) {
140
145
  return READ_ONLY_PREFIX_RE.test(segment) && !DANGEROUS_FLAG_RE.test(segment);
@@ -311,4 +316,4 @@ function assertShellScope(command, cwd, isolatedRoot) {
311
316
  }
312
317
  }
313
318
 
314
- module.exports = { canonical, key, within, linkedWorktree, classifyShell, workspaceFingerprint, shellProbeKey, inputOf, cwdOf, commandOf, mutationPaths, worktreeRoots, directMutationInWorkspace, assertTargets, assertShellScope };
319
+ module.exports = { canonical, key, within, linkedWorktree, classifyShell, isVerificationShell, workspaceFingerprint, shellProbeKey, inputOf, cwdOf, commandOf, mutationPaths, worktreeRoots, directMutationInWorkspace, assertTargets, assertShellScope };
@@ -196,14 +196,30 @@ function reclaimStaleMutationProbes(context, keepKey) {
196
196
  }
197
197
  }
198
198
 
199
- function registerMutationProbe(context, key, fingerprint, reserveIteration, observationCwd, timeoutMs) {
199
+ function registerMutationProbe(context, key, fingerprint, reserveIteration, observationCwd, options = {}) {
200
200
  if (!MUTATION_PROBE_KEY.test(String(key || '')) || !MUTATION_PROBE_KEY.test(String(fingerprint || ''))) {
201
201
  throw new Error('invalid mutation probe identity');
202
202
  }
203
203
  return withMutationProbeLock(context, () => {
204
204
  refreshWorkflowForProbe(context);
205
205
  reclaimStaleMutationProbes(context, key);
206
- if (reserveIteration) assertIterationCapacity(context, 'shell:untrusted');
206
+ let activeReservation = Boolean(reserveIteration);
207
+ let atLimitAllowance = false;
208
+ if (activeReservation) {
209
+ const budget = ensureWorkflowBudget(context.workflow);
210
+ const limit = budget.limits.maxIterations;
211
+ const reserved = mutationProbeReservations(context);
212
+ if (options.allowAtLimit === true && budget.state === 'active' && limit !== null &&
213
+ budget.counters.iterations === limit && reserved === 0) {
214
+ // Verification-shaped commands get observation-only admission at the
215
+ // exact mutation limit. If they mutate, settlement still records the
216
+ // mutation and exhausts the budget after execution (#164).
217
+ activeReservation = false;
218
+ atLimitAllowance = true;
219
+ } else {
220
+ assertIterationCapacity(context, 'shell:untrusted');
221
+ }
222
+ }
207
223
  const dir = mutationProbeDir(context);
208
224
  fs.mkdirSync(dir, { recursive: true });
209
225
  const file = mutationProbeFile(context, key);
@@ -212,11 +228,12 @@ function registerMutationProbe(context, key, fingerprint, reserveIteration, obse
212
228
  const probe = {
213
229
  schemaVersion: 1,
214
230
  fingerprint,
215
- reserveIteration: Boolean(reserveIteration),
216
- countIteration: Boolean(reserveIteration),
231
+ reserveIteration: activeReservation,
232
+ countIteration: options.countIteration === undefined ? Boolean(reserveIteration) : Boolean(options.countIteration),
233
+ atLimitAllowance,
217
234
  observationCwd: observationCwd ? path.resolve(observationCwd) : null,
218
235
  observedAt,
219
- reclaimAfterAt: observedAt + mutationProbeLeaseMs(timeoutMs),
236
+ reclaimAfterAt: observedAt + mutationProbeLeaseMs(options.timeoutMs),
220
237
  };
221
238
  atomicWriteJson(file, probe);
222
239
  return probe;
@@ -3,14 +3,9 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { getWorkspaceRoot, getSessionDir } = require('./lib/harness-state');
5
5
  const { loadWorkflow, saveWorkflow, isMajorWorkflow, peekMutationProbe, settleMutationProbe, settleMutationProbeConservative } = require('./lib/workflow-runtime');
6
- const { cwdOf, shellProbeKey, workspaceFingerprint, directMutationInWorkspace } = require('./lib/workflow-isolation');
6
+ const { cwdOf, shellProbeKey, isVerificationShell, workspaceFingerprint, directMutationInWorkspace } = require('./lib/workflow-isolation');
7
7
  const { observeTool } = require('./lib/telemetry');
8
8
 
9
- // Commands that count as "verification ran" for the Stop gate
10
- // (hooks/scripts/stop-gate.js). Recall over precision: under-blocking the
11
- // gate is the correct failure direction, so a broad net is fine.
12
- const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
13
-
14
9
  let inputData = '';
15
10
  const timeout = setTimeout(() => {
16
11
  processState(null);
@@ -127,7 +122,7 @@ function processState(payload) {
127
122
  }
128
123
  if ((toolName === 'Bash' || toolName === 'PowerShell') && !isFailed) {
129
124
  const command = (payload.tool_input && payload.tool_input.command) || '';
130
- if (VERIFY_COMMAND_RE.test(command)) {
125
+ if (isVerificationShell(command)) {
131
126
  state.lastVerifyAt = Date.now();
132
127
  state.lastVerifyExitCode = exitCode ?? null;
133
128
  }
@@ -4,7 +4,7 @@
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { loadWorkflow, saveWorkflow, isMajorWorkflow, matchingRun, registerMutationProbe, recordBudgetEvent, readHookInput, WORKFLOW_CONTROLLER_COMMANDS } = require('./lib/workflow-runtime');
7
- const { key, classifyShell, workspaceFingerprint, shellProbeKey, cwdOf, commandOf, mutationPaths, directMutationInWorkspace, linkedWorktree, assertTargets, assertShellScope } = require('./lib/workflow-isolation');
7
+ const { key, classifyShell, isVerificationShell, workspaceFingerprint, shellProbeKey, cwdOf, commandOf, mutationPaths, directMutationInWorkspace, linkedWorktree, assertTargets, assertShellScope } = require('./lib/workflow-isolation');
8
8
 
9
9
  const DIRECT = new Set(['Edit', 'Write', 'apply_patch']);
10
10
  const SHELL = new Set(['Bash', 'PowerShell', 'exec_command']);
@@ -88,14 +88,12 @@ function decide(payload) {
88
88
  return;
89
89
  }
90
90
  const probeKey = shellProbeKey(payload, cwd);
91
- registerMutationProbe(
92
- context,
93
- probeKey,
94
- fingerprint,
95
- workflow.strategy === 'iterative-single',
96
- cwd,
97
- shellTimeoutMs(payload)
98
- );
91
+ const iterative = workflow.strategy === 'iterative-single';
92
+ registerMutationProbe(context, probeKey, fingerprint, iterative, cwd, {
93
+ countIteration: iterative,
94
+ allowAtLimit: iterative && isVerificationShell(command),
95
+ timeoutMs: shellTimeoutMs(payload),
96
+ });
99
97
  }
100
98
 
101
99
  readHookInput(decide);
@@ -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.12.1",
4
+ "version": "0.12.2",
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.12.1",
3
+ "version": "0.12.2",
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.12.1",
3
+ "version": "0.12.2",
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",
@@ -135,6 +135,11 @@ function linkedWorktree(cwd, boundRoot) {
135
135
  // Unsupported shell syntax still fails closed.
136
136
  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;
137
137
  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;
138
+ const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
139
+
140
+ function isVerificationShell(command) {
141
+ return VERIFY_COMMAND_RE.test(String(command || ''));
142
+ }
138
143
 
139
144
  function isReadOnlySegment(segment) {
140
145
  return READ_ONLY_PREFIX_RE.test(segment) && !DANGEROUS_FLAG_RE.test(segment);
@@ -311,4 +316,4 @@ function assertShellScope(command, cwd, isolatedRoot) {
311
316
  }
312
317
  }
313
318
 
314
- module.exports = { canonical, key, within, linkedWorktree, classifyShell, workspaceFingerprint, shellProbeKey, inputOf, cwdOf, commandOf, mutationPaths, worktreeRoots, directMutationInWorkspace, assertTargets, assertShellScope };
319
+ module.exports = { canonical, key, within, linkedWorktree, classifyShell, isVerificationShell, workspaceFingerprint, shellProbeKey, inputOf, cwdOf, commandOf, mutationPaths, worktreeRoots, directMutationInWorkspace, assertTargets, assertShellScope };
@@ -196,14 +196,30 @@ function reclaimStaleMutationProbes(context, keepKey) {
196
196
  }
197
197
  }
198
198
 
199
- function registerMutationProbe(context, key, fingerprint, reserveIteration, observationCwd, timeoutMs) {
199
+ function registerMutationProbe(context, key, fingerprint, reserveIteration, observationCwd, options = {}) {
200
200
  if (!MUTATION_PROBE_KEY.test(String(key || '')) || !MUTATION_PROBE_KEY.test(String(fingerprint || ''))) {
201
201
  throw new Error('invalid mutation probe identity');
202
202
  }
203
203
  return withMutationProbeLock(context, () => {
204
204
  refreshWorkflowForProbe(context);
205
205
  reclaimStaleMutationProbes(context, key);
206
- if (reserveIteration) assertIterationCapacity(context, 'shell:untrusted');
206
+ let activeReservation = Boolean(reserveIteration);
207
+ let atLimitAllowance = false;
208
+ if (activeReservation) {
209
+ const budget = ensureWorkflowBudget(context.workflow);
210
+ const limit = budget.limits.maxIterations;
211
+ const reserved = mutationProbeReservations(context);
212
+ if (options.allowAtLimit === true && budget.state === 'active' && limit !== null &&
213
+ budget.counters.iterations === limit && reserved === 0) {
214
+ // Verification-shaped commands get observation-only admission at the
215
+ // exact mutation limit. If they mutate, settlement still records the
216
+ // mutation and exhausts the budget after execution (#164).
217
+ activeReservation = false;
218
+ atLimitAllowance = true;
219
+ } else {
220
+ assertIterationCapacity(context, 'shell:untrusted');
221
+ }
222
+ }
207
223
  const dir = mutationProbeDir(context);
208
224
  fs.mkdirSync(dir, { recursive: true });
209
225
  const file = mutationProbeFile(context, key);
@@ -212,11 +228,12 @@ function registerMutationProbe(context, key, fingerprint, reserveIteration, obse
212
228
  const probe = {
213
229
  schemaVersion: 1,
214
230
  fingerprint,
215
- reserveIteration: Boolean(reserveIteration),
216
- countIteration: Boolean(reserveIteration),
231
+ reserveIteration: activeReservation,
232
+ countIteration: options.countIteration === undefined ? Boolean(reserveIteration) : Boolean(options.countIteration),
233
+ atLimitAllowance,
217
234
  observationCwd: observationCwd ? path.resolve(observationCwd) : null,
218
235
  observedAt,
219
- reclaimAfterAt: observedAt + mutationProbeLeaseMs(timeoutMs),
236
+ reclaimAfterAt: observedAt + mutationProbeLeaseMs(options.timeoutMs),
220
237
  };
221
238
  atomicWriteJson(file, probe);
222
239
  return probe;
@@ -3,14 +3,9 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { getWorkspaceRoot, getSessionDir } = require('./lib/harness-state');
5
5
  const { loadWorkflow, saveWorkflow, isMajorWorkflow, peekMutationProbe, settleMutationProbe, settleMutationProbeConservative } = require('./lib/workflow-runtime');
6
- const { cwdOf, shellProbeKey, workspaceFingerprint, directMutationInWorkspace } = require('./lib/workflow-isolation');
6
+ const { cwdOf, shellProbeKey, isVerificationShell, workspaceFingerprint, directMutationInWorkspace } = require('./lib/workflow-isolation');
7
7
  const { observeTool } = require('./lib/telemetry');
8
8
 
9
- // Commands that count as "verification ran" for the Stop gate
10
- // (hooks/scripts/stop-gate.js). Recall over precision: under-blocking the
11
- // gate is the correct failure direction, so a broad net is fine.
12
- const VERIFY_COMMAND_RE = /\b(test|spec|jest|vitest|mocha|pytest|rspec|phpunit|tsc|eslint|lint|build|compile|verify|check)\b/i;
13
-
14
9
  let inputData = '';
15
10
  const timeout = setTimeout(() => {
16
11
  processState(null);
@@ -127,7 +122,7 @@ function processState(payload) {
127
122
  }
128
123
  if ((toolName === 'Bash' || toolName === 'PowerShell') && !isFailed) {
129
124
  const command = (payload.tool_input && payload.tool_input.command) || '';
130
- if (VERIFY_COMMAND_RE.test(command)) {
125
+ if (isVerificationShell(command)) {
131
126
  state.lastVerifyAt = Date.now();
132
127
  state.lastVerifyExitCode = exitCode ?? null;
133
128
  }
@@ -4,7 +4,7 @@
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { loadWorkflow, saveWorkflow, isMajorWorkflow, matchingRun, registerMutationProbe, recordBudgetEvent, readHookInput, WORKFLOW_CONTROLLER_COMMANDS } = require('./lib/workflow-runtime');
7
- const { key, classifyShell, workspaceFingerprint, shellProbeKey, cwdOf, commandOf, mutationPaths, directMutationInWorkspace, linkedWorktree, assertTargets, assertShellScope } = require('./lib/workflow-isolation');
7
+ const { key, classifyShell, isVerificationShell, workspaceFingerprint, shellProbeKey, cwdOf, commandOf, mutationPaths, directMutationInWorkspace, linkedWorktree, assertTargets, assertShellScope } = require('./lib/workflow-isolation');
8
8
 
9
9
  const DIRECT = new Set(['Edit', 'Write', 'apply_patch']);
10
10
  const SHELL = new Set(['Bash', 'PowerShell', 'exec_command']);
@@ -88,14 +88,12 @@ function decide(payload) {
88
88
  return;
89
89
  }
90
90
  const probeKey = shellProbeKey(payload, cwd);
91
- registerMutationProbe(
92
- context,
93
- probeKey,
94
- fingerprint,
95
- workflow.strategy === 'iterative-single',
96
- cwd,
97
- shellTimeoutMs(payload)
98
- );
91
+ const iterative = workflow.strategy === 'iterative-single';
92
+ registerMutationProbe(context, probeKey, fingerprint, iterative, cwd, {
93
+ countIteration: iterative,
94
+ allowAtLimit: iterative && isVerificationShell(command),
95
+ timeoutMs: shellTimeoutMs(payload),
96
+ });
99
97
  }
100
98
 
101
99
  readHookInput(decide);
@@ -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.12.1",
4
+ "version": "0.12.2",
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",