harness-everything 0.15.0 → 0.16.1
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/AGENTS.md +2 -2
- package/CHANGELOG.md +23 -0
- package/README.md +7 -7
- package/VERIFICATION.md +3 -3
- package/bin/cli.js +17 -0
- package/ci/invariant-routing.test.js +58 -158
- package/ci/mechanism-2a-rule-of-3.test.js +73 -138
- package/ci/mechanism-2ac-atomic-commit-check.test.js +2 -2
- package/ci/mechanism-2af-worktree-isolation.test.js +38 -196
- package/ci/mechanism-2ah-workflow-lifecycle.test.js +20 -831
- package/ci/mechanism-2ai-workspace-fingerprint-batch.test.js +3 -4
- package/ci/mechanism-2al-codex-hooks-compat.test.js +286 -0
- package/ci/mechanism-2b-boundary-guard.test.js +6 -16
- package/ci/mechanism-2e-scope-guard.test.js +8 -24
- package/ci/mechanism-2f-stop-gate.test.js +21 -75
- package/ci/mechanism-2n-opencode-plugin.test.js +13 -28
- package/ci/openai-plugin-package.test.js +8 -8
- package/ci/openai-plugin-runtime.test.js +2 -2
- package/ci/router-workflow-plan.test.js +6 -6
- package/ci/self-evolve-loop.test.js +4 -1
- package/ci/workflow-execution-contract.test.js +51 -129
- package/ci/workflow-plan-consumers-edge.test.js +1 -1
- package/ci/workflow-plan-consumers.test.js +6 -6
- package/docs/architecture.md +21 -21
- package/docs/enforcement-locks.md +41 -102
- package/docs/mechanism-first-skill-mesh.md +2 -2
- package/docs/philosophy.md +2 -2
- package/docs/platform-capabilities.md +19 -3
- package/docs/platform-compatibility.json +4 -4
- package/docs/routing.md +14 -14
- package/docs/workflow-runtime.md +80 -110
- package/evals/harness-everything/tasks/positive-tier1.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier2.yaml +1 -1
- package/evals/harness-everything/tasks/positive-tier3.yaml +1 -1
- package/evals/using-git-worktrees/tasks/positive-trigger.yaml +1 -1
- package/fable-mode/SKILL.md +1 -1
- 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/fable-mode/scripts/workflow-plan-consumer.js +4 -3
- package/harness-everything/SKILL.md +11 -11
- package/harness-everything/references/triage-and-tiers.md +11 -11
- package/harness-everything/scripts/kernel-router-core.js +5 -5
- package/harness-everything/scripts/kernel-router.js +15 -17
- package/harness-everything/scripts/router-contract-core.js +1 -1
- package/hooks/hooks.json +10 -23
- package/hooks/scripts/atomic-commit-check.js +1 -1
- package/hooks/scripts/boundary-guard.js +4 -4
- package/hooks/scripts/contract-test.js +8 -11
- package/hooks/scripts/depth-guard.js +2 -2
- package/hooks/scripts/lib/workflow-runtime.js +24 -402
- package/hooks/scripts/rule-of-3-tracker.js +28 -31
- package/hooks/scripts/rule-of-3.js +4 -28
- package/hooks/scripts/state-persist.js +36 -136
- package/hooks/scripts/stop-gate.js +15 -48
- package/hooks/scripts/subagent-scope-guard.js +8 -8
- package/hooks/scripts/workflow-disposition.js +2 -10
- package/hooks/scripts/workflow-gate.js +33 -84
- package/hooks/scripts/workflow-mutation-denied.js +3 -15
- package/hooks/scripts/workflow-stop-gate.js +23 -38
- package/opencode-plugin/README.md +9 -10
- package/opencode-plugin/index.mjs +14 -30
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +4 -2
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/hooks.json +3 -3
- package/plugins/harness-everything/hooks/scripts/atomic-commit-check.js +1 -1
- package/plugins/harness-everything/hooks/scripts/boundary-guard.js +4 -4
- package/plugins/harness-everything/hooks/scripts/contract-test.js +8 -11
- package/plugins/harness-everything/hooks/scripts/depth-guard.js +2 -2
- package/plugins/harness-everything/hooks/scripts/lib/workflow-runtime.js +24 -402
- package/plugins/harness-everything/hooks/scripts/rule-of-3-tracker.js +28 -31
- package/plugins/harness-everything/hooks/scripts/rule-of-3.js +4 -28
- package/plugins/harness-everything/hooks/scripts/state-persist.js +36 -136
- package/plugins/harness-everything/hooks/scripts/stop-gate.js +15 -48
- package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +8 -8
- package/plugins/harness-everything/hooks/scripts/workflow-disposition.js +2 -10
- package/plugins/harness-everything/hooks/scripts/workflow-gate.js +33 -84
- package/plugins/harness-everything/hooks/scripts/workflow-mutation-denied.js +3 -15
- package/plugins/harness-everything/hooks/scripts/workflow-stop-gate.js +23 -38
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/scripts/lib/advisory-text.js +16 -29
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +1 -1
- 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/fable-mode/scripts/workflow-plan-consumer.js +4 -3
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +11 -11
- package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +11 -11
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router-core.js +5 -5
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +15 -17
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +1 -1
- package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +5 -5
- package/scripts/codex-hooks-compat.js +521 -0
- package/scripts/lib/advisory-text.js +16 -29
- package/using-git-worktrees/SKILL.md +5 -5
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# Harness
|
|
1
|
+
# Harness Guidance + Rule-of-3 Plugin for opencode
|
|
2
2
|
|
|
3
|
-
Adds
|
|
3
|
+
Adds lifecycle guidance, verification reminders, and the Rule-of-3 zoom-out boundary in opencode, addressing the
|
|
4
4
|
limitation that skills are otherwise advisory-only on that platform.
|
|
5
5
|
|
|
6
|
-
> **Evidence boundary:** the plugin is implemented against opencode's real plugin API and covered by deterministic mechanism tests. Live plugin loading remains unverified beyond one scoped surface: retained evidence supports project-scope `.js` loading and edit/verification state on OpenCode 1.18.31 (macOS) — [live-host evidence](../benchmarks/results/live-host/opencode-2026-09-16/README.md). The
|
|
6
|
+
> **Evidence boundary:** the plugin is implemented against opencode's real plugin API and covered by deterministic mechanism tests. Live plugin loading remains unverified beyond one scoped surface: retained evidence supports project-scope `.js` loading and edit/verification state on OpenCode 1.18.31 (macOS) — [live-host evidence](../benchmarks/results/live-host/opencode-2026-09-16/README.md). The retained snapshot predates #190 and is historical evidence only: its final snapshot was post-reset; the old hard-lock was only an interactive observation, with no retained blocked-tool trace. Current behavior has no permanent post-reflection hard lock; only the third matching verification failure pauses edits for reflection. Reflection was operator-seeded, then agent-rewritten, so no behavioral-effectiveness claim is made. Global scope, npm-package installation, and other host versions remain unverified. See [`../docs/platform-capabilities.md`](../docs/platform-capabilities.md).
|
|
7
7
|
|
|
8
8
|
## Problem
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Harness now keeps workflow/productivity rules advisory on both Claude Code and opencode. Explicit permission boundaries and the third-failure Rule-of-3 reflection are the intentional blocking exceptions. In opencode, skills
|
|
11
11
|
only SUGGEST rules (soft guidance). This means:
|
|
12
12
|
- Under pressure, agents can bypass skills
|
|
13
13
|
- Verification can be skipped
|
|
@@ -24,7 +24,7 @@ see issue #37 for how that was found and fixed. **Naming caveat (issue
|
|
|
24
24
|
#127):** opencode's discovery glob only matches `*.js`/`*.ts`, so the file
|
|
25
25
|
must be copied to a `.js` name when installed - see Installation below.
|
|
26
26
|
|
|
27
|
-
It implements three
|
|
27
|
+
It implements three lifecycle mechanisms across opencode's real hooks:
|
|
28
28
|
|
|
29
29
|
The module follows opencode's V1 plugin contract directly: the named
|
|
30
30
|
`HarnessEnforcement` export is an async factory receiving the opencode context
|
|
@@ -41,7 +41,7 @@ V1 `(input, output)` shape, including
|
|
|
41
41
|
Fires after every `edit`, `write` or `apply_patch` tool call and marks
|
|
42
42
|
verification as pending.
|
|
43
43
|
|
|
44
|
-
### 2. Verification
|
|
44
|
+
### 2. Verification reminder (`event`, on `session.idle`)
|
|
45
45
|
|
|
46
46
|
opencode has no "before complete" hook to block the way Claude Code's Stop
|
|
47
47
|
hook does. `session.idle` - fired when the agent's turn ends - is the closest
|
|
@@ -52,7 +52,7 @@ and on failure calls `client.session.prompt()` to push a synthetic follow-up
|
|
|
52
52
|
message into the session - forcing the agent to keep working instead of
|
|
53
53
|
actually stopping.
|
|
54
54
|
|
|
55
|
-
### 3.
|
|
55
|
+
### 3. Rule-of-3 zoom-out boundary (`tool.execute.before` + the verification observer above)
|
|
56
56
|
|
|
57
57
|
Enforces Rule of 3 on repeated verification failures (same failing command +
|
|
58
58
|
truncated error, not arbitrary tool failures - `tool.execute.after` has no
|
|
@@ -64,8 +64,7 @@ normalized success/failure field to key a signature on for tools in general):
|
|
|
64
64
|
edit hook and records `lastReflection`
|
|
65
65
|
- the same signature failing again after that reflection was recorded
|
|
66
66
|
hard-locks the breaker
|
|
67
|
-
-
|
|
68
|
-
`apply_patch` call, blocking further edits until the state file is cleared
|
|
67
|
+
- after a valid reflection, the signature count resets; another three matching failures trigger another zoom-out
|
|
69
68
|
or a new session starts
|
|
70
69
|
|
|
71
70
|
Idle handling is bounded: after a failed verification, the plugin records that
|
|
@@ -143,7 +142,7 @@ their patch target names the report file.
|
|
|
143
142
|
`ci/mechanism-2n-opencode-plugin.test.js` imports `index.mjs` and drives its
|
|
144
143
|
exported hooks directly with a mock `client`/`event` context - the same shape
|
|
145
144
|
opencode's plugin loader passes in - covering the edit → idle → follow-up →
|
|
146
|
-
reflection-artifact → retry →
|
|
145
|
+
reflection-artifact → retry → fresh three-failure cycle, repeated-idle idempotency,
|
|
147
146
|
session isolation, reset behavior, legacy-state migration, corrupt-state
|
|
148
147
|
fail-closed behavior, and patch-based reflection writes.
|
|
149
148
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* message via client.session.prompt() to force the agent
|
|
18
18
|
* to keep going instead of truly stopping.)
|
|
19
19
|
* verify.js -> inlined into the session.idle handler
|
|
20
|
-
* circuit-breaker.js -> tool.execute.before (throws to
|
|
20
|
+
* circuit-breaker.js -> tool.execute.before (throws to pause edit tools for the zoom-out boundary
|
|
21
21
|
* once locked) + failure tracking inside session.idle
|
|
22
22
|
* compliance.js -> folded into the same state writes, no separate stage
|
|
23
23
|
*
|
|
@@ -50,7 +50,7 @@ function getStateHome() {
|
|
|
50
50
|
// Keys state per real workspace (`directory`, which opencode itself resolves
|
|
51
51
|
// - never a cwd walk) instead of one flat dir shared by every project on the
|
|
52
52
|
// machine. The pre-fix `~/.harness-state` had no such key at all: a circuit
|
|
53
|
-
// breaker trip in one opencode project
|
|
53
|
+
// breaker trip in one opencode project affected every other one too
|
|
54
54
|
// (issue #42 item #4).
|
|
55
55
|
function getWorkspaceKey(directory) {
|
|
56
56
|
let real = resolve(directory)
|
|
@@ -162,7 +162,6 @@ function defaultEditState() {
|
|
|
162
162
|
function defaultBreakerState() {
|
|
163
163
|
return {
|
|
164
164
|
failures: {},
|
|
165
|
-
hardLock: false,
|
|
166
165
|
lastReflection: null,
|
|
167
166
|
lastReflectionSignature: null,
|
|
168
167
|
reflectionPending: false,
|
|
@@ -177,7 +176,8 @@ function normalizeEditState(state) {
|
|
|
177
176
|
}
|
|
178
177
|
|
|
179
178
|
function normalizeBreakerState(state) {
|
|
180
|
-
|
|
179
|
+
const { hardLock: _retiredHardLock, ...rest } = state || {}
|
|
180
|
+
return { ...defaultBreakerState(), ...rest, failures: (state && state.failures) || {} }
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
function isRecord(value) {
|
|
@@ -193,7 +193,6 @@ function loadBreakerState(file) {
|
|
|
193
193
|
if (
|
|
194
194
|
!isRecord(state) ||
|
|
195
195
|
(state.failures !== undefined && !isRecord(state.failures)) ||
|
|
196
|
-
(state.hardLock !== undefined && typeof state.hardLock !== "boolean") ||
|
|
197
196
|
(state.reflectionPending !== undefined && typeof state.reflectionPending !== "boolean") ||
|
|
198
197
|
!finiteOrNull(state.lastReflection) ||
|
|
199
198
|
!finiteOrNull(state.reflectionRequestedAt) ||
|
|
@@ -209,7 +208,8 @@ function loadBreakerState(file) {
|
|
|
209
208
|
) throw new Error("invalid breaker state shape")
|
|
210
209
|
return normalizeBreakerState(state)
|
|
211
210
|
} catch {
|
|
212
|
-
|
|
211
|
+
console.warn(`Harness: ignoring corrupt circuit-breaker state at "${file}" and starting fresh.`)
|
|
212
|
+
return defaultBreakerState()
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
@@ -246,6 +246,11 @@ function completeReflection(breakerFile, reportFile) {
|
|
|
246
246
|
const completedAt = Math.max(Date.now(), (breaker.reflectionRequestedAt || 0) + 1)
|
|
247
247
|
breaker.lastReflection = completedAt
|
|
248
248
|
breaker.lastReflectionSignature = breaker.reflectionSignature
|
|
249
|
+
if (breaker.reflectionSignature && breaker.failures[breaker.reflectionSignature]) {
|
|
250
|
+
breaker.failures[breaker.reflectionSignature].count = 0
|
|
251
|
+
breaker.failures[breaker.reflectionSignature].firstSeen = completedAt
|
|
252
|
+
breaker.failures[breaker.reflectionSignature].lastSeen = completedAt
|
|
253
|
+
}
|
|
249
254
|
breaker.reflectionPending = false
|
|
250
255
|
breaker.reflectionRequestedAt = null
|
|
251
256
|
breaker.reflectionToken = null
|
|
@@ -304,9 +309,8 @@ function runVerification(cwd) {
|
|
|
304
309
|
|
|
305
310
|
/**
|
|
306
311
|
* Records a verification failure against the circuit breaker and returns the
|
|
307
|
-
* action to take: allow (with retries remaining)
|
|
308
|
-
*
|
|
309
|
-
* reflection was recorded).
|
|
312
|
+
* action to take: allow (with retries remaining) or force_reflection on the
|
|
313
|
+
* third matching failure. Completing reflection resets that signature's count.
|
|
310
314
|
*/
|
|
311
315
|
function tripBreaker(breakerFile, signature) {
|
|
312
316
|
const breaker = loadBreakerState(breakerFile)
|
|
@@ -319,15 +323,6 @@ function tripBreaker(breakerFile, signature) {
|
|
|
319
323
|
entry.lastSeen = Date.now()
|
|
320
324
|
|
|
321
325
|
if (entry.count >= 3) {
|
|
322
|
-
if (
|
|
323
|
-
breaker.lastReflection &&
|
|
324
|
-
breaker.lastReflection > entry.firstSeen &&
|
|
325
|
-
breaker.lastReflectionSignature === signature
|
|
326
|
-
) {
|
|
327
|
-
breaker.hardLock = true
|
|
328
|
-
saveJSON(breakerFile, breaker)
|
|
329
|
-
return { action: "hard_lock", count: entry.count }
|
|
330
|
-
}
|
|
331
326
|
if (!breaker.reflectionPending) {
|
|
332
327
|
breaker.reflectionPending = true
|
|
333
328
|
breaker.reflectionRequestedAt = Date.now()
|
|
@@ -610,12 +605,6 @@ export const HarnessEnforcement = async ({ client, directory }) => {
|
|
|
610
605
|
if (!EDIT_TOOLS.has(input.tool)) return
|
|
611
606
|
const { breakerFile, reflectionFile } = pathsFor(input.sessionID)
|
|
612
607
|
const breaker = loadBreakerState(breakerFile)
|
|
613
|
-
if (breaker.hardLock) {
|
|
614
|
-
throw new Error(
|
|
615
|
-
`Harness circuit breaker hard-locked after a repeat failure post-reflection. ` +
|
|
616
|
-
`Delete "${breakerFile}" or start a new session to reset.`,
|
|
617
|
-
)
|
|
618
|
-
}
|
|
619
608
|
if (breaker.reflectionPending && !isReflectionWrite(input, output, reflectionFile)) {
|
|
620
609
|
throw new Error(
|
|
621
610
|
`Harness reflection is required before another code edit. ` +
|
|
@@ -715,12 +704,7 @@ export const HarnessEnforcement = async ({ client, directory }) => {
|
|
|
715
704
|
saveJSON(editStateFile, state)
|
|
716
705
|
|
|
717
706
|
let text
|
|
718
|
-
if (trip.action === "
|
|
719
|
-
text =
|
|
720
|
-
`Harness: same verification failure ("${failing.command}") returned after a reflection was ` +
|
|
721
|
-
`already recorded. The circuit breaker is now hard-locked - edits are blocked until it is reset.`
|
|
722
|
-
recordCompliance(complianceFile, (c) => c.circuitBreakerTrips++)
|
|
723
|
-
} else if (trip.action === "force_reflection") {
|
|
707
|
+
if (trip.action === "force_reflection") {
|
|
724
708
|
const { stateDir } = pathsFor(activeSession)
|
|
725
709
|
text =
|
|
726
710
|
`Harness: "${failing.command}" has now failed 3 times with the same error. Stop and reflect ` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
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": {
|
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
"test:contract-integrity": "node ci/contract-integrity.test.js",
|
|
55
55
|
"contract:integrity": "node contract-integrity/scripts/audit.js",
|
|
56
56
|
"waza:check": "node scripts/waza-harness-check.js",
|
|
57
|
-
"test:waza:compat": "node ci/mechanism-2aj-waza-reference-adapter.test.js"
|
|
57
|
+
"test:waza:compat": "node ci/mechanism-2aj-waza-reference-adapter.test.js",
|
|
58
|
+
"codex:hooks": "node scripts/codex-hooks-compat.js",
|
|
59
|
+
"test:codex:hooks-compat": "node ci/mechanism-2al-codex-hooks-compat.test.js"
|
|
58
60
|
},
|
|
59
61
|
"engines": {
|
|
60
62
|
"node": ">=22"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"command": "node \"${PLUGIN_ROOT}/hooks/scripts/workflow-gate.js\"",
|
|
40
40
|
"commandWindows": "node \"${PLUGIN_ROOT}\\hooks\\scripts\\workflow-gate.js\"",
|
|
41
41
|
"timeout": 15,
|
|
42
|
-
"statusMessage": "
|
|
42
|
+
"statusMessage": "Reviewing Harness workflow/worktree guidance"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
},
|
|
@@ -248,14 +248,14 @@
|
|
|
248
248
|
"command": "node \"${PLUGIN_ROOT}/hooks/scripts/workflow-stop-gate.js\"",
|
|
249
249
|
"commandWindows": "node \"${PLUGIN_ROOT}\\hooks\\scripts\\workflow-stop-gate.js\"",
|
|
250
250
|
"timeout": 15,
|
|
251
|
-
"statusMessage": "
|
|
251
|
+
"statusMessage": "Reviewing verification reminder"
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
254
|
"type": "command",
|
|
255
255
|
"command": "node \"${PLUGIN_ROOT}/hooks/scripts/stop-gate.js\"",
|
|
256
256
|
"commandWindows": "node \"${PLUGIN_ROOT}\\hooks\\scripts\\stop-gate.js\"",
|
|
257
257
|
"timeout": 15,
|
|
258
|
-
"statusMessage": "
|
|
258
|
+
"statusMessage": "Reviewing verification reminder"
|
|
259
259
|
}
|
|
260
260
|
]
|
|
261
261
|
},
|
|
@@ -143,7 +143,7 @@ function main(payload) {
|
|
|
143
143
|
const changedFiles = statusRaw.split('\n').length;
|
|
144
144
|
console.error(`[Atomic Commit Check] ${state.editCount} edits since the last commit (${changedFiles} files currently changed).`);
|
|
145
145
|
console.error('If a logically complete chunk of work is done, commit it now before continuing - large uncommitted diffs are harder to review and harder to recover from.');
|
|
146
|
-
process.exit(
|
|
146
|
+
process.exit(0);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
process.exit(0);
|
|
@@ -25,10 +25,10 @@ process.stdin.on('end', () => {
|
|
|
25
25
|
if (filePath && !input.limit && fs.existsSync(filePath)) {
|
|
26
26
|
const stat = fs.statSync(filePath);
|
|
27
27
|
if (stat.isFile() && stat.size > MAX_BYTES_WITHOUT_LIMIT) {
|
|
28
|
-
console.error(`[Boundary
|
|
28
|
+
console.error(`[Boundary Reminder]: "${filePath}" is ${(stat.size / 1024).toFixed(0)}KB.`);
|
|
29
29
|
console.error(`Reading a file this large in one call risks context bloat and "lost in the middle" degradation.`);
|
|
30
30
|
console.error(`Re-issue the Read with an explicit "offset"/"limit" to pull a targeted slice, or use Grep to locate the relevant section first.`);
|
|
31
|
-
process.exit(
|
|
31
|
+
process.exit(0);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -41,10 +41,10 @@ process.stdin.on('end', () => {
|
|
|
41
41
|
return asPathSegment.test(searchPath) || asPathSegment.test(pattern);
|
|
42
42
|
});
|
|
43
43
|
if (hitsNoiseDir) {
|
|
44
|
-
console.error(`[Boundary
|
|
44
|
+
console.error(`[Boundary Reminder]: search targets a noise directory (${NOISE_DIRS.join(', ')}).`);
|
|
45
45
|
console.error(`These produce huge, low-signal result sets and rarely contain what you're actually looking for.`);
|
|
46
46
|
console.error(`Scope the search to source directories instead, or state explicitly why this directory must be searched.`);
|
|
47
|
-
process.exit(
|
|
47
|
+
process.exit(0);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
listRunContracts,
|
|
23
23
|
getWorkerId,
|
|
24
24
|
} = require('./lib/fable-contracts');
|
|
25
|
-
const { loadWorkflow, matchingRun
|
|
25
|
+
const { loadWorkflow, matchingRun } = require('./lib/workflow-runtime');
|
|
26
26
|
const { createLearningOpportunity } = require('./lib/learning-opportunity');
|
|
27
27
|
|
|
28
28
|
function resultState(payload) {
|
|
@@ -119,7 +119,7 @@ process.stdin.on('end', () => {
|
|
|
119
119
|
if (resolution.ambiguous || !resolution.match) {
|
|
120
120
|
console.error(`[Contract Test] Ambiguous Fable check command matched ${runMatches.length} active run contract(s): ${command}`);
|
|
121
121
|
console.error('No contract was mutated. Correlate the worker/session to one run before accepting this check.');
|
|
122
|
-
process.exit(
|
|
122
|
+
process.exit(0);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const entry = resolution.match;
|
|
@@ -127,14 +127,14 @@ process.stdin.on('end', () => {
|
|
|
127
127
|
const worker = getWorkerId(payload);
|
|
128
128
|
if (!worker || (entry.contract.workerId && worker !== entry.contract.workerId)) {
|
|
129
129
|
console.error('[Contract Test] Active workflow check lacks a matching observed worker identity.');
|
|
130
|
-
process.exit(
|
|
130
|
+
process.exit(0);
|
|
131
131
|
}
|
|
132
132
|
for (const id of entry.contract.dependsOn || []) {
|
|
133
133
|
const dependency = JSON.parse(fs.readFileSync(path.join(entry.runRoot, 'contracts', `${id}.json`), 'utf8'));
|
|
134
134
|
const escaped = workflowContext.workflow.escapes?.some(item => item.stageId === id && item.runId === entry.contract.runId);
|
|
135
135
|
if (dependency.status !== 'pass' && !escaped) {
|
|
136
136
|
console.error('[Contract Test] Dependency has not passed: ' + id);
|
|
137
|
-
process.exit(
|
|
137
|
+
process.exit(0);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -164,12 +164,9 @@ process.stdin.on('end', () => {
|
|
|
164
164
|
}
|
|
165
165
|
console.log(`[Contract Test] ${contract.planId}/${contract.runId}/${contract.stageId}: ${contract.status.toUpperCase()} ${contract.checkCommand}`);
|
|
166
166
|
if (contract.status === 'fail') {
|
|
167
|
-
if (workflowContext.workflow?.workflowId) {
|
|
168
|
-
recordBudgetEvent(workflowContext, 'revision', { evidence: `${contract.stageId}:${contract.evidence || 'verification-failed'}` });
|
|
169
|
-
}
|
|
170
167
|
console.error(`Evidence: ${contract.evidence || '(no output captured)'}`);
|
|
171
|
-
console.error(`
|
|
172
|
-
process.exit(
|
|
168
|
+
console.error(`Review this failed stage before relying on its output. Artifact: ${contract.outputPath || '(no path recorded)'}`);
|
|
169
|
+
process.exit(0);
|
|
173
170
|
}
|
|
174
171
|
process.exit(0);
|
|
175
172
|
}
|
|
@@ -179,12 +176,12 @@ process.stdin.on('end', () => {
|
|
|
179
176
|
const legacy = resolveLegacyContract(stateRoot, command, payload);
|
|
180
177
|
if (legacy && legacy.ambiguous) {
|
|
181
178
|
console.error(`[Contract Test] Ambiguous legacy stage contracts matched: ${command}`);
|
|
182
|
-
process.exit(
|
|
179
|
+
process.exit(0);
|
|
183
180
|
}
|
|
184
181
|
if (legacy && legacy.failed) {
|
|
185
182
|
console.error(`[Contract Test] Legacy stage "${legacy.contract.stageId}" FAILED its named check: ${legacy.contract.checkCommand}`);
|
|
186
183
|
console.error(`Evidence: ${legacy.contract.evidence || '(no output captured)'}`);
|
|
187
|
-
process.exit(
|
|
184
|
+
process.exit(0);
|
|
188
185
|
}
|
|
189
186
|
process.exit(0);
|
|
190
187
|
} catch (_) {
|
|
@@ -52,10 +52,10 @@ process.stdin.on('end', () => {
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
if (!wasRead) {
|
|
55
|
-
console.error(`[Depth
|
|
55
|
+
console.error(`[Depth Reminder]: "${filePath}" already exists but hasn't been Read in this session.`);
|
|
56
56
|
console.error(`Write() overwrites the whole file - doing that blind risks destroying content you never looked at.`);
|
|
57
57
|
console.error(`Read the file first (even a quick pass), then retry the Write, or use Edit for a targeted change.`);
|
|
58
|
-
process.exit(
|
|
58
|
+
process.exit(0);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
process.exit(0);
|