harness-everything 0.8.0 → 0.9.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +12 -2
- package/AGENTS.md +36 -35
- package/CHANGELOG.md +51 -0
- package/README.md +2 -2
- package/benchmarks/results/reviews/pr142-verification.json +95 -0
- package/ci/action-gate.test.js +83 -3
- package/ci/doc-capability-consistency.test.js +3 -0
- package/ci/invariant-routing.test.js +87 -74
- package/ci/mechanism-2af-worktree-isolation.test.js +152 -0
- package/ci/mechanism-2ag-router-worktree-contract.test.js +44 -0
- package/ci/mechanism-2ah-workflow-lifecycle.test.js +140 -0
- package/ci/openai-plugin-package.test.js +9 -6
- package/ci/phase4-ensemble-memory.test.js +128 -10
- package/ci/router-workflow-plan.test.js +23 -0
- package/ci/workflow-execution-contract.test.js +133 -0
- package/ci/workflow-plan-consumers.test.js +18 -0
- package/docs/architecture.md +111 -114
- package/docs/mechanism-first-skill-mesh.md +92 -103
- package/docs/openai-plugin.md +1 -1
- package/docs/philosophy.md +40 -33
- package/docs/platform-capabilities.md +2 -2
- package/docs/reviews/pr-142-issue-131.md +89 -0
- package/docs/routing.md +61 -124
- package/docs/workflow-runtime.md +109 -0
- package/fable-mode/CONTRACT-FORMAT.md +3 -1
- package/fable-mode/SKILL.md +1 -1
- package/fable-mode/agents/fable-orchestrator.md +9 -7
- 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 +13 -8
- package/harness-everything/SKILL.md +11 -10
- package/harness-everything/references/triage-and-tiers.md +76 -107
- package/harness-everything/schemas/router-task-shape.schema.json +4 -2
- package/harness-everything/schemas/router-workflow-plan.schema.json +3 -2
- package/harness-everything/scripts/kernel-router-core.js +265 -0
- package/harness-everything/scripts/kernel-router.js +118 -223
- package/harness-everything/scripts/router-contract-core.js +633 -0
- package/harness-everything/scripts/router-contract.js +21 -581
- package/harness-everything/scripts/tier-router.js +19 -1
- package/hooks/hooks.json +25 -4
- package/hooks/scripts/action-gate.js +111 -2
- package/hooks/scripts/contract-test.js +27 -2
- package/hooks/scripts/lib/workflow-isolation.js +102 -0
- package/hooks/scripts/lib/workflow-runtime.js +254 -0
- package/hooks/scripts/state-persist.js +1 -0
- package/hooks/scripts/subagent-scope-guard.js +20 -3
- package/hooks/scripts/workflow-disposition.js +104 -0
- package/hooks/scripts/workflow-gate.js +56 -0
- package/hooks/scripts/workflow-stop-gate.js +44 -0
- package/multi-agent-workspace/SKILL.md +2 -2
- package/multi-agent-workspace/scripts/index_memory.js +107 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +2 -2
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/hooks.json +44 -1
- package/plugins/harness-everything/hooks/scripts/action-gate.js +111 -2
- package/plugins/harness-everything/hooks/scripts/contract-test.js +27 -2
- package/plugins/harness-everything/hooks/scripts/lib/workflow-isolation.js +102 -0
- package/plugins/harness-everything/hooks/scripts/lib/workflow-runtime.js +254 -0
- package/plugins/harness-everything/hooks/scripts/state-persist.js +1 -0
- package/plugins/harness-everything/hooks/scripts/subagent-scope-guard.js +20 -3
- package/plugins/harness-everything/hooks/scripts/workflow-disposition.js +104 -0
- package/plugins/harness-everything/hooks/scripts/workflow-gate.js +56 -0
- package/plugins/harness-everything/hooks/scripts/workflow-stop-gate.js +44 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/scripts/lib/advisory-text.js +51 -54
- package/plugins/harness-everything/skills/fable-mode/CONTRACT-FORMAT.md +3 -1
- package/plugins/harness-everything/skills/fable-mode/SKILL.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +9 -7
- 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 +13 -8
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +11 -10
- package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +76 -107
- package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +4 -2
- package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +3 -2
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router-core.js +265 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +118 -223
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +633 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract.js +21 -581
- package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +19 -1
- package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +2 -2
- package/plugins/harness-everything/skills/multi-agent-workspace/scripts/index_memory.js +107 -1
- package/plugins/harness-everything/skills/self-evolve/SKILL.md +6 -6
- package/plugins/harness-everything/skills/self-evolve/references/memory-resolution.md +22 -33
- package/plugins/harness-everything/skills/self-evolve/scripts/persist-memory.js +336 -57
- package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +1 -0
- package/plugins/harness-everything/skills/using-git-worktrees/SKILL.md +14 -16
- package/plugins/harness-everything/skills/using-git-worktrees/references/workflow-details.md +44 -85
- package/scripts/lib/advisory-text.js +51 -54
- package/self-evolve/SKILL.md +6 -6
- package/self-evolve/references/memory-resolution.md +22 -33
- package/self-evolve/scripts/persist-memory.js +336 -57
- package/self-evolve/scripts/self-regression.js +1 -0
- package/using-git-worktrees/SKILL.md +14 -16
- package/using-git-worktrees/references/workflow-details.md +44 -85
|
@@ -1,252 +1,147 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
2
4
|
const crypto = require('crypto');
|
|
3
5
|
const fs = require('fs');
|
|
4
|
-
const os = require('os');
|
|
5
6
|
const path = require('path');
|
|
6
|
-
const
|
|
7
|
-
const {
|
|
8
|
-
createDegradedRouterContract,
|
|
9
|
-
validateRouterContract,
|
|
10
|
-
} = require('./router-contract');
|
|
11
|
-
const { applyEnsemblePolicy } = require('./ensemble-policy');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Harness Kernel Router
|
|
15
|
-
*
|
|
16
|
-
* tier-router remains the human-readable classifier/guide detector. The
|
|
17
|
-
* kernel consumes its versioned side-channel JSON contract instead of
|
|
18
|
-
* scraping human-readable stdout. Phase 2 selects an execution topology, but
|
|
19
|
-
* the router still only emits a plan: executors retain responsibility for
|
|
20
|
-
* spawning agents, creating workspaces, running tools, and enforcing gates.
|
|
21
|
-
* Phase 4 attaches ensemble-review only as a post-selection modifier.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const INVARIANT_TEXT = {
|
|
25
|
-
'scope-lock': 'Route before execution: stay inside the authorized task/repository scope.',
|
|
26
|
-
'verify-before-claim': 'Verify before claim: completion claims require objective evidence appropriate to the change.',
|
|
27
|
-
'replan-after-repeated-failure': 'Re-plan on repetition: after 3 same-signature failures, stop micro-retrying and zoom out/re-diagnose.',
|
|
28
|
-
'evaluate-suggestions-before-skip': 'Evaluate before skip: read each suggested skill\'s complete SKILL.md entry/basic flow before omitting it.',
|
|
29
|
-
'loop-budget': 'Stop iterative-single when its explicit iteration budget is exhausted.',
|
|
30
|
-
'objective-verification': 'Use an objective check for iterative work; self-critique alone is not verification.',
|
|
31
|
-
'stage-contracts': 'Fable stages must have explicit stage contracts and pass conditions.',
|
|
32
|
-
'cold-verification': 'Fable delivery requires a cold/independent verifier where specified.',
|
|
33
|
-
'parallel-scope-contract': 'Parallel work requires declared independent scopes and non-overlapping/read-only writes.',
|
|
34
|
-
'synthesis-barrier': 'Do not claim parallel completion until all workstreams reach the synthesis barrier.',
|
|
35
|
-
'handoff-contracts': 'Multi-agent workspace work uses orchestrator-owned handoff contracts; workers do not form a peer mesh.',
|
|
36
|
-
'workspace-state': 'Durable workspace state remains scoped to the workspace and existing state conventions.',
|
|
37
|
-
'pre-action-approval': 'Irreversible/external side effects require approval before the exact payload executes.',
|
|
38
|
-
'preserve-disagreement': 'Ensemble synthesis must retain unresolved minority positions and evidence gaps.',
|
|
39
|
-
'independent-ensemble-verifier': 'Ensemble delivery requires a verifier independent from the candidate identities; agreement alone is not proof.',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const SKILL_TEXT = {
|
|
43
|
-
'tdd': 'tdd: useful for behavioral changes where executable tests can drive the implementation.',
|
|
44
|
-
'verification-loop': 'verification-loop: useful for systematic build/lint/test/diff evidence before delivery.',
|
|
45
|
-
'fable-mode': 'fable-mode / fable-discipline: useful for macro planning or deliberate multi-agent decomposition.',
|
|
46
|
-
'fable-discipline': null,
|
|
47
|
-
'multi-agent-workspace': 'multi-agent-workspace: useful when durable bounded delegation, handoffs, or workspace memory are required.',
|
|
48
|
-
};
|
|
7
|
+
const core = require('./kernel-router-core');
|
|
49
8
|
|
|
50
|
-
function
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for (const line of lines) {
|
|
56
|
-
if (line.includes('=> BASE EXECUTION LOOP:')) {
|
|
57
|
-
skipLegacyContinuation = true;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (skipLegacyContinuation && line.includes('Track exactly ONE item in-progress')) {
|
|
61
|
-
skipLegacyContinuation = false;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
skipLegacyContinuation = false;
|
|
65
|
-
filtered.push(line);
|
|
9
|
+
function loadRuntime() {
|
|
10
|
+
for (const prefix of ['../..', '../../..']) {
|
|
11
|
+
const candidate = path.resolve(__dirname, prefix, 'hooks/scripts/lib/workflow-runtime.js');
|
|
12
|
+
if (fs.existsSync(candidate)) return { runtime: require(candidate), hooksRoot: path.dirname(path.dirname(candidate)) };
|
|
66
13
|
}
|
|
67
|
-
|
|
68
|
-
return filtered.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
14
|
+
throw new Error('workflow runtime unavailable in this installation');
|
|
69
15
|
}
|
|
70
16
|
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!validation.valid) {
|
|
76
|
-
return createDegradedRouterContract(
|
|
77
|
-
'router-contract-invalid',
|
|
78
|
-
`Structured router contract failed validation: ${validation.errors.join('; ')}`,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
return parsed;
|
|
82
|
-
} catch (err) {
|
|
83
|
-
return createDegradedRouterContract(
|
|
84
|
-
'router-contract-unavailable',
|
|
85
|
-
`Structured router contract unavailable: ${err.message}`,
|
|
86
|
-
);
|
|
17
|
+
function issueMemoryCapability(workflow) {
|
|
18
|
+
if (!workflow || !workflow.workflowPlan || workflow.workflowPlan.memory?.write === 'none') {
|
|
19
|
+
if (workflow) delete workflow.memoryAuthorization;
|
|
20
|
+
return null;
|
|
87
21
|
}
|
|
22
|
+
const token = crypto.randomBytes(24).toString('base64url');
|
|
23
|
+
workflow.memoryAuthorization = {
|
|
24
|
+
schemaVersion: 1,
|
|
25
|
+
capabilityHash: crypto.createHash('sha256').update(token, 'utf8').digest('hex'),
|
|
26
|
+
writerRole: 'coordinator',
|
|
27
|
+
sessionId: workflow.sessionId,
|
|
28
|
+
workflowId: workflow.workflowId,
|
|
29
|
+
writeDisposition: workflow.workflowPlan.memory.write,
|
|
30
|
+
issuedAt: new Date().toISOString(),
|
|
31
|
+
usedAt: null,
|
|
32
|
+
};
|
|
33
|
+
return token;
|
|
88
34
|
}
|
|
89
35
|
|
|
90
|
-
function
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
36
|
+
function persistWorkflow(plan, payload, prompt) {
|
|
37
|
+
if (!payload || !(payload.session_id || payload.sessionId)) return null;
|
|
38
|
+
const { runtime, hooksRoot } = loadRuntime();
|
|
39
|
+
const context = runtime.loadWorkflow(payload);
|
|
40
|
+
const { writeCurrentSession } = require(path.join(hooksRoot, 'lib/harness-state'));
|
|
41
|
+
writeCurrentSession(context.root, context.sessionId);
|
|
42
|
+
if (context.workflow && runtime.OPEN_STATES.has(context.workflow.state)) {
|
|
43
|
+
if (!context.workflow.workflowPlan) throw new Error('legacy unresolved workflow needs explicit migration; refusing to replace its obligations');
|
|
44
|
+
// Steering/status prompts cannot drop an unresolved execution contract.
|
|
45
|
+
// Stronger new work is retained as a pending route until explicit replan.
|
|
46
|
+
if (runtime.isMajorWorkflow(plan) && !runtime.isMajorWorkflow(context.workflow)) {
|
|
47
|
+
context.workflow.state = 'blocked';
|
|
48
|
+
context.workflow.pendingPlan = plan;
|
|
49
|
+
context.workflow.blockReason = 'stronger-route-requires-replan';
|
|
50
|
+
context.workflow.mutationIsolation = plan.mutationIsolation;
|
|
51
|
+
}
|
|
52
|
+
context.workflow.lastPromptHash = crypto.createHash('sha256').update(prompt).digest('hex').slice(0, 24);
|
|
53
|
+
const memoryCapability = issueMemoryCapability(context.workflow);
|
|
54
|
+
runtime.saveWorkflow(context);
|
|
55
|
+
return { ...context, hooksRoot, retained: true, memoryCapability };
|
|
98
56
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
57
|
+
const selected = plan.strategySelection === 'selected' && Boolean(plan.strategy);
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
context.workflow = {
|
|
60
|
+
schemaVersion: 2,
|
|
61
|
+
workflowId: crypto.randomUUID(),
|
|
62
|
+
createdAt: new Date(now).toISOString(),
|
|
63
|
+
createdAtMs: now,
|
|
64
|
+
sessionId: context.sessionId,
|
|
65
|
+
promptHash: crypto.createHash('sha256').update(prompt).digest('hex').slice(0, 24),
|
|
105
66
|
tier: plan.tier,
|
|
106
67
|
strategy: plan.strategy,
|
|
107
68
|
strategySelection: plan.strategySelection,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
parallelism: plan.parallelism,
|
|
111
|
-
workspace: plan.workspace,
|
|
112
|
-
memory: plan.memory,
|
|
69
|
+
requiredInvariants: plan.requiredInvariants || [],
|
|
70
|
+
suggestedSkills: plan.suggestedSkills || [],
|
|
113
71
|
verification: plan.verification,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
72
|
+
mutationIsolation: plan.mutationIsolation || { required: false },
|
|
73
|
+
workflowPlan: plan,
|
|
74
|
+
state: plan.fallback?.disposition === 'blocked' ? 'blocked' : selected ? 'pending' : 'deferred',
|
|
75
|
+
runId: null,
|
|
76
|
+
revision: 0,
|
|
77
|
+
escapes: [],
|
|
78
|
+
enforcement: {
|
|
79
|
+
mode: 'mandatory-applicable-workflow',
|
|
80
|
+
escapePolicy: 'scoped-evidence-only',
|
|
81
|
+
reasoningPolicy: 'model-controls-how',
|
|
82
|
+
},
|
|
117
83
|
};
|
|
118
|
-
|
|
84
|
+
const memoryCapability = issueMemoryCapability(context.workflow);
|
|
85
|
+
runtime.saveWorkflow(context);
|
|
86
|
+
return { ...context, hooksRoot, retained: false, memoryCapability };
|
|
119
87
|
}
|
|
120
88
|
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
function enforceSuggestionEvaluation(plan) {
|
|
127
|
-
if (uniqueSuggestedSkills(plan).length === 0) return;
|
|
128
|
-
if (!Array.isArray(plan.requiredInvariants)) plan.requiredInvariants = [];
|
|
129
|
-
if (!plan.requiredInvariants.includes('evaluate-suggestions-before-skip')) {
|
|
130
|
-
plan.requiredInvariants.push('evaluate-suggestions-before-skip');
|
|
89
|
+
function run(raw) {
|
|
90
|
+
const promptArg = process.argv.slice(2).join(' ');
|
|
91
|
+
let payload = null;
|
|
92
|
+
if (raw.trim()) {
|
|
93
|
+
try { payload = JSON.parse(raw); } catch (_) { console.error('[Workflow Router] Invalid hook payload; lifecycle persistence unavailable.'); }
|
|
131
94
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
console.log(
|
|
148
|
-
console.log(
|
|
149
|
-
console.log(
|
|
150
|
-
if (
|
|
151
|
-
console.log(' -
|
|
152
|
-
console.log(' -
|
|
153
|
-
console.log(' -
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
function printKernelContract(plan) {
|
|
158
|
-
console.log('\n=> REQUIRED HARNESS INVARIANTS:');
|
|
159
|
-
for (const invariant of plan.requiredInvariants || []) {
|
|
160
|
-
console.log(` - ${invariant}: ${INVARIANT_TEXT[invariant] || 'Required by the selected workflow plan.'}`);
|
|
95
|
+
const prompt = promptArg || (typeof payload?.prompt === 'string' ? payload.prompt : '');
|
|
96
|
+
const result = core.route(promptArg, raw || null);
|
|
97
|
+
let plan = result.contract.workflowPlan;
|
|
98
|
+
let persisted = null;
|
|
99
|
+
let failure = null;
|
|
100
|
+
try {
|
|
101
|
+
persisted = persistWorkflow(plan, payload, prompt);
|
|
102
|
+
if (persisted?.retained) plan = persisted.workflow.workflowPlan;
|
|
103
|
+
} catch (error) { failure = error; }
|
|
104
|
+
if (result.sanitized) console.log(result.sanitized);
|
|
105
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
106
|
+
if (persisted?.retained) console.log('\n=> Retaining unresolved workflow from earlier prompts; the checkpoint below is the active execution contract.');
|
|
107
|
+
core.printWorkflowPlan(plan);
|
|
108
|
+
core.printRoutingCheckpoint(plan);
|
|
109
|
+
core.printKernelContract(plan);
|
|
110
|
+
console.log('\n=> WORKFLOW EXECUTION CONTRACT (MANDATORY WHEN SELECTED):');
|
|
111
|
+
console.log(' - State: ' + (persisted?.workflow.state || (plan.strategy ? 'unpersisted' : 'deferred')));
|
|
112
|
+
console.log(' - Selected workflow: ' + (plan.strategy || 'deferred'));
|
|
113
|
+
if (plan.strategySelection === 'selected') {
|
|
114
|
+
console.log(' - Execute the selected workflow to resolution. Reasoning and implementation remain flexible inside it.');
|
|
115
|
+
console.log(' - Completion requires the workflow obligations and objective verification to resolve.');
|
|
116
|
+
console.log(' - Generic simple/routine/already-clear reasons cannot waive the selected workflow.');
|
|
117
|
+
console.log(' - Tier 3 / Fable source and artifact mutations require Git worktree isolation; unavailable isolation means BLOCKED.');
|
|
118
|
+
console.log(' - Escape is limited to workflow-uncovered-scope or host-capability-unavailable, with stage, scope and evidence; other obligations remain mandatory.');
|
|
161
119
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
emitted.add(skill);
|
|
171
|
-
if (skill === 'fable-mode') emitted.add('fable-discipline');
|
|
172
|
-
}
|
|
173
|
-
console.log(' - Read-before-skip: evaluate every suggested skill entry before omission; adopting one suggestion does not waive evaluation of the others.');
|
|
174
|
-
if (plan.strategy && plan.strategy.startsWith('fable-')) {
|
|
175
|
-
console.log(' - Use these selectively after evaluation; Tier 3/Fable does not create a universal skill pipeline.');
|
|
176
|
-
}
|
|
177
|
-
} else if (plan.strategy === 'direct-single') {
|
|
178
|
-
console.log(' - No domain skill was suggested. Prefer the bounded direct path and load a focused skill only when it adds value.');
|
|
179
|
-
} else if (plan.strategySelection === 'deferred') {
|
|
180
|
-
console.log(' - Strategy is deferred/unclassified. Do not infer triviality; choose the smallest justified approach from task evidence.');
|
|
120
|
+
if (persisted) {
|
|
121
|
+
const controller = path.join(persisted.hooksRoot, 'workflow-disposition.js');
|
|
122
|
+
console.log(' - Workflow id: ' + persisted.workflow.workflowId);
|
|
123
|
+
console.log(' - Memory write disposition: ' + (persisted.workflow.workflowPlan.memory?.write || 'none'));
|
|
124
|
+
if (persisted.memoryCapability) console.log(' - Memory capability (single-use, workflow/session-bound): ' + persisted.memoryCapability);
|
|
125
|
+
console.log(' - Stage specification: ' + path.join(persisted.sessionDir, 'workflow-stages.json'));
|
|
126
|
+
console.log(' - Enter/replan: node "' + controller + '" start --session-id "' + persisted.sessionId + '"');
|
|
127
|
+
console.log(' - Escape one declared stage: node "' + controller + '" escape --session-id "' + persisted.sessionId + '" --stage-id "<id>" --reason-code workflow-uncovered-scope --scope "<uncovered scope>" --evidence "<evidence>"');
|
|
181
128
|
} else {
|
|
182
|
-
console.log(' -
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (plan.ensemble) {
|
|
186
|
-
console.log(`\n=> ENSEMBLE REVIEW: bounded to ${plan.ensemble.maxCandidates} candidates; synthesis=${plan.ensemble.synthesis}; verifier=${plan.ensemble.verifier}. Preserve minority positions and do not claim improvement without paired evidence.`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (plan.actionGate && plan.actionGate.required) {
|
|
190
|
-
console.log(`\n=> ACTION GATE REQUIRED: ${plan.actionGate.reasonCodes.join(', ')}; disposition=${plan.actionGate.disposition}. The router only declares this requirement; execution must not bypass approval.`);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (plan.fallback && plan.fallback.disposition !== 'none') {
|
|
194
|
-
console.log(`\n=> ROUTING FALLBACK: ${plan.fallback.disposition}/${plan.fallback.mode}; ${plan.fallback.reasonCodes.join(', ')}.`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (plan.routingStatus === 'degraded') {
|
|
198
|
-
console.log('\n=> ROUTING DEGRADATION: Structured routing is degraded. Keep the strategy deferred unless independent evidence supports a route; do not silently downgrade to Tier 1/direct execution.');
|
|
129
|
+
console.log(' - Runtime state was not persisted; host enforcement is unavailable/unknown, not proven hard enforcement.');
|
|
199
130
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
function run(prompt, stdinPayload) {
|
|
205
|
-
const classifierPath = path.join(__dirname, 'tier-router.js');
|
|
206
|
-
const contractPath = path.join(os.tmpdir(), `harness-router-${process.pid}-${crypto.randomUUID()}.json`);
|
|
207
|
-
const args = prompt ? [classifierPath, prompt] : [classifierPath];
|
|
208
|
-
const result = spawnSync(process.execPath, args, {
|
|
209
|
-
input: prompt ? undefined : stdinPayload,
|
|
210
|
-
encoding: 'utf8',
|
|
211
|
-
env: {
|
|
212
|
-
...process.env,
|
|
213
|
-
HARNESS_ROUTER_CONTRACT_PATH: contractPath,
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
if (result.error) {
|
|
218
|
-
console.error(`[Harness Kernel] Failed to execute tier-router.js: ${result.error.message}`);
|
|
219
|
-
process.exit(1);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const sanitized = sanitizeClassifierOutput(result.stdout);
|
|
223
|
-
if (sanitized) console.log(sanitized);
|
|
224
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
225
|
-
|
|
226
|
-
const contract = readStructuredContract(contractPath);
|
|
227
|
-
applyEnsemblePolicy(contract, resolvePrompt(prompt, stdinPayload));
|
|
228
|
-
enforceSuggestionEvaluation(contract.workflowPlan);
|
|
229
|
-
try {
|
|
230
|
-
fs.rmSync(contractPath, { force: true });
|
|
231
|
-
} catch (err) {
|
|
232
|
-
// Temporary contract cleanup is best effort only.
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
printWorkflowPlan(contract.workflowPlan);
|
|
236
|
-
printRoutingCheckpoint(contract.workflowPlan);
|
|
237
|
-
printKernelContract(contract.workflowPlan);
|
|
238
|
-
|
|
239
|
-
if (result.status !== 0) process.exit(result.status === null ? 1 : result.status);
|
|
131
|
+
if (failure) {
|
|
132
|
+
console.error('[Workflow Router] ' + failure.message);
|
|
133
|
+
process.exitCode = 2;
|
|
134
|
+
} else if (result.status !== 0) process.exitCode = result.status || 1;
|
|
240
135
|
}
|
|
241
136
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
run(prompt, null);
|
|
245
|
-
} else if (process.stdin.isTTY) {
|
|
246
|
-
run('', null);
|
|
247
|
-
} else {
|
|
137
|
+
if (process.argv.length > 2 || process.stdin.isTTY) run('');
|
|
138
|
+
else {
|
|
248
139
|
let input = '';
|
|
140
|
+
let finished = false;
|
|
141
|
+
const finish = () => { if (finished) return; finished = true; clearTimeout(timeout); run(input); };
|
|
142
|
+
const timeout = setTimeout(finish, 500);
|
|
249
143
|
process.stdin.setEncoding('utf8');
|
|
250
144
|
process.stdin.on('data', chunk => { input += chunk; });
|
|
251
|
-
process.stdin.on('end',
|
|
145
|
+
process.stdin.on('end', finish);
|
|
146
|
+
process.stdin.on('error', finish);
|
|
252
147
|
}
|