harness-everything 0.11.2 → 0.11.3

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.11.2",
13
+ "version": "0.11.3",
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.11.2",
4
+ "version": "0.11.3",
5
5
  "author": {
6
6
  "name": "Miya Daniel"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.11.3](https://github.com/dyphn1/Harness-everything/compare/v0.11.2...v0.11.3) (2026-09-18)
6
+
7
+ ### Bug Fixes
8
+
9
+ * **workflow:** Resolve verification-after-edit-missing without a host exit code ([e5dae03](https://github.com/dyphn1/Harness-everything/commit/e5dae03799cca594ac3e4f9d0facca6209dec3c8)), closes [#153](https://github.com/dyphn1/Harness-everything/issues/153)
10
+
5
11
  ## [0.11.2](https://github.com/dyphn1/Harness-everything/compare/v0.11.1...v0.11.2) (2026-09-18)
6
12
 
7
13
  ### Bug Fixes
@@ -175,8 +175,11 @@ try {
175
175
  check(stop().status === 2, 'direct/iterative route cannot complete unverified mutation');
176
176
  check(stop({ stop_hook_active: true }).status === 0 && read(file).state === 'blocked', 'iterative retry reports incomplete state');
177
177
  check(control('start').status === 0, 'blocked iterative route can resume without a Fable stage map');
178
+ // Issue #153: a host is not guaranteed to report a numeric exit code at
179
+ // all (state-persist.js's own isFailed logic already treats "no exit code
180
+ // and no stderr" as success), so this must resolve rather than deadlock.
178
181
  node('hooks/scripts/state-persist.js', { ...payload, tool_name: 'Bash', tool_input: { command: 'npm test' }, tool_response: { stdout: 'no code' } });
179
- check(stop().status === 2, 'verification without numeric exit status is unknown');
182
+ check(stop().status === 0 && read(file).state === 'satisfied', 'verification without a host-reported numeric exit status still resolves completion');
180
183
  node('hooks/scripts/state-persist.js', { ...payload, tool_name: 'Bash', tool_input: { command: 'npm test' }, tool_response: { exitCode: 0, stdout: 'passed' } });
181
184
  check(stop().status === 0 && read(file).state === 'satisfied', 'observed iterative verification resolves completion');
182
185
  console.log(`PASS: workflow lifecycle (${passed} assertions)`);
@@ -20,4 +20,57 @@ helper.check(
20
20
  );
21
21
  fs.unlinkSync(dirtyFile);
22
22
 
23
+ console.log('\n[2f] workflow-stop-gate (iterative-single) verification signal...');
24
+
25
+ const now = Date.now();
26
+
27
+ // Bug reproduction (issue #153): a verify-classified command that genuinely
28
+ // succeeded but whose host never reported a numeric exit code (observed on
29
+ // at least one live host - state-persist.js writes lastVerifyExitCode: null
30
+ // for any non-failing command when the PostToolUse payload has no numeric
31
+ // exitCode) must not be treated as unverified.
32
+ helper.writeState('workflow-run.json', {
33
+ schemaVersion: 1, sessionId: helper.SESSION_ID, strategy: 'iterative-single',
34
+ state: 'running', lastMutationAt: now - 5000,
35
+ });
36
+ helper.writeState('handoff-state.json', {
37
+ status: 'idle', lastEditAt: now - 5000, lastVerifyAt: now, lastVerifyExitCode: null,
38
+ });
39
+ const hostExitCodeUnavailable = helper.runHook('workflow-stop-gate.js', { session_id: helper.SESSION_ID });
40
+ helper.check(
41
+ '2f. workflow-stop-gate resolves when verify ran after edit even without a numeric exit code',
42
+ hostExitCodeUnavailable.code === 0 && helper.readState('workflow-run.json').state === 'satisfied',
43
+ `Got exit=${hostExitCodeUnavailable.code}, stderr="${hostExitCodeUnavailable.stderr.slice(0, 300)}"`
44
+ );
45
+
46
+ // Regression guard: explicit exit 0 (host does report it) must still resolve.
47
+ helper.writeState('workflow-run.json', {
48
+ schemaVersion: 1, sessionId: helper.SESSION_ID, strategy: 'iterative-single',
49
+ state: 'running', lastMutationAt: now - 5000,
50
+ });
51
+ helper.writeState('handoff-state.json', {
52
+ status: 'idle', lastEditAt: now - 5000, lastVerifyAt: now, lastVerifyExitCode: 0,
53
+ });
54
+ const hostExitCodeReported = helper.runHook('workflow-stop-gate.js', { session_id: helper.SESSION_ID });
55
+ helper.check(
56
+ '2f. workflow-stop-gate still resolves when the host does report exit code 0',
57
+ hostExitCodeReported.code === 0 && helper.readState('workflow-run.json').state === 'satisfied',
58
+ `Got exit=${hostExitCodeReported.code}`
59
+ );
60
+
61
+ // Negative control: no verification ran after the edit at all -> stays blocked.
62
+ helper.writeState('workflow-run.json', {
63
+ schemaVersion: 1, sessionId: helper.SESSION_ID, strategy: 'iterative-single',
64
+ state: 'running', lastMutationAt: now,
65
+ });
66
+ helper.writeState('handoff-state.json', {
67
+ status: 'idle', lastEditAt: now, lastVerifyAt: now - 5000, lastVerifyExitCode: null,
68
+ });
69
+ const staleVerify = helper.runHook('workflow-stop-gate.js', { session_id: helper.SESSION_ID });
70
+ helper.check(
71
+ '2f. workflow-stop-gate still blocks when no verification ran after the edit',
72
+ staleVerify.code === 2 && staleVerify.stderr.includes('verification-after-edit-missing'),
73
+ `Got exit=${staleVerify.code}, stderr="${staleVerify.stderr.slice(0, 300)}"`
74
+ );
75
+
23
76
  helper.finish();
@@ -21,7 +21,15 @@ function decide(payload) {
21
21
  } else {
22
22
  const handoff = readJson(path.join(sessionDir, 'handoff-state.json'));
23
23
  const lastEdit = Math.max(workflow.lastMutationAt || 0, handoff?.lastEditAt || 0);
24
- if (lastEdit && ((handoff?.lastVerifyAt || 0) < lastEdit || handoff?.lastVerifyExitCode !== 0)) unresolved = ['verification-after-edit-missing'];
24
+ // state-persist.js only ever records lastVerifyAt/lastVerifyExitCode from
25
+ // its `!isFailed` branch, so reaching this timestamp already proves the
26
+ // command succeeded. Do not additionally require lastVerifyExitCode===0:
27
+ // hosts are not guaranteed to report a numeric exit code at all (some
28
+ // PostToolUse payloads omit it entirely), in which case state-persist.js
29
+ // correctly records success with lastVerifyExitCode: null, and requiring
30
+ // a literal 0 here would make this gate permanently unresolvable on that
31
+ // host after any edit.
32
+ if (lastEdit && (handoff?.lastVerifyAt || 0) < lastEdit) unresolved = ['verification-after-edit-missing'];
25
33
  }
26
34
  if (unresolved.length) {
27
35
  workflow.unresolved = unresolved;
@@ -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.11.2",
4
+ "version": "0.11.3",
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.11.2",
3
+ "version": "0.11.3",
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.11.2",
3
+ "version": "0.11.3",
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",
@@ -21,7 +21,15 @@ function decide(payload) {
21
21
  } else {
22
22
  const handoff = readJson(path.join(sessionDir, 'handoff-state.json'));
23
23
  const lastEdit = Math.max(workflow.lastMutationAt || 0, handoff?.lastEditAt || 0);
24
- if (lastEdit && ((handoff?.lastVerifyAt || 0) < lastEdit || handoff?.lastVerifyExitCode !== 0)) unresolved = ['verification-after-edit-missing'];
24
+ // state-persist.js only ever records lastVerifyAt/lastVerifyExitCode from
25
+ // its `!isFailed` branch, so reaching this timestamp already proves the
26
+ // command succeeded. Do not additionally require lastVerifyExitCode===0:
27
+ // hosts are not guaranteed to report a numeric exit code at all (some
28
+ // PostToolUse payloads omit it entirely), in which case state-persist.js
29
+ // correctly records success with lastVerifyExitCode: null, and requiring
30
+ // a literal 0 here would make this gate permanently unresolvable on that
31
+ // host after any edit.
32
+ if (lastEdit && (handoff?.lastVerifyAt || 0) < lastEdit) unresolved = ['verification-after-edit-missing'];
25
33
  }
26
34
  if (unresolved.length) {
27
35
  workflow.unresolved = unresolved;
@@ -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.11.2",
4
+ "version": "0.11.3",
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",