harness-everything 0.30.4 → 0.30.6
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 +38 -0
- package/README.md +17 -7
- package/bin/cli.js +5 -0
- package/ci/fable-mode-test.js +112 -51
- package/ci/mechanism-2t-workspace-identity.test.js +3 -1
- package/ci/mechanism-2w-platform-compatibility.test.js +39 -0
- package/ci/mechanism-2z-claude-plugin-manifest.test.js +12 -0
- package/ci/router-workflow-plan.test.js +92 -7
- package/docs/platform-capabilities.md +14 -1
- package/docs/troubleshooting.md +31 -1
- package/docs/workflows/fable-mode.md +10 -9
- package/evals/fable-mode/tasks/negative-tier2-no-model.yaml +3 -3
- package/evals/fable-mode/tasks/positive-haiku-mode.yaml +1 -1
- package/evals/fable-mode/tasks/positive-opus-mode.yaml +1 -1
- package/evals/fable-mode/tasks/positive-sonnet-mode.yaml +1 -1
- package/evals/fable-mode/tasks/positive-trigger.yaml +1 -1
- package/fable-mode/SKILL.md +18 -12
- package/fable-mode/agents/fable-orchestrator.md +7 -6
- package/fable-mode/agents/fable-verifier.md +1 -1
- package/fable-mode/agents/fable-worker-haiku.md +5 -4
- package/fable-mode/agents/fable-worker-sonnet.md +5 -4
- package/fable-mode/behavior-profile-matrix.json +36 -0
- package/fable-mode/execution-guardrails/SKILL.md +3 -6
- package/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
- package/fable-mode/fable-haiku/SKILL.md +7 -5
- package/fable-mode/fable-haiku/references/routing-notes.md +2 -1
- package/fable-mode/fable-opus/SKILL.md +8 -6
- package/fable-mode/fable-sonnet/SKILL.md +8 -6
- package/fable-mode/model-matrix.json +7 -30
- package/fable-mode/references/model-matrix.md +35 -30
- package/fable-mode/references/profile-lookup.md +19 -0
- package/fable-mode/runtime-model-floor-matrix.json +47 -0
- package/fable-mode/scripts/model-selector.js +106 -60
- package/fable-mode/scripts/workflow-plan-consumer.js +1 -1
- package/harness-everything/SKILL.md +11 -14
- package/harness-everything/references/router-workflow-plan.md +2 -2
- package/harness-everything/schemas/router-task-shape.schema.json +3 -2
- package/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
- package/harness-everything/scripts/router-contract-core.js +19 -12
- package/harness-everything/scripts/tier-router.js +100 -12
- 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/SKILL.md +18 -12
- package/plugins/harness-everything/skills/fable-mode/agents/fable-orchestrator.md +7 -6
- package/plugins/harness-everything/skills/fable-mode/agents/fable-verifier.md +1 -1
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-haiku.md +5 -4
- package/plugins/harness-everything/skills/fable-mode/agents/fable-worker-sonnet.md +5 -4
- package/plugins/harness-everything/skills/fable-mode/behavior-profile-matrix.json +36 -0
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +3 -6
- package/plugins/harness-everything/skills/fable-mode/execution-guardrails/references/guardrail-rules.md +8 -9
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +7 -5
- package/plugins/harness-everything/skills/fable-mode/fable-haiku/references/routing-notes.md +2 -1
- package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +8 -6
- package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +8 -6
- package/plugins/harness-everything/skills/fable-mode/model-matrix.json +7 -30
- package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +35 -30
- package/plugins/harness-everything/skills/fable-mode/references/profile-lookup.md +19 -0
- package/plugins/harness-everything/skills/fable-mode/runtime-model-floor-matrix.json +47 -0
- package/plugins/harness-everything/skills/fable-mode/scripts/model-selector.js +106 -60
- package/plugins/harness-everything/skills/fable-mode/scripts/workflow-plan-consumer.js +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +11 -14
- package/plugins/harness-everything/skills/harness-everything/references/router-workflow-plan.md +2 -2
- package/plugins/harness-everything/skills/harness-everything/schemas/router-task-shape.schema.json +3 -2
- package/plugins/harness-everything/skills/harness-everything/schemas/router-workflow-plan.schema.json +6 -5
- package/plugins/harness-everything/skills/harness-everything/scripts/router-contract-core.js +19 -12
- package/plugins/harness-everything/skills/harness-everything/scripts/tier-router.js +100 -12
- package/sbom.cdx.json +7 -7
|
@@ -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.30.
|
|
13
|
+
"version": "0.30.6",
|
|
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.30.
|
|
4
|
+
"version": "0.30.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.30.6](https://github.com/dyphn1/Harness-everything/compare/v0.30.5...v0.30.6) (2026-10-06)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **fable-mode:** align Claude nested skill contract ([aadfe34](https://github.com/dyphn1/Harness-everything/commit/aadfe349e6f9a6ce9623f9b408ad8c6d9958208e))
|
|
10
|
+
* **fable-mode:** compress guardrail reference contract ([302efcd](https://github.com/dyphn1/Harness-everything/commit/302efcd0433c75be10619dbad7aa2b925b57400f))
|
|
11
|
+
* **fable-mode:** preserve unknown audit states and raw aliases ([2716be9](https://github.com/dyphn1/Harness-everything/commit/2716be9feb3e6b986c0f5da884ed4a686b95791b))
|
|
12
|
+
* **fable-mode:** remove residual model-gated routing ([32fee42](https://github.com/dyphn1/Harness-everything/commit/32fee423e9af7416172bf146cb876f72d61e331e))
|
|
13
|
+
* **fable-mode:** stay within skill token budget ([d934d36](https://github.com/dyphn1/Harness-everything/commit/d934d36cef17d5e0b30dfffd888cdab5d7c9a747))
|
|
14
|
+
* **fable:** accept curly-quoted lookups and restore execution deep dive ([08517e4](https://github.com/dyphn1/Harness-everything/commit/08517e4469bab46042358df136446e2495144a7f)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
|
|
15
|
+
* **fable:** dispatch profile lookups before loading stage references ([58ce883](https://github.com/dyphn1/Harness-everything/commit/58ce8834d3fa0ab288f9106f26758d4945ca49b6)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
|
|
16
|
+
* **router:** preserve Fable profile alias across normalization ([50e84da](https://github.com/dyphn1/Harness-everything/commit/50e84da3f14216800e4c757996663debfb7a8f7a))
|
|
17
|
+
* **router:** scope fable lookup negations to their own list ([032b52b](https://github.com/dyphn1/Harness-everything/commit/032b52bba809237c4b891e44fc7ee7b082262ddb)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
|
|
18
|
+
* **router:** select bounded Fable profile lookups before staged topology ([0d6fdac](https://github.com/dyphn1/Harness-everything/commit/0d6fdac675a9225f9425fcb699444992032371a3)), closes [#290](https://github.com/dyphn1/Harness-everything/issues/290)
|
|
19
|
+
|
|
20
|
+
## [Unreleased]
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Select a bounded Fable profile lookup before loading staged workflow references; preserve execution topology and required obligations (#290).
|
|
25
|
+
- Validate entire Fable lookup clauses so same-clause execution keeps its obligations, while canonical JSON field lists remain bounded lookups (#294).
|
|
26
|
+
- Scope lookup negations to their own noun list so an un-negated action after a negation keeps execution routing (#294).
|
|
27
|
+
|
|
28
|
+
## [0.30.5](https://github.com/dyphn1/Harness-everything/compare/v0.30.4...v0.30.5) (2026-10-06)
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* align Windows PowerShell quick-start contract ([f780f77](https://github.com/dyphn1/Harness-everything/commit/f780f777559d14f9bcf929793d954ddcaf2c6cf7))
|
|
33
|
+
* enforce Windows-safe shell contract ([a373a16](https://github.com/dyphn1/Harness-everything/commit/a373a16f5bdd3659e290715c971f530e66ffe2c1))
|
|
34
|
+
* fit Harness routing contract within budget ([6fa9065](https://github.com/dyphn1/Harness-everything/commit/6fa90657611bcbc85f669e864a3245b469e99d23))
|
|
35
|
+
* keep host-shell invariant within skill budget ([1381dc9](https://github.com/dyphn1/Harness-everything/commit/1381dc9877a0facccbea8f31a3047055eabaf5b2))
|
|
36
|
+
* preserve semantic contract phrases within Waza budget ([ab29217](https://github.com/dyphn1/Harness-everything/commit/ab292175cc691c6bd01d1e0da219a15c6779f7be))
|
|
37
|
+
* preserve skill metadata while restoring token budget ([189660a](https://github.com/dyphn1/Harness-everything/commit/189660ad6283ecad84d8a07232515fd80da6691c))
|
|
38
|
+
* restore Harness skill token budget ([80b1eee](https://github.com/dyphn1/Harness-everything/commit/80b1eeeceb681d9551bfbe4d3c881ae2c3e5747f))
|
|
39
|
+
* restore required skill sections within Waza budget ([acbb314](https://github.com/dyphn1/Harness-everything/commit/acbb314ec4e7769c2a2c253918ac67e1158052fe))
|
|
40
|
+
* show Windows-safe npx entry point ([d208b77](https://github.com/dyphn1/Harness-everything/commit/d208b7735912b7282dc158068b3e5c17a5c62e0d))
|
|
41
|
+
* sync packaged Windows shell contract ([201eb5a](https://github.com/dyphn1/Harness-everything/commit/201eb5a53cbea4e696db633f857b6c30c15f1855))
|
|
42
|
+
|
|
5
43
|
## [0.30.4](https://github.com/dyphn1/Harness-everything/compare/v0.30.3...v0.30.4) (2026-10-01)
|
|
6
44
|
|
|
7
45
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -56,12 +56,14 @@ Harness integrates directly into your workspace. There is no heavy daemon, no pa
|
|
|
56
56
|
# /plugin install harness-everything
|
|
57
57
|
|
|
58
58
|
# Option B: install Harness hooks/skills/advisory integrations into your workspace
|
|
59
|
-
npx github:dyphn1/Harness-everything install
|
|
59
|
+
npx github:dyphn1/Harness-everything install # POSIX shells / Git Bash
|
|
60
|
+
npx.cmd github:dyphn1/Harness-everything install # Windows PowerShell
|
|
60
61
|
|
|
61
62
|
# Option C: install or update the native Claude Code/Codex plugin
|
|
62
|
-
# npx github:dyphn1/Harness-everything plugin-sync
|
|
63
|
-
#
|
|
64
|
-
#
|
|
63
|
+
# npx github:dyphn1/Harness-everything plugin-sync # POSIX shells / Git Bash
|
|
64
|
+
# npx.cmd github:dyphn1/Harness-everything plugin-sync # Windows PowerShell
|
|
65
|
+
# ./scripts/plugin-sync.sh # POSIX shells / Git Bash
|
|
66
|
+
# node scripts/plugin-sync.js # Windows-safe direct entry
|
|
65
67
|
|
|
66
68
|
# OpenAI/Codex local plugin packaging is repository-owned under:
|
|
67
69
|
# .agents/plugins/marketplace.json
|
|
@@ -69,6 +71,8 @@ npx github:dyphn1/Harness-everything install
|
|
|
69
71
|
# See docs/openai-plugin.md for local import/install and public Skills-only submission.
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
**Windows PowerShell:** use npm-family `.cmd` shims such as `npx.cmd` rather than weakening `ExecutionPolicy`. When reading Harness Markdown from Windows PowerShell 5.1, specify UTF-8 explicitly, for example `Get-Content -Encoding UTF8 -Raw <path>`.
|
|
75
|
+
|
|
72
76
|
### Expected Behavior After Installation:
|
|
73
77
|
1. **Use the selected surface's real mechanism:** Claude Code hooks, the local OpenAI plugin lifecycle hooks, OpenCode's plugin API, or advisory instructions depending on what you installed.
|
|
74
78
|
2. **Preflight / session context where packaged:** Hook-capable surfaces can inject environment/session context automatically; advisory-only surfaces must not be described as if they do.
|
|
@@ -100,9 +104,15 @@ The native `plugin-sync` command detects the installed host CLIs and applies the
|
|
|
100
104
|
The installer records its state directories in `.git/info/exclude` — a local-only git ignore file — so Harness state never lands in a commit and your working tree (including `.gitignore`) is never modified. Everything owned by the general installer is removed with the built-in uninstaller:
|
|
101
105
|
|
|
102
106
|
```bash
|
|
103
|
-
|
|
104
|
-
npx github:dyphn1/Harness-everything uninstall
|
|
105
|
-
npx github:dyphn1/Harness-everything uninstall --
|
|
107
|
+
# POSIX shells / Git Bash
|
|
108
|
+
npx github:dyphn1/Harness-everything uninstall
|
|
109
|
+
npx github:dyphn1/Harness-everything uninstall --local --skills -y
|
|
110
|
+
npx github:dyphn1/Harness-everything uninstall --global
|
|
111
|
+
|
|
112
|
+
# Windows PowerShell
|
|
113
|
+
npx.cmd github:dyphn1/Harness-everything uninstall
|
|
114
|
+
npx.cmd github:dyphn1/Harness-everything uninstall --local --skills -y
|
|
115
|
+
npx.cmd github:dyphn1/Harness-everything uninstall --global
|
|
106
116
|
```
|
|
107
117
|
|
|
108
118
|
---
|
package/bin/cli.js
CHANGED
|
@@ -55,6 +55,11 @@ Harness OS - AI Agent Operating System CLI
|
|
|
55
55
|
|
|
56
56
|
Usage:
|
|
57
57
|
npx github:dyphn1/Harness-everything <command> [options]
|
|
58
|
+
npx.cmd github:dyphn1/Harness-everything <command> [options] # Windows PowerShell
|
|
59
|
+
|
|
60
|
+
Windows PowerShell note:
|
|
61
|
+
Use npx.cmd so restrictive ExecutionPolicy cannot redirect this command to npx.ps1.
|
|
62
|
+
Harness does not require changing ExecutionPolicy.
|
|
58
63
|
|
|
59
64
|
Commands:
|
|
60
65
|
install Install Harness OS hooks & skills into the local repository
|
package/ci/fable-mode-test.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const { evaluateRuntimeFloor } = require('../fable-mode/scripts/model-selector.js');
|
|
8
9
|
|
|
9
10
|
const root = path.resolve(__dirname, '..');
|
|
10
11
|
const selector = path.join(root, 'fable-mode', 'scripts', 'model-selector.js');
|
|
@@ -13,19 +14,17 @@ const auditDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-fable-mode-'));
|
|
|
13
14
|
const auditFile = path.join(auditDir, 'audit.jsonl');
|
|
14
15
|
|
|
15
16
|
function runNode(file, args) {
|
|
16
|
-
return spawnSync(process.execPath, [file, ...args], {
|
|
17
|
-
cwd: root,
|
|
18
|
-
encoding: 'utf8'
|
|
19
|
-
});
|
|
17
|
+
return spawnSync(process.execPath, [file, ...args], { cwd: root, encoding: 'utf8' });
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
function selectorArgs(overrides = {}) {
|
|
23
21
|
const input = {
|
|
24
22
|
requested: 'opus',
|
|
25
|
-
available: 'opus,sonnet,haiku',
|
|
26
23
|
'available-agents': 'fable-orchestrator,fable-worker-sonnet,fable-worker-haiku',
|
|
27
24
|
fallback: 'stop',
|
|
28
|
-
|
|
25
|
+
host: 'codex',
|
|
26
|
+
'runtime-model': 'gpt-6.1-sol',
|
|
27
|
+
'runtime-effort': 'xhigh',
|
|
29
28
|
'audit-file': auditFile,
|
|
30
29
|
'stage-brief': 'Choose the architecture',
|
|
31
30
|
'pass-condition': 'ADR exists and names the decision',
|
|
@@ -33,104 +32,166 @@ function selectorArgs(overrides = {}) {
|
|
|
33
32
|
'verifier-result': 'pending',
|
|
34
33
|
...overrides
|
|
35
34
|
};
|
|
36
|
-
return Object.entries(input).flatMap(([key, value]) => [
|
|
35
|
+
return Object.entries(input).flatMap(([key, value]) => ['--' + key, String(value)]);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
function readRecord(result) {
|
|
40
|
-
assert.ok(result.stdout.trim(),
|
|
39
|
+
assert.ok(result.stdout.trim(), 'selector produced no JSON: ' + result.stderr);
|
|
41
40
|
return JSON.parse(result.stdout);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
console.log('=== Fable
|
|
43
|
+
console.log('=== Fable behavior profile + runtime floor contract tests ===');
|
|
44
|
+
|
|
45
|
+
const publicDescription = 'Stage large tasks through plans, named fable agents, failable checks, and skeptical review; fable on opus|sonnet|haiku selects a behavior profile, not a runtime model.';
|
|
46
|
+
const fableSkillText = fs.readFileSync(path.join(root, 'fable-mode', 'SKILL.md'), 'utf8');
|
|
47
|
+
assert.ok(fableSkillText.includes('description: "' + publicDescription + '"'));
|
|
48
|
+
|
|
49
|
+
for (const task of ['positive-opus-mode.yaml', 'positive-sonnet-mode.yaml', 'positive-haiku-mode.yaml', 'positive-trigger.yaml']) {
|
|
50
|
+
const source = fs.readFileSync(path.join(root, 'evals', 'fable-mode', 'tasks', task), 'utf8');
|
|
51
|
+
assert.ok(source.includes('description: "' + publicDescription + '"'), task + ' must keep the exact public fable-mode description');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const behavior = JSON.parse(fs.readFileSync(path.join(root, 'fable-mode', 'behavior-profile-matrix.json'), 'utf8'));
|
|
55
|
+
assert.strictEqual(behavior.profiles.orchestrator.legacyAlias, 'opus');
|
|
56
|
+
assert.ok(behavior.profiles.reasoning.aliases.includes('sonnect'));
|
|
57
|
+
|
|
58
|
+
const floors = JSON.parse(fs.readFileSync(path.join(root, 'fable-mode', 'runtime-model-floor-matrix.json'), 'utf8'));
|
|
59
|
+
assert.strictEqual(floors.hosts.claude.orchestrator.minVersion, '5.5');
|
|
60
|
+
assert.strictEqual(floors.hosts.claude.orchestrator.minEffort, 'medium');
|
|
61
|
+
assert.strictEqual(floors.hosts.claude.reasoning.minVersion, '5.5');
|
|
62
|
+
assert.strictEqual(floors.hosts.claude.mechanical.minVersion, '5.5');
|
|
63
|
+
assert.strictEqual(floors.hosts.codex.orchestrator.minGeneration, 6);
|
|
64
|
+
assert.strictEqual(floors.hosts.codex.orchestrator.minEffort, 'xhigh');
|
|
65
|
+
assert.strictEqual(floors.hosts.codex.reasoning.minEffort, 'medium');
|
|
66
|
+
assert.strictEqual(floors.hosts.codex.mechanical.family, 'luna');
|
|
67
|
+
assert.strictEqual(floors.hosts.codex.mechanical.minEffort, 'xhigh');
|
|
68
|
+
|
|
69
|
+
for (const profile of ['fable-opus', 'fable-sonnet', 'fable-haiku']) {
|
|
70
|
+
const source = fs.readFileSync(path.join(root, 'fable-mode', profile, 'SKILL.md'), 'utf8');
|
|
71
|
+
assert.match(source, /Reference profile, not a standalone plugin skill/);
|
|
72
|
+
}
|
|
73
|
+
for (const agent of ['fable-orchestrator.md', 'fable-worker-sonnet.md', 'fable-worker-haiku.md', 'fable-verifier.md']) {
|
|
74
|
+
const source = fs.readFileSync(path.join(root, 'fable-mode', 'agents', agent), 'utf8');
|
|
75
|
+
assert.match(source, /^model: inherit$/m, agent + ' must not pin a branded runtime model');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const guardrailSource = fs.readFileSync(path.join(root, 'fable-mode', 'execution-guardrails', 'SKILL.md'), 'utf8');
|
|
79
|
+
assert.match(guardrailSource, /reference-only, not an always-on carrier/);
|
|
80
|
+
assert.doesNotMatch(guardrailSource, /These rules are always-on/);
|
|
45
81
|
|
|
46
82
|
const selected = runNode(selector, selectorArgs());
|
|
47
83
|
assert.strictEqual(selected.status, 0, selected.stderr);
|
|
48
84
|
const selectedRecord = readRecord(selected);
|
|
49
85
|
assert.strictEqual(selectedRecord.status, 'selected');
|
|
50
|
-
assert.strictEqual(selectedRecord.
|
|
51
|
-
assert.strictEqual(selectedRecord.
|
|
52
|
-
assert.strictEqual(selectedRecord.
|
|
86
|
+
assert.strictEqual(selectedRecord.requestedProfile, 'orchestrator');
|
|
87
|
+
assert.strictEqual(selectedRecord.effectiveProfile, 'orchestrator');
|
|
88
|
+
assert.strictEqual(selectedRecord.profileAlias, 'opus');
|
|
53
89
|
assert.strictEqual(selectedRecord.agent, 'fable-orchestrator');
|
|
90
|
+
assert.strictEqual(selectedRecord.runtimeFloorStatus, 'meets-recommended');
|
|
54
91
|
|
|
55
|
-
const sonnect = runNode(selector, selectorArgs({
|
|
92
|
+
const sonnect = runNode(selector, selectorArgs({
|
|
93
|
+
requested: 'sonnect',
|
|
94
|
+
host: 'claude',
|
|
95
|
+
'runtime-model': 'claude-sonnet-5.5',
|
|
96
|
+
'runtime-effort': 'medium'
|
|
97
|
+
}));
|
|
56
98
|
assert.strictEqual(sonnect.status, 0, sonnect.stderr);
|
|
57
99
|
const sonnectRecord = readRecord(sonnect);
|
|
58
|
-
assert.strictEqual(sonnectRecord.
|
|
59
|
-
assert.strictEqual(sonnectRecord.
|
|
100
|
+
assert.strictEqual(sonnectRecord.requestedProfile, 'reasoning');
|
|
101
|
+
assert.strictEqual(sonnectRecord.profileAlias, 'sonnect');
|
|
102
|
+
assert.strictEqual(sonnectRecord.runtimeFloorStatus, 'meets-recommended');
|
|
60
103
|
|
|
61
|
-
const
|
|
104
|
+
const belowFloor = runNode(selector, selectorArgs({
|
|
62
105
|
requested: 'opus',
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'
|
|
106
|
+
host: 'codex',
|
|
107
|
+
'runtime-model': 'gpt-6-sol',
|
|
108
|
+
'runtime-effort': 'medium'
|
|
109
|
+
}));
|
|
110
|
+
assert.strictEqual(belowFloor.status, 0, belowFloor.stderr);
|
|
111
|
+
const belowFloorRecord = readRecord(belowFloor);
|
|
112
|
+
assert.strictEqual(belowFloorRecord.status, 'selected');
|
|
113
|
+
assert.strictEqual(belowFloorRecord.effectiveProfile, 'orchestrator');
|
|
114
|
+
assert.strictEqual(belowFloorRecord.runtimeFloorStatus, 'below-recommended');
|
|
115
|
+
assert.strictEqual(belowFloorRecord.escalationRequired, false);
|
|
116
|
+
|
|
117
|
+
const unknownFamily = evaluateRuntimeFloor('reasoning', {
|
|
118
|
+
host: 'codex',
|
|
119
|
+
runtimeModel: 'vendor-private-model',
|
|
120
|
+
runtimeEffort: 'xhigh'
|
|
121
|
+
});
|
|
122
|
+
assert.strictEqual(unknownFamily.status, 'unknown');
|
|
123
|
+
assert.match(unknownFamily.reason, /not comparable/);
|
|
124
|
+
|
|
125
|
+
const unknownEffort = evaluateRuntimeFloor('reasoning', {
|
|
126
|
+
host: 'codex',
|
|
127
|
+
runtimeModel: 'gpt-6.1-sol',
|
|
128
|
+
runtimeEffort: 'custom-effort'
|
|
129
|
+
});
|
|
130
|
+
assert.strictEqual(unknownEffort.status, 'unknown');
|
|
131
|
+
assert.match(unknownEffort.reason, /not comparable/);
|
|
132
|
+
|
|
133
|
+
const inline = runNode(selector, selectorArgs({
|
|
134
|
+
requested: 'sonnet',
|
|
135
|
+
'available-agents': 'fable-worker-haiku',
|
|
136
|
+
fallback: 'inline'
|
|
66
137
|
}));
|
|
67
138
|
assert.strictEqual(inline.status, 0, inline.stderr);
|
|
68
139
|
const inlineRecord = readRecord(inline);
|
|
69
140
|
assert.strictEqual(inlineRecord.status, 'fallback');
|
|
70
|
-
assert.strictEqual(inlineRecord.
|
|
71
|
-
assert.match(inlineRecord.fallbackReason, /
|
|
72
|
-
assert.strictEqual(inlineRecord.escalationRequired, false);
|
|
141
|
+
assert.strictEqual(inlineRecord.effectiveProfile, 'reasoning');
|
|
142
|
+
assert.match(inlineRecord.fallbackReason, /execute the same reasoning behavior profile inline/);
|
|
73
143
|
|
|
74
|
-
const blocked = runNode(selector, selectorArgs({
|
|
144
|
+
const blocked = runNode(selector, selectorArgs({
|
|
145
|
+
requested: 'sonnet',
|
|
146
|
+
'available-agents': 'fable-worker-haiku',
|
|
147
|
+
fallback: 'stop'
|
|
148
|
+
}));
|
|
75
149
|
assert.strictEqual(blocked.status, 2);
|
|
76
150
|
const blockedRecord = readRecord(blocked);
|
|
77
151
|
assert.strictEqual(blockedRecord.status, 'blocked');
|
|
78
|
-
assert.strictEqual(blockedRecord.
|
|
152
|
+
assert.strictEqual(blockedRecord.effectiveProfile, null);
|
|
79
153
|
assert.strictEqual(blockedRecord.escalationRequired, true);
|
|
80
154
|
assert.match(blockedRecord.fallbackReason, /inline fallback was not authorized/);
|
|
81
155
|
|
|
82
|
-
const missingAgent = runNode(selector, selectorArgs({
|
|
83
|
-
requested: 'sonnet',
|
|
84
|
-
available: 'sonnet',
|
|
85
|
-
'available-agents': 'fable-worker-haiku',
|
|
86
|
-
fallback: 'inline',
|
|
87
|
-
'host-model': 'opus'
|
|
88
|
-
}));
|
|
89
|
-
assert.strictEqual(missingAgent.status, 0, missingAgent.stderr);
|
|
90
|
-
const missingAgentRecord = readRecord(missingAgent);
|
|
91
|
-
assert.strictEqual(missingAgentRecord.status, 'fallback');
|
|
92
|
-
assert.strictEqual(missingAgentRecord.effectiveModel, 'opus');
|
|
93
|
-
assert.match(missingAgentRecord.fallbackReason, /fable-worker-sonnet is not in availableAgents/);
|
|
94
|
-
|
|
95
156
|
const missingField = runNode(selector, selectorArgs({ 'pass-condition': '' }));
|
|
96
157
|
assert.strictEqual(missingField.status, 2);
|
|
97
158
|
assert.match(missingField.stderr, /passCondition is required/);
|
|
98
159
|
|
|
99
160
|
const explicitRoute = runNode(router, ['fable on sonnect for architecture synthesis']);
|
|
100
161
|
assert.strictEqual(explicitRoute.status, 0);
|
|
101
|
-
assert.match(explicitRoute.stdout, /REQUESTED FABLE
|
|
102
|
-
assert.match(explicitRoute.stdout,
|
|
162
|
+
assert.match(explicitRoute.stdout, /REQUESTED FABLE PROFILE: sonnect/);
|
|
163
|
+
assert.match(explicitRoute.stdout, /=> ROUTE: fable-mode\/SKILL\.md/);
|
|
164
|
+
assert.doesNotMatch(explicitRoute.stdout, /REQUESTED FABLE MODEL MODE/);
|
|
165
|
+
assert.doesNotMatch(explicitRoute.stdout, /=> ROUTE: fable-mode\/fable-sonnet\/SKILL\.md/);
|
|
103
166
|
|
|
104
167
|
const ordinaryTier2 = runNode(router, ['Fix this checkout bug and add a regression test.']);
|
|
105
168
|
assert.strictEqual(ordinaryTier2.status, 0);
|
|
106
169
|
assert.match(ordinaryTier2.stdout, /RECOMMENDED TIER: Tier 2/);
|
|
107
|
-
assert.doesNotMatch(ordinaryTier2.stdout, /REQUESTED FABLE
|
|
108
|
-
assert.doesNotMatch(ordinaryTier2.stdout, /fable-opus\/SKILL\.md/);
|
|
170
|
+
assert.doesNotMatch(ordinaryTier2.stdout, /REQUESTED FABLE PROFILE/);
|
|
109
171
|
|
|
110
172
|
const warningBatchContract = '**Warning batching.** Collect minor concerns and list them together in the next natural report or handoff; their count alone MUST NOT stop, pause, or return the worker/stage. An independently material, confirmed concern may stop the current stage and be surfaced immediately.';
|
|
111
173
|
for (const rel of [
|
|
112
174
|
'fable-mode/agents/fable-worker-haiku.md',
|
|
113
175
|
'fable-mode/agents/fable-worker-sonnet.md',
|
|
114
176
|
'fable-mode/agents/fable-orchestrator.md',
|
|
115
|
-
'fable-mode/execution-guardrails/SKILL.md'
|
|
177
|
+
'fable-mode/execution-guardrails/SKILL.md'
|
|
116
178
|
]) {
|
|
117
179
|
const prompt = fs.readFileSync(path.join(root, rel), 'utf8').replace(/\s+/g, ' ');
|
|
118
|
-
assert.ok(prompt.includes(warningBatchContract),
|
|
119
|
-
assert.doesNotMatch(
|
|
120
|
-
prompt,
|
|
121
|
-
/minor concerns.{0,200}stop and surface all at once before continuing/i,
|
|
122
|
-
`${rel} must not introduce a warning-count stop condition`
|
|
123
|
-
);
|
|
180
|
+
assert.ok(prompt.includes(warningBatchContract), rel + ' must keep minor-concern batching reporting-only');
|
|
124
181
|
}
|
|
125
182
|
|
|
126
183
|
const auditLines = fs.readFileSync(auditFile, 'utf8').trim().split(/\r?\n/).filter(Boolean);
|
|
127
184
|
assert.strictEqual(auditLines.length, 5);
|
|
128
185
|
for (const line of auditLines) {
|
|
129
186
|
const record = JSON.parse(line);
|
|
130
|
-
for (const field of [
|
|
131
|
-
|
|
187
|
+
for (const field of [
|
|
188
|
+
'requestedProfile', 'effectiveProfile', 'profileAlias', 'runtimeModel', 'runtimeEffort',
|
|
189
|
+
'recommendedRuntimeFloor', 'runtimeFloorStatus', 'runtimeFloorReason', 'fallbackReason',
|
|
190
|
+
'stageBrief', 'passCondition', 'verificationCommand', 'verifierResult'
|
|
191
|
+
]) {
|
|
192
|
+
assert.ok(Object.prototype.hasOwnProperty.call(record, field), 'audit missing ' + field);
|
|
132
193
|
}
|
|
133
194
|
}
|
|
134
195
|
fs.rmSync(auditDir, { recursive: true, force: true });
|
|
135
196
|
|
|
136
|
-
console.log('PASS:
|
|
197
|
+
console.log('PASS: profiles are behavior contracts; runtime floors are advisory; agent fallback remains explicit');
|
|
@@ -131,7 +131,9 @@ check('hooks can resolve state from a bound session after cwd changes', result.s
|
|
|
131
131
|
const installed = path.join(fixture, 'installed', 'fable-mode', 'scripts');
|
|
132
132
|
fs.mkdirSync(installed, { recursive: true });
|
|
133
133
|
fs.copyFileSync(path.join(root, 'fable-mode', 'scripts', 'model-selector.js'), path.join(installed, 'model-selector.js'));
|
|
134
|
-
|
|
134
|
+
for (const matrix of ['behavior-profile-matrix.json', 'runtime-model-floor-matrix.json']) {
|
|
135
|
+
fs.copyFileSync(path.join(root, 'fable-mode', matrix), path.join(installed, '..', matrix));
|
|
136
|
+
}
|
|
135
137
|
const fable = path.join(installed, 'model-selector.js');
|
|
136
138
|
const fableArgs = ['--requested', 'haiku', '--available', 'haiku', '--available-agents', 'fable-worker-haiku', '--stage-brief', 'identity', '--pass-condition', 'audit', '--verification-command', 'node test', '--verifier-result', 'pass', '--session-id', 'issue-42-fable-session'];
|
|
137
139
|
result = spawnSync(process.execPath, [fable, ...fableArgs], { cwd: outer, env: baseEnv, encoding: 'utf8' });
|
|
@@ -152,7 +152,46 @@ try {
|
|
|
152
152
|
fs.rmSync(tempOutput.replace(/\.zip$/i, '') + '.manifest.json', { force: true });
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// Windows PowerShell 5.1 treats BOM-less UTF-8 as a legacy code page when
|
|
156
|
+
// Get-Content has no explicit encoding, and resolves bare npm-family commands
|
|
157
|
+
// to .ps1 shims before .cmd. The routing skill is the agent-facing boundary
|
|
158
|
+
// that must prevent both failures before Harness itself starts.
|
|
159
|
+
const canonicalHarnessSkill = fs.readFileSync(path.join(ROOT, 'harness-everything', 'SKILL.md'), 'utf8');
|
|
160
|
+
const packagedHarnessSkill = fs.readFileSync(path.join(ROOT, 'plugins', 'harness-everything', 'skills', 'harness-everything', 'SKILL.md'), 'utf8');
|
|
161
|
+
assert.match(canonicalHarnessSkill, /Windows PowerShell/);
|
|
162
|
+
assert.match(canonicalHarnessSkill, /npx\.cmd/);
|
|
163
|
+
assert.match(canonicalHarnessSkill, /Get-Content -Encoding UTF8 -Raw/);
|
|
164
|
+
assert.strictEqual(packagedHarnessSkill, canonicalHarnessSkill, 'packaged Harness skill must preserve the Windows shell contract byte-for-text');
|
|
165
|
+
assert.match(canonicalHarnessSkill, /—/u, 'UTF-8 em dash must remain intact');
|
|
166
|
+
assert.match(canonicalHarnessSkill, /🚦 Harness Status/u, 'UTF-8 Harness Status marker must remain intact');
|
|
167
|
+
|
|
168
|
+
const readme = fs.readFileSync(path.join(ROOT, 'README.md'), 'utf8');
|
|
169
|
+
assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything install/);
|
|
170
|
+
assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall/);
|
|
171
|
+
assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall --local --skills -y/);
|
|
172
|
+
assert.match(readme, /npx\.cmd github:dyphn1\/Harness-everything uninstall --global/);
|
|
173
|
+
assert.match(readme, /Get-Content -Encoding UTF8 -Raw/);
|
|
174
|
+
assert.doesNotMatch(readme, /powershell -File scripts\/plugin-sync\.ps1/);
|
|
175
|
+
|
|
176
|
+
const troubleshootingDocs = fs.readFileSync(path.join(ROOT, 'docs', 'troubleshooting.md'), 'utf8');
|
|
177
|
+
for (const command of [
|
|
178
|
+
'install',
|
|
179
|
+
'install --global',
|
|
180
|
+
'uninstall',
|
|
181
|
+
'install --cursor',
|
|
182
|
+
'install --codex',
|
|
183
|
+
'plugin-sync --json'
|
|
184
|
+
]) {
|
|
185
|
+
assert.ok(
|
|
186
|
+
troubleshootingDocs.includes(`npx.cmd github:dyphn1/Harness-everything ${command}`),
|
|
187
|
+
`troubleshooting must include Windows PowerShell form for: ${command}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
155
191
|
const capabilityDocs = fs.readFileSync(path.join(ROOT, 'docs', 'platform-capabilities.md'), 'utf8');
|
|
192
|
+
assert.match(capabilityDocs, /npx\.cmd github:dyphn1\/Harness-everything plugin-sync/);
|
|
193
|
+
assert.match(capabilityDocs, /node scripts\/plugin-sync\.js/);
|
|
194
|
+
assert.doesNotMatch(capabilityDocs, /powershell -File scripts\/plugin-sync\.ps1/);
|
|
156
195
|
assert.match(capabilityDocs, /platform-compatibility\.json/);
|
|
157
196
|
assert.match(capabilityDocs, /Continue\.dev[\s\S]*`Unknown` for standalone `SKILL\.md` discovery/);
|
|
158
197
|
assert.match(capabilityDocs, /live-host evidence|Partial` live-host loading/i);
|
|
@@ -81,6 +81,18 @@ if (manifestHooks !== undefined) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
assert.ok(Array.isArray(manifest.skills) && manifest.skills.length > 0, 'Claude plugin must declare packaged skills');
|
|
84
|
+
assert.ok(manifest.skills.includes('./fable-mode'), 'Claude plugin must expose fable-mode as the public Fable entrypoint');
|
|
85
|
+
for (const nestedRef of [
|
|
86
|
+
'./fable-mode/fable-opus',
|
|
87
|
+
'./fable-mode/fable-sonnet',
|
|
88
|
+
'./fable-mode/fable-haiku',
|
|
89
|
+
'./fable-mode/execution-guardrails',
|
|
90
|
+
]) {
|
|
91
|
+
assert.ok(
|
|
92
|
+
!manifest.skills.includes(nestedRef),
|
|
93
|
+
`${nestedRef} is a nested reference contract, not an independently routable Claude skill`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
84
96
|
for (const skillRef of manifest.skills) {
|
|
85
97
|
const skillDir = resolvePluginPath(skillRef, `skill ${skillRef}`);
|
|
86
98
|
assert.ok(fs.statSync(skillDir).isDirectory(), `skill path must be a directory: ${skillRef}`);
|
|
@@ -202,10 +202,95 @@ if (validContract(fableTypo, 'fable keyword trivial edit')) {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
const explicitFable = runTier('fable on opus audit the entire repository architecture');
|
|
205
|
-
if (validContract(explicitFable, 'explicit Fable
|
|
205
|
+
if (validContract(explicitFable, 'explicit Fable profile')) {
|
|
206
206
|
check(explicitFable.contract.workflowPlan.strategy === 'fable-staged', 'explicit Fable request selects Fable topology');
|
|
207
|
-
check(explicitFable.contract.workflowPlan.
|
|
208
|
-
check(explicitFable.
|
|
207
|
+
check(explicitFable.contract.workflowPlan.profileSelection.requested === 'orchestrator', 'legacy opus alias normalizes to orchestrator profile');
|
|
208
|
+
check(explicitFable.contract.workflowPlan.profileSelection.alias === 'opus', 'legacy alias remains auditable');
|
|
209
|
+
check(explicitFable.result.stdout.includes('REQUESTED FABLE PROFILE: opus'), 'explicit Fable profile remains visible');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const explicitSonnect = runTier('fable on sonnect for architecture synthesis');
|
|
213
|
+
if (validContract(explicitSonnect, 'explicit Fable sonnect alias')) {
|
|
214
|
+
check(explicitSonnect.contract.workflowPlan.profileSelection.requested === 'reasoning', 'sonnect normalizes to reasoning profile');
|
|
215
|
+
check(explicitSonnect.contract.workflowPlan.profileSelection.alias === 'sonnect', 'raw sonnect alias remains auditable end-to-end');
|
|
216
|
+
check(explicitSonnect.result.stdout.includes('REQUESTED FABLE PROFILE: sonnect'), 'router output preserves raw sonnect alias');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// #290: mentioning a profile in a bounded lookup must not launch orchestration.
|
|
220
|
+
for (const prompt of [
|
|
221
|
+
"Explain what 'fable on sonnect' means; return only a small JSON selection record.",
|
|
222
|
+
"Resolve fable on haiku into a profile JSON record; no orchestration or delegation.",
|
|
223
|
+
"解釋 fable on opus 的意思,只回傳 JSON。",
|
|
224
|
+
"Explain what 'run fable on sonnect' means; return only JSON.",
|
|
225
|
+
'Explain fable on sonnet; return JSON with requestedProfile, effectiveProfile.',
|
|
226
|
+
"Explain what 'fable on sonnect' means; return only a small JSON selection record with requestedProfile, effectiveProfile, profileAlias, assignedRole, runtimeModel, runtimeEffort. No orchestration or delegation.",
|
|
227
|
+
'Explain fable on haiku; output JSON with `requestedProfile`, `runtimeModel` and `runtimeEffort`.',
|
|
228
|
+
'解釋 fable on opus 的意思,只回傳 JSON,包含 requestedProfile、effectiveProfile、runtimeEffort。',
|
|
229
|
+
"Explain what 'use fable on opus' means; no execution or model switching.",
|
|
230
|
+
'Explain fable on sonnet; return JSON; no stages, delegation or execution.',
|
|
231
|
+
'請說明 fable on sonnet 的意思,並只回傳含 requestedProfile 的 JSON。',
|
|
232
|
+
'Explain fable on sonnet, please return JSON.',
|
|
233
|
+
'Explain fable on sonnet and please return only JSON.',
|
|
234
|
+
'請說明 fable on sonnet,請只回傳 JSON。',
|
|
235
|
+
'Explain what “run fable on sonnect” means; return only JSON.',
|
|
236
|
+
'Explain what ‘use fable on opus’ means.',
|
|
237
|
+
'Explain fable on sonnet; no model switching.',
|
|
238
|
+
"Explain fable on sonnet; don't switch model or delegate.",
|
|
239
|
+
'Explain fable on sonnet; no stages and no delegation.',
|
|
240
|
+
'解釋 fable on sonnet,不要委派或切換模型。',
|
|
241
|
+
]) {
|
|
242
|
+
const lookup = runTier(prompt);
|
|
243
|
+
const repeat = runTier(prompt);
|
|
244
|
+
if (validContract(lookup, 'bounded Fable lookup')) {
|
|
245
|
+
check(lookup.contract.classification.tier === 'tier1', 'lookup remains a bounded Tier 1 operation');
|
|
246
|
+
check(lookup.contract.workflowPlan.strategy === 'direct-single', 'lookup selects direct-single before loading references');
|
|
247
|
+
check(lookup.contract.workflowPlan.parallelism.allowed === false, 'lookup does not delegate');
|
|
248
|
+
check(lookup.result.stdout.includes('references/profile-lookup.md'), 'lookup selects only compact reference');
|
|
249
|
+
check(!lookup.result.stdout.includes('RECOMMENDED KNOWLEDGE GUIDES'), 'lookup omits unrelated keyword guides');
|
|
250
|
+
check(!lookup.result.stdout.includes('with fable-mode/scripts/model-selector.js'), 'lookup does not send agent to selector implementation');
|
|
251
|
+
check(JSON.stringify(lookup.contract) === JSON.stringify(repeat.contract), 'lookup contract is deterministic');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
for (const prompt of [
|
|
255
|
+
'Use fable on sonnect to implement a multi-stage architecture migration.',
|
|
256
|
+
'Explain fable on opus and then implement a repository-wide migration.',
|
|
257
|
+
'Explain how to run fable-staged across all modules.',
|
|
258
|
+
'Explain fable on sonnet then run it to analyze security.',
|
|
259
|
+
'Explain fable on sonnet and analyze the authentication design.',
|
|
260
|
+
'Explain fable on sonnet; then send the findings to Slack.',
|
|
261
|
+
'Explain fable on sonnet and update README.',
|
|
262
|
+
'Explain fable on haiku, add a regression test.',
|
|
263
|
+
'Explain fable on opus and commit the documentation change.',
|
|
264
|
+
'說明 fable on sonnet 並更新 README。',
|
|
265
|
+
'解釋 fable on haiku,新增回歸測試。',
|
|
266
|
+
'解析 fable on opus 並刪除過期文件。',
|
|
267
|
+
'Explain fable on sonnet before updating README.',
|
|
268
|
+
'Explain fable on sonnet; return JSON after updating README.',
|
|
269
|
+
"Explain fable on sonnet before updating README.md to change 'Instalation' to 'Installation'.",
|
|
270
|
+
'解釋 fable on sonnet 後更新 README。',
|
|
271
|
+
'Explain fable on sonnet; no delegation before deleting stale files.',
|
|
272
|
+
'Explain fable on sonnet; return JSON with requestedProfile, update README.',
|
|
273
|
+
'Explain fable on sonnet; output JSON with requestedProfile and then publish the result.',
|
|
274
|
+
'Explain fable on sonnet; return JSON after restarting the server.',
|
|
275
|
+
'Explain fable on sonnet; no execution except updating README.',
|
|
276
|
+
'Explain fable on sonnet; provide a profile result while running the tests.',
|
|
277
|
+
'Explain fable on sonnet; return JSON with unknownField.',
|
|
278
|
+
'Explain fable on sonnet; some unknown continuation.',
|
|
279
|
+
// A negation scopes only its own noun list; affirmative actions after it execute.
|
|
280
|
+
'Explain fable on sonnet; no delegation, do execution.',
|
|
281
|
+
'Explain fable on sonnet; no delegation, switch model.',
|
|
282
|
+
'Explain fable on sonnet; no delegation and stage execution.',
|
|
283
|
+
'Explain fable on sonnet; without orchestration do execution.',
|
|
284
|
+
'Explain fable on sonnet; no delegation or switch model.',
|
|
285
|
+
'解釋 fable on sonnet,不要委派,執行。',
|
|
286
|
+
'解釋 fable on sonnet,不要委派,切換模型。',
|
|
287
|
+
]) {
|
|
288
|
+
const execution = runTier(prompt);
|
|
289
|
+
if (validContract(execution, 'Fable execution boundary')) {
|
|
290
|
+
check(execution.contract.workflowPlan.strategy === 'fable-staged', 'execution/macro scope preserves staged topology');
|
|
291
|
+
check(!execution.contract.workflowPlan.reasonCodes.includes('fable-profile-lookup'), 'mixed execution never records the bounded lookup reason');
|
|
292
|
+
check(execution.contract.workflowPlan.requiredInvariants.includes('isolated-worktree-before-mutation'), 'execution retains isolation obligation');
|
|
293
|
+
}
|
|
209
294
|
}
|
|
210
295
|
|
|
211
296
|
const explicitOverride = runTier('Use iterative-single to audit the entire repository architecture.');
|
|
@@ -250,10 +335,10 @@ if (validContract(noWorkspaceState, 'workspace state block')) {
|
|
|
250
335
|
const unavailableModel = runTier('fable on opus audit the entire repository architecture', {
|
|
251
336
|
context: { hostCapabilities: { modelAvailability: 'unavailable', subagents: 'available' } },
|
|
252
337
|
});
|
|
253
|
-
if (validContract(unavailableModel, 'model capability
|
|
254
|
-
check(unavailableModel.contract.workflowPlan.
|
|
255
|
-
check(unavailableModel.contract.workflowPlan.fallback.disposition
|
|
256
|
-
check(unavailableModel.contract.workflowPlan.fallback.reasonCodes.includes('requested-model-capability-unavailable'), 'model
|
|
338
|
+
if (validContract(unavailableModel, 'runtime model capability advisory')) {
|
|
339
|
+
check(unavailableModel.contract.workflowPlan.profileSelection.requested === 'orchestrator', 'opus remains a behavior-profile alias');
|
|
340
|
+
check(unavailableModel.contract.workflowPlan.fallback.disposition !== 'blocked', 'runtime model availability does not block Fable profile selection');
|
|
341
|
+
check(!unavailableModel.contract.workflowPlan.fallback.reasonCodes.includes('requested-model-capability-unavailable'), 'router no longer treats a branded model as a Fable requirement');
|
|
257
342
|
}
|
|
258
343
|
|
|
259
344
|
const hookUnavailable = runTier('Drop the prod database table.', {
|
|
@@ -38,6 +38,14 @@ This is a **repository/agent contract**, not an automatic upgrade to any platfor
|
|
|
38
38
|
|
|
39
39
|
The full ten-dimension entries and official source URLs are in [platform-compatibility.json](platform-compatibility.json). “Supported” in the standalone-skills column refers to the host’s documented skill surface plus the repository’s verified path contract; it does not mean that a live Harness session was run on every platform.
|
|
40
40
|
|
|
41
|
+
### Claude Code Fable entrypoint boundary
|
|
42
|
+
|
|
43
|
+
The Claude plugin registers `./fable-mode` as the public Fable skill. The nested `fable-opus`, `fable-sonnet`, `fable-haiku`, and `execution-guardrails` directories are reference contracts inside that skill tree; they are not independently registered or host-routable Claude skills. The legacy prompts `fable on opus`, `fable on sonnet`/`sonnect`, and `fable on haiku` select Fable behavior profiles (orchestrator/reasoning/mechanical), not branded runtime models. `model-selector.js` records the profile and separately evaluates the host-specific advisory runtime floor; a below-floor or unknown runtime does not block Fable by itself.
|
|
44
|
+
|
|
45
|
+
The router distinguishes bounded profile explanations from Fable execution. Supported lookup prompts select `direct-single` and `references/profile-lookup.md`; the public skill dispatches before loading stage references. Entire explanation, format and negative-constraint clauses are checked against a bounded lookup vocabulary; comma/and-separated canonical output fields remain lookup data. Unknown continuations, same-clause actions (including `before`/`after`), mixed execution or macro scope retain execution routing and required invariants. This is a deterministic routing and packaged instruction contract, not a host-enforced reference-read limit or a measured token-cost guarantee.
|
|
46
|
+
|
|
47
|
+
The nested execution-guardrails file is also not an always-on carrier. Fable agents that need those rules carry them inline; any non-Fable surface must load, copy, or inject the rules through a carrier the host actually executes before claiming that they apply there.
|
|
48
|
+
|
|
41
49
|
## Installer target contract
|
|
42
50
|
|
|
43
51
|
The general installer’s target paths are tested independently from host discovery. This prevents a successful file copy from being reported as proof that an agent will load the file.
|
|
@@ -69,10 +77,15 @@ The package is tested at the mechanism layer and remains subject to the host’s
|
|
|
69
77
|
The repository also ships a host-facing synchronization command for the native marketplace/plugin surfaces:
|
|
70
78
|
|
|
71
79
|
```bash
|
|
80
|
+
# POSIX shells / Git Bash
|
|
72
81
|
npx github:dyphn1/Harness-everything plugin-sync
|
|
73
82
|
# or, from a checkout:
|
|
74
83
|
./scripts/plugin-sync.sh
|
|
75
|
-
|
|
84
|
+
|
|
85
|
+
# Windows PowerShell
|
|
86
|
+
npx.cmd github:dyphn1/Harness-everything plugin-sync
|
|
87
|
+
# or, from a checkout:
|
|
88
|
+
node scripts/plugin-sync.js
|
|
76
89
|
```
|
|
77
90
|
|
|
78
91
|
The command detects Claude Code and Codex independently, ensures the Harness marketplace is configured, reads the installed-plugin state, then refreshes that marketplace before choosing the operation:
|