openhermes 4.11.2 → 4.13.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.
Files changed (74) hide show
  1. package/CONTEXT.md +1 -1
  2. package/ETHOS.md +1 -1
  3. package/README.md +12 -18
  4. package/bootstrap.ts +73 -148
  5. package/docs/HOW-IT-WORKS.md +162 -0
  6. package/docs/adr/ADR-0001-rebuild-vs-increment.md +30 -0
  7. package/docs/adr/ADR-0002-routing-graph-vs-linear-chain.md +36 -0
  8. package/docs/adr/ADR-0003-per-directory-plan-storage.md +34 -0
  9. package/docs/adr/ADR-0004-composer-fragment-architecture.md +42 -0
  10. package/docs/adr/ADR-0005-hook-system-design.md +42 -0
  11. package/docs/adr/README.md +9 -0
  12. package/harness/codex/AUTOPILOT.md +30 -23
  13. package/harness/codex/CHARTER.md +3 -3
  14. package/harness/lib/composer/compose.test.ts +11 -0
  15. package/harness/lib/composer/fragments/02-delegation.md +2 -1
  16. package/harness/lib/composer/fragments/04-task-flow.md +42 -2
  17. package/harness/lib/composer/fragments/08-routing.md +1 -1
  18. package/harness/lib/composer/fragments/09-guardrails.md +17 -4
  19. package/harness/lib/composer/index.ts +1 -1
  20. package/harness/lib/guards/guard-config.ts +72 -0
  21. package/harness/lib/hooks/builtins/confidence-gate-hook.ts +2 -4
  22. package/harness/lib/hooks/builtins/delegation-depth-hook.ts +23 -4
  23. package/harness/lib/hooks/builtins/dynamic-route-hook.ts +99 -0
  24. package/harness/lib/hooks/builtins/next-route-hook.ts +24 -0
  25. package/harness/lib/hooks/builtins/plan-check-hook.ts +2 -2
  26. package/harness/lib/hooks/builtins/route-tracking-hook.ts +79 -25
  27. package/harness/lib/hooks/hooks.test.ts +117 -205
  28. package/harness/lib/hooks/index.ts +38 -30
  29. package/harness/lib/hooks/registry.ts +309 -416
  30. package/harness/lib/hooks/types.ts +116 -71
  31. package/harness/lib/plans/plan-location.ts +134 -0
  32. package/harness/lib/routing/index.ts +21 -0
  33. package/harness/lib/routing/route-guidance.ts +147 -0
  34. package/harness/lib/routing/route-resolver.ts +58 -0
  35. package/harness/lib/routing/routing.test.ts +195 -0
  36. package/harness/lib/routing/skill-frontmatter.ts +125 -0
  37. package/harness/lib/routing/types.ts +52 -0
  38. package/harness/skills/oh-ascii/SKILL.md +1 -1
  39. package/harness/skills/oh-fusion/DEEP.md +56 -33
  40. package/harness/skills/oh-fusion/SKILL.md +30 -16
  41. package/harness/skills/oh-init/DEEP.md +2 -2
  42. package/harness/skills/oh-manifest/SKILL.md +1 -0
  43. package/harness/skills/oh-plan-review/DEEP.md +1 -1
  44. package/harness/skills/oh-planner/DEEP.md +3 -3
  45. package/harness/skills/oh-review/DEEP.md +2 -0
  46. package/harness/skills/oh-review/SKILL.md +1 -0
  47. package/package.json +56 -55
  48. package/harness/lib/background/background.test.ts +0 -197
  49. package/harness/lib/background/index.ts +0 -7
  50. package/harness/lib/background/interfaces.ts +0 -31
  51. package/harness/lib/background/manager.ts +0 -320
  52. package/harness/lib/hooks/builtins/error-recovery-hook.ts +0 -107
  53. package/harness/lib/hooks/builtins/memory-sync-hook.ts +0 -73
  54. package/harness/lib/hooks/builtins/sanity-check-hook.ts +0 -52
  55. package/harness/lib/memory/index.ts +0 -18
  56. package/harness/lib/memory/interfaces.ts +0 -53
  57. package/harness/lib/memory/memory-manager.ts +0 -205
  58. package/harness/lib/memory/memory.test.ts +0 -491
  59. package/harness/lib/memory/plan-store.ts +0 -366
  60. package/harness/lib/recovery/handler.ts +0 -243
  61. package/harness/lib/recovery/index.ts +0 -14
  62. package/harness/lib/recovery/interfaces.ts +0 -48
  63. package/harness/lib/recovery/patterns.ts +0 -149
  64. package/harness/lib/recovery/recovery.test.ts +0 -312
  65. package/harness/lib/sanity/anomaly-tracker.ts +0 -127
  66. package/harness/lib/sanity/checker.ts +0 -178
  67. package/harness/lib/sanity/index.ts +0 -13
  68. package/harness/lib/sanity/interfaces.ts +0 -24
  69. package/harness/lib/sanity/sanity.test.ts +0 -472
  70. package/harness/lib/sync/file-watcher.ts +0 -174
  71. package/harness/lib/sync/index.ts +0 -11
  72. package/harness/lib/sync/interfaces.ts +0 -27
  73. package/harness/lib/sync/plan-sync.ts +0 -536
  74. package/harness/lib/sync/sync.test.ts +0 -832
