harness-everything 0.16.2 → 0.16.4
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -0
- package/ci/consistency-check.js +100 -0
- package/ci/mechanism-33-issues-205-207.test.js +59 -0
- package/commitlint.config.js +2 -2
- package/docker-verify.sh +59 -24
- package/docs/architecture.md +1 -1
- package/docs/mechanism-first-skill-mesh.md +1 -1
- package/docs/workflows/zoom-out.md +6 -6
- package/fable-mode/CONTRACT-FORMAT.md +2 -1
- package/fable-mode/SKILL.md +1 -1
- package/fable-mode/agents/fable-orchestrator.md +6 -5
- package/fable-mode/execution-guardrails/SKILL.md +1 -1
- package/fable-mode/fable-haiku/SKILL.md +1 -1
- package/fable-mode/fable-opus/SKILL.md +1 -1
- package/fable-mode/fable-sonnet/SKILL.md +1 -1
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/references/router-workflow-plan.md +1 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +2 -1
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +6 -5
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +1 -1
|
@@ -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.16.
|
|
13
|
+
"version": "0.16.4",
|
|
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.16.
|
|
4
|
+
"version": "0.16.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.16.4](https://github.com/dyphn1/Harness-everything/compare/v0.16.3...v0.16.4) (2026-09-20)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **repo:** close issues 205-207 ([423ff63](https://github.com/dyphn1/Harness-everything/commit/423ff638f08cdaa14d4ab0856bfe2d2bc6b050db)), closes [#205](https://github.com/dyphn1/Harness-everything/issues/205) [#206](https://github.com/dyphn1/Harness-everything/issues/206) [#205](https://github.com/dyphn1/Harness-everything/issues/205) [#206](https://github.com/dyphn1/Harness-everything/issues/206) [#207](https://github.com/dyphn1/Harness-everything/issues/207)
|
|
10
|
+
|
|
11
|
+
## [0.16.3](https://github.com/dyphn1/Harness-everything/compare/v0.16.2...v0.16.3) (2026-09-20)
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **docker:** Verify current re-trip contract in docker-verify.sh ([#200](https://github.com/dyphn1/Harness-everything/issues/200)) ([610ce32](https://github.com/dyphn1/Harness-everything/commit/610ce32733a74dd4a16f68d171346ea8b10f8d62)), closes [#190](https://github.com/dyphn1/Harness-everything/issues/190) [#42](https://github.com/dyphn1/Harness-everything/issues/42)
|
|
16
|
+
|
|
5
17
|
## [0.16.2](https://github.com/dyphn1/Harness-everything/compare/v0.16.1...v0.16.2) (2026-09-20)
|
|
6
18
|
|
|
7
19
|
### Bug Fixes
|
package/ci/consistency-check.js
CHANGED
|
@@ -164,6 +164,106 @@ for (const [rel, pattern] of retiredClaims) {
|
|
|
164
164
|
check(`${rel}: retired enforcement claim absent (${pattern})`, !pattern.test(text), 'stale pre-#190 contract wording');
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
// The per-file entries above only freeze the files a past fix touched, so the
|
|
168
|
+
// same retired wording survived elsewhere (#204: docs/workflows/zoom-out.md,
|
|
169
|
+
// docker-verify.sh). Scan the whole agent-facing surface instead: docs, routed
|
|
170
|
+
// skills, shared references, the OpenCode README, root shell/container entry
|
|
171
|
+
// points, and the packaged plugin copies that ship to other hosts.
|
|
172
|
+
const RETIRED_CLAIM_DIRS = [
|
|
173
|
+
'docs',
|
|
174
|
+
'zoom-out',
|
|
175
|
+
'fable-mode',
|
|
176
|
+
'harness-everything/references',
|
|
177
|
+
'plugins/harness-everything',
|
|
178
|
+
];
|
|
179
|
+
const RETIRED_CLAIM_EXTENSIONS = new Set(['.md', '.sh', '.js', '.mjs']);
|
|
180
|
+
|
|
181
|
+
// Files that legitimately keep the retired wording: retained pre-#190
|
|
182
|
+
// live-host evidence, release history, capability docs whose hard-lock lines
|
|
183
|
+
// are explicitly labelled historical/reported-only, code that strips the
|
|
184
|
+
// retired state field, and the CI guards that assert its absence.
|
|
185
|
+
const RETIRED_CLAIM_ALLOWLIST = [
|
|
186
|
+
/^benchmarks\/results\/live-host\/opencode-2026-09-16\//,
|
|
187
|
+
/^CHANGELOG\.md$/,
|
|
188
|
+
/^docs\/platform-capabilities\.md$/,
|
|
189
|
+
/^opencode-plugin\/index\.mjs$/,
|
|
190
|
+
/^ci\//,
|
|
191
|
+
/^behavioral-evals\/[^/]+\.js$/,
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// A line that denies the retired behavior or dates it ("no permanent hard
|
|
195
|
+
// lock", "historical hard-lock evidence", "predates #190") is the correct
|
|
196
|
+
// wording, not drift. Only the broad hard-lock family needs this escape; the
|
|
197
|
+
// assertive patterns below are wrong in any context, so they never get it.
|
|
198
|
+
const HISTORICAL_CONTEXT =
|
|
199
|
+
/pre-#190|\b(?:no|not|never|without|remove[sd]?|retired|historical|predates|former(?:ly)?|legacy|post-reset|no longer)\b|rather than|instead of/i;
|
|
200
|
+
|
|
201
|
+
const RETIRED_CLAIM_SCAN = [
|
|
202
|
+
{ label: 'hard-lock escalation', pattern: /hard[\s-]?lock/i, historicalAllowed: true },
|
|
203
|
+
{ label: 'reset gated on a second cycle', pattern: /only after the (?:second|repeated) cycle/i },
|
|
204
|
+
{ label: 'cycle-escalation reset branch', pattern: /\|\s*(?:second|repeated|repeat) cycle\s*\|\s*reset/i },
|
|
205
|
+
{ label: 'machine-blocked disposition', pattern: /machine[\s-]blocked/i },
|
|
206
|
+
{ label: 'replan budget', pattern: /replan[\s-]budget/i },
|
|
207
|
+
{ label: 'runtime worker leases', pattern: /worker lease/i },
|
|
208
|
+
{ label: 'retired loop-budget invariant', pattern: /\bloop-budget\b/i },
|
|
209
|
+
{ label: 'explicit iteration budget', pattern: /explicit iteration budget/i },
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
function collectRetiredClaimTargets() {
|
|
213
|
+
const targets = new Set();
|
|
214
|
+
const walk = (dir) => {
|
|
215
|
+
let entries;
|
|
216
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
const p = path.join(dir, entry.name);
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') walk(p);
|
|
221
|
+
} else if (RETIRED_CLAIM_EXTENSIONS.has(path.extname(entry.name))) {
|
|
222
|
+
targets.add(p);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
for (const dir of RETIRED_CLAIM_DIRS) walk(path.join(ROOT, dir));
|
|
227
|
+
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
|
|
228
|
+
if (entry.isFile() && (entry.name.endsWith('.sh') || entry.name === 'Dockerfile')) {
|
|
229
|
+
targets.add(path.join(ROOT, entry.name));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const behavioralDir = path.join(ROOT, 'behavioral-evals');
|
|
233
|
+
if (fs.existsSync(behavioralDir)) {
|
|
234
|
+
for (const entry of fs.readdirSync(behavioralDir, { withFileTypes: true })) {
|
|
235
|
+
if (entry.isFile() && entry.name.endsWith('.md')) targets.add(path.join(behavioralDir, entry.name));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const opencodeReadme = path.join(ROOT, 'opencode-plugin', 'README.md');
|
|
239
|
+
if (fs.existsSync(opencodeReadme)) targets.add(opencodeReadme);
|
|
240
|
+
return [...targets].sort();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const retiredClaimTargets = collectRetiredClaimTargets();
|
|
244
|
+
const retiredClaimHits = new Map(RETIRED_CLAIM_SCAN.map((rule) => [rule.label, []]));
|
|
245
|
+
for (const file of retiredClaimTargets) {
|
|
246
|
+
const rel = path.relative(ROOT, file).split(path.sep).join('/');
|
|
247
|
+
if (RETIRED_CLAIM_ALLOWLIST.some((allowed) => allowed.test(rel))) continue;
|
|
248
|
+
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
|
|
249
|
+
lines.forEach((line, index) => {
|
|
250
|
+
for (const rule of RETIRED_CLAIM_SCAN) {
|
|
251
|
+
if (!rule.pattern.test(line)) continue;
|
|
252
|
+
if (rule.historicalAllowed && HISTORICAL_CONTEXT.test(line)) continue;
|
|
253
|
+
retiredClaimHits.get(rule.label).push(`${rel}:${index + 1}`);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
for (const rule of RETIRED_CLAIM_SCAN) {
|
|
258
|
+
const hits = retiredClaimHits.get(rule.label);
|
|
259
|
+
check(
|
|
260
|
+
`retired-claim scan: ${rule.label} absent across the agent-facing surface`,
|
|
261
|
+
hits.length === 0,
|
|
262
|
+
`stale pre-#190 contract wording at ${hits.join(', ')}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
console.log(`Scanned ${retiredClaimTargets.length} file(s) for retired advisory-contract claims.`);
|
|
266
|
+
|
|
167
267
|
// --- 3+4. Distribution manifests -----------------------------------------
|
|
168
268
|
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
169
269
|
const pluginJsonPath = path.join(ROOT, '.claude-plugin', 'plugin.json');
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
10
|
+
let failed = 0;
|
|
11
|
+
|
|
12
|
+
function check(condition, message) {
|
|
13
|
+
if (condition) {
|
|
14
|
+
console.log(`PASS ${message}`);
|
|
15
|
+
} else {
|
|
16
|
+
failed += 1;
|
|
17
|
+
console.error(`FAIL ${message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const dockerVerify = fs.readFileSync(path.join(ROOT, 'docker-verify.sh'), 'utf8');
|
|
22
|
+
const legacyStatePath = '.claude/harness-everything/state/sessions/default';
|
|
23
|
+
check(/STATE_DIR=\$\(node -e[\s\S]*getSessionDir\(null, 'default'\)/.test(dockerVerify),
|
|
24
|
+
'#205 docker verifier resolves one canonical session state directory');
|
|
25
|
+
check(!dockerVerify.includes(legacyStatePath),
|
|
26
|
+
'#205 docker verifier does not seed or read the legacy repo-relative state directory');
|
|
27
|
+
check((dockerVerify.match(/\$STATE_DIR\//g) || []).length >= 10,
|
|
28
|
+
'#205 docker verifier routes all state checks through STATE_DIR');
|
|
29
|
+
|
|
30
|
+
const architecture = fs.readFileSync(path.join(ROOT, 'docs/architecture.md'), 'utf8');
|
|
31
|
+
check(architecture.includes('advisory loop-awareness reminders'),
|
|
32
|
+
'#206 architecture names loop-awareness as advisory');
|
|
33
|
+
check(!/\bloop budgets\b/.test(architecture),
|
|
34
|
+
'#206 architecture does not describe retired loop budgets as enforcement');
|
|
35
|
+
|
|
36
|
+
const commitlintConfig = require(path.join(ROOT, 'commitlint.config.js'));
|
|
37
|
+
check(JSON.stringify(commitlintConfig.rules['subject-case']) === JSON.stringify([2, 'always', 'lower-case']),
|
|
38
|
+
'#207 commitlint aligns subject-case with repository Conventional Commit history');
|
|
39
|
+
|
|
40
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-issue-207-'));
|
|
41
|
+
const messageFile = path.join(tempDir, 'commit-message.txt');
|
|
42
|
+
fs.writeFileSync(messageFile, 'fix(runtime): align post-190 advisory contracts\n', 'utf8');
|
|
43
|
+
const commitlintPackage = require.resolve('@commitlint/cli/package.json');
|
|
44
|
+
const commitlintMeta = JSON.parse(fs.readFileSync(commitlintPackage, 'utf8'));
|
|
45
|
+
const commitlintBin = typeof commitlintMeta.bin === 'string'
|
|
46
|
+
? commitlintMeta.bin
|
|
47
|
+
: commitlintMeta.bin.commitlint;
|
|
48
|
+
const commitlintCli = path.resolve(path.dirname(commitlintPackage), commitlintBin);
|
|
49
|
+
const result = spawnSync(process.execPath, [commitlintCli, '--edit', messageFile], {
|
|
50
|
+
cwd: ROOT,
|
|
51
|
+
encoding: 'utf8',
|
|
52
|
+
});
|
|
53
|
+
check(result.status === 0,
|
|
54
|
+
'#207 representative lowercase repository subject passes commitlint');
|
|
55
|
+
|
|
56
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
57
|
+
|
|
58
|
+
console.log(`\n${failed === 0 ? 'PASS' : 'FAIL'}: issues #205-#207 regression mechanism (${failed} failure${failed === 1 ? '' : 's'})`);
|
|
59
|
+
process.exit(failed === 0 ? 0 : 1);
|
package/commitlint.config.js
CHANGED
package/docker-verify.sh
CHANGED
|
@@ -26,13 +26,12 @@ npm test
|
|
|
26
26
|
echo -e "\n[Step 3] Running Mechanism Checks..."
|
|
27
27
|
|
|
28
28
|
# 3a. Rule of 3 Circuit Breaker actually blocks
|
|
29
|
-
# Runtime state
|
|
30
|
-
#
|
|
31
|
-
|
|
29
|
+
# Runtime state is resolved once through the same workspace-keyed helper used by
|
|
30
|
+
# the hooks. Invocations with no session_id use the fixed default session.
|
|
31
|
+
STATE_DIR=$(node -e "process.stdout.write(require('./hooks/scripts/lib/harness-state').getSessionDir(null, 'default'))")
|
|
32
32
|
echo -e "\n---> Running 3a: Rule of 3 Circuit Breaker"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
echo '{"count":3,"lastHash":"verify-test","zoomOutResolved":false}' > .claude/harness-everything/state/sessions/default/rule-of-3-state.json
|
|
33
|
+
rm -f "$STATE_DIR/zoom-out-report.md"
|
|
34
|
+
echo '{"count":3,"lastHash":"verify-test","zoomOutResolved":false}' > "$STATE_DIR/rule-of-3-state.json"
|
|
36
35
|
|
|
37
36
|
# We expect this to exit with 2 and print CRITICAL trigger message
|
|
38
37
|
set +e
|
|
@@ -68,8 +67,8 @@ echo "✅ PASS: Rule of 3 reset check passed."
|
|
|
68
67
|
|
|
69
68
|
# 3a-bis. Zoom-out reflection report releases the breaker (self-recovery path)
|
|
70
69
|
echo -e "\n---> Running 3a-bis: Zoom-out report releases the breaker"
|
|
71
|
-
echo '{"count":3,"lastHash":"verify-test","zoomOutResolved":false,"lastFailureAt":0,"zoomOutCycles":0}' >
|
|
72
|
-
printf '## Goal\nx\n## Failed Attempts\nx\n## Verified Facts\nx\n## Diagnosis\nx\n## Decision\nRESUME: new approach\n' >
|
|
70
|
+
echo '{"count":3,"lastHash":"verify-test","zoomOutResolved":false,"lastFailureAt":0,"zoomOutCycles":0}' > "$STATE_DIR/rule-of-3-state.json"
|
|
71
|
+
printf '## Goal\nx\n## Failed Attempts\nx\n## Verified Facts\nx\n## Diagnosis\nx\n## Decision\nRESUME: new approach\n' > "$STATE_DIR/zoom-out-report.md"
|
|
73
72
|
|
|
74
73
|
set +e
|
|
75
74
|
node hooks/scripts/rule-of-3.js
|
|
@@ -79,31 +78,67 @@ if [ "$EXIT_CODE" -ne 0 ]; then
|
|
|
79
78
|
echo "❌ FAIL: Valid reflection report did not release the breaker (expected 0, got $EXIT_CODE)"
|
|
80
79
|
exit 1
|
|
81
80
|
fi
|
|
82
|
-
if ! node -e "const s=require(
|
|
81
|
+
if ! node -e "const s=require(process.argv[1]); process.exit(s.count===0 && s.zoomOutResolved===true && s.zoomOutCycles===1 ? 0 : 1)" "$STATE_DIR/rule-of-3-state.json"; then
|
|
83
82
|
echo "❌ FAIL: Breaker state not updated after report release"
|
|
84
|
-
cat
|
|
83
|
+
cat "$STATE_DIR/rule-of-3-state.json"
|
|
85
84
|
exit 1
|
|
86
85
|
fi
|
|
87
86
|
echo "✅ PASS: Valid reflection report released the breaker (self-recovery)."
|
|
88
87
|
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
|
|
88
|
+
# 3a-ter. Re-trip: three more failures on the same signature after a released
|
|
89
|
+
# cycle ask for ANOTHER zoom-out. #190 retired the second-stage escalation, so
|
|
90
|
+
# the contract is exit 2 plus the reflection-required message the hook actually
|
|
91
|
+
# prints - never a permanent lock (ci/mechanism-2a-rule-of-3.test.js:58-64).
|
|
92
|
+
echo -e "\n---> Running 3a-ter: Re-trip requests another zoom-out"
|
|
93
|
+
# Seed through the hooks' own resolver in the already resolved workspace-keyed
|
|
94
|
+
# state root.
|
|
95
|
+
RETRIP_AT=$(node -e "process.stdout.write(String(Date.now()))")
|
|
96
|
+
node -e "require('fs').writeFileSync(process.argv[1], JSON.stringify({count:3,lastHash:'verify-test',zoomOutResolved:false,lastFailureAt:Number(process.argv[2]),zoomOutCycles:1}))" "$STATE_DIR/rule-of-3-state.json" "$RETRIP_AT"
|
|
92
97
|
set +e
|
|
93
98
|
node hooks/scripts/rule-of-3.js 2>rule-of-3-err.log
|
|
94
99
|
EXIT_CODE=$?
|
|
95
100
|
set -e
|
|
96
101
|
if [ "$EXIT_CODE" -ne 2 ]; then
|
|
97
|
-
echo "❌ FAIL:
|
|
102
|
+
echo "❌ FAIL: Re-trip did not request another zoom-out (expected 2, got $EXIT_CODE)"
|
|
98
103
|
cat rule-of-3-err.log
|
|
99
104
|
exit 1
|
|
100
105
|
fi
|
|
101
|
-
if ! grep -q "
|
|
102
|
-
echo "❌ FAIL:
|
|
106
|
+
if ! grep -q "RULE OF 3 CIRCUIT BREAKER TRIGGERED" rule-of-3-err.log; then
|
|
107
|
+
echo "❌ FAIL: Re-trip output did not contain the circuit-breaker trigger message"
|
|
103
108
|
cat rule-of-3-err.log
|
|
104
109
|
exit 1
|
|
105
110
|
fi
|
|
106
|
-
|
|
111
|
+
if ! grep -q "ACTION REQUIRED (reflect first)" rule-of-3-err.log; then
|
|
112
|
+
echo "❌ FAIL: Re-trip output did not demand reflection"
|
|
113
|
+
cat rule-of-3-err.log
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
# Negative guard: the retired escalation wording must never come back (#190).
|
|
117
|
+
if grep -qi "hard lock" rule-of-3-err.log; then # this must never match
|
|
118
|
+
echo "❌ FAIL: Re-trip printed the retired hard-lock escalation, which no longer exists"
|
|
119
|
+
cat rule-of-3-err.log
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
echo "✅ PASS: Re-trip asked for another zoom-out, with no retired escalation wording."
|
|
123
|
+
|
|
124
|
+
# A fresh report, written after the re-trip failure, releases the breaker again.
|
|
125
|
+
printf '## Goal\nx\n## Failed Attempts\nx\n## Verified Facts\nx\n## Diagnosis\nx\n## Decision\nRESUME: second untried approach\n' > "$STATE_DIR/zoom-out-report.md"
|
|
126
|
+
set +e
|
|
127
|
+
node hooks/scripts/rule-of-3.js
|
|
128
|
+
EXIT_CODE=$?
|
|
129
|
+
set -e
|
|
130
|
+
if [ "$EXIT_CODE" -ne 0 ]; then
|
|
131
|
+
echo "❌ FAIL: Fresh reflection report did not release the re-tripped breaker (expected 0, got $EXIT_CODE)"
|
|
132
|
+
exit 1
|
|
133
|
+
fi
|
|
134
|
+
if ! node -e "const s=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); process.exit(s.count===0 && s.zoomOutResolved===true && s.zoomOutCycles===2 ? 0 : 1)" "$STATE_DIR/rule-of-3-state.json"; then
|
|
135
|
+
echo "❌ FAIL: Breaker state not updated after the second report release"
|
|
136
|
+
cat "$STATE_DIR/rule-of-3-state.json"
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
echo "✅ PASS: A fresh report released the re-tripped breaker (advisory re-trip, not an escalation)."
|
|
140
|
+
|
|
141
|
+
# harness:reset stays an optional manual clear, not a required escalation step.
|
|
107
142
|
npm run harness:reset
|
|
108
143
|
|
|
109
144
|
|
|
@@ -134,17 +169,17 @@ echo "✅ PASS: Boundary Guard blocked large read successfully."
|
|
|
134
169
|
|
|
135
170
|
# 3c. State persistence (WAL) records failure and handles clear
|
|
136
171
|
echo -e "\n---> Running 3c: State persistence (WAL)"
|
|
137
|
-
rm -f
|
|
172
|
+
rm -f "$STATE_DIR/handoff-state.json"
|
|
138
173
|
|
|
139
174
|
# Record failure
|
|
140
175
|
echo '{"tool_name":"Bash","tool_response":{"stdout":"","stderr":"npm ERR! verify-test failure"}}' | node hooks/scripts/state-persist.js
|
|
141
176
|
|
|
142
|
-
if [ ! -f "
|
|
177
|
+
if [ ! -f "$STATE_DIR/handoff-state.json" ]; then
|
|
143
178
|
echo "❌ FAIL: handoff-state.json was not created"
|
|
144
179
|
exit 1
|
|
145
180
|
fi
|
|
146
181
|
|
|
147
|
-
STATUS=$(node -e "console.log(JSON.parse(require('fs').readFileSync(
|
|
182
|
+
STATUS=$(node -e "console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).status)" "$STATE_DIR/handoff-state.json")
|
|
148
183
|
if [ "$STATUS" != "failed" ]; then
|
|
149
184
|
echo "❌ FAIL: Expected status to be 'failed', got '$STATUS'"
|
|
150
185
|
exit 1
|
|
@@ -215,8 +250,8 @@ echo "✅ PASS: Subagent scope guard successfully blocked out-of-scope modificat
|
|
|
215
250
|
|
|
216
251
|
# 3f. Stop gate bounces an unverified-edit stop exactly once
|
|
217
252
|
echo -e "\n---> Running 3f: Stop Gate"
|
|
218
|
-
rm -f
|
|
219
|
-
node -e "require('fs').
|
|
253
|
+
rm -f "$STATE_DIR/stop-gate-state.json"
|
|
254
|
+
node -e "require('fs').writeFileSync(process.argv[1], JSON.stringify({status:'idle',lastEditAt:Date.now(),lastVerifyAt:0}))" "$STATE_DIR/handoff-state.json"
|
|
220
255
|
echo "dirty" > .verify-dirty.tmp
|
|
221
256
|
|
|
222
257
|
set +e
|
|
@@ -246,7 +281,7 @@ if [ "$EXIT_CODE" -ne 0 ]; then
|
|
|
246
281
|
fi
|
|
247
282
|
|
|
248
283
|
# Loop guard: stop_hook_active always passes
|
|
249
|
-
rm -f
|
|
284
|
+
rm -f "$STATE_DIR/stop-gate-state.json"
|
|
250
285
|
set +e
|
|
251
286
|
echo '{"stop_hook_active":true}' | node hooks/scripts/stop-gate.js
|
|
252
287
|
EXIT_CODE=$?
|
|
@@ -255,7 +290,7 @@ if [ "$EXIT_CODE" -ne 0 ]; then
|
|
|
255
290
|
echo "❌ FAIL: Stop gate blocked despite stop_hook_active (expected 0, got $EXIT_CODE)"
|
|
256
291
|
exit 1
|
|
257
292
|
fi
|
|
258
|
-
rm -f .verify-dirty.tmp
|
|
293
|
+
rm -f .verify-dirty.tmp "$STATE_DIR/handoff-state.json" "$STATE_DIR/stop-gate-state.json"
|
|
259
294
|
echo "✅ PASS: Stop gate bounced once, then respected the batch and loop guards."
|
|
260
295
|
|
|
261
296
|
# Clean up temporary test output logs
|
package/docs/architecture.md
CHANGED
|
@@ -71,7 +71,7 @@ For selected Fable topologies on Claude:
|
|
|
71
71
|
- `workflow-stop-gate.js` reports unresolved stage/verification evidence without rejecting Stop;
|
|
72
72
|
- `workflow-disposition.js` records explicit lifecycle/audit decisions; numeric budgets are no longer authoritative control state.
|
|
73
73
|
|
|
74
|
-
Other topologies use their own applicable mechanisms: loop
|
|
74
|
+
Other topologies use their own applicable mechanisms: advisory loop-awareness reminders, regular verification stop-gates, action gates, and task-specific skills. Mechanism coverage is not assumed identical across hosts.
|
|
75
75
|
|
|
76
76
|
### Scoped runtime advisories and capabilities
|
|
77
77
|
|
|
@@ -55,7 +55,7 @@ HARNESS_DECISION
|
|
|
55
55
|
tier: 2
|
|
56
56
|
strategy: iterative-single
|
|
57
57
|
workflow_state: active
|
|
58
|
-
required:
|
|
58
|
+
required: scope-lock, objective-verification, loop-awareness
|
|
59
59
|
suggest: tdd, verification-loop
|
|
60
60
|
suggestion_policy: evaluate-applicability
|
|
61
61
|
escape_policy: workflow-uncovered-scope-only
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Source of truth: `zoom-out/SKILL.md`.
|
|
6
6
|
|
|
7
|
-
Contract summary from SKILL.md — Trigger: three same-signature failures or an explicit loop/rethink request. Output: fact-checked report ending in `RESUME` or `ESCALATE`. State: writes the session `zoom-out-report.md`; reset may clear breaker state. Gate: `hooks/scripts/rule-of-3.js` plus a valid report;
|
|
7
|
+
Contract summary from SKILL.md — Trigger: three same-signature failures or an explicit loop/rethink request. Output: fact-checked report ending in `RESUME` or `ESCALATE`. State: writes the session `zoom-out-report.md`; reset may clear breaker state. Gate: `hooks/scripts/rule-of-3.js` plus a valid report; a later three-failure cycle requests another reflection. USE FOR: "you are stuck in a loop", "the same error keeps failing", "rethink your assumptions". DO NOT USE FOR: routine single-failure debugging, greenfield planning without failures.
|
|
8
8
|
|
|
9
9
|
## 1. Skill Behavior Workflow
|
|
10
10
|
|
|
@@ -16,9 +16,9 @@ graph TD
|
|
|
16
16
|
Fill --> Decide{RESUME or ESCALATE?}
|
|
17
17
|
Decide -->|untried path| Resume[RESUME On Untried Path]
|
|
18
18
|
Decide -->|genuine user decision| Escalate[ESCALATE With Options]
|
|
19
|
-
Resume --> Again{
|
|
20
|
-
Again -->|
|
|
21
|
-
|
|
19
|
+
Resume --> Again{Three More Matching Failures?}
|
|
20
|
+
Again -->|yes| Cycle[Another Zoom-Out Cycle]
|
|
21
|
+
Cycle --> Evolve[Record Insight With self-evolve]
|
|
22
22
|
Again -->|no| Done([Report Complete])
|
|
23
23
|
Escalate --> Done
|
|
24
24
|
```
|
|
@@ -47,7 +47,7 @@ graph TD
|
|
|
47
47
|
Write --> Choice{Untried Path Exists?}
|
|
48
48
|
Choice -->|yes| Resume2[RESUME On Untried Path]
|
|
49
49
|
Choice -->|no| Esc2[ESCALATE With Options]
|
|
50
|
-
Resume2 -->|
|
|
50
|
+
Resume2 -->|three more matching failures| Cycle2[Another Zoom-Out Cycle Then self-evolve]
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
Template: `zoom-out/templates/zoom-out-report.template.md`. Enforcement: `hooks/scripts/rule-of-3.js`. Deep dive: `zoom-out/references/circuit-breaker.md`.
|
|
@@ -58,5 +58,5 @@ Template: `zoom-out/templates/zoom-out-report.template.md`. Enforcement: `hooks/
|
|
|
58
58
|
- [ ] Goal, files, configuration, and logs were rechecked with read-only tools before any resume
|
|
59
59
|
- [ ] `templates/zoom-out-report.template.md` was filled and the session `zoom-out-report.md` ends in `RESUME` or `ESCALATE`
|
|
60
60
|
- [ ] Resume targeted only an untried path; genuine user decisions were escalated with options
|
|
61
|
-
- [ ]
|
|
61
|
+
- [ ] A later three-failure cycle ran another zoom-out; `npm run harness:reset` was used only as an optional manual clear, with the insight recorded via `self-evolve`
|
|
62
62
|
- [ ] Routine single-failure debugging and greenfield planning without failures were not routed here
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Issue #85 Phase 3 makes the stage map a machine-readable execution contract.
|
|
4
4
|
The router still chooses only the execution topology. Fable owns the stage map,
|
|
5
|
-
model selector, worker briefs, stage audit, replan
|
|
5
|
+
model selector, worker briefs, stage audit, advisory replan limits, and cold
|
|
6
|
+
verifier.
|
|
6
7
|
Phase 4 adds an optional bounded ensemble modifier without creating a new base
|
|
7
8
|
topology.
|
|
8
9
|
|
package/fable-mode/SKILL.md
CHANGED
|
@@ -77,11 +77,12 @@ spawn workers.
|
|
|
77
77
|
|
|
78
78
|
For `fable-parallel`, spawn only stages in the same validated execution batch;
|
|
79
79
|
never invent an additional parallel edge. A stage depending on another stage is
|
|
80
|
-
not in the same ready batch.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
not in the same ready batch. Treat `workflowPlan.limits.maxWorkers` as a
|
|
81
|
+
planning cap you are expected to respect: it is advisory. `workflow-plan-consumer.js`
|
|
82
|
+
returns every dependency-ready stage without chunking to that cap, and nothing
|
|
83
|
+
leases or counts workers at runtime, so exceeding it is your own judgement call,
|
|
84
|
+
not a blocked action. For other Fable strategies, serialize the returned batches
|
|
85
|
+
unless a later validated plan says otherwise.
|
|
85
86
|
|
|
86
87
|
**3. Verify with a check that can fail — external artifacts only.** Each stage
|
|
87
88
|
defines a pass condition an external artifact satisfies: a test that runs, a file
|
|
@@ -110,7 +110,7 @@ This does not choose a topology and does not itself enforce the gate. Phase 5 ow
|
|
|
110
110
|
|
|
111
111
|
Examples:
|
|
112
112
|
|
|
113
|
-
- `
|
|
113
|
+
- `loop-awareness` and `objective-verification` are mandatory for `iterative-single`, on top of the base `scope-lock`, `verify-before-claim`, and `replan-after-repeated-failure`;
|
|
114
114
|
- `tdd` and `verification-loop` remain advisory skill suggestions;
|
|
115
115
|
- `stage-contracts` and `cold-verification` are mandatory for Fable topologies;
|
|
116
116
|
- `multi-agent-workspace` is suggested only when the selected topology actually requires durable workspace behavior.
|
package/package.json
CHANGED
|
@@ -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.16.
|
|
4
|
+
"version": "0.16.4",
|
|
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",
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Issue #85 Phase 3 makes the stage map a machine-readable execution contract.
|
|
4
4
|
The router still chooses only the execution topology. Fable owns the stage map,
|
|
5
|
-
model selector, worker briefs, stage audit, replan
|
|
5
|
+
model selector, worker briefs, stage audit, advisory replan limits, and cold
|
|
6
|
+
verifier.
|
|
6
7
|
Phase 4 adds an optional bounded ensemble modifier without creating a new base
|
|
7
8
|
topology.
|
|
8
9
|
|
|
@@ -77,11 +77,12 @@ spawn workers.
|
|
|
77
77
|
|
|
78
78
|
For `fable-parallel`, spawn only stages in the same validated execution batch;
|
|
79
79
|
never invent an additional parallel edge. A stage depending on another stage is
|
|
80
|
-
not in the same ready batch.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
not in the same ready batch. Treat `workflowPlan.limits.maxWorkers` as a
|
|
81
|
+
planning cap you are expected to respect: it is advisory. `workflow-plan-consumer.js`
|
|
82
|
+
returns every dependency-ready stage without chunking to that cap, and nothing
|
|
83
|
+
leases or counts workers at runtime, so exceeding it is your own judgement call,
|
|
84
|
+
not a blocked action. For other Fable strategies, serialize the returned batches
|
|
85
|
+
unless a later validated plan says otherwise.
|
|
85
86
|
|
|
86
87
|
**3. Verify with a check that can fail — external artifacts only.** Each stage
|
|
87
88
|
defines a pass condition an external artifact satisfies: a test that runs, a file
|
package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md
CHANGED
|
@@ -110,7 +110,7 @@ This does not choose a topology and does not itself enforce the gate. Phase 5 ow
|
|
|
110
110
|
|
|
111
111
|
Examples:
|
|
112
112
|
|
|
113
|
-
- `
|
|
113
|
+
- `loop-awareness` and `objective-verification` are mandatory for `iterative-single`, on top of the base `scope-lock`, `verify-before-claim`, and `replan-after-repeated-failure`;
|
|
114
114
|
- `tdd` and `verification-loop` remain advisory skill suggestions;
|
|
115
115
|
- `stage-contracts` and `cold-verification` are mandatory for Fable topologies;
|
|
116
116
|
- `multi-agent-workspace` is suggested only when the selected topology actually requires durable workspace behavior.
|