@rune-kit/rune 2.27.0 → 2.29.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/.codex-plugin/plugin.json +14 -0
- package/README.md +76 -42
- package/compiler/__tests__/adapter-model-mapping.test.js +22 -24
- package/compiler/__tests__/adapters.test.js +4 -1
- package/compiler/__tests__/doctor-mesh.test.js +55 -1
- package/compiler/__tests__/governance-collector.test.js +1 -0
- package/compiler/__tests__/hooks-codex.test.js +85 -0
- package/compiler/__tests__/hooks-doctor-tier.test.js +3 -5
- package/compiler/__tests__/hooks-drift.test.js +2 -2
- package/compiler/__tests__/hooks-install.test.js +2 -2
- package/compiler/__tests__/hooks-tiers.test.js +3 -4
- package/compiler/__tests__/setup.test.js +59 -0
- package/compiler/__tests__/status.test.js +7 -1
- package/compiler/__tests__/tier-override.test.js +42 -1
- package/compiler/__tests__/transforms.test.js +15 -0
- package/compiler/adapters/codex.js +88 -11
- package/compiler/adapters/hooks/codex.js +178 -0
- package/compiler/adapters/hooks/index.js +10 -0
- package/compiler/adapters/openclaw.js +2 -2
- package/compiler/bin/rune.js +5 -4
- package/compiler/commands/hooks/install.js +2 -2
- package/compiler/commands/hooks/status.js +1 -1
- package/compiler/commands/setup.js +101 -28
- package/compiler/doctor.js +12 -6
- package/compiler/emitter.js +46 -3
- package/compiler/governance-collector.js +3 -2
- package/compiler/status.js +4 -7
- package/compiler/transforms/branding.js +2 -2
- package/compiler/transforms/subagents.js +3 -3
- package/hooks/codex-hooks.json +96 -0
- package/hooks/context-watch/index.cjs +8 -5
- package/hooks/intent-router/index.cjs +3 -0
- package/hooks/lib/hook-output.cjs +11 -3
- package/hooks/post-session-reflect/index.cjs +65 -24
- package/hooks/pre-compact/index.cjs +10 -4
- package/hooks/pre-tool-guard/index.cjs +38 -36
- package/hooks/run-hook +1 -1
- package/hooks/run-hook.cmd +1 -1
- package/hooks/secrets-scan/index.cjs +18 -2
- package/package.json +3 -2
- package/skills/browser-pilot/SKILL.md +1 -0
- package/skills/completion-gate/SKILL.md +5 -5
- package/skills/design/SKILL.md +33 -6
- package/skills/doc-processor/SKILL.md +2 -2
- package/skills/hallucination-guard/SKILL.md +1 -0
- package/skills/journal/SKILL.md +1 -0
- package/skills/problem-solver/SKILL.md +32 -1
- package/skills/retro/SKILL.md +2 -2
- package/skills/session-bridge/SKILL.md +1 -0
- package/skills/session-bridge/scripts/load-invariants.js +1 -1
- package/skills/verification/SKILL.md +42 -1
- package/skills/video-creator/SKILL.md +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const
|
|
7
|
+
const { stateFile } = require('../lib/context-key.cjs');
|
|
8
8
|
const { captureConsole } = require('../lib/hook-output.cjs');
|
|
9
9
|
// Hook stdout is a JSON contract, not free text (Codex rejects bare lines and
|
|
10
10
|
// discards the output). Capture the prints below and emit one envelope on exit.
|
|
@@ -12,12 +12,17 @@ captureConsole('Stop');
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
const cwd = process.cwd();
|
|
15
|
-
|
|
15
|
+
let sessionId;
|
|
16
|
+
try {
|
|
17
|
+
sessionId = JSON.parse(fs.readFileSync(0, 'utf-8')).session_id;
|
|
18
|
+
} catch {
|
|
19
|
+
// Older runtimes may not provide hook input.
|
|
20
|
+
}
|
|
16
21
|
|
|
17
22
|
// === H3: Flush Session Metrics ===
|
|
18
23
|
|
|
19
|
-
const metricsJsonl =
|
|
20
|
-
const counterFile =
|
|
24
|
+
const metricsJsonl = stateFile('rune-metrics', sessionId, cwd).replace(/\.json$/, '.jsonl');
|
|
25
|
+
const counterFile = stateFile('rune-context-watch', sessionId, cwd);
|
|
21
26
|
const runeMetricsDir = path.join(cwd, '.rune', 'metrics');
|
|
22
27
|
|
|
23
28
|
// Resolve skill names → expected model from agent frontmatter
|
|
@@ -101,33 +106,69 @@ function flushMetrics() {
|
|
|
101
106
|
const modelsUsed = resolveSkillModels(skillCounts);
|
|
102
107
|
|
|
103
108
|
// Use session ID from context-watch (shared) or generate new
|
|
104
|
-
const
|
|
109
|
+
const metricSessionId = sessionId
|
|
110
|
+
|| watchState.sessionId
|
|
105
111
|
|| `s-${now.slice(0, 10).replace(/-/g, '')}-${now.slice(11, 19).replace(/:/g, '')}`;
|
|
106
112
|
|
|
107
|
-
// 1.
|
|
113
|
+
// 1. Upsert sessions.jsonl. Stop can fire more than once in one session;
|
|
114
|
+
// appending every time inflated session counts and duration metrics.
|
|
115
|
+
let previousSession = null;
|
|
116
|
+
const sessionsFile = path.join(runeMetricsDir, 'sessions.jsonl');
|
|
117
|
+
let sessionEntries = [];
|
|
118
|
+
if (fs.existsSync(sessionsFile)) {
|
|
119
|
+
sessionEntries = fs
|
|
120
|
+
.readFileSync(sessionsFile, 'utf-8')
|
|
121
|
+
.trim()
|
|
122
|
+
.split('\n')
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.flatMap((line) => {
|
|
125
|
+
try {
|
|
126
|
+
return [JSON.parse(line)];
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
previousSession = [...sessionEntries].reverse().find((entry) => entry.id === metricSessionId) || null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const mergedSkillCounts = { ...(previousSession?.skill_counts || {}) };
|
|
135
|
+
for (const [skill, count] of Object.entries(skillCounts)) {
|
|
136
|
+
mergedSkillCounts[skill] = (mergedSkillCounts[skill] || 0) + count;
|
|
137
|
+
}
|
|
138
|
+
const mergedSkillDurations = { ...(previousSession?.skill_durations || {}) };
|
|
139
|
+
for (const [skill, duration] of Object.entries(skillDurations)) {
|
|
140
|
+
mergedSkillDurations[skill] = (mergedSkillDurations[skill] || 0) + duration;
|
|
141
|
+
}
|
|
142
|
+
const mergedModels = { ...(previousSession?.models_used || {}) };
|
|
143
|
+
for (const [model, count] of Object.entries(modelsUsed)) {
|
|
144
|
+
mergedModels[model] = (mergedModels[model] || 0) + count;
|
|
145
|
+
}
|
|
146
|
+
const mergedSkills = Object.keys(mergedSkillCounts);
|
|
147
|
+
const mergedPrimarySkill = Object.entries(mergedSkillCounts)
|
|
148
|
+
.sort((a, b) => b[1] - a[1])[0]?.[0]
|
|
149
|
+
|| previousSession?.primary_skill
|
|
150
|
+
|| primarySkill;
|
|
151
|
+
|
|
108
152
|
const sessionEntry = {
|
|
109
|
-
id:
|
|
153
|
+
id: metricSessionId,
|
|
110
154
|
date: now.slice(0, 10),
|
|
111
155
|
duration_min: durationMin,
|
|
112
156
|
tool_calls: watchState.count,
|
|
113
157
|
tool_distribution: watchState.toolCounts,
|
|
114
|
-
skill_invocations:
|
|
115
|
-
skills_used:
|
|
116
|
-
primary_skill:
|
|
117
|
-
models_used:
|
|
118
|
-
|
|
158
|
+
skill_invocations: Object.values(mergedSkillCounts).reduce((sum, count) => sum + count, 0),
|
|
159
|
+
skills_used: mergedSkills,
|
|
160
|
+
primary_skill: mergedPrimarySkill,
|
|
161
|
+
models_used: mergedModels,
|
|
162
|
+
skill_counts: Object.keys(mergedSkillCounts).length > 0 ? mergedSkillCounts : undefined,
|
|
163
|
+
skill_durations: Object.keys(mergedSkillDurations).length > 0 ? mergedSkillDurations : undefined
|
|
119
164
|
};
|
|
120
165
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (allLines.length > 100) {
|
|
128
|
-
fs.writeFileSync(sessionsFile, allLines.slice(-100).join('\n') + '\n');
|
|
129
|
-
}
|
|
130
|
-
} catch { /* cap is best-effort */ }
|
|
166
|
+
sessionEntries = sessionEntries.filter((entry) => entry.id !== metricSessionId);
|
|
167
|
+
sessionEntries.push(sessionEntry);
|
|
168
|
+
fs.writeFileSync(
|
|
169
|
+
sessionsFile,
|
|
170
|
+
`${sessionEntries.slice(-100).map((entry) => JSON.stringify(entry)).join('\n')}\n`,
|
|
171
|
+
);
|
|
131
172
|
|
|
132
173
|
// 2. Merge into skills.json (running totals)
|
|
133
174
|
const skillsFile = path.join(runeMetricsDir, 'skills.json');
|
|
@@ -153,7 +194,7 @@ function flushMetrics() {
|
|
|
153
194
|
if (skillChain.length > 0) {
|
|
154
195
|
const chainsFile = path.join(runeMetricsDir, 'chains.jsonl');
|
|
155
196
|
const chainEntry = {
|
|
156
|
-
session:
|
|
197
|
+
session: metricSessionId,
|
|
157
198
|
chain: skillChain,
|
|
158
199
|
depth: skillChain.length
|
|
159
200
|
};
|
|
@@ -170,7 +211,7 @@ function flushMetrics() {
|
|
|
170
211
|
.map(([s, c]) => `${s}(${c})`)
|
|
171
212
|
.join(', ');
|
|
172
213
|
|
|
173
|
-
console.log(`\n📊 [Rune metrics] Session ${
|
|
214
|
+
console.log(`\n📊 [Rune metrics] Session ${metricSessionId} — ${durationMin}min, ${watchState.count} tool calls, ${skillEvents.length} new skill invocations`);
|
|
174
215
|
if (skillList) console.log(` Skills: ${skillList}`);
|
|
175
216
|
console.log(` Saved to .rune/metrics/\n`);
|
|
176
217
|
}
|
|
@@ -7,20 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const
|
|
10
|
+
const { stateFile } = require('../lib/context-key.cjs');
|
|
11
11
|
|
|
12
12
|
const { captureConsole } = require('../lib/hook-output.cjs');
|
|
13
13
|
// Hook stdout is a JSON contract, not free text (Codex rejects bare lines and
|
|
14
14
|
// discards the output). Capture the prints below and emit one envelope on exit.
|
|
15
|
-
captureConsole('PreCompact');
|
|
15
|
+
captureConsole('PreCompact', { captureError: true });
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
const cwd = process.cwd();
|
|
19
19
|
const runeDir = path.join(cwd, '.rune');
|
|
20
20
|
|
|
21
|
+
let sessionId;
|
|
22
|
+
try {
|
|
23
|
+
sessionId = JSON.parse(fs.readFileSync(0, 'utf-8')).session_id;
|
|
24
|
+
} catch {
|
|
25
|
+
// Older runtimes may not provide hook input.
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
// Read context-watch state (tool counts, session timing)
|
|
22
|
-
const
|
|
23
|
-
const counterFile = path.join(os.tmpdir(), `rune-context-watch-${hash}.json`);
|
|
29
|
+
const counterFile = stateFile('rune-context-watch', sessionId, cwd);
|
|
24
30
|
|
|
25
31
|
let watchState = null;
|
|
26
32
|
try {
|
|
@@ -66,14 +66,14 @@ function appendGateOutcome(gate, outcome, detail) {
|
|
|
66
66
|
*
|
|
67
67
|
* @param {string} toolName
|
|
68
68
|
* @param {object} toolInput
|
|
69
|
-
* @returns {string} target
|
|
69
|
+
* @returns {string[]} target paths
|
|
70
70
|
*/
|
|
71
|
-
function
|
|
72
|
-
if (toolName !== 'apply_patch') return
|
|
73
|
-
const patch =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return match
|
|
71
|
+
function patchTargetPaths(toolName, toolInput) {
|
|
72
|
+
if (toolName !== 'apply_patch') return [];
|
|
73
|
+
const patch =
|
|
74
|
+
typeof toolInput === 'string' ? toolInput : toolInput.command || toolInput.input || toolInput.patch || '';
|
|
75
|
+
if (typeof patch !== 'string') return [];
|
|
76
|
+
return Array.from(patch.matchAll(/^\*\*\* (?:Update|Add|Delete) File:\s*(.+)$/gm), (match) => match[1].trim());
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// Read tool_input from Claude Code hook stdin
|
|
@@ -92,11 +92,9 @@ process.stdin.on('end', () => {
|
|
|
92
92
|
process.exit(0);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const basename = path.basename(filePath);
|
|
99
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
95
|
+
const directPath = toolInput.file_path || toolInput.path;
|
|
96
|
+
const filePaths = directPath ? [directPath] : patchTargetPaths(toolName, toolInput);
|
|
97
|
+
if (filePaths.length === 0) process.exit(0);
|
|
100
98
|
|
|
101
99
|
// Load project-specific privacy config
|
|
102
100
|
const privacyConfig = loadPrivacyConfig();
|
|
@@ -147,33 +145,37 @@ process.stdin.on('end', () => {
|
|
|
147
145
|
const activeSkill = process.env.RUNE_ACTIVE_SKILL || '';
|
|
148
146
|
const isElevated = elevatedSkills.has(activeSkill);
|
|
149
147
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
148
|
+
for (const filePath of filePaths) {
|
|
149
|
+
const basename = path.basename(filePath);
|
|
150
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
151
|
+
const isSafe = safeExceptions.some((p) => p.test(basename) || p.test(normalized));
|
|
152
|
+
if (isSafe) continue;
|
|
153
|
+
|
|
154
|
+
const isBlocked = blockPatterns.some((p) => p.test(basename) || p.test(normalized));
|
|
155
|
+
if (isBlocked) {
|
|
156
|
+
// Append block outcome BEFORE printing/exiting — fail-safe, never affects exit code.
|
|
157
|
+
// Only "blocked" is captured here; "passed" and "bypassed" are not observable
|
|
158
|
+
// at this layer and intentionally remain uncaptured (see GAP-1 in governance-collector.js).
|
|
159
|
+
appendGateOutcome('privacy-mesh', 'blocked', `file matched BLOCK-tier pattern: ${basename}`);
|
|
160
|
+
|
|
161
|
+
console.log(`\n🚫 [Rune privacy-mesh] BLOCKED: ${filePath}`);
|
|
162
|
+
console.log(' This file matches a BLOCK-tier pattern (private keys, certificates).');
|
|
163
|
+
console.log(' Override: add path to .rune/privacy.json "allow" list if intentional.\n');
|
|
164
|
+
process.exit(2); // Exit code 2 = BLOCK
|
|
165
|
+
}
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
const isWarned = warnPatterns.some((p) => p.test(basename) || p.test(normalized));
|
|
168
|
+
if (isWarned && !isElevated) {
|
|
169
|
+
// Content-aware check: scan first 4KB for secret patterns
|
|
170
|
+
const contentWarning = scanContentForSecrets(filePath);
|
|
170
171
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
console.log(`\n⚠ [Rune privacy-mesh] Sensitive file: ${filePath}`);
|
|
173
|
+
if (contentWarning) {
|
|
174
|
+
console.log(` Content scan: ${contentWarning}`);
|
|
175
|
+
}
|
|
176
|
+
console.log(' This file may contain secrets. Confirm access is intentional.');
|
|
177
|
+
console.log(' Elevated skills (sentinel, review, audit) bypass this warning.\n');
|
|
174
178
|
}
|
|
175
|
-
console.log(' This file may contain secrets. Confirm access is intentional.');
|
|
176
|
-
console.log(' Elevated skills (sentinel, review, audit) bypass this warning.\n');
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
process.exit(0);
|
package/hooks/run-hook
CHANGED
package/hooks/run-hook.cmd
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@node "%~dp0run-hook.
|
|
1
|
+
@node "%~dp0run-hook.cjs" %*
|
|
@@ -6,10 +6,26 @@
|
|
|
6
6
|
// Zero false-positive tolerance on BLOCK patterns.
|
|
7
7
|
|
|
8
8
|
const { execSync } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const { captureConsole } = require('../lib/hook-output.cjs');
|
|
11
|
+
|
|
12
|
+
captureConsole('PreToolUse', { captureError: true });
|
|
9
13
|
|
|
10
14
|
// Only intercept git commit commands
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
let rawInput = '';
|
|
16
|
+
try {
|
|
17
|
+
rawInput = fs.readFileSync(0, 'utf-8');
|
|
18
|
+
} catch {
|
|
19
|
+
// Claude compatibility fallback below.
|
|
20
|
+
}
|
|
21
|
+
let event = {};
|
|
22
|
+
try {
|
|
23
|
+
event = JSON.parse(rawInput.trim() || process.env.CLAUDE_TOOL_INPUT || '{}');
|
|
24
|
+
} catch {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
const toolInput = event.tool_input || event;
|
|
28
|
+
const command = (toolInput.command || '').trim();
|
|
13
29
|
|
|
14
30
|
// Check if this is a git commit command
|
|
15
31
|
if (!/^git\s+commit\b/.test(command)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rune-kit/rune",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.29.0",
|
|
4
|
+
"description": "66-skill mesh for AI coding assistants — native lifecycle hooks for Claude Code and Codex, 5-layer architecture, 248 connections + 45 signals, and a 13-platform compiler.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"rune": "./compiler/bin/rune.js"
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"commands/",
|
|
53
53
|
"agents/",
|
|
54
54
|
"hooks/",
|
|
55
|
+
".codex-plugin/",
|
|
55
56
|
"references/"
|
|
56
57
|
],
|
|
57
58
|
"homepage": "https://rune-kit.github.io/rune",
|
|
@@ -25,6 +25,7 @@ Browser automation for testing and verification using MCP Playwright tools. Navi
|
|
|
25
25
|
- `launch` (L1): verify live site after deployment
|
|
26
26
|
- `perf` (L2): Lighthouse / Core Web Vitals measurement
|
|
27
27
|
- `audit` (L2): visual verification during quality assessment
|
|
28
|
+
- `design` (L2): render the surface before any visual property is claimed (design Step 5.4 — render blindness)
|
|
28
29
|
|
|
29
30
|
## Calls (outbound)
|
|
30
31
|
|
|
@@ -148,11 +148,11 @@ For each claim, look for corresponding evidence in the conversation context:
|
|
|
148
148
|
|
|
149
149
|
| Claim Type | Required Evidence | Where to Find |
|
|
150
150
|
|---|---|---|
|
|
151
|
-
| "tests pass" | Test runner stdout with pass count |
|
|
152
|
-
| "build succeeds" | Build command stdout showing success |
|
|
153
|
-
| "lint clean" | Linter stdout (even if empty = 0 errors) |
|
|
154
|
-
| "fixed" | Git diff showing the change + test proving fix |
|
|
155
|
-
| "implemented" | Files created/modified matching the plan |
|
|
151
|
+
| "tests pass" | Test runner stdout with pass count | Shell output from test command |
|
|
152
|
+
| "build succeeds" | Build command stdout showing success | Shell output from build command |
|
|
153
|
+
| "lint clean" | Linter stdout (even if empty = 0 errors) | Shell output from lint command |
|
|
154
|
+
| "fixed" | Git diff showing the change + test proving fix | File-edit evidence + test output |
|
|
155
|
+
| "implemented" | Files created/modified matching the plan | File changes compared with the plan |
|
|
156
156
|
| "no security issues" | Sentinel report with PASS verdict | Sentinel skill output |
|
|
157
157
|
| "coverage ≥ X%" | Coverage tool output with actual percentage | Test runner with coverage flag |
|
|
158
158
|
|
package/skills/design/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: design
|
|
|
3
3
|
description: "Design system reasoning. Maps product domain to style, palette, typography, and platform-specific patterns. Generates .rune/design-system.md as the shared design contract for all UI-generating skills."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.9.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: creation
|
|
@@ -27,6 +27,7 @@ Design system reasoning layer. Converts a product description into a concrete de
|
|
|
27
27
|
|
|
28
28
|
- `scout` (L2): detect existing design tokens, component library, platform targets
|
|
29
29
|
- `asset-creator` (L3): generate base visual assets (logo, OG image) from design system
|
|
30
|
+
- `browser-pilot` (L3): render the surface and inspect it before claiming any visual property holds (Step 5.4)
|
|
30
31
|
- `review` (L2): accessibility violations found → flag for fix in next code review
|
|
31
32
|
|
|
32
33
|
## Called By (inbound)
|
|
@@ -477,21 +478,47 @@ sm: 6px | md: 8px | lg: 12px | xl: 16px | full: 9999px
|
|
|
477
478
|
[detected library or "custom"]
|
|
478
479
|
|
|
479
480
|
## Pre-Delivery Checklist
|
|
480
|
-
|
|
481
|
-
- [ ]
|
|
482
|
-
- [ ]
|
|
481
|
+
> Items marked 👁 describe what a human would SEE — tick them only from a render, or mark them ASSUMED (Step 5.4).
|
|
482
|
+
- [ ] 👁 Color contrast ≥ 4.5:1 for all text
|
|
483
|
+
- [ ] 👁 Focus-visible ring on ALL interactive elements (never outline-none alone)
|
|
484
|
+
- [ ] 👁 Touch targets ≥ 44×44px on mobile / ≥ 24×24px on desktop, with 8px gap between targets (matches Step 2.9 Rule 1)
|
|
483
485
|
- [ ] All icon-only buttons have aria-label
|
|
484
486
|
- [ ] All inputs have associated <label> or aria-label
|
|
485
|
-
- [ ] Empty state, error state, loading state for all async data
|
|
487
|
+
- [ ] 👁 Empty state, error state, loading state for all async data
|
|
486
488
|
- [ ] cursor-pointer on all clickable non-button elements
|
|
487
489
|
- [ ] prefers-reduced-motion respected for all animations
|
|
488
490
|
- [ ] Dark mode support (or explicit reasoning why not)
|
|
489
|
-
- [ ] Responsive tested at 375px / 768px / 1024px / 1440px
|
|
491
|
+
- [ ] 👁 Responsive tested at 375px / 768px / 1024px / 1440px
|
|
490
492
|
- [ ] No pure #000 or #fff in semantic tokens (use oklch neutrals)
|
|
491
493
|
- [ ] No lorem ipsum / placeholder copy in shipped output (use real data or labelled `[ PLACEHOLDER: ... ]` blocks)
|
|
492
494
|
- [ ] If multi-language: CJK-capable font listed FIRST in stack (`"Noto Sans SC", "Inter", ...`)
|
|
493
495
|
```
|
|
494
496
|
|
|
497
|
+
### Step 5.4 — Render Blindness (advisory)
|
|
498
|
+
|
|
499
|
+
A checklist item ticked from source is a prediction, not an observation. You emit markup and
|
|
500
|
+
imagine the result, and **the imagined render is always flattering** — overflow, wrapping,
|
|
501
|
+
contrast failure, misalignment and collision are invisible in source form. Every item in the
|
|
502
|
+
checklist above that describes what a human would *see* is unverified until the surface is
|
|
503
|
+
rendered and inspected, or the specific property is computed.
|
|
504
|
+
|
|
505
|
+
| Claim | What source-reading proves | What actually settles it |
|
|
506
|
+
|-------|---------------------------|--------------------------|
|
|
507
|
+
| "Contrast ≥ 4.5:1" | The token values were chosen | Compute the ratio for the rendered pair — a token used on an unexpected background fails anyway |
|
|
508
|
+
| "Responsive at 375px" | Breakpoints exist | Render at 375px and look: wrapping, overflow, tap-target collision |
|
|
509
|
+
| "No overflow / clean alignment" | Nothing | Render — this class of failure has no textual signature |
|
|
510
|
+
| "Focus ring visible" | The CSS rule exists | Tab through it; a parent `overflow:hidden` or a later rule can eat it |
|
|
511
|
+
| "Empty / error / loading states work" | The branches exist | Render each one; the happy path is the only state most designs are ever seen in |
|
|
512
|
+
|
|
513
|
+
**Advisory, not a gate.** When the runtime grants a browser, `browser-pilot` is the strongest
|
|
514
|
+
check available and costs one call — take it, then report what was seen. When it does not,
|
|
515
|
+
say so: mark the visual items **ASSUMED (not rendered)** rather than ticking them. A checklist
|
|
516
|
+
of predictions labelled as observations is worse than a short one that is honest — see
|
|
517
|
+
`completion-gate` → `references/claim-discipline.md`.
|
|
518
|
+
|
|
519
|
+
Highest-yield renders, when budget allows only a few: narrowest breakpoint, longest realistic
|
|
520
|
+
content string, empty state, dark mode.
|
|
521
|
+
|
|
495
522
|
### Step 5.5 — UI Design Contract (UI-SPEC.md)
|
|
496
523
|
|
|
497
524
|
After generating the design system, lock key visual decisions in `.rune/ui-spec.md` — a binding contract that prevents design drift during implementation.
|
|
@@ -41,7 +41,7 @@ None — pure L3 utility. Receives content, produces formatted output.
|
|
|
41
41
|
|
|
42
42
|
| Format | Generate | Parse | Node.js Library | Python Library |
|
|
43
43
|
|--------|----------|-------|-----------------|----------------|
|
|
44
|
-
| PDF | Yes | Yes (via
|
|
44
|
+
| PDF | Yes | Yes (via the runtime's document/PDF reader) | jsPDF, Puppeteer (HTML→PDF) | reportlab, weasyprint |
|
|
45
45
|
| DOCX | Yes | Yes | docx (officegen) | python-docx |
|
|
46
46
|
| XLSX | Yes | Yes | ExcelJS | openpyxl |
|
|
47
47
|
| PPTX | Yes | Yes | pptxgenjs | python-pptx |
|
|
@@ -157,7 +157,7 @@ Identify file format from extension and MIME type.
|
|
|
157
157
|
|
|
158
158
|
| Format | Extraction Strategy |
|
|
159
159
|
|--------|-------------------|
|
|
160
|
-
| PDF | Use
|
|
160
|
+
| PDF | Use the runtime's native document/PDF reader when available; otherwise use a supported PDF parsing library |
|
|
161
161
|
| DOCX | docx library → extract text, tables, images |
|
|
162
162
|
| XLSX | ExcelJS → extract sheets, rows, formulas |
|
|
163
163
|
| PPTX | pptxgenjs → extract slides, text, notes |
|
|
@@ -39,6 +39,7 @@ Post-generation validation that verifies AI-generated code references actually e
|
|
|
39
39
|
- `review-intake` (L2): verify imports in code submitted for review
|
|
40
40
|
- `skill-forge` (L2): verify imports in newly generated skill code
|
|
41
41
|
- `adversary` (L2): verify APIs/packages in plan actually exist
|
|
42
|
+
- `logic-guardian` (L2): verify referenced functions and imports after guarded logic edits
|
|
42
43
|
|
|
43
44
|
## Execution
|
|
44
45
|
|
package/skills/journal/SKILL.md
CHANGED
|
@@ -39,6 +39,7 @@ None — pure L3 state management utility.
|
|
|
39
39
|
- `graft` (L2): auto-log graft operations — source URL, mode, challenge score, files changed
|
|
40
40
|
- `retro` (L2): record retrospective insights and decisions
|
|
41
41
|
- `improve-architecture` (L2): record an ADR when the user rejects a deepening candidate with a load-bearing reason
|
|
42
|
+
- `logic-guardian` (L2): record guarded logic changes for cross-session persistence
|
|
42
43
|
|
|
43
44
|
## Files Managed
|
|
44
45
|
|
|
@@ -3,7 +3,7 @@ name: problem-solver
|
|
|
3
3
|
description: "Structured reasoning frameworks for complex problems. 19 analytical frameworks, 12 cognitive bias detectors, 10 decomposition methods, 10 mental models, Cynefin domain classification, ethical dimension check, and 6 communication patterns. McKinsey-grade problem solving for AI coding assistants."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.6.0"
|
|
7
7
|
layer: L3
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: reasoning
|
|
@@ -77,6 +77,10 @@ NEVER skip bias detection. Every problem has biases — explicitly address them.
|
|
|
77
77
|
This is the #1 value-add from structured reasoning. Without it, solutions are just dressed-up gut feelings.
|
|
78
78
|
</HARD-GATE>
|
|
79
79
|
|
|
80
|
+
Two catalogs run here, not one: the biases of the people and organisation in the problem, and the failure modes of the model doing the analysis. Skipping the second is how a rigorous-looking analysis reaches a confidently wrong conclusion.
|
|
81
|
+
|
|
82
|
+
#### Human and organisational biases
|
|
83
|
+
|
|
80
84
|
Scan the problem statement and context for bias indicators. Check the top 6 most dangerous biases:
|
|
81
85
|
|
|
82
86
|
| Bias | Detection Question | Debiasing Strategy |
|
|
@@ -96,6 +100,32 @@ Additional biases to check when relevant:
|
|
|
96
100
|
- **Survivorship Bias**: Are we only looking at successful cases? Who tried this approach and failed?
|
|
97
101
|
- **Recency Bias**: Are we extrapolating from the last few data points instead of looking at 5-10 years of data?
|
|
98
102
|
|
|
103
|
+
#### Model failure modes (the reasoner's own biases — ALSO run)
|
|
104
|
+
|
|
105
|
+
The table above catalogs how *humans and organisations* misjudge. It says nothing about how
|
|
106
|
+
*you* misjudge. These are the failure modes of the thing doing the analysis, and naming one
|
|
107
|
+
is the first countermeasure — they are invisible from the inside precisely because each one
|
|
108
|
+
feels like competence.
|
|
109
|
+
|
|
110
|
+
| Failure mode | What it feels like | Countermeasure |
|
|
111
|
+
|--------------|--------------------|----------------|
|
|
112
|
+
| **Pattern-match satisfaction** | The first explanation that fits a familiar template feels like the diagnosis | Familiarity is retrieval, not verification. Hold a second hypothesis before investigating the first |
|
|
113
|
+
| **Template hijack** | A question whose surface matches a stored template ("flaky test → add retry", "slow query → add index") fires the template's answer before this question's constraints are read | Re-derive from *this* problem's details. Familiarity raises the risk, not lowers it |
|
|
114
|
+
| **Fluent ≠ true** | Confidence rises as well-formed prose flows; the argument feels stronger the longer it gets | Confidence tracks evidence, not token count. Audit each point where it rose and name what moved it |
|
|
115
|
+
| **Prior-as-fact** | Training knowledge stated in the grammar of observed fact | Type the claim (`completion-gate` → `references/claim-discipline.md`). Priors decay — APIs, defaults, prices, versions |
|
|
116
|
+
| **Frame adoption** | The asker's framing inherited as fact ("the cache is broken again") | Trust their goal absolutely; treat their diagnosis as testimony to verify |
|
|
117
|
+
| **Completion pressure** | Producing something answer-shaped now feels better than checking one more thing | An answer-shaped non-answer is worse than "here is what I verified, here is what is still open" |
|
|
118
|
+
| **Surface blindness** | Any claim about the form of your own output — which symbols it contains, how many units it has — read back as true | You see tokens, not characters; a re-read always passes. Verify unit by unit or by tool (`verification` → Constraint Loop) |
|
|
119
|
+
|
|
120
|
+
**Three tells you are inside one right now**: the answer arrived instantly with high
|
|
121
|
+
confidence; your draft never used one of the problem's stated details; two or three attempts
|
|
122
|
+
failed inside the same framing. Any tell means stop and re-derive — never repeat a failed
|
|
123
|
+
probe harder.
|
|
124
|
+
|
|
125
|
+
**Output**: alongside the human-bias warnings, name any model failure mode active in THIS
|
|
126
|
+
analysis and what you did about it. "None apparent" is a valid answer only if you can say
|
|
127
|
+
which tell you checked.
|
|
128
|
+
|
|
99
129
|
**Steel Manning** (apply when evaluating competing options):
|
|
100
130
|
Before dismissing any option, construct the STRONGEST possible version of the argument for it. If you can't articulate why a smart, informed person would choose it, you haven't understood it yet. Steel Manning prevents strawman dismissals and forces genuine evaluation.
|
|
101
131
|
|
|
@@ -335,6 +365,7 @@ Structure the output report using the selected pattern.
|
|
|
335
365
|
### Bias Warnings
|
|
336
366
|
- ⚠️ [Bias 1]: [how it might affect this analysis] → [debiasing action taken]
|
|
337
367
|
- ⚠️ [Bias 2]: [how it might affect this analysis] → [debiasing action taken]
|
|
368
|
+
- 🤖 [Model failure mode]: [which tell fired] → [what was re-derived or verified]
|
|
338
369
|
|
|
339
370
|
### Reasoning Chain
|
|
340
371
|
1. [step with evidence or reasoning]
|
package/skills/retro/SKILL.md
CHANGED
|
@@ -316,8 +316,8 @@ Pull from all installed domain packs:
|
|
|
316
316
|
- **Engineering**: git history (commits, velocity, test ratio, fix ratio, hotspots)
|
|
317
317
|
- **Revenue** (@rune-pro/sales): pipeline metrics, deal velocity, churn risk
|
|
318
318
|
- **Support** (@rune-pro/support): ticket volume, SLA compliance, CSAT
|
|
319
|
-
- **Finance** (@rune-
|
|
320
|
-
- **Compliance** (@rune-
|
|
319
|
+
- **Finance** (@rune-pro/finance): burn rate, runway, budget variance
|
|
320
|
+
- **Compliance** (@rune-pro/legal): framework status, audit dates, open items
|
|
321
321
|
|
|
322
322
|
### Business Execution Steps
|
|
323
323
|
|
|
@@ -41,6 +41,7 @@ Solve the #1 developer complaint: context loss across sessions. Session-bridge a
|
|
|
41
41
|
- `context-pack` (L3): coordinate state for sub-agent handoff
|
|
42
42
|
- `neural-memory` (L3): sync key decisions back to `.rune/` files after Capture Mode
|
|
43
43
|
- `adversary` (L2): (oracle-mode) detach protocol when target model is opus-class for non-blocking dispatch
|
|
44
|
+
- `logic-guardian` (L2): preserve logic-manifest state across sessions
|
|
44
45
|
|
|
45
46
|
## State Files Managed
|
|
46
47
|
|
|
@@ -3,7 +3,7 @@ name: verification
|
|
|
3
3
|
description: "Universal verification runner. Runs lint, type-check, tests, and build. Use after any code change to verify nothing is broken."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.8.0"
|
|
7
7
|
layer: L3
|
|
8
8
|
model: haiku
|
|
9
9
|
group: validation
|
|
@@ -207,6 +207,47 @@ Exit 0 without a confirming output artifact or success string = UNVERIFIED.
|
|
|
207
207
|
Report the specific line that confirmed success (e.g., "3 passed, 0 failed").
|
|
208
208
|
</HARD-GATE>
|
|
209
209
|
|
|
210
|
+
### Surface-Constraint Verification (the Constraint Loop)
|
|
211
|
+
|
|
212
|
+
Everything above verifies *behaviour* with a tool. Some deliverables instead carry a
|
|
213
|
+
constraint on their own **surface form**: a banned or required character, an exact word or
|
|
214
|
+
line count, a positional pattern, a strict format, a naming scheme every entry must follow,
|
|
215
|
+
a diff that must not touch a listed path. These look trivial and are the opposite — a model
|
|
216
|
+
generates meaning-first and reads its own output as tokens, not characters, so the
|
|
217
|
+
constraint sits exactly where its perception is weakest. The most natural wording for the
|
|
218
|
+
topic is usually the likeliest violator.
|
|
219
|
+
|
|
220
|
+
**Re-reading the output and judging that it complies is not verification.** A re-read always
|
|
221
|
+
passes. That is the whole failure mode.
|
|
222
|
+
|
|
223
|
+
Run this loop whenever a deliverable carries a mechanically checkable surface constraint:
|
|
224
|
+
|
|
225
|
+
1. **Expand the constraint before producing anything.** Restate it as a test every governed
|
|
226
|
+
unit must pass, and decide *how you will count* before there is anything to count. List
|
|
227
|
+
the on-topic vocabulary most likely to violate it — starting with the subject's own name,
|
|
228
|
+
which the constraint may rule out — and pick compliant substitutes up front.
|
|
229
|
+
2. **Draft away from the final answer** — a scratch file or reasoning space, never straight
|
|
230
|
+
into the deliverable.
|
|
231
|
+
3. **Verify mechanically, strongest tool available.** A script, `grep`, `wc`, a formatter's
|
|
232
|
+
`--check`, a schema validator — seconds of work and the strongest possible evidence. With
|
|
233
|
+
no tool available, decompose the text into the units the constraint governs and test each
|
|
234
|
+
one explicitly (spell the word out; count with a running index). Manual decomposition is
|
|
235
|
+
the fallback for tool-poor runtimes, not a substitute where a tool exists.
|
|
236
|
+
4. **Repair and re-verify the whole artifact.** A fix can introduce a new violation
|
|
237
|
+
elsewhere, so re-scan everything — one green check on the edited line says nothing about
|
|
238
|
+
its neighbours. Loop until one complete pass over the final text is clean.
|
|
239
|
+
5. **Ship the verified text byte-for-byte.** Any post-verification rewording, however small,
|
|
240
|
+
invalidates the check — touch one unit and step 3 runs again.
|
|
241
|
+
|
|
242
|
+
<HARD-GATE name="surface-constraint">
|
|
243
|
+
"The output satisfies the constraint" is a verified claim only after step 3 has run against
|
|
244
|
+
the EXACT delivered text. Asserted from a re-read, it is an assumption wearing the grammar of
|
|
245
|
+
an observation — see `completion-gate` → `references/claim-discipline.md`. Report the check
|
|
246
|
+
that was run, not the conclusion.
|
|
247
|
+
</HARD-GATE>
|
|
248
|
+
|
|
249
|
+
Report it like any other phase: `Constraint: <the rule> | Check: <command or method> | Units tested: N | Violations: 0`.
|
|
250
|
+
|
|
210
251
|
## Error Recovery
|
|
211
252
|
|
|
212
253
|
- If project type cannot be detected: report "Unknown project type" and skip all checks
|
|
@@ -265,7 +265,7 @@ Known failure modes for this skill. Check these before declaring done.
|
|
|
265
265
|
- Storyboard created scene-by-scene with transitions
|
|
266
266
|
- Shot list categorized by type (screen recording, terminal, code, diagram)
|
|
267
267
|
- Assets needed checklist generated
|
|
268
|
-
- video-plan.md written to output_path
|
|
268
|
+
- `video-plan.md` written to `output_path` with the runtime's file-edit capability
|
|
269
269
|
- Video Plan Created report emitted with scene count, shot count, and asset count
|
|
270
270
|
|
|
271
271
|
## Cost Profile
|