@@ -0,0 +1,195 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { consumeRouteGuidance, parseSkillFrontmatter, resolveRoute } from "./index.ts";
4
+
5
+ describe("routing frontmatter", () => {
6
+ it("parses scalar route values", () => {
7
+ const parsed = parseSkillFrontmatter(`---
8
+ name: oh-test
9
+ route:
10
+ pass: oh-builder
11
+ fail: oh-review
12
+ blocker: surface
13
+ ---`);
14
+
15
+ assert.ok(parsed);
16
+ assert.deepEqual(parsed.route, {
17
+ pass: ["oh-builder"],
18
+ fail: ["oh-review"],
19
+ blocker: ["surface"],
20
+ });
21
+ });
22
+
23
+ it("parses yaml route lists", () => {
24
+ const parsed = parseSkillFrontmatter(`---
25
+ name: oh-test
26
+ route:
27
+ pass:
28
+ - oh-gauntlet
29
+ - oh-ship
30
+ fail:
31
+ - oh-builder
32
+ blocker: surface
33
+ ---`);
34
+
35
+ assert.ok(parsed);
36
+ assert.deepEqual(parsed.route, {
37
+ pass: ["oh-gauntlet", "oh-ship"],
38
+ fail: ["oh-builder"],
39
+ blocker: ["surface"],
40
+ });
41
+ });
42
+
43
+ it("parses inline route arrays", () => {
44
+ const parsed = parseSkillFrontmatter(`---
45
+ name: oh-test
46
+ route:
47
+ pass: [oh-gauntlet, oh-ship]
48
+ fail: [surface, oh-expert]
49
+ blocker: surface
50
+ ---`);
51
+
52
+ assert.ok(parsed);
53
+ assert.deepEqual(parsed.route, {
54
+ pass: ["oh-gauntlet", "oh-ship"],
55
+ fail: ["surface", "oh-expert"],
56
+ blocker: ["surface"],
57
+ });
58
+ });
59
+ });
60
+
61
+ describe("resolveRoute", () => {
62
+ const route = {
63
+ pass: ["oh-gauntlet", "oh-ship"],
64
+ fail: ["oh-builder"],
65
+ blocker: ["surface"],
66
+ };
67
+
68
+ it("defaults to the first candidate for an outcome", () => {
69
+ const resolved = resolveRoute(route, { outcome: "pass" });
70
+ assert.deepEqual(resolved, {
71
+ outcome: "pass",
72
+ candidates: ["oh-gauntlet", "oh-ship"],
73
+ selected: "oh-gauntlet",
74
+ reason: 'Selected first declared route for outcome "pass".',
75
+ });
76
+ });
77
+
78
+ it("prefers an evidence target when it matches a candidate", () => {
79
+ const resolved = resolveRoute(route, { outcome: "pass", target: "oh-ship" });
80
+ assert.deepEqual(resolved, {
81
+ outcome: "pass",
82
+ candidates: ["oh-gauntlet", "oh-ship"],
83
+ selected: "oh-ship",
84
+ reason: 'Selected "oh-ship" from output evidence.',
85
+ });
86
+ });
87
+
88
+ it("prefers oh-ship for verified done ship work", () => {
89
+ const resolved = resolveRoute(route, {
90
+ outcome: "pass",
91
+ verification: "verified",
92
+ action: "done",
93
+ work: "ship",
94
+ });
95
+
96
+ assert.deepEqual(resolved, {
97
+ outcome: "pass",
98
+ verification: "verified",
99
+ action: "done",
100
+ work: "ship",
101
+ candidates: ["oh-gauntlet", "oh-ship"],
102
+ selected: "oh-ship",
103
+ reason: 'Selected "oh-ship" for verified ship-ready work.',
104
+ });
105
+ });
106
+
107
+ it("prefers oh-gauntlet for unverified work", () => {
108
+ const resolved = resolveRoute(route, {
109
+ outcome: "pass",
110
+ verification: "unverified",
111
+ action: "done",
112
+ work: "ship",
113
+ });
114
+
115
+ assert.deepEqual(resolved, {
116
+ outcome: "pass",
117
+ verification: "unverified",
118
+ action: "done",
119
+ work: "ship",
120
+ candidates: ["oh-gauntlet", "oh-ship"],
121
+ selected: "oh-gauntlet",
122
+ reason: 'Selected "oh-gauntlet" because work is still unverified.',
123
+ });
124
+ });
125
+
126
+ it("prefers oh-gauntlet for verify work", () => {
127
+ const resolved = resolveRoute(route, {
128
+ outcome: "pass",
129
+ verification: "verified",
130
+ action: "done",
131
+ work: "verify",
132
+ });
133
+
134
+ assert.deepEqual(resolved, {
135
+ outcome: "pass",
136
+ verification: "verified",
137
+ action: "done",
138
+ work: "verify",
139
+ candidates: ["oh-gauntlet", "oh-ship"],
140
+ selected: "oh-gauntlet",
141
+ reason: 'Selected "oh-gauntlet" for verification work.',
142
+ });
143
+ });
144
+
145
+ it("prefers oh-builder for fixable implementation work", () => {
146
+ const resolved = resolveRoute({
147
+ pass: ["oh-gauntlet", "oh-ship", "oh-builder"],
148
+ fail: ["oh-builder"],
149
+ blocker: ["surface"],
150
+ }, {
151
+ outcome: "pass",
152
+ verification: "verified",
153
+ action: "fixable",
154
+ work: "implement",
155
+ });
156
+
157
+ assert.deepEqual(resolved, {
158
+ outcome: "pass",
159
+ verification: "verified",
160
+ action: "fixable",
161
+ work: "implement",
162
+ candidates: ["oh-gauntlet", "oh-ship", "oh-builder"],
163
+ selected: "oh-builder",
164
+ reason: 'Selected "oh-builder" for fixable implementation work.',
165
+ });
166
+ });
167
+ });
168
+
169
+ describe("consumeRouteGuidance", () => {
170
+ it("promotes a selected route into an explicit next-route instruction", () => {
171
+ const consumed = consumeRouteGuidance([
172
+ "Review complete",
173
+ 'ROUTE_GUIDANCE: {"outcome":"pass","candidates":["oh-gauntlet","oh-ship"],"selected":"oh-ship","reason":"Selected \\\"oh-ship\\\" from output evidence."}',
174
+ ].join("\n"));
175
+
176
+ assert.equal(consumed.selected, "oh-ship");
177
+ assert.match(consumed.output, /NEXT_ROUTE: oh-ship/);
178
+ });
179
+
180
+ it("leaves output unchanged when no route guidance is present", () => {
181
+ const output = "plain output";
182
+ const consumed = consumeRouteGuidance(output);
183
+
184
+ assert.equal(consumed.selected, null);
185
+ assert.equal(consumed.output, output);
186
+ });
187
+
188
+ it("ignores malformed route guidance safely", () => {
189
+ const output = 'Review complete\nROUTE_GUIDANCE: {"selected":42}';
190
+ const consumed = consumeRouteGuidance(output);
191
+
192
+ assert.equal(consumed.selected, null);
193
+ assert.equal(consumed.output, output);
194
+ });
195
+ });
@@ -0,0 +1,125 @@
1
+ import fs from "node:fs";
2
+ import type { RouteOutcome, SkillRouteMap, SkillRoutingFrontmatter } from "./types.ts";
3
+
4
+ const EMPTY_ROUTES: SkillRouteMap = {
5
+ pass: [],
6
+ fail: [],
7
+ blocker: [],
8
+ };
9
+
10
+ function stripQuotes(value: string): string {
11
+ return value.trim().replace(/^['"]|['"]$/g, "");
12
+ }
13
+
14
+ function parseRouteValue(value: string): string[] {
15
+ const trimmed = value.trim();
16
+ if (!trimmed) return [];
17
+
18
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
19
+ return trimmed
20
+ .slice(1, -1)
21
+ .split(",")
22
+ .map((entry) => stripQuotes(entry))
23
+ .filter(Boolean);
24
+ }
25
+
26
+ return [stripQuotes(trimmed)].filter(Boolean);
27
+ }
28
+
29
+ function isRouteOutcome(value: string): value is RouteOutcome {
30
+ return value === "pass" || value === "fail" || value === "blocker";
31
+ }
32
+
33
+ export function extractFrontmatter(source: string): string | null {
34
+ const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
35
+ return match?.[1] ?? null;
36
+ }
37
+
38
+ export function parseSkillFrontmatter(source: string): SkillRoutingFrontmatter | null {
39
+ const rawFrontmatter = extractFrontmatter(source);
40
+ if (!rawFrontmatter) return null;
41
+
42
+ const route: SkillRouteMap = {
43
+ pass: [],
44
+ fail: [],
45
+ blocker: [],
46
+ };
47
+
48
+ let name: string | undefined;
49
+ let description: string | undefined;
50
+ let tier: string | undefined;
51
+ let inRouteBlock = false;
52
+ let activeRouteKey: RouteOutcome | null = null;
53
+
54
+ for (const rawLine of rawFrontmatter.split(/\r?\n/)) {
55
+ const line = rawLine.trimEnd();
56
+ const trimmed = line.trim();
57
+
58
+ if (!trimmed) continue;
59
+
60
+ if (/^route:\s*$/.test(trimmed)) {
61
+ inRouteBlock = true;
62
+ activeRouteKey = null;
63
+ continue;
64
+ }
65
+
66
+ if (inRouteBlock) {
67
+ const routeMatch = line.match(/^\s{2,}(pass|fail|blocker):\s*(.*)$/);
68
+ if (routeMatch && isRouteOutcome(routeMatch[1])) {
69
+ activeRouteKey = routeMatch[1];
70
+ route[activeRouteKey].push(...parseRouteValue(routeMatch[2]));
71
+ continue;
72
+ }
73
+
74
+ const listMatch = line.match(/^\s{4,}-\s+(.+)$/);
75
+ if (listMatch && activeRouteKey) {
76
+ route[activeRouteKey].push(stripQuotes(listMatch[1]));
77
+ continue;
78
+ }
79
+
80
+ if (!/^\s/.test(line)) {
81
+ inRouteBlock = false;
82
+ activeRouteKey = null;
83
+ } else {
84
+ activeRouteKey = null;
85
+ continue;
86
+ }
87
+ }
88
+
89
+ const fieldMatch = line.match(/^(name|description|tier):\s*(.+)$/);
90
+ if (!fieldMatch) continue;
91
+
92
+ const value = stripQuotes(fieldMatch[2]);
93
+ switch (fieldMatch[1]) {
94
+ case "name":
95
+ name = value;
96
+ break;
97
+ case "description":
98
+ description = value;
99
+ break;
100
+ case "tier":
101
+ tier = value;
102
+ break;
103
+ }
104
+ }
105
+
106
+ return {
107
+ name,
108
+ description,
109
+ tier,
110
+ route,
111
+ };
112
+ }
113
+
114
+ export function readSkillFrontmatter(skillFilePath: string): SkillRoutingFrontmatter | null {
115
+ if (!fs.existsSync(skillFilePath)) return null;
116
+ return parseSkillFrontmatter(fs.readFileSync(skillFilePath, "utf8"));
117
+ }
118
+
119
+ export function emptySkillRoutes(): SkillRouteMap {
120
+ return {
121
+ pass: [...EMPTY_ROUTES.pass],
122
+ fail: [...EMPTY_ROUTES.fail],
123
+ blocker: [...EMPTY_ROUTES.blocker],
124
+ };
125
+ }
@@ -0,0 +1,52 @@
1
+ export const ROUTE_OUTCOMES = ["pass", "fail", "blocker"] as const;
2
+ export const ROUTE_VERIFICATIONS = ["verified", "unverified"] as const;
3
+ export const ROUTE_ACTIONS = ["done", "fixable", "needs-context", "blocked"] as const;
4
+ export const ROUTE_WORK_TYPES = ["implement", "verify", "ship", "diagnose", "surface"] as const;
5
+
6
+ export type RouteOutcome = (typeof ROUTE_OUTCOMES)[number];
7
+ export type RouteVerification = (typeof ROUTE_VERIFICATIONS)[number];
8
+ export type RouteAction = (typeof ROUTE_ACTIONS)[number];
9
+ export type RouteWork = (typeof ROUTE_WORK_TYPES)[number];
10
+
11
+ export interface SkillRouteMap {
12
+ pass: string[];
13
+ fail: string[];
14
+ blocker: string[];
15
+ }
16
+
17
+ export interface SkillRoutingFrontmatter {
18
+ name?: string;
19
+ description?: string;
20
+ tier?: string;
21
+ route: SkillRouteMap;
22
+ }
23
+
24
+ export interface RouteEvidence {
25
+ outcome: RouteOutcome;
26
+ verification?: RouteVerification;
27
+ action?: RouteAction;
28
+ work?: RouteWork;
29
+ target?: string;
30
+ reason?: string;
31
+ }
32
+
33
+ export interface RouteResolution {
34
+ outcome: RouteOutcome;
35
+ verification?: RouteVerification;
36
+ action?: RouteAction;
37
+ work?: RouteWork;
38
+ candidates: string[];
39
+ selected: string | null;
40
+ reason: string;
41
+ }
42
+
43
+ export interface RuntimeRouteDecision {
44
+ selected: string;
45
+ source: "next_route" | "route_guidance";
46
+ outcome?: RouteOutcome;
47
+ verification?: RouteVerification;
48
+ action?: RouteAction;
49
+ work?: RouteWork;
50
+ candidates?: string[];
51
+ reason?: string;
52
+ }
@@ -19,7 +19,7 @@ Create and validate ASCII diagrams in three phases: design, generate, validate.
19
19
  3. Fence all diagrams in markdown code blocks
20
20
  4. Keep diagrams under 80 columns with max 3 nesting levels
21
21
  5. For complex diagrams, author PlantUML and render with `-utxt`
22
- 6. Validate structural alignment with `scripts/check_ascii_alignment.py`
22
+ 6. Validate visual alignment manually: check that box-drawing characters connect cleanly and all columns align within the markdown code block
23
23
  7. Fix reported issues and re-validate until clean
24
24
 
25
25
  ## Routing
@@ -18,42 +18,57 @@ Confirm: content loaded, frontmatter present, no access restrictions. For multip
18
18
 
19
19
  ## Phase 2: Analysis
20
20
 
21
- ### Depth Scoring
22
- | Metric | Assessment |
23
- |--------|-----------|
24
- | Lines | SKILL.md length |
25
- | Concrete rules | "must/never/always/banned" count |
26
- | Examples | Before/after or usage code blocks |
27
- | Anti-patterns | Explicit "don't" sections |
28
- | Workflow steps | Sequential, actionable steps |
29
- | Routing table | pass/fail/blocker defined |
21
+ ### Required Lenses
30
22
 
31
- **Score:** High (70-100) = concrete + examples + routing. Medium (30-69) = some structure. Low (0-29) = vague, no rules.
23
+ Analyze every source through these lenses:
32
24
 
33
- ### Overlap Detection
34
- Compare against existing `harness/skills/oh-*` skills. Overlap: none / partial (complementary) / complete (redundant).
25
+ - **OH gaps** — what OH does not currently cover, or covers weakly
26
+ - **OH wins** where OH already outperforms the source and should not regress
27
+ - **Missed patterns** — reusable patterns the source surfaces that OH should absorb
28
+ - **Overlap target** — the strongest existing `oh-*` candidate for merge, if any
35
29
 
36
- ### Convention Check
37
- - Clear description for triggering? Actionable instructions? Anti-patterns? Examples? Measurable outcomes? No time-sensitive refs? No platform assumptions?
30
+ ### Merge Rubric
38
31
 
39
- ### Report Template
32
+ Choose one verdict:
33
+
34
+ | Verdict | Use when |
35
+ |---------|----------|
36
+ | Merge | The source upgrades an existing OH capability more than it defines a new one |
37
+ | Standalone | The source adds a distinct capability with its own trigger, workflow, and routing needs |
38
+ | Discard | The source is redundant, weak, or anti-native |
39
+
40
+ ### Strength Check
41
+
42
+ Reject or rewrite any protocol that makes OH weaker, slower, noisier, or less OpenCode-native.
43
+
44
+ ### Fusion Report Template
40
45
  ```markdown
41
- **Depth:** <0-100> — <High/Medium/Low>
42
- **Overlap:** <existing skill> — <none/partial/complete>
43
- **Verdict:** <keep / fuse / discard / ask>
44
- **Action:** <port directly / fuse with X / discard>
46
+ ## OH gaps
47
+ - ...
48
+
49
+ ## OH wins
50
+ - ...
51
+
52
+ ## Missed patterns
53
+ - ...
54
+
55
+ ## Merge verdict
56
+ - Verdict: <merge | standalone | discard>
57
+ - Target: <existing oh-* or new oh-*>
58
+ - Why: <short rationale>
59
+
60
+ ## Action plan
61
+ 1. ...
62
+ 2. ...
63
+
64
+ ## Approval gate
65
+ - Status: pending approval
66
+ - Next move after approval: <skill/action>
45
67
  ```
46
68
 
47
69
  ## Phase 3: Decision
48
70
 
49
- | Verdict | Action |
50
- |---------|--------|
51
- | Keep | High signal, no overlap. Port to `oh-<name>`. |
52
- | Fuse | Partial overlap with existing. Merge complementary DNA. |
53
- | Discard | Low signal or complete overlap. Surface reasoning. |
54
- | Ask | Ambiguous quality. Surface findings. |
55
-
56
- When in doubt: prefer fuse over keep, keep over discard if ANY unique signal.
71
+ Default to `merge` when the source sharpens an existing OH workflow. Default to `standalone` only when the capability stays clean, distinct, and reusable after removing overlap.
57
72
 
58
73
  ## Phase 4: Adaptation
59
74
 
@@ -65,10 +80,10 @@ tier: <2|3|4>
65
80
  ```
66
81
 
67
82
  ### Body Structure
68
- 1. **Summary** — one-paragraph of what the skill does
83
+ 1. **Summary** — one short paragraph
69
84
  2. **When to Use** — clear triggering context
70
- 3. **Workflow** — numbered steps (the core)
71
- 4. **Anti-patterns** — what NOT to do
85
+ 3. **Protocol** — numbered workflow
86
+ 4. **Rubric / guardrails** — what to merge, reject, or escalate
72
87
  5. **Routing** — pass/fail/blocker table
73
88
 
74
89
  ### Adaptation Rules
@@ -78,11 +93,16 @@ tier: <2|3|4>
78
93
  - Keep all concrete rules, examples, and anti-patterns from the original.
79
94
  - Discard fluff, philosophy, and motivational language.
80
95
  - Preserve the original's unique signal — that's why you're importing it.
96
+ - Keep the result OpenCode/OpenHermes-native only.
81
97
 
82
98
  ### Naming
83
99
  Match `^[a-z0-9]+(-[a-z0-9]+)*$`, prefix `oh-`. Original name if good fit, adapt if not. Fusion names signal combined purpose.
84
100
 
85
- ## Phase 5: Fusion (opt — skip for single imports)
101
+ ## Phase 5: Approval Gate
102
+
103
+ Before any harness mutation, surface the fusion report and wait for approval. Approved intent already present in the conversation counts. Do not ask new questions unless a blocker cannot be resolved from the codebase or prior conversation.
104
+
105
+ ## Phase 6: Fusion (opt — skip for single imports)
86
106
 
87
107
  For each skill being fused, identify:
88
108
  - **Unique concepts** — content only this skill has
@@ -101,7 +121,7 @@ Structure so each source contributes its strength. Do NOT concatenate — must r
101
121
  ### Naming
102
122
  Name signals combined purpose, not individual sources. E.g., `oh-facade` from redesign + design-taste + high-end-visual, not `oh-redesign-plus-taste`.
103
123
 
104
- ## Phase 6: Integration
124
+ ## Phase 7: Integration
105
125
 
106
126
  1. **Create file** → user dir (`~/.config/opencode/skills/oh-<name>/SKILL.md`)
107
127
  2. **Wire AUTOPILOT** → add to auto-classify matrix in `harness/codex/AUTOPILOT.md`: signal keywords → classification → "Load **oh-<name>**. Do not ask."
@@ -113,8 +133,11 @@ Name signals combined purpose, not individual sources. E.g., `oh-facade` from re
113
133
  ## Anti-patterns
114
134
 
115
135
  - Importing without analysis (always run Phase 2)
116
- - Keeping everything ~50% of external skills is fluff
136
+ - Asking before checking code or prior conversation
137
+ - Changing the harness before the approval gate clears
138
+ - Keeping everything — most external skill text is fluff
117
139
  - Fusing incompatible domains (confusing to model and user)
118
140
  - Naming after source ("oh-tailwind-v2") instead of capability ("oh-styles")
119
141
  - Skipping route frontmatter — without it, autopilot can't route
120
142
  - Overwriting existing routing without checking for collisions
143
+ - Shipping a protocol that weakens OH or makes it less native
@@ -3,34 +3,48 @@ name: oh-fusion
3
3
  description: "Use when the user has an existing skill, finds a skill in their .agents/skills, or wants to bring an external capability into OH as a skill."
4
4
  tier: 3
5
5
  route:
6
- pass:
7
- - oh-skills-link
8
- - oh-skill-craft
9
- fail: oh-skill-craft
6
+ pass: [oh-skill-craft, oh-skills-link]
7
+ fail: surface
10
8
  blocker: surface
11
9
  ---
12
10
 
13
11
  # oh-fusion
14
12
 
15
- Skill ingestion pipeline: Discover → Analyze → DecideAdapt Fuse → Integrate.
13
+ Skill fusion pipeline: Discover → Analyze → VerdictApproval Gate → Integrate.
16
14
 
17
- ## Steps
15
+ ## Protocol
18
16
 
19
17
  1. Load skill content — read from `.agents/skills/`, `npx skills`, URL, user path, or inline text.
20
- 2. Analyze depth score by lines, concrete rules, examples, anti-patterns, workflow steps, and routing.
21
- 3. Detect overlap — compare against existing `oh-*` skills. Report none/partial/complete.
22
- 4. Decide verdict Keep (high signal, no overlap), Fuse (partial overlap, merge DNA), Discard (low/no signal), or Ask (ambiguous).
23
- 5. Adapt to OH-native format remove emojis, convert paths, add routing, preserve unique signal.
24
- 6. Fuse if merging identify unique concepts from each source, resolve conflicts, write one coherent workflow.
25
- 7. Integrate create skill file, wire AUTOPILOT, routing, AGENTS.md, openhermes.md.
26
- 8. Verify route to oh-skills-link to confirm discovery.
18
+ 2. Analyze the source against OH report `OH gaps`, `OH wins`, and `missed patterns`.
19
+ 3. Compare overlap — identify the best existing `oh-*` target, if any.
20
+ 4. Decide with a rubric:
21
+ - **Merge** when the source mainly strengthens an existing OH capability.
22
+ - **Standalone** when the source adds a distinct, reusable capability.
23
+ - **Discard** when the signal is weak or redundant.
24
+ 5. Produce a fusion report with these sections, in order:
25
+ - `OH gaps`
26
+ - `OH wins`
27
+ - `missed patterns`
28
+ - `merge verdict`
29
+ - `action plan`
30
+ - `approval gate`
31
+ 6. Resolve from code and prior conversation first. Ask only if a blocker remains.
32
+ 7. After approval, adapt to OH-native form and route directly to implementation.
33
+ 8. Verify discovery with `oh-skills-link` when a new or renamed skill is added.
34
+
35
+ ## Merge Rubric
36
+
37
+ - **Merge into existing `oh-*`** when domain, trigger, and route already exist; the source mostly adds sharper rules, better sequencing, or stronger edge-case handling.
38
+ - **Create standalone `oh-*`** when the source introduces a capability with distinct trigger words, workflow, and handoff points.
39
+ - **Discard** when the source adds fluff, duplicates OH, or weakens OpenCode/OpenHermes-native operation.
27
40
 
28
41
  ## Routing
29
42
 
30
43
  | Outcome | Route |
31
44
  |---------|-------|
32
- | Integration complete | → oh-skills-link (verify discovery) |
33
- | Fusion needs iteration | → oh-skill-craft |
45
+ | Approved merge or standalone plan | → oh-skill-craft |
46
+ | New or renamed skill needs discovery check | → oh-skills-link |
34
47
  | Analysis: discard | → surface |
35
- | Analysis: ask | → surface with recs |
48
+ | Approval not yet granted | → surface |
36
49
  | Blocker | → surface |
50
+
@@ -26,7 +26,7 @@ OpenHermes is the primary orchestrator. All routing, planning, and delegation fl
26
26
  - **Test command**: <fill or auto-detect>
27
27
 
28
28
  ## Key Directives
29
- - Plan first. Write to `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md` before multi-file changes.
29
+ - Plan first. Write to `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md` before multi-file changes.
30
30
  - OpenHermes delegates everything to sub-agents — never executes directly.
31
31
  - Verify before claiming success. Read files, run commands, confirm output.
32
32
  - Use oh-* skills on demand via the skill tool.
@@ -40,7 +40,7 @@ Ask user to fill or auto-detect from manifests.
40
40
  ```markdown
41
41
  ## OpenHermes Orchestrator
42
42
  OpenHermes is the primary orchestrator.
43
- - **Plan**: `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md`
43
+ - **Plan**: `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md`
44
44
  - **Never execute**: delegates everything to sub-agents
45
45
  - **Verify before claim**: read files, run commands, confirm output
46
46
  ```
@@ -29,3 +29,4 @@ Full build orchestration loop: pre-flight → plan → build → verify → loop
29
29
  | pass | → oh-planner |
30
30
  | fail | → oh-expert (diagnose loop failure) |
31
31
  | blocker | → surface with context and options |
32
+
@@ -80,7 +80,7 @@ One section at a time: Architecture → Code Quality → Tests → Performance.
80
80
 
81
81
  ## Output
82
82
 
83
- Plan file (`~/.local/share/openhermes/plans/<project-name>/plan-<nnn>.md`) updated with findings and decisions.
83
+ Plan file (`~/.local/share/openhermes/plans/<project-name>/plan-<nnn>.md`) updated with findings and decisions.
84
84
 
85
85
  ## Anti-patterns
86
86
 
@@ -92,18 +92,18 @@ Runs sequentially: **Strategy → Architecture → Design → Engineering → DX
92
92
  Every plan written by oh-planner uses this canonical format.
93
93
 
94
94
  ### Storage
95
- Canonical path: `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md`
95
+ Canonical path: `~/.local/share/openhermes/plans/<project>/plan-<nnn>.md`
96
96
 
97
97
  ### Template
98
98
  ```markdown
99
99
  # PLAN: <project>
100
100
 
101
- Plan ID: <project>/plan-<nnn>
101
+ Plan ID: <project>/plan-<nnn>
102
102
  Project: <project>
103
103
  Status: active | in-progress | blocked | complete | abandoned
104
104
  Created: <ts> | Updated: <ts>
105
105
  Project Path: <absolute-path>
106
- Plan Path: <canonical-path>/<project>/plan-<nnn>.md
106
+ Plan Path: <canonical-path>/<project>/plan-<nnn>.md
107
107
  Objective: <short>
108
108
 
109
109
  ## Current State
@@ -23,6 +23,8 @@ AGENTS.md, CLAUDE.md, CONTRIBUTING.md, CONTEXT.md, ADRs, eslint/biome/prettier c
23
23
  ### 5. Aggregate
24
24
  Present under `## Standards` / `## Spec`. Do not merge. End with total + worst issue.
25
25
 
26
+ Concrete, low-risk, fixable findings should be converted into implementation work and dispatched to oh-builder immediately instead of stopping as report-only notes.
27
+
26
28
  ### Safety Check (inline before spawning)
27
29
  - SQL injection, LLM trust boundary violations, conditional side effects (test vs prod), hardcoded secrets
28
30
  - Block immediately if critical — do not spawn sub-agents.
@@ -29,3 +29,4 @@ Two-axis review: Standards + Spec, parallel sub-agents. Three modes: Diff Review
29
29
  | pass | → oh-gauntlet or oh-ship |
30
30
  | fail | → oh-builder |
31
31
  | blocker | → surface |
32
+