@rune-kit/rune 2.29.0 → 2.30.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 (51) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +49 -2
  3. package/agents/audit.md +1 -1
  4. package/agents/cook.md +1 -1
  5. package/agents/journal.md +1 -1
  6. package/agents/reviewer.md +23 -1
  7. package/agents/session-bridge.md +1 -1
  8. package/agents/skill-forge.md +1 -1
  9. package/agents/skill-router.md +1 -1
  10. package/agents/trend-scout.md +1 -1
  11. package/agents/worktree.md +1 -1
  12. package/compiler/__tests__/skill-attribution.test.js +109 -0
  13. package/compiler/__tests__/update.test.js +416 -0
  14. package/compiler/bin/rune.js +19 -0
  15. package/compiler/commands/update.js +354 -0
  16. package/package.json +1 -1
  17. package/skills/audit/SKILL.md +3 -3
  18. package/skills/completion-gate/SKILL.md +1 -1
  19. package/skills/constraint-check/SKILL.md +1 -1
  20. package/skills/context-engine/SKILL.md +2 -2
  21. package/skills/cook/SKILL.md +2 -2
  22. package/skills/cook/references/loop-detection.md +1 -1
  23. package/skills/cook/references/mid-run-signals.md +1 -1
  24. package/skills/debug/SKILL.md +2 -2
  25. package/skills/dependency-doctor/SKILL.md +1 -1
  26. package/skills/design/SKILL.md +1 -1
  27. package/skills/docs/SKILL.md +1 -1
  28. package/skills/docs-seeker/SKILL.md +3 -0
  29. package/skills/hallucination-guard/SKILL.md +2 -2
  30. package/skills/incident/SKILL.md +1 -1
  31. package/skills/integrity-check/SKILL.md +1 -1
  32. package/skills/launch/SKILL.md +1 -1
  33. package/skills/preflight/SKILL.md +35 -9
  34. package/skills/rescue/SKILL.md +1 -1
  35. package/skills/research/SKILL.md +1 -1
  36. package/skills/review/SKILL.md +135 -84
  37. package/skills/review/references/rules/config.md +63 -0
  38. package/skills/review/references/rules/default.md +57 -0
  39. package/skills/review/references/rules/go.md +65 -0
  40. package/skills/review/references/rules/index.md +43 -0
  41. package/skills/review/references/rules/python.md +64 -0
  42. package/skills/review/references/rules/rust.md +65 -0
  43. package/skills/review/references/rules/sql.md +65 -0
  44. package/skills/review/references/rules/ts-js.md +80 -0
  45. package/skills/sast/SKILL.md +1 -1
  46. package/skills/scout/SKILL.md +3 -0
  47. package/skills/sentinel/SKILL.md +29 -6
  48. package/skills/skill-router/SKILL.md +1 -1
  49. package/skills/team/SKILL.md +2 -2
  50. package/skills/verification/SKILL.md +3 -4
  51. package/skills/watchdog/SKILL.md +1 -1
@@ -0,0 +1,354 @@
1
+ /**
2
+ * `rune update` — one-shot updater for an already-configured project.
3
+ *
4
+ * Mirrors the manual "Updating" flow from the README in a single command:
5
+ * 1. `git pull --ff-only` any detected paid tier repos (Pro / Business).
6
+ * Detection reuses setup's logic: $RUNE_PRO_ROOT / $RUNE_BUSINESS_ROOT
7
+ * env vars, then sibling dirs (../Pro, ../Business), then well-known
8
+ * paths. Absent tiers and non-git checkouts are skipped with a note;
9
+ * a FAILED pull aborts the update (fail loud — never silently continue).
10
+ * 2. Re-run the managed setup rewrite in place, non-interactively: the
11
+ * installed platforms, preset, and tiers are detected from the project's
12
+ * existing hook config instead of prompting. Delegates to `runSetup`.
13
+ * 3. Verify: compiled-output doctor (when a rune.config.json build exists)
14
+ * + hook drift report, then print a short summary. Codex users are
15
+ * reminded to re-trust hooks via `/hooks` when `.codex/hooks.json`
16
+ * changed.
17
+ *
18
+ * Non-interactive by design — safe to run from scripts. Flags:
19
+ * --no-pull skip step 1 (tier repos managed some other way)
20
+ * --preset <p> override the detected preset
21
+ * --tier <t>[,<t>] override the detected tier list
22
+ * --dry preview: skip pulls, pass dry to setup (no writes)
23
+ */
24
+
25
+ import { execFile } from 'node:child_process';
26
+ import { existsSync } from 'node:fs';
27
+ import { readdir, readFile } from 'node:fs/promises';
28
+ import path from 'node:path';
29
+ import { ADAPTERS, detectPlatforms } from '../adapters/hooks/index.js';
30
+ import { checkHookDrift } from './hooks/drift.js';
31
+ import { TIER_ENV_VARS } from './hooks/tiers.js';
32
+ import { detectTiers, runSetup } from './setup.js';
33
+
34
+ /** Installed hook configs that may carry tier tokens (env var references). */
35
+ const TIER_SCAN_FILES = ['.claude/settings.json', '.codex/hooks.json'];
36
+
37
+ /** Rule/workflow dirs whose Rune-managed files carry `rune-tier:` frontmatter. */
38
+ const TIER_SCAN_DIRS = ['.cursor/rules', '.windsurf/rules', '.windsurf/workflows', '.antigravity/rules'];
39
+
40
+ const CODEX_HOOKS_REL_PATH = '.codex/hooks.json';
41
+
42
+ /**
43
+ * Detect which paid tiers are wired into this project's installed hook config.
44
+ * Reads the config files Rune manages and looks for tier env-var tokens
45
+ * (`RUNE_PRO_ROOT` / `RUNE_BUSINESS_ROOT`) or `rune-tier:` frontmatter.
46
+ * Free preset entries never contain either marker, so a Free-only install
47
+ * returns [].
48
+ *
49
+ * @param {string} projectRoot
50
+ * @returns {Promise<string[]>} tier names, e.g. ['pro']
51
+ */
52
+ export async function detectInstalledTiers(projectRoot) {
53
+ const chunks = [];
54
+ for (const rel of TIER_SCAN_FILES) {
55
+ const filePath = path.join(projectRoot, rel);
56
+ if (existsSync(filePath)) {
57
+ chunks.push(await readFile(filePath, 'utf-8').catch(() => ''));
58
+ }
59
+ }
60
+ for (const rel of TIER_SCAN_DIRS) {
61
+ const dir = path.join(projectRoot, rel);
62
+ if (!existsSync(dir)) continue;
63
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
64
+ for (const entry of entries) {
65
+ if (!entry.isFile() || !/\.(md|mdc)$/.test(entry.name)) continue;
66
+ chunks.push(await readFile(path.join(dir, entry.name), 'utf-8').catch(() => ''));
67
+ }
68
+ }
69
+ const text = chunks.join('\n');
70
+ const tiers = [];
71
+ for (const [tier, envVar] of Object.entries(TIER_ENV_VARS)) {
72
+ const marker = new RegExp(`${envVar}|rune-tier:\\s*${tier}\\b`);
73
+ if (marker.test(text)) tiers.push(tier);
74
+ }
75
+ return tiers;
76
+ }
77
+
78
+ /**
79
+ * Detect the preset the project was installed with, by asking each detected
80
+ * platform adapter for its status. A `mixed` install resolves to `gentle`
81
+ * (same fallback the drift reporter uses for canonical comparison).
82
+ *
83
+ * @param {string} projectRoot
84
+ * @returns {Promise<'gentle'|'strict'|null>} null when nothing is installed
85
+ */
86
+ export async function detectInstalledPreset(projectRoot) {
87
+ for (const id of detectPlatforms(projectRoot)) {
88
+ const status = await ADAPTERS[id].status(projectRoot);
89
+ if (!status.installed || !status.preset) continue;
90
+ if (status.preset === 'mixed') return 'gentle';
91
+ if (status.preset === 'gentle' || status.preset === 'strict') return status.preset;
92
+ }
93
+ return null;
94
+ }
95
+
96
+ /**
97
+ * Default command runner — thin execFile wrapper that never throws.
98
+ * @returns {Promise<{code: number, stdout: string, stderr: string}>}
99
+ */
100
+ function defaultExec(cmd, argv) {
101
+ return new Promise((resolve) => {
102
+ execFile(cmd, argv, { windowsHide: true }, (err, stdout, stderr) => {
103
+ if (err) {
104
+ resolve({
105
+ code: typeof err.code === 'number' ? err.code : 1,
106
+ stdout: stdout ?? '',
107
+ stderr: stderr || err.message,
108
+ });
109
+ } else {
110
+ resolve({ code: 0, stdout: stdout ?? '', stderr: stderr ?? '' });
111
+ }
112
+ });
113
+ });
114
+ }
115
+
116
+ /**
117
+ * `git pull --ff-only` each detected tier repo.
118
+ *
119
+ * Statuses per tier:
120
+ * - absent — tier not detected (no env var / sibling / well-known path)
121
+ * - skipped — detected but not a git checkout (npx/tarball install)
122
+ * - pulled — git pull succeeded
123
+ * - failed — git pull failed (dirty tree, auth, diverged…) → result.ok=false
124
+ *
125
+ * @param {{ detected: ReturnType<typeof detectTiers>, exec?: typeof defaultExec }} opts
126
+ * @returns {Promise<{ ok: boolean, results: Array<{tier: string, root: string|null, status: string, detail: string}> }>}
127
+ */
128
+ export async function pullTierRepos({ detected, exec = defaultExec }) {
129
+ const results = [];
130
+ for (const tier of Object.keys(TIER_ENV_VARS)) {
131
+ const info = detected[tier];
132
+ if (!info) {
133
+ results.push({ tier, root: null, status: 'absent', detail: 'not detected — skipping' });
134
+ continue;
135
+ }
136
+ // detectTiers points at <tierRoot>/hooks/manifest.json — walk back to the root.
137
+ const tierRoot = path.resolve(path.dirname(path.dirname(info.path)));
138
+ if (!existsSync(path.join(tierRoot, '.git'))) {
139
+ results.push({
140
+ tier,
141
+ root: tierRoot,
142
+ status: 'skipped',
143
+ detail: `${tierRoot} is not a git repo — pull skipped (update it the way it was installed)`,
144
+ });
145
+ continue;
146
+ }
147
+ const { code, stdout, stderr } = await exec('git', ['-C', tierRoot, 'pull', '--ff-only']);
148
+ if (code === 0) {
149
+ const summary = (stdout.trim().split('\n').pop() || 'done').trim();
150
+ results.push({ tier, root: tierRoot, status: 'pulled', detail: summary });
151
+ } else {
152
+ results.push({ tier, root: tierRoot, status: 'failed', detail: (stderr || stdout).trim() });
153
+ }
154
+ }
155
+ return { ok: results.every((r) => r.status !== 'failed'), results };
156
+ }
157
+
158
+ /**
159
+ * Compiled-output verification. Only meaningful for build-pipeline projects
160
+ * (rune.config.json with a non-Claude platform) — Claude Code loads source
161
+ * SKILL.md files natively, so there is no compiled output to check.
162
+ */
163
+ async function defaultDoctorCheck(projectRoot, runeRoot) {
164
+ const configPath = path.join(projectRoot, 'rune.config.json');
165
+ if (!existsSync(configPath)) {
166
+ return { skipped: true, reason: 'no rune.config.json — compiled-output check skipped' };
167
+ }
168
+ let config;
169
+ try {
170
+ config = JSON.parse(await readFile(configPath, 'utf-8'));
171
+ } catch (err) {
172
+ return { skipped: true, reason: `rune.config.json unreadable (${err.message}) — compiled-output check skipped` };
173
+ }
174
+ if (!config.platform || config.platform === 'claude') {
175
+ return { skipped: true, reason: 'Claude Code native — no compiled output to check' };
176
+ }
177
+ const { runDoctor } = await import('../doctor.js');
178
+ const { getAdapter } = await import('../adapters/index.js');
179
+ const resolvedRuneRoot = config.source === '@rune-kit/rune' ? runeRoot : config.source || runeRoot;
180
+ const results = await runDoctor({
181
+ outputRoot: projectRoot,
182
+ adapter: getAdapter(config.platform),
183
+ config,
184
+ runeRoot: resolvedRuneRoot,
185
+ });
186
+ return { skipped: false, healthy: results.healthy, results };
187
+ }
188
+
189
+ /**
190
+ * The full update flow. Pure orchestration — every side-effecting collaborator
191
+ * is injectable via `deps` for tests.
192
+ *
193
+ * @param {object} opts
194
+ * @param {string} opts.projectRoot
195
+ * @param {string} opts.runeRoot
196
+ * @param {object} [opts.args] — CLI flags: no-pull, preset, tier, dry
197
+ * @param {object} [opts.deps] — { exec, runSetupFn, checkHookDriftFn, doctorFn, skillTarget, wellKnownPaths }
198
+ * @returns {Promise<object>} structured result for formatUpdateResult
199
+ */
200
+ export async function runUpdate({ projectRoot, runeRoot, args = {}, deps = {} }) {
201
+ const notes = [];
202
+
203
+ // ── 0. Something to update? ──
204
+ const platforms = detectPlatforms(projectRoot);
205
+ if (platforms.length === 0) {
206
+ return {
207
+ ok: false,
208
+ reason:
209
+ 'No Rune installation detected in this project — nothing to update. Run `npx @rune-kit/rune setup` first.',
210
+ pull: { ok: true, results: [] },
211
+ notes,
212
+ };
213
+ }
214
+
215
+ // ── 1. Pull tier repos (fail loud on pull errors) ──
216
+ const detected = detectTiers(projectRoot, deps.wellKnownPaths ? { wellKnownPaths: deps.wellKnownPaths } : {});
217
+ let pull;
218
+ if (args['no-pull'] || args.dry) {
219
+ const why = args['no-pull'] ? '--no-pull' : '--dry';
220
+ pull = {
221
+ ok: true,
222
+ results: Object.keys(TIER_ENV_VARS).map((tier) => ({
223
+ tier,
224
+ root: null,
225
+ status: detected[tier] ? 'skipped' : 'absent',
226
+ detail: detected[tier] ? `pull skipped (${why})` : 'not detected — skipping',
227
+ })),
228
+ };
229
+ } else {
230
+ pull = await pullTierRepos({ detected, exec: deps.exec });
231
+ }
232
+ if (!pull.ok) {
233
+ return {
234
+ ok: false,
235
+ reason: 'tier pull failed — resolve it manually (dirty tree? auth?) then re-run `rune update`',
236
+ pull,
237
+ notes,
238
+ };
239
+ }
240
+
241
+ // ── 2. Re-run managed setup, reusing what is already installed ──
242
+ const preset = args.preset || (await detectInstalledPreset(projectRoot)) || 'gentle';
243
+ let tiers;
244
+ if (args.tier) {
245
+ tiers = String(args.tier)
246
+ .split(',')
247
+ .map((t) => t.trim())
248
+ .filter(Boolean);
249
+ } else {
250
+ const installed = await detectInstalledTiers(projectRoot);
251
+ tiers = installed.filter((tier) => detected[tier]);
252
+ for (const tier of installed) {
253
+ if (!detected[tier]) {
254
+ const envVar = TIER_ENV_VARS[tier] || `RUNE_${tier.toUpperCase()}_ROOT`;
255
+ notes.push(
256
+ `${tier}: hooks are installed but the tier repo was not found — skipping. Set $${envVar} or clone the repo next to Free, then re-run \`rune update\`.`,
257
+ );
258
+ }
259
+ }
260
+ }
261
+
262
+ const codexHooksPath = path.join(projectRoot, CODEX_HOOKS_REL_PATH);
263
+ const codexBefore = existsSync(codexHooksPath) ? await readFile(codexHooksPath, 'utf-8').catch(() => null) : null;
264
+
265
+ const setupArgs = {
266
+ here: true,
267
+ preset,
268
+ dry: args.dry,
269
+ ...(tiers.length > 0 ? { tier: tiers.join(',') } : { 'no-tier': true }),
270
+ };
271
+ const setup = await (deps.runSetupFn || runSetup)({
272
+ projectRoot,
273
+ runeRoot,
274
+ args: setupArgs,
275
+ skillTarget: deps.skillTarget,
276
+ });
277
+
278
+ const codexAfter = existsSync(codexHooksPath) ? await readFile(codexHooksPath, 'utf-8').catch(() => null) : null;
279
+ const codexReTrust = platforms.includes('codex') && codexBefore !== codexAfter;
280
+
281
+ // ── 3. Verify: compiled output + hook drift ──
282
+ const doctor = await (deps.doctorFn || defaultDoctorCheck)(projectRoot, runeRoot);
283
+ const drift = await (deps.checkHookDriftFn || checkHookDrift)(projectRoot);
284
+
285
+ return { ok: true, pull, platforms, preset, tiers, setup, drift, doctor, codexReTrust, notes };
286
+ }
287
+
288
+ const PULL_ICONS = { pulled: '✓', absent: '·', skipped: '·', failed: '✗' };
289
+
290
+ /**
291
+ * Render a runUpdate result for console output.
292
+ * @param {Awaited<ReturnType<typeof runUpdate>>} result
293
+ * @returns {string}
294
+ */
295
+ export function formatUpdateResult(result) {
296
+ const lines = [];
297
+ lines.push('');
298
+ lines.push(' Rune Update');
299
+ lines.push(' ────────────');
300
+
301
+ if (result.pull?.results?.length > 0) {
302
+ lines.push(' Tier repos:');
303
+ for (const r of result.pull.results) {
304
+ const icon = PULL_ICONS[r.status] || '·';
305
+ const label = r.status === 'failed' ? 'pull FAILED' : r.status;
306
+ lines.push(` ${icon} ${r.tier} — ${label}: ${r.detail}`);
307
+ }
308
+ }
309
+
310
+ if (!result.ok) {
311
+ lines.push('');
312
+ lines.push(` ✗ Update aborted: ${result.reason}`);
313
+ lines.push('');
314
+ return lines.join('\n');
315
+ }
316
+
317
+ lines.push(' Setup rewrite:');
318
+ lines.push(
319
+ ` Platforms: ${(result.setup?.platforms || result.platforms || []).join(', ') || '—'} | Preset: ${result.preset} | Tiers: Free${result.tiers?.length ? ` + ${result.tiers.join(' + ')}` : ''}`,
320
+ );
321
+ for (const sr of result.setup?.skillResults || []) {
322
+ if (sr.installed?.length > 0) {
323
+ lines.push(` Skills: ${sr.tier}: ${sr.installed.length} installed`);
324
+ } else if (sr.skipped?.length > 0) {
325
+ lines.push(` Skills: ${sr.tier}: ${sr.skipped.length} already present / skipped`);
326
+ }
327
+ }
328
+ if (result.setup && result.setup.written === false) {
329
+ lines.push(' (dry-run — no files written)');
330
+ }
331
+
332
+ lines.push(' Verify:');
333
+ if (result.doctor?.skipped) {
334
+ lines.push(` Doctor: skipped — ${result.doctor.reason}`);
335
+ } else if (result.doctor) {
336
+ lines.push(` Doctor: ${result.doctor.healthy ? '✓ healthy' : '✗ issues found — run `rune doctor` for detail'}`);
337
+ }
338
+ if (result.drift?.summary) {
339
+ const s = result.drift.summary;
340
+ lines.push(` Hook drift: ${s.drifted} drifted, ${s.missing} missing, ${s.errors} error(s)`);
341
+ }
342
+
343
+ for (const note of result.notes || []) {
344
+ lines.push(` ⚠ ${note}`);
345
+ }
346
+
347
+ if (result.codexReTrust) {
348
+ lines.push('');
349
+ lines.push(' ⚠ Codex: .codex/hooks.json changed — open /hooks in Codex to review and re-trust the definitions.');
350
+ }
351
+
352
+ lines.push('');
353
+ return lines.join('\n');
354
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rune-kit/rune",
3
- "version": "2.29.0",
3
+ "version": "2.30.0",
4
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": {
@@ -5,7 +5,7 @@ metadata:
5
5
  author: runedev
6
6
  version: "0.5.0"
7
7
  layer: L2
8
- model: sonnet
8
+ model: opus
9
9
  group: quality
10
10
  tools: "Read, Bash, Glob, Grep"
11
11
  emit: audit.complete
@@ -585,7 +585,7 @@ LOW — Nice to have. Style inconsistencies, minor refactors, doc gaps.
585
585
  INFO — Observation only. Architecture notes, tech debt acknowledgment.
586
586
  ```
587
587
 
588
- Apply confidence filtering: only report findings with >80% confidence. Consolidate similar issues (e.g., "12 functions missing error handling in src/services/" — not 12 separate findings). Adapt judgment to project type (a `console.log` in a CLI tool is fine; in a production API handler, it's not).
588
+ Apply the Falsification Pass (`../review/SKILL.md` → Step 6): drop a finding only when what you read contains direct counter-evidence against its key claim — never merely because you are unsure. A finding you cannot disprove is reported and typed `ASSUMED` with the unchecked premise named. Consolidate similar issues (e.g., "12 functions missing error handling in src/services/" — not 12 separate findings). Adapt judgment to project type (a `console.log` in a CLI tool is fine; in a production API handler, it's not).
589
589
 
590
590
  ## Output Format
591
591
 
@@ -644,7 +644,7 @@ Report saved to: AUDIT-REPORT.md
644
644
 
645
645
  1. MUST complete all 8 phases (Phase 8 may report "no data" if .rune/metrics/ doesn't exist yet) — if any phase is skipped, state explicitly which phase and why
646
646
  2. MUST delegate Phase 1 to dependency-doctor and Phase 2 to sentinel — no manual replacements
647
- 3. MUST apply confidence filter — only report findings with >80% confidence; consolidate similar issues
647
+ 3. MUST apply the Falsification Pass drop findings only on direct counter-evidence, never on uncertainty; consolidate similar issues
648
648
  4. MUST include at least 3 positive findings — an audit with no positives is incomplete
649
649
  5. MUST produce quantified health scores (1-10 per dimension) — not vague "needs work"
650
650
  6. MUST NOT fabricate findings — every finding requires a specific file:line citation
@@ -6,7 +6,7 @@ metadata:
6
6
  author: runedev
7
7
  version: "1.10.0"
8
8
  layer: L3
9
- model: haiku
9
+ model: sonnet
10
10
  group: validation
11
11
  tools: "Read, Bash, Glob, Grep"
12
12
  ---
@@ -6,7 +6,7 @@ metadata:
6
6
  author: runedev
7
7
  version: "1.1.0"
8
8
  layer: L3
9
- model: haiku
9
+ model: sonnet
10
10
  group: validation
11
11
  tools: "Read, Glob, Grep"
12
12
  ---
@@ -6,7 +6,7 @@ metadata:
6
6
  author: runedev
7
7
  version: "1.3.0"
8
8
  layer: L3
9
- model: haiku
9
+ model: sonnet
10
10
  group: state
11
11
  tools: "Read, Glob, Grep"
12
12
  emit: context.preview, output.density.set
@@ -321,7 +321,7 @@ When ORANGE or RED is reached, use this table to determine whether compaction is
321
321
 
322
322
  ### Mid-Loop Compaction (Phase 4 Emergency)
323
323
 
324
- > From goclaw (nextlevelbuilder/goclaw, 832★): "Compact during run, not just at session boundary."
324
+ Compact during the run, not just at the session boundary.
325
325
 
326
326
  When context hits RED during Phase 4 (implementation), compaction IS possible at **clean split points**:
327
327
 
@@ -640,7 +640,7 @@ Escalation chain: debug-fix (3x) → re-plan (1x) → brainstorm rescue (1x) →
640
640
 
641
641
  ### Structured Escalation Report
642
642
 
643
- > From agency-agents (msitarzewski/agency-agents, 50.8k★): "After 3 retry failures, structured escalation prevents cargo-cult retrying."
643
+ After 3 retry failures, structured escalation prevents cargo-cult retrying.
644
644
 
645
645
  When escalation chain exhausts (all retries hit) or cook returns `BLOCKED`, produce a Structured Escalation Report instead of a vague "I can't do this":
646
646
 
@@ -678,7 +678,7 @@ When escalation chain exhausts (all retries hit) or cook returns `BLOCKED`, prod
678
678
 
679
679
  ### Subagent Question Gate
680
680
 
681
- > From superpowers (obra/superpowers, 84k★): "Subagents that start work without asking questions produce the wrong thing 40% of the time."
681
+ Subagents that start work without asking questions frequently produce the wrong thing.
682
682
 
683
683
  Before dispatching a sub-skill (fix, test, review) for a non-trivial task (3+ files OR ambiguous scope):
684
684
 
@@ -4,7 +4,7 @@ The Analysis Paralysis Guard (5-read counter in SKILL.md) catches obvious paraly
4
4
  This catches **same-input-same-output loops** — where the agent keeps calling the same tool
5
5
  with the same arguments and getting the same result, making zero progress.
6
6
 
7
- Source: goclaw (832★) — SHA256-based loop detection distinguishes true stuck loops from productive retries.
7
+ Hash-based detection distinguishes true stuck loops from productive retries.
8
8
 
9
9
  ## Detection Logic
10
10
 
@@ -1,7 +1,7 @@
1
1
  # Mid-Run Signal Detection — Two-Stage Intent Classification
2
2
 
3
3
  When user sends a message DURING cook execution (mid-phase), classify intent before acting.
4
- Source: goclaw (832★) — two-stage intent classification prevents expensive LLM calls for simple signals.
4
+ Two-stage intent classification prevents expensive LLM calls for simple signals.
5
5
 
6
6
  ## Stage 1 — Keyword Fast-Path
7
7
 
@@ -5,7 +5,7 @@ metadata:
5
5
  author: runedev
6
6
  version: "1.4.0"
7
7
  layer: L2
8
- model: sonnet
8
+ model: opus
9
9
  group: development
10
10
  tools: "Read, Bash, Glob, Grep"
11
11
  emit: bug.diagnosed, agent.stuck
@@ -282,7 +282,7 @@ Track fix attempts in the Debug Report. If this is attempt N>1 for the same symp
282
282
 
283
283
  ### 3+ Fixes as Architectural Signal
284
284
 
285
- > From superpowers (obra/superpowers, 84k★): "Each fix revealing new problems elsewhere = structural issue, not a bug hunt."
285
+ Each fix revealing new problems elsewhere is a structural issue, not a bug hunt.
286
286
 
287
287
  When 3+ **distinct** fixes fail (not retries of the same fix), STOP treating it as a bug:
288
288
 
@@ -5,7 +5,7 @@ metadata:
5
5
  author: runedev
6
6
  version: "0.2.0"
7
7
  layer: L3
8
- model: haiku
8
+ model: sonnet
9
9
  group: deps
10
10
  tools: "Read, Bash, Glob, Grep"
11
11
  ---
@@ -5,7 +5,7 @@ metadata:
5
5
  author: runedev
6
6
  version: "0.9.0"
7
7
  layer: L2
8
- model: sonnet
8
+ model: opus
9
9
  group: creation
10
10
  tools: "Read, Write, Edit, Glob, Grep"
11
11
  ---
@@ -177,7 +177,7 @@ Delegate to `rune:git changelog` to produce a changelog entry from commits since
177
177
 
178
178
  #### Step 4 — Cross-Doc Consistency Pass
179
179
 
180
- > From gstack (garrytan/gstack, 50.9k★): "Cross-document consistency prevents the #2 docs problem: docs that exist but contradict each other."
180
+ Cross-document consistency prevents the second-most-common docs problem: docs that exist but contradict each other.
181
181
 
182
182
  After updating any doc, verify consistency across all project documentation:
183
183
 
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: docs-seeker
3
3
  description: "Find documentation for APIs, libraries, and error messages. Looks up official docs, changelog entries, and migration guides."
4
+ context: fork
5
+ agent: general-purpose
6
+ model: haiku
4
7
  metadata:
5
8
  author: runedev
6
9
  version: "0.2.0"
@@ -5,7 +5,7 @@ metadata:
5
5
  author: runedev
6
6
  version: "0.3.0"
7
7
  layer: L3
8
- model: haiku
8
+ model: sonnet
9
9
  group: validation
10
10
  tools: "Read, Bash, Glob, Grep"
11
11
  ---
@@ -78,7 +78,7 @@ If export not found → mark as **WARN** (symbol may not be exported).
78
78
 
79
79
  ### Step 3 — Verify external packages (Dependency Check Before Import)
80
80
 
81
- > From taste-skill (Leonxlnx/taste-skill, 3.4k★): "Before importing ANY 3rd party lib, check package.json."
81
+ Before importing ANY third-party library, check the dependency manifest.
82
82
 
83
83
  Use `Read` on the project's dependency manifest to confirm each external package is listed:
84
84
 
@@ -6,7 +6,7 @@ metadata:
6
6
  author: runedev
7
7
  version: "0.2.0"
8
8
  layer: L2
9
- model: sonnet
9
+ model: opus
10
10
  group: delivery
11
11
  tools: "Read, Write, Edit, Bash, Glob, Grep"
12
12
  listen: incident.detected
@@ -6,7 +6,7 @@ metadata:
6
6
  author: runedev
7
7
  version: "0.2.0"
8
8
  layer: L3
9
- model: haiku
9
+ model: sonnet
10
10
  group: validation
11
11
  tools: "Read, Glob, Grep"
12
12
  listen: quarantine.notice.emitted
@@ -8,7 +8,7 @@ metadata:
8
8
  author: runedev
9
9
  version: "0.4.0"
10
10
  layer: L1
11
- model: sonnet
11
+ model: opus
12
12
  group: orchestrator
13
13
  tools: "Read, Write, Edit, Bash, Glob, Grep"
14
14
  listen: audit.complete