@rune-kit/rune 2.23.0 → 2.25.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/README.md +43 -16
- package/compiler/__tests__/adapter-model-mapping.test.js +6 -6
- package/compiler/__tests__/adapters.test.js +13 -0
- package/compiler/adapters/codex.js +29 -4
- package/compiler/adapters/windsurf.js +14 -7
- package/extensions/ai-ml/skills/llm-architect.md +3 -3
- package/package.json +1 -1
- package/skills/adversary/SKILL.md +40 -2
- package/skills/adversary/references/oracle-mode.md +3 -2
- package/skills/ba/SKILL.md +6 -1
- package/skills/brainstorm/SKILL.md +22 -1
- package/skills/brainstorm/references/design-it-twice.md +5 -2
- package/skills/cook/SKILL.md +19 -1
- package/skills/council/SKILL.md +222 -0
- package/skills/council/references/dispatch-protocol.md +77 -0
- package/skills/marketing/SKILL.md +26 -1
- package/skills/mcp-builder/SKILL.md +2 -2
- package/skills/plan/SKILL.md +13 -2
- package/skills/problem-solver/SKILL.md +21 -2
- package/skills/review/SKILL.md +52 -3
- package/skills/session-bridge/SKILL.md +2 -2
- package/skills/video-creator/SKILL.md +74 -2
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: council
|
|
3
|
+
description: "Gathers N independent, decorrelated perspectives across model families (external CLIs when present, subagents when not) and arbitrates inline. Use when a decision needs genuine debate — not same-model echo — for critique, review, or judge calls. Honest about degradation: never claims 'consensus' from a single model family."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
metadata:
|
|
6
|
+
author: runedev
|
|
7
|
+
version: "0.1.0"
|
|
8
|
+
layer: L3
|
|
9
|
+
model: sonnet
|
|
10
|
+
group: reasoning
|
|
11
|
+
tools: "Read, Write, Bash, Grep, Glob, Task"
|
|
12
|
+
emit: council.dispatched, council.result
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# council
|
|
16
|
+
|
|
17
|
+
## Purpose
|
|
18
|
+
|
|
19
|
+
One coordination primitive that gathers N INDEPENDENT perspectives for any skill needing debate or multi-view judgment. Detects installed AI CLIs → fans a question across distinct model families → degrades gracefully to subagents when no bridge exists → gates off-topic/malformed voices → normalizes claims → **arbitrates INLINE** (no separate arbiter skill — single producer→single consumer is YAGNI).
|
|
20
|
+
|
|
21
|
+
Council's entire value proposition is *decorrelated* model bias — two different architectures independently reaching the same conclusion is signal; two instances of the same model agreeing is an echo. council refuses to launder the second case as the first.
|
|
22
|
+
|
|
23
|
+
<HARD-GATE>
|
|
24
|
+
If fewer than 2 distinct real `model_family` values answered (excluding `is_fallback` voices AND voices with `model_family: "unknown"` — unconfirmed wrapper-CLI backends, see `references/dispatch-protocol.md` §Detect), council MUST stamp the run `NO_DECORRELATION` and the Arbitrate step MUST NOT use consensus language ("voices agree", "consensus view", "the panel concludes"). Say what happened instead: "N subagents of the same model family produced overlapping output — treat as one perspective, not independent confirmation."
|
|
25
|
+
</HARD-GATE>
|
|
26
|
+
|
|
27
|
+
## Triggers
|
|
28
|
+
|
|
29
|
+
- Called by `adversary` — Step 0.6, mode=critique, for plans flagged high-risk/critical-path/expensive-to-reverse
|
|
30
|
+
- Called by `review` — Step 1.6, mode=review, when blast radius is 50+ callers with a HIGH-severity change
|
|
31
|
+
- Called by `brainstorm` — Step 3.75, mode=judge, Design-It-Twice Mode only, when N=4 spawned for a remote/external dependency, diversity landed in the 0.4-0.59 marginal band, or the user requests a second opinion on which candidate design is strongest
|
|
32
|
+
- Called by `problem-solver` — Step 6.5, mode=judge, when a one-way-door decision has a high-impact top solution, or the ethical dimension check surfaced a severe harm/fairness concern
|
|
33
|
+
- `/rune council <question>` — manual multi-perspective gathering on any question
|
|
34
|
+
- Auto-trigger: none (always explicit — council is expensive relative to a single pass, callers opt in)
|
|
35
|
+
|
|
36
|
+
## Calls (outbound)
|
|
37
|
+
|
|
38
|
+
None at the skill level — council dispatches via `Bash` (external CLI bridge) and `Task` (subagent fallback) directly. It does not call other Rune skills.
|
|
39
|
+
|
|
40
|
+
## Called By (inbound)
|
|
41
|
+
|
|
42
|
+
- `adversary` (L2): Step 0.6 — decorrelated critique before red-teaming a high-risk plan
|
|
43
|
+
- `review` (L2): Step 1.6 — decorrelated bug-finding when blast radius escalation fires
|
|
44
|
+
- `brainstorm` (L2): Step 3.75 — decorrelated judgment on which Design-It-Twice candidate is strongest, narrow trigger only
|
|
45
|
+
- `problem-solver` (L3): Step 6.5 — decorrelated judgment on whether a high-stakes framework conclusion holds (documented L3→L3 coordination)
|
|
46
|
+
- User: `/rune council` direct invocation
|
|
47
|
+
|
|
48
|
+
## Data Flow
|
|
49
|
+
|
|
50
|
+
### Feeds Into →
|
|
51
|
+
|
|
52
|
+
- `adversary` (L2): `CouncilResult.agreement` → seeds Step 6 Verdict with cross-family-verified findings instead of (or alongside) single-pass analysis
|
|
53
|
+
- `review` (L2): `CouncilResult.agreement` → seeds Step 6 Report's CRITICAL/HIGH findings for the escalated symbol
|
|
54
|
+
- `brainstorm` (L2): `CouncilResult.agreement` → seeds Step 4 Recommend with cross-family-verified judgment on the strongest candidate design, alongside (never replacing) the diversity score
|
|
55
|
+
- `problem-solver` (L3): `CouncilResult.agreement` → seeds Step 6's top solution with a cross-family-verified soundness check before Step 7 communication structuring
|
|
56
|
+
- `.rune/council/run-*.json`: every run's full result — free tier owns this schema; Pro Council Cockpit (deferred, Phase 2) reads it for a live panel
|
|
57
|
+
|
|
58
|
+
### Fed By ←
|
|
59
|
+
|
|
60
|
+
- Caller's `PerspectiveRequest` — question, mode, n, diversity constraints, evidence requirements (see `.rune/council-voice-contract.md`)
|
|
61
|
+
- `.rune/runtimes.json` — cached CLI detection from the current session
|
|
62
|
+
|
|
63
|
+
## Workflow
|
|
64
|
+
|
|
65
|
+
Contract source of truth: `.rune/council-voice-contract.md` (Voice v2). This section is the executable protocol; the contract file is the schema. If the two disagree, the contract wins — update this file to match, don't silently drift.
|
|
66
|
+
|
|
67
|
+
### Step 1: DETECT
|
|
68
|
+
|
|
69
|
+
1. Check for a cached runtime report at `.rune/runtimes.json`. If it exists and is from the current session (same date), reuse it — skip to Step 2.
|
|
70
|
+
2. Otherwise probe for the `1devtool-agent` bridge (the local CLI that fans prompts out to installed AI coding agents — Claude, Codex, Gemini, Antigravity, Cline, Amp, OpenCode, Qwen, Grok, Aider). Detection and family-mapping details: `references/dispatch-protocol.md` §Detect.
|
|
71
|
+
3. Write `.rune/runtimes.json`: `{ detected: [{runtime, status, model_family, version}], checked_at: <session marker>, bridge_path: <resolved path> }` — exact field list owned by `references/dispatch-protocol.md` §Detect, keep the two in sync.
|
|
72
|
+
4. No bridge found → `runtime_report.detected = []`. This is not an error — proceed straight to subagent-only mode (Step 3 DEGRADE).
|
|
73
|
+
|
|
74
|
+
### Step 2: ALLOCATE
|
|
75
|
+
|
|
76
|
+
1. Read `request.n` (2-5) and `request.diversity.prefer_model_families`.
|
|
77
|
+
2. Rank available runtimes by distinct `model_family` — prefer 1 voice per distinct family before doubling up on any family.
|
|
78
|
+
3. Reserve mandatory slots per the Voice Contract's correlated-agreement guards, scaled to `n`:
|
|
79
|
+
- **`n >= 3`**: reserve both — **Perturbation slot** (inverted framing — argue the opposite starting position) and **Devil's-advocate slot** (mandated to argue AGAINST the majority as it forms; voices run independently/blind, so this is a *framing* instruction, not a sequencing one).
|
|
80
|
+
- **`n == 2`**: reserve the Perturbation slot only, leave the second slot answering the question plainly. Reserving both at `n=2` would leave zero voices reading the question at face value, which starves ARBITRATE of a baseline reading to compare the perturbation against.
|
|
81
|
+
4. If falling back to subagents for some/all slots (Step 3), pin each subagent to a distinct persona/constraint using the `brainstorm` Design-It-Twice technique (each subagent pinned to exactly ONE stance — enforced via prompt template) — this does NOT create real decorrelation (same model family), but it reduces prose-verbatim groupthink, which matters for the correlated-agreement guards in Step 5.
|
|
82
|
+
5. Compose each voice's prompt as **fully self-contained** — external voices have zero conversation context. Include: the question, the mode, evidence requirements, and (for critique/review modes) the artifact or plan text inline. Never say "see above" or "as discussed."
|
|
83
|
+
|
|
84
|
+
### Step 3: DISPATCH + DEGRADE
|
|
85
|
+
|
|
86
|
+
0. **Skeleton write** (live-status entry point): before dispatching anything, write `.rune/council/run-<request.id>.json` with every allocated slot present and `status: "queued"` (no claims yet). This is what makes the run file useful for a live reader (e.g. Pro Council Cockpit, deferred to Phase 2) — without this write, a run in progress is indistinguishable from a run that hasn't started.
|
|
87
|
+
|
|
88
|
+
For each allocated slot:
|
|
89
|
+
|
|
90
|
+
1. **External slot** (`source: external-cli`): update that voice's `status: "running"` in the run file, dispatch per `references/dispatch-protocol.md` §Dispatch with `budget.per_voice_timeout_s` enforced. On timeout or non-zero exit → mark `is_fallback: true` and immediately dispatch a subagent for that slot instead (do not drop the slot) — update `status` back to `"running"` for the fallback attempt, not straight to failed.
|
|
91
|
+
2. **Subagent slot** (`source: subagent`): update `status: "running"`, dispatch via `Task` with the persona/constraint from Step 2.4 baked into the prompt. `runtime: "internal"`, `model_family: "anthropic"`, `is_fallback: false` if this was the slot's original allocation, `is_fallback: true` if it's covering a dead external slot.
|
|
92
|
+
3. Run all slots in parallel (single message, multiple tool calls) — sequential dispatch defeats the purpose of gathering independent perspectives at reasonable cost.
|
|
93
|
+
4. As each slot resolves (raw response received, whether or not it will later pass GATE), record `latency_ms` and update that voice's `status: "complete"` in the run file — GATE (Step 4) may still later flip a completed voice to `"dropped"`, but "complete" at this point means "produced a raw answer," not "counted toward the result." Populate `runtime_report.used` / `runtime_report.degraded_to_subagent` at the same time.
|
|
94
|
+
5. **Write after every status transition**, not just at the end — overwrite `.rune/council/run-<request.id>.json` each time a voice moves `queued → running → complete`. This is a small file; the incremental writes are what make "live per-voice status" (per the Voice Contract) real rather than aspirational. A caller or Cockpit reading the file mid-run sees genuine progress, not a stale skeleton.
|
|
95
|
+
|
|
96
|
+
### Step 4: GATE
|
|
97
|
+
|
|
98
|
+
For each raw voice response, before it counts toward anything:
|
|
99
|
+
|
|
100
|
+
1. **Well-formed check**: does the response parse into the expected Voice shape (stance + claims, each claim with `text` and optionally `anchor`/`evidence`)? Malformed → `validity.well_formed: fail`, `dropped_by: "council.gate"`, add to `voices_dropped[]` with the raw-output reason. Do not attempt to salvage a malformed response by re-prompting mid-run — that's a retry loop the contract doesn't budget for.
|
|
101
|
+
2. **On-topic check (the Agy fix)**: extract what the voice *thinks* it answered into `question_echo`, compare against `request.question`. If the voice answered a different question than asked (common failure mode for wrapper CLIs that inject their own system framing) → `validity.on_topic: fail`, `dropped_by: "council.gate"`, drop it.
|
|
102
|
+
3. Every dropped voice MUST appear in `voices_dropped[]` with `{runtime, reason, dropped_by}` — no silent drops. A voice that fails gate is not "missing," it's "dropped for reason X" — the caller and any human reading the run file need to know a slot was spent and produced nothing usable.
|
|
103
|
+
4. Flip that voice's `status: "complete" → "dropped"` in the run file for anything gated out here (per Step 3.5's incremental-write convention) — a live reader should see the drop, not just infer it from the voice being absent later.
|
|
104
|
+
|
|
105
|
+
### Step 5: NORMALIZE (claim-matching)
|
|
106
|
+
|
|
107
|
+
Determines whether two voices made "the same claim" — meaningless until defined, so apply this exact strategy, in order:
|
|
108
|
+
|
|
109
|
+
1. **Anchor match (deterministic, cheap)**: two claims match if they share the same `anchor` (`file:line`, `plan-step-id`, or `symbol`). No inference needed.
|
|
110
|
+
2. **Arbiter cluster (fallback for keyless claims)**: for claims without a matching anchor, group by semantic similarity — but a cluster is only a valid match if the grouped claims **share at least one `evidence.ref`**. Prose/wording similarity ALONE never establishes a match — two voices using similar phrasing because they were trained on similar data is exactly the correlation this primitive exists to detect and discount, not average away.
|
|
111
|
+
3. Record `matched_by: {method: "anchor"|"arbiter", model: <this council run's identity>}` on every match.
|
|
112
|
+
4. **Verification gate**: a claim's `evidence.verified` MUST be set by a `verified_by` that is NOT the voice that produced the claim (mechanical check — run the test/repro yourself, or have a different-family voice check it — never trust self-certification). Claims with `verified: unchecked` or self-certified `verified_by` do not count toward consensus, regardless of how many voices repeated them.
|
|
113
|
+
5. **Correlated-agreement penalty**: if 2+ voices' claim text is near-verbatim (same phrasing, same examples), treat this as a correlation signal — **regardless of what `model_family` label those voices carry.** Do NOT gate this check on "same family or unknown family" — `model_family` is itself an unverified, CLI-brand-based label (see Sharp Edges: "confirmed" family is not runtime-verified), so requiring the labels to already look correlated before checking for textual correlation defeats the one heuristic that could catch a family-label error behaviorally. Near-verbatim text from voices with DIFFERENT confirmed families is not proof of a mislabeling, but it is not free confirmation either — flag it in `agreement.dissent` as "near-verbatim across confirmed-distinct families — either genuine convergent reasoning or a sign the family labels don't reflect the actual backends" and do NOT let it anchor a `consensus_claims` entry on textual similarity alone (Step 5.2's anchor/shared-evidence.ref requirement still applies). Near-verbatim text from the same family or an unconfirmed wrapper is the stronger, unambiguous case — note it in `agreement.dissent` even if the claim itself is correct, so the caller doesn't over-weight it.
|
|
114
|
+
|
|
115
|
+
### Step 6: ARBITRATE (inline)
|
|
116
|
+
|
|
117
|
+
1. Apply the min-decorrelation gate (see HARD-GATE above): count distinct real `model_family` values among voices that passed GATE, **excluding both `is_fallback` voices AND voices with `model_family: "unknown"`** (unconfirmed wrapper-CLI backends — see `references/dispatch-protocol.md` §Detect; an unconfirmed wrapper is not evidence of a distinct architecture, it may be the same backend as another voice in the run). `< 2` → `decorrelation: NO_DECORRELATION`. `>= 2` → `decorrelation: MULTI_FAMILY`.
|
|
118
|
+
2. Build `agreement.consensus_claims`: clusters (from Step 5) with **>= 2 voices from >= 2 distinct, confirmed `model_family` (never `unknown`), all evidence `verified: true`**. Anything short of this bar is NOT consensus — label it `unverified agreement` and put it in `agreement.dissent` with that label, not in `consensus_claims`.
|
|
119
|
+
3. Build `agreement.dissent`: everything else — genuine disagreement, unverified agreement, single-voice claims, and the correlated-agreement-penalty notes from Step 5.5. Dissent is surfaced, NEVER averaged away or silently dropped in favor of a "majority view."
|
|
120
|
+
4. Check the perturbation slot (Step 2.3): did the claims that survive the inverted framing match the claims from the standard framing? Agreement that survives inversion is trustworthy; agreement that doesn't is flagged in dissent as "framing-sensitive."
|
|
121
|
+
5. Check the devil's-advocate slot (skip if `n == 2` — no such slot was reserved per Step 2.3): if it produced no substantive counter-claims after genuinely trying, note that in Strength Notes-equivalent (the forming majority held up under a mandated attack) — this is useful positive signal, not a wasted slot.
|
|
122
|
+
6. Set `needs_decision: true` if dissent contains a CRITICAL/HIGH-severity claim that no consensus resolves — the caller (adversary/review) should surface this to the user rather than silently picking a side.
|
|
123
|
+
7. Final write of `.rune/council/run-<request.id>.json` with the full `CouncilResult` (schema: `.rune/council-voice-contract.md`) — this overwrites the skeleton/incremental versions from Step 3.0/3.5/4.4 with the complete arbitrated result; every voice's `status` should now read `complete` or `dropped`, never `queued`/`running`.
|
|
124
|
+
8. Emit `council.dispatched` at Step 3 start, `council.result` after Step 6 write completes.
|
|
125
|
+
|
|
126
|
+
## Output Format
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
## Council Result: [request.id]
|
|
130
|
+
- **Question**: [request.question]
|
|
131
|
+
- **Mode**: critique | generate | judge | review
|
|
132
|
+
- **Decorrelation**: MULTI_FAMILY | NO_DECORRELATION
|
|
133
|
+
- **Voices**: [voices_valid]/[voices_requested] valid ([N] dropped)
|
|
134
|
+
- **Runtime**: [detected families] → [used] (degraded: [N])
|
|
135
|
+
|
|
136
|
+
### Consensus (verified, cross-family)
|
|
137
|
+
- [claim] — voices: [ids], families: [list]
|
|
138
|
+
|
|
139
|
+
### Dissent
|
|
140
|
+
- [claim] — [voice id] — [reason: disagreement | unverified | correlated-penalty | framing-sensitive]
|
|
141
|
+
|
|
142
|
+
### Needs Decision
|
|
143
|
+
[If true: the specific unresolved CRITICAL/HIGH claim, framed for the caller to surface to the user]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Returns
|
|
147
|
+
|
|
148
|
+
| Field | Type | Description |
|
|
149
|
+
|-------|------|-------------|
|
|
150
|
+
| `result` | CouncilResult (JSON) | Full structured result per Voice Contract v2 — see `.rune/council-voice-contract.md` |
|
|
151
|
+
| `decorrelation` | enum | `MULTI_FAMILY` \| `NO_DECORRELATION` — the honesty stamp, always present |
|
|
152
|
+
| `agreement.consensus_claims` | array | Only cross-family, verified claims — may be empty |
|
|
153
|
+
| `agreement.dissent` | array | Never empty if any voice disagreed or agreement was unverified/correlated |
|
|
154
|
+
| `voices_dropped` | array | Every gated-out voice with reason — never silent |
|
|
155
|
+
| `run_file` | path | `.rune/council/run-<id>.json` — full artifact for caller or Pro Cockpit |
|
|
156
|
+
|
|
157
|
+
## Constraints
|
|
158
|
+
|
|
159
|
+
1. MUST stamp `NO_DECORRELATION` and strip consensus language when < 2 distinct real model families answered — this is the HARD-GATE, not a suggestion
|
|
160
|
+
2. MUST NOT count prose/wording similarity alone as a claim match — anchor or shared evidence.ref only
|
|
161
|
+
3. MUST NOT accept self-certified verification — `verified_by` must differ from the producing voice
|
|
162
|
+
4. MUST record every dropped voice in `voices_dropped[]` with a reason — no silent drops
|
|
163
|
+
5. MUST run all allocated voices in parallel, not sequentially — the value is independence, not throughput, but sequential dispatch adds latency without adding independence
|
|
164
|
+
6. MUST NOT retry a malformed or off-topic voice mid-run — drop it via GATE and move on; retries reintroduce correlation with the prompt that just failed
|
|
165
|
+
7. MUST NOT dispatch an external CLI without going through `references/dispatch-protocol.md` safety properties (authorization, read-only sandbox, stdin-not-args, binary preflight)
|
|
166
|
+
8. MUST operate in JUDGMENT mode only (critique/review/judge) — `generate` mode is declared in the contract but deferred; if a caller requests `mode: generate`, respond with `NOT_IMPLEMENTED` rather than improvising artifact generation across voices
|
|
167
|
+
|
|
168
|
+
## Mesh Gates
|
|
169
|
+
|
|
170
|
+
| Gate | Requires | If Missing |
|
|
171
|
+
|------|----------|------------|
|
|
172
|
+
| Request Gate | Caller supplies a self-contained `PerspectiveRequest` (question, mode, n) | Cannot run — ask caller for a well-formed request |
|
|
173
|
+
| Runtime Gate | None — council works with zero external CLIs by design | N/A — subagent-only mode is a valid, first-class outcome |
|
|
174
|
+
|
|
175
|
+
## Sharp Edges
|
|
176
|
+
|
|
177
|
+
| Failure Mode | Severity | Mitigation |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| Caller (or a future reader) treats `NO_DECORRELATION` output as consensus anyway | CRITICAL | HARD-GATE strips consensus wording at the source; Output Format always prints the decorrelation stamp first line |
|
|
180
|
+
| Wrapper CLI (Cline/Amp/OpenCode/Aider) misreported as a distinct model family when its backend is actually Claude | HIGH | `references/dispatch-protocol.md` family map defaults unconfirmed wrapper backends to `unknown` — excluded from distinct-family count until confirmed |
|
|
181
|
+
| Prose-similarity claims counted as consensus because arbiter clustering got lazy | CRITICAL | Step 5.2 hard requirement: shared `evidence.ref`, not text similarity, gates the arbiter-cluster match |
|
|
182
|
+
| Self-certified verification (voice grades its own claim) | CRITICAL | Step 5.4: `verified_by` must differ from producing voice, mechanically checked |
|
|
183
|
+
| Off-topic voice counted toward `n` because its answer merely looked substantive | HIGH | Step 4.2 `question_echo` vs `request.question` gate — the Agy fix — before anything downstream sees the voice |
|
|
184
|
+
| External CLI hangs past `per_voice_timeout_s`, stalling the whole run | MEDIUM | Step 3.1: timeout triggers immediate subagent fallback for that slot, not a wait-and-retry |
|
|
185
|
+
| Devil's-advocate/perturbation slots quietly reused as regular voices under time pressure | MEDIUM | Step 2.3 reserves them explicitly in ALLOCATE — Step 6.4/6.5 checks they were actually applied before arbitrating |
|
|
186
|
+
| Bundle/prompt interpolated into shell args for external dispatch | CRITICAL | `references/dispatch-protocol.md` inherits adversary's stdin-only transport — never inline `-p "<bundle>"` |
|
|
187
|
+
| Council invoked for every trivial decision, burning cost on low-stakes calls | MEDIUM | Triggers section: council is opt-in per caller (adversary high-risk gate, review blast-radius gate, brainstorm Design-It-Twice high-stakes gate, problem-solver one-way-door/ethics gate) — never auto-fires on every plan/diff/design/analysis |
|
|
188
|
+
| **"Confirmed" `model_family` is CLI-brand identity, not verified runtime-backend identity** — a user pointing 2+ "confirmed" CLIs (e.g. `claude` + `codex`, or an IDE CLI like `agy` with a model picker) at the same actual backend via BYOK/proxy/gateway override collapses the real distinct-family count without tripping the `is_fallback`/`unknown` exclusions. Found via a real council self-test dispatch (2026-07-11, grok + 2 subagent voices independently converged on this). | CRITICAL, currently UNCLOSED | Step 5.5's near-verbatim check now fires regardless of family label (previous version only fired for same-family/unknown, which this exact scenario bypassed). There is no mechanical way to verify a CLI's actual serving backend from user-space today — council cannot cryptographically close this gap, only flag the behavioral symptom. Documented here rather than falsely claimed solved. |
|
|
189
|
+
| GATE's `question_echo` on-topic check is self-reported by the same voice being graded — a shallow-but-parseable non-answer that echoes the question passes | MEDIUM | Partial: the arbiter reads the echoed question against the actual answer content at Step 6, not just the schema shape, so an echo with no substantive claims behind it still contributes little to consensus/dissent even if it technically passes GATE |
|
|
190
|
+
|
|
191
|
+
## Self-Validation
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
SELF-VALIDATION (run before emitting output):
|
|
195
|
+
- [ ] Decorrelation stamp computed from ACTUAL distinct model_family count, excluding is_fallback AND model_family:"unknown" (unconfirmed wrapper CLIs), not assumed
|
|
196
|
+
- [ ] Every voice that reached ARBITRATE passed GATE (on_topic: pass, well_formed: pass)
|
|
197
|
+
- [ ] Every dropped voice appears in voices_dropped[] with a reason
|
|
198
|
+
- [ ] Every consensus_claims entry has >=2 voices from >=2 distinct CONFIRMED families (never unknown), all verified:true
|
|
199
|
+
- [ ] No consensus_claims entry backed only by prose similarity (check matched_by.method)
|
|
200
|
+
- [ ] No claim's verified_by equals its producing voice id
|
|
201
|
+
- [ ] Perturbation slot dispatched with its reserved framing; devil's-advocate slot too (unless n==2, where only perturbation is reserved per Step 2.3)
|
|
202
|
+
- [ ] .rune/council/run-<id>.json written with full CouncilResult
|
|
203
|
+
- [ ] Run file was written incrementally (skeleton at Step 3.0, status updates through 3.5/4.4), not only once at the end — a live reader mid-run would have seen real progress
|
|
204
|
+
IF ANY check fails → fix before reporting done. Do NOT defer to completion-gate.
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Done When
|
|
208
|
+
|
|
209
|
+
- All allocated voices dispatched in parallel (external where available, subagent fallback where not)
|
|
210
|
+
- Every voice gated (dropped or valid, never ambiguous)
|
|
211
|
+
- Claims normalized via anchor-first/arbiter-cluster-with-shared-evidence strategy
|
|
212
|
+
- Decorrelation honestly stamped (`MULTI_FAMILY` or `NO_DECORRELATION`)
|
|
213
|
+
- Consensus built only from cross-family, mechanically-verified claims; everything else in dissent
|
|
214
|
+
- `.rune/council/run-<id>.json` written
|
|
215
|
+
- `council.result` emitted
|
|
216
|
+
- Self-Validation checklist: all checks passed
|
|
217
|
+
|
|
218
|
+
## Cost Profile
|
|
219
|
+
|
|
220
|
+
~1500-4000 tokens input per voice (self-contained prompt, no shared conversation context) × N voices (2-5), plus ~500-1000 tokens for arbitration. External-CLI voices cost real wall-clock (subject to `per_voice_timeout_s`) but zero incremental Claude token cost. Sonnet model for the orchestrating pass (gate/normalize/arbitrate reasoning); dispatched voices use whatever model each runtime/subagent resolves to.
|
|
221
|
+
|
|
222
|
+
**Scope guardrail**: council gathers and arbitrates perspectives — it does not implement fixes, does not rewrite the artifact under discussion, and does not run in `generate` mode (deferred to Phase 2 cockpit work). If asked to produce code or a revised plan, hand the arbitrated findings back to the caller (`adversary`/`review`) to act on.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Council Dispatch Protocol
|
|
2
|
+
|
|
3
|
+
Concrete mechanics for Step 1 (DETECT) and Step 3 (DISPATCH) of `council`'s Workflow. This reference exists because the safe-transport rules are shared with `adversary`'s oracle-mode cross-model escalation — read that file first; this one adds council-specific detection, family mapping, and N-voice timeout/fallback handling on top.
|
|
4
|
+
|
|
5
|
+
Base safety properties (do not duplicate — inherit): `../../adversary/references/cross-model-escalation.md` — explicit per-invocation authorization, read-only sandbox, stdin-not-inline-args, binary preflight, reconcile-as-data-not-verdict.
|
|
6
|
+
|
|
7
|
+
## §Detect
|
|
8
|
+
|
|
9
|
+
council's bridge is the `1devtool-agent` shim — a local CLI that fans a prompt out to whichever AI coding CLIs are actually installed (Claude, Codex, Gemini, Antigravity, Cline, Amp, OpenCode, Qwen, Grok, Aider) and returns the response. It ships as part of the user's local tooling, not as a Rune dependency — treat it as opportunistic and never assume it exists.
|
|
10
|
+
|
|
11
|
+
1. Try `1devtool-agent list --json` on PATH.
|
|
12
|
+
2. If not found on PATH, check the conventional per-user install location for the current OS (e.g. `~/.1devtool/bin/1devtool-agent` on POSIX, `%USERPROFILE%\.1devtool\bin\1devtool-agent.cmd` on Windows). Do not hardcode a specific user's path into the skill — resolve relative to the current user's home directory at run time.
|
|
13
|
+
3. Neither found → `runtime_report.detected = []`. This is the expected, common case. Proceed to subagent-only mode. Do not surface this as a warning or error to the user — it's a normal degradation, not a misconfiguration.
|
|
14
|
+
4. Found → run `list --json`, parse the returned CLI statuses (`detected` / `not-found` / version), map each `detected` entry's `--to=` id to its `model_family` per the table below, and cache:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"checked_at": "<session-scoped marker, not a wall-clock timestamp>",
|
|
19
|
+
"bridge_path": "<resolved path>",
|
|
20
|
+
"detected": [
|
|
21
|
+
{ "runtime": "codex", "status": "detected", "model_family": "openai", "version": "..." },
|
|
22
|
+
{ "runtime": "gemini", "status": "detected", "model_family": "google", "version": "..." }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
to `.rune/runtimes.json` — this is the canonical schema; `skills/council/SKILL.md` Step 1.3 must match it field-for-field. Reuse this cache for the rest of the session — do not re-run `list` on every council invocation. Only entries with `status: "detected"` are eligible for allocation in Step 2.
|
|
28
|
+
|
|
29
|
+
### Model family map
|
|
30
|
+
|
|
31
|
+
`1devtool-agent run --to=<agent>` accepts: `claude | codex | gemini | agy | cline | amp | opencode | qwen | grok | aider`. Map to `model_family` for the decorrelation count:
|
|
32
|
+
|
|
33
|
+
| `--to=` value | `model_family` | Confidence |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `claude` | `anthropic` | confirmed |
|
|
36
|
+
| `codex` | `openai` | confirmed |
|
|
37
|
+
| `gemini` | `google` | confirmed |
|
|
38
|
+
| `agy` (Antigravity) | `google` | confirmed (Gemini-based) |
|
|
39
|
+
| `grok` | `xai` | confirmed |
|
|
40
|
+
| `qwen` | `alibaba` | confirmed |
|
|
41
|
+
| `cline` / `amp` / `opencode` / `aider` | `unknown` | **wrapper CLI — backend model is user-configured and not reported by `list --json`** |
|
|
42
|
+
|
|
43
|
+
Wrapper CLIs (`cline`, `amp`, `opencode`, `aider`) route to whatever backend model the user configured for them — frequently Claude or GPT. Labeling one of these as a distinct family without confirmation is exactly the "decorrelation theater" the min-decorrelation gate exists to prevent. Default `model_family: "unknown"` for these and **exclude `unknown` from the distinct-family count** in council's Step 6 ARBITRATE, same as `is_fallback` voices. If a future version can read the wrapper's actual configured backend (e.g. from its config file), promote it to a confirmed family — until then, treat it as unconfirmed.
|
|
44
|
+
|
|
45
|
+
**"Confirmed" here means "this CLI is vendor-dedicated by product design," not "this specific invocation's response was verified to come from that vendor's model."** The distinction matters: any of the six "confirmed" rows above can, in principle, be redirected to a different backend by the user (BYOK / base-URL override / corporate LLM gateway) or, for IDE-style CLIs, a built-in model picker — and `1devtool-agent list --json` reports CLI identity, not the identity of the model that actually generated a given response. This table has no mechanism to detect that redirection; it is a real, currently-unclosed limitation (see `skills/council/SKILL.md` Sharp Edges), not something resolved by this table alone. Do not extend the `unknown` treatment to these six rows without confirmed evidence a specific one is commonly reconfigured — that would just move the same unverified-assumption problem, not fix it — but do not read "confirmed" here as cryptographically verified either.
|
|
46
|
+
|
|
47
|
+
## §Dispatch
|
|
48
|
+
|
|
49
|
+
For each external slot allocated in Step 2:
|
|
50
|
+
|
|
51
|
+
1. Write the fully self-contained voice prompt (question + mode + evidence requirements + inline artifact) to a temp file. Never inline it into a shell argument.
|
|
52
|
+
2. Invoke:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
printf '%s' "$VOICE_PROMPT" | 1devtool-agent run --to=<agent> --prompt-stdin --timeout=<budget.per_voice_timeout_s> --json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`--json` so the response comes back as a structured envelope council can parse into the Voice shape, rather than free text requiring re-parsing.
|
|
59
|
+
3. Per-call authorization: dispatching through `1devtool-agent` is council's normal operating mode (not a rare escalation like adversary's oracle-mode), so it does not require a fresh user confirmation on every single voice within one council run — but the FIRST time in a session that council is about to dispatch to any external CLI, confirm with the user that fanning this question out to their installed AI CLIs is expected. In non-interactive contexts (CI, `/loop`, scheduled runs) skip external dispatch entirely and run subagent-only — announce the skip in the output, do not silently downgrade.
|
|
60
|
+
4. Exit code 0 → parse the JSON envelope into a Voice (see main SKILL.md Step 4 GATE for validation). Non-zero exit or timeout → `is_fallback: true`, immediately dispatch a subagent for this slot instead (Step 3.2 in the main workflow). Do not retry the same external CLI a second time in the same run — one failure per slot is enough signal to fall back.
|
|
61
|
+
5. Record `latency_ms` from dispatch to response (or to timeout) regardless of outcome.
|
|
62
|
+
|
|
63
|
+
## Hard caps (inherited + council-specific)
|
|
64
|
+
|
|
65
|
+
- Per-voice prompt ≤ 4k chars for the inline artifact portion (same cap as oracle-mode bundles) — truncate with an explicit `... [truncated]` marker rather than silently cutting content.
|
|
66
|
+
- Max 5 voices per council run (contract's `n: 2..5`) — if a caller requests more, cap at 5 and note the cap in the output.
|
|
67
|
+
- `per_voice_timeout_s` comes from the caller's `PerspectiveRequest.budget`; council does not invent its own default beyond what the contract specifies for the mode.
|
|
68
|
+
|
|
69
|
+
## Checklist (dispatch-time)
|
|
70
|
+
|
|
71
|
+
- [ ] Bridge detection cached in `.rune/runtimes.json` for this session — not re-probed per voice
|
|
72
|
+
- [ ] Wrapper CLI family defaulted to `unknown` unless backend confirmed
|
|
73
|
+
- [ ] Prompt delivered via stdin from a temp file — never inline-interpolated
|
|
74
|
+
- [ ] First external dispatch this session confirmed with the user (not per-voice)
|
|
75
|
+
- [ ] Non-interactive context → external dispatch skipped entirely, announced, subagent-only
|
|
76
|
+
- [ ] Timeout or non-zero exit → immediate subagent fallback for that slot, no same-CLI retry
|
|
77
|
+
- [ ] `latency_ms` recorded for every voice, success or failure
|
|
@@ -3,7 +3,7 @@ name: marketing
|
|
|
3
3
|
description: "Create marketing assets and execute launch strategy. Use when crafting landing copy, social banners, SEO meta, blog posts, or video scripts — coordinates with launch (deploy + announce) and the @rune-pro/growth pack (research / content / CRO)."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.7.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: delivery
|
|
@@ -183,6 +183,28 @@ Produce ready-to-post content:
|
|
|
183
183
|
|
|
184
184
|
**Product Hunt tagline** (under 60 characters)
|
|
185
185
|
|
|
186
|
+
### Step 4.5 — Caption Doctrine (captions that start conversations)
|
|
187
|
+
|
|
188
|
+
A social caption's job is NOT to summarize the post/video — it's to HOOK a scroller and
|
|
189
|
+
start a conversation. A caption that re-tells the content is dead weight ("headline, not
|
|
190
|
+
transcript"). Apply this to every caption attached to the Step 4 posts and to any video/
|
|
191
|
+
image ship.
|
|
192
|
+
|
|
193
|
+
**Format (4 short pieces, whole caption well under ~500 chars):**
|
|
194
|
+
1. **Hook line** (~100 chars max — mobile feeds truncate around ~125 chars and most readers never tap "See more"): tension / curiosity / bold claim, in DIFFERENT words than the content's own hook.
|
|
195
|
+
2. **Optional context line** (~90 chars): ONE extra stake / number / twist the content doesn't spell out. Not a plot summary.
|
|
196
|
+
3. **One genuine open-ended question**: real opinion, many valid answers ("How much do you usually bump budget each time?"). This is the comment engine.
|
|
197
|
+
4. **Soft CTA to the first comment**: "link in the first comment 👇".
|
|
198
|
+
|
|
199
|
+
**Platform reality checklist (2025):**
|
|
200
|
+
- **Link goes in the FIRST COMMENT, never the caption body.** Top-performing FB content is overwhelmingly linkless and platforms downrank in-body links (widely reported).
|
|
201
|
+
- **No engagement bait.** Meta demotes explicit begging ("comment YES if…", "tag a friend", react/share requests). Genuine opinion questions are exempt and encouraged.
|
|
202
|
+
- **0-1 hashtag, never a wall** — Meta flags an "inordinate amount of hashtags" as spam.
|
|
203
|
+
- **Max 3 emoji** (emotional ones) or none. No em-dash. No superlatives ("best ever", "game-changer", "revolutionary").
|
|
204
|
+
- **NEVER transcript the content.** If the caption reads like the voiceover or the post body, rewrite it.
|
|
205
|
+
|
|
206
|
+
**Long-form intro posts** (a detailed FB/LinkedIn intro, distinct from a short caption): every paragraph ≤ 2 lines with a blank line between, keep technical terms in their usual form, include one honest caveat, humble tone — no oversell, no hashtag wall, link still in the first comment.
|
|
207
|
+
|
|
186
208
|
### Step 5 — SEO metadata
|
|
187
209
|
|
|
188
210
|
Produce for the landing page:
|
|
@@ -318,6 +340,9 @@ Known failure modes for this skill. Check these before declaring done.
|
|
|
318
340
|
| Counter-playbook without detection mechanism | HIGH | Every scenario needs a monitoring method — "watch for price drops" is useless without specifying WHERE to watch and HOW to automate |
|
|
319
341
|
| Counter-playbook with unrealistic response windows | MEDIUM | If response window is 24h but pre-built assets don't exist, the playbook will fail — either extend window or create assets NOW |
|
|
320
342
|
| Generating counter-playbook for blue ocean products | LOW | Skip Step 3.5 if no direct competitors — counter-strategies need someone to counter |
|
|
343
|
+
| Caption that transcripts/summarizes the content | MEDIUM | Step 4.5: caption is a HOOK, not a recap — if it reads like the post body, rewrite. Add a genuine question to drive comments |
|
|
344
|
+
| Link placed in caption body instead of first comment | MEDIUM | Step 4.5: platforms downrank in-body links — link goes in the first comment; caption body stays linkless |
|
|
345
|
+
| Engagement-bait or hashtag wall in captions | MEDIUM | Step 4.5: Meta demotes "comment YES / tag a friend" and hashtag walls — use 0-1 hashtag and a real opinion question |
|
|
321
346
|
|
|
322
347
|
## Done When
|
|
323
348
|
|
|
@@ -370,8 +370,8 @@ When building MCP servers that call AI providers, support **dual-model configura
|
|
|
370
370
|
```typescript
|
|
371
371
|
// config.ts
|
|
372
372
|
const config = {
|
|
373
|
-
primaryModel: process.env.PRIMARY_MODEL || 'claude-sonnet-
|
|
374
|
-
backgroundModel: process.env.BACKGROUND_MODEL || 'claude-haiku-4-5
|
|
373
|
+
primaryModel: process.env.PRIMARY_MODEL || 'claude-sonnet-5',
|
|
374
|
+
backgroundModel: process.env.BACKGROUND_MODEL || 'claude-haiku-4-5',
|
|
375
375
|
};
|
|
376
376
|
```
|
|
377
377
|
|
package/skills/plan/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: plan
|
|
|
3
3
|
description: "Create structured implementation plans from requirements. Produces master plan + phase files for enterprise-scale project management. Master plan = overview (<80 lines). Phase files = execution detail (<200 lines each). Each session handles 1 phase. Uses opus for deep reasoning."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "1.7.
|
|
6
|
+
version: "1.7.1"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: opus
|
|
9
9
|
group: creation
|
|
@@ -22,6 +22,17 @@ Strategic planning engine for the Rune ecosystem. Produces a **master plan + pha
|
|
|
22
22
|
|
|
23
23
|
This is enterprise-grade project management: BA produces WHAT → Plan produces HOW (structured into phases) → ANY coder executes each phase with full context.
|
|
24
24
|
|
|
25
|
+
> **Goal-first — leverage native goal/outcome commands (advisory, 2026).** Current
|
|
26
|
+
> agent models (Opus 4.8, Sonnet 5, and the API-only Fable) perform best when handed
|
|
27
|
+
> the full goal + constraints up front, then left to execute at high effort. Some
|
|
28
|
+
> platforms now expose this natively: Claude Code `/goal` sets a run's north-star;
|
|
29
|
+
> Managed Agents **Outcomes** (`user.define_outcome` + rubric) grade-and-iterate to a
|
|
30
|
+
> target. The master plan IS that goal contract in durable form — when a native
|
|
31
|
+
> goal/outcome command is present, seed it from the master plan's Overview +
|
|
32
|
+
> acceptance criteria instead of re-stating the goal loosely. Advisory only: the
|
|
33
|
+
> master plan + phase files stay the source of truth on every platform (goal/Outcome
|
|
34
|
+
> are single-session; the plan survives across sessions and platforms).
|
|
35
|
+
|
|
25
36
|
<HARD-GATE>
|
|
26
37
|
NEVER produce a single monolithic plan file for non-trivial tasks.
|
|
27
38
|
Non-trivial = 3+ phases OR 5+ files OR estimated > 100 LOC total change.
|
|
@@ -402,7 +413,7 @@ Append to plan output when invoked standalone. Suppress when called as sub-skill
|
|
|
402
413
|
```yaml
|
|
403
414
|
chain_metadata:
|
|
404
415
|
skill: "rune:plan"
|
|
405
|
-
version: "1.7.
|
|
416
|
+
version: "1.7.1"
|
|
406
417
|
status: "[DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED]"
|
|
407
418
|
domain: "[area planned]"
|
|
408
419
|
files_changed:
|
|
@@ -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.5.0"
|
|
7
7
|
layer: L3
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: reasoning
|
|
@@ -20,7 +20,7 @@ Inspired by McKinsey problem-solving methodology and cognitive science research
|
|
|
20
20
|
|
|
21
21
|
## Calls (outbound)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
- `council` (L3): Step 6.5 — decorrelated judgment on whether a high-stakes framework conclusion holds, narrow trigger only (documented L3→L3 coordination — see Step 6.5)
|
|
24
24
|
|
|
25
25
|
## Called By (inbound)
|
|
26
26
|
|
|
@@ -281,6 +281,21 @@ From the framework output, derive 2-3 actionable solutions. For each:
|
|
|
281
281
|
|
|
282
282
|
Rank solutions by impact/effort ratio.
|
|
283
283
|
|
|
284
|
+
### Step 6.5 — Decorrelated Judgment (council, high-stakes only)
|
|
285
|
+
|
|
286
|
+
Step 2's bias check and Step 4's framework application are one model's reasoning, however structured. For the subset of problems where being wrong is expensive and hard to undo, call `rune:council` (mode=judge) on the tentative conclusion before Step 7's communication structuring — the same discipline `adversary` Step 0.6 applies to plans.
|
|
287
|
+
|
|
288
|
+
**Trigger — call council when ANY of:**
|
|
289
|
+
- Step 3b's Reversibility Filter classified the decision as a one-way door (irreversible) AND the top-ranked solution from Step 6 has `impact: high`
|
|
290
|
+
- Step 5.5's Ethical Dimension Check surfaced a Harm or Fairness concern rated severe
|
|
291
|
+
- User explicitly asks for a second opinion or "gut check" before committing to the conclusion
|
|
292
|
+
|
|
293
|
+
**Do NOT call council for**: two-way-door decisions, decompositions, root-cause analyses, or any solution ranked `impact: low`/`medium` — council is opt-in overhead reserved for high-stakes/irreversible judgment calls, not a default tax on every problem-solver invocation.
|
|
294
|
+
|
|
295
|
+
**Request**: `{ question: <problem statement + selected framework + reasoning chain + top-ranked solution — self-contained>, mode: "judge", n: 3, diversity: { prefer_model_families: true }, evidence_required: [reasoning] }`.
|
|
296
|
+
|
|
297
|
+
**Consume**: fold `agreement.consensus_claims` into Step 6's top solution as a `[council-verified]` note that the framework's application and conclusion hold up under independent scrutiny. Fold `agreement.dissent` into that solution's "Bias risk" line, tagged `[council-dissent]` — dissent is surfaced, not resolved by picking a side. If `decorrelation: NO_DECORRELATION`, state plainly that no independent model family was reachable — do not claim the conclusion was externally validated.
|
|
298
|
+
|
|
284
299
|
### Step 7 — Select Communication Structure
|
|
285
300
|
|
|
286
301
|
Choose how to present the analysis based on audience:
|
|
@@ -305,6 +320,7 @@ Structure the output report using the selected pattern.
|
|
|
305
320
|
- Do not produce more than 3 recommended solutions — prioritize quality over quantity
|
|
306
321
|
- Max 5 evaluation criteria in Weighted Matrix — more causes choice overload
|
|
307
322
|
- Decompositions MUST pass MECE test — no overlapping or missing branches
|
|
323
|
+
- [council] MUST NOT call council for every high-stakes-looking problem — narrow trigger only (one-way-door decision with a high-impact solution, or a severe ethical concern); when invoked, council output supplements Step 6, it does not replace bias-check/framework discipline
|
|
308
324
|
|
|
309
325
|
## Output Format
|
|
310
326
|
|
|
@@ -314,6 +330,7 @@ Structure the output report using the selected pattern.
|
|
|
314
330
|
- **Domain**: [Clear / Complicated / Complex / Chaotic / Confused] — [one-line justification]
|
|
315
331
|
- **Framework**: [chosen framework and reason]
|
|
316
332
|
- **Confidence**: high | medium | low
|
|
333
|
+
- **Council**: [not invoked | MULTI_FAMILY (N families) | NO_DECORRELATION — same-family subagents only]
|
|
317
334
|
|
|
318
335
|
### Bias Warnings
|
|
319
336
|
- ⚠️ [Bias 1]: [how it might affect this analysis] → [debiasing action taken]
|
|
@@ -362,6 +379,7 @@ Structure the output report using the selected pattern.
|
|
|
362
379
|
| Strawmanning the least-favored option | MEDIUM | Steel Manning: build strongest case for option you dislike before dismissing |
|
|
363
380
|
| Running full PESTLE on a purely technical problem | LOW | PESTLE is for macro-environment — skip for algorithm/implementation choices |
|
|
364
381
|
| Skipping ethics check on user-facing decisions | MEDIUM | Step 5.5: lightweight check — warnings not gates, but don't skip for stakeholder-affecting decisions |
|
|
382
|
+
| (council) Reporting council output as consensus when decorrelation is NO_DECORRELATION | CRITICAL | Step 6.5 consume rule: report the decorrelation stamp plainly, never imply independent validation from same-family subagents |
|
|
365
383
|
|
|
366
384
|
## Done When
|
|
367
385
|
|
|
@@ -374,6 +392,7 @@ Structure the output report using the selected pattern.
|
|
|
374
392
|
- 2-3 solutions ranked by impact/effort ratio with bias risk noted
|
|
375
393
|
- Next Action identified (single most important immediate step)
|
|
376
394
|
- Analysis Report emitted with communication structure
|
|
395
|
+
- (council) If high-stakes trigger matched (Step 6.5): council invoked before Step 7, decorrelation stamp reported plainly, consensus/dissent folded into the top solution
|
|
377
396
|
|
|
378
397
|
## Cost Profile
|
|
379
398
|
|
package/skills/review/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: review
|
|
|
3
3
|
description: "Code quality review — patterns, security, performance, correctness. Finds bugs, suggests improvements, triggers fix for issues found. Escalates to opus for security-critical code."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "1.
|
|
6
|
+
version: "1.4.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: development
|
|
@@ -44,6 +44,7 @@ Every review MUST cite at least one specific concern, suggestion, or explicit ap
|
|
|
44
44
|
- `sast` (L3): static analysis security scan on reviewed code
|
|
45
45
|
- L4 extension packs: domain-specific review patterns when context matches (e.g., @rune/ui for frontend, @rune/security for auth code)
|
|
46
46
|
- `neural-memory` | After review complete | Capture code quality insight
|
|
47
|
+
- `council` (L3): Step 1.6 — decorrelated bug-finding on the diff when blast radius is 50+ callers with a HIGH-severity change, mode=review
|
|
47
48
|
|
|
48
49
|
## Called By (inbound)
|
|
49
50
|
|
|
@@ -63,6 +64,7 @@ Every review MUST cite at least one specific concern, suggestion, or explicit ap
|
|
|
63
64
|
- `review` → `improve-architecture` — when reviewer flag mentions "shallow", "wrapper", "indirection", or pass-through pattern
|
|
64
65
|
- `review` ← `fix` — complex fix requests self-review
|
|
65
66
|
- `review` → `sentinel` — security-critical code → sentinel deep scan
|
|
67
|
+
- `review` → `council` — blast radius 50+ callers with HIGH-severity change → decorrelated bug-finding on the diff
|
|
66
68
|
|
|
67
69
|
## Execution
|
|
68
70
|
|
|
@@ -95,6 +97,31 @@ Use Grep to count direct callers/importers of each modified symbol:
|
|
|
95
97
|
Modifying a symbol with 50+ callers + HIGH severity change (logic, types, behavior) → adversarial analysis REQUIRED. Quick review is NOT sufficient for high-blast-radius changes.
|
|
96
98
|
</HARD-GATE>
|
|
97
99
|
|
|
100
|
+
### Step 1.6: Decorrelated Bug-Finding (council, high-blast-radius only)
|
|
101
|
+
|
|
102
|
+
review's own pass is one model reading the diff once. For the same 50+ caller / HIGH-severity
|
|
103
|
+
symbols that trigger the Step 1.5 HARD-GATE, call `rune:council` (mode=review) on the diff
|
|
104
|
+
itself BEFORE writing up Step 2's findings — a second architecture reading the same code
|
|
105
|
+
independently catches bugs a single pass rationalizes past.
|
|
106
|
+
|
|
107
|
+
This is complementary to (not a replacement for) the existing adversary escalation: council
|
|
108
|
+
here examines the ALREADY-WRITTEN diff for bugs; adversary examines plan-level risk before
|
|
109
|
+
code exists. Both can fire on the same high-blast-radius change.
|
|
110
|
+
|
|
111
|
+
**Request**: `{ question: <the diff, symbol name, and blast radius context — self-contained>,
|
|
112
|
+
mode: "review", n: 3, diversity: { prefer_model_families: true },
|
|
113
|
+
evidence_required: [repro, reasoning] }`.
|
|
114
|
+
|
|
115
|
+
**Consume**: fold `agreement.consensus_claims` into Step 2's CRITICAL/HIGH findings, tagged
|
|
116
|
+
`[council-verified]`. Fold `agreement.dissent` into MEDIUM findings or the report's NEEDS
|
|
117
|
+
DISCUSSION section, tagged `[council-dissent]`. If `decorrelation: NO_DECORRELATION`, do not
|
|
118
|
+
claim independent confirmation in the report — say plainly that no second model family was
|
|
119
|
+
reachable.
|
|
120
|
+
|
|
121
|
+
**Skip if**: blast radius is under 50 callers, or the change is not HIGH-severity — council is
|
|
122
|
+
opt-in overhead reserved for the same tier that already requires adversarial escalation, not a
|
|
123
|
+
default tax on every review.
|
|
124
|
+
|
|
98
125
|
### Step 2: Logic Check (Production-Critical Focus)
|
|
99
126
|
|
|
100
127
|
Read each changed file. Prioritize bugs that **pass CI but break production** — these are the highest-value findings because linters and type checkers already catch the rest.
|
|
@@ -296,6 +323,24 @@ Proceed to Step 6 only after Stage 1 passes. Code quality findings (bugs, patter
|
|
|
296
323
|
|
|
297
324
|
The review report MUST show both stages: spec compliance verdict first, then code quality findings.
|
|
298
325
|
|
|
326
|
+
### Step 5.7: Subtractive Pass (over-engineering lens)
|
|
327
|
+
|
|
328
|
+
Stage 1 catches SPEC-CREEP (unrequested *features*). This pass goes further: it hunts complexity worth **deleting** even inside requested scope, and reports it as a one-line-per-finding cut list with a net-lines total. Run it on any diff that adds a class, wrapper, config option, or dependency. Skip for pure config/docs/style diffs.
|
|
329
|
+
|
|
330
|
+
Tag each finding, one line: `<file>:L<line>: <tag> <what>. <replacement>.`
|
|
331
|
+
|
|
332
|
+
| Tag | Cuts | Replacement |
|
|
333
|
+
|-----|------|-------------|
|
|
334
|
+
| `delete:` | Dead code, unused flexibility, speculative feature | Nothing |
|
|
335
|
+
| `stdlib:` | Hand-rolled thing the standard library ships | Name the function |
|
|
336
|
+
| `native:` | Dependency or code doing what the platform already does | Name the feature |
|
|
337
|
+
| `yagni:` | Abstraction with one implementation, config nobody sets, layer with one caller | Inline it until a 2nd caller exists |
|
|
338
|
+
| `shrink:` | Same logic, fewer lines | Show the shorter form |
|
|
339
|
+
|
|
340
|
+
End with the only metric that matters: `net: -<N> lines, -<M> deps possible.` Nothing to cut → `Lean already.` and move on.
|
|
341
|
+
|
|
342
|
+
**Ranking discipline**: these are LOW/MEDIUM findings (complexity, not correctness) — a subtractive suggestion NEVER outranks a real bug. A single smoke test or assert-based self-check is the ponytail minimum, not bloat — never flag it for deletion. This pass lists cuts; it does not apply them (route to `rune:fix`).
|
|
343
|
+
|
|
299
344
|
### Step 6: Report
|
|
300
345
|
|
|
301
346
|
Produce a structured severity-ranked report.
|
|
@@ -559,6 +604,7 @@ LOW — style inconsistency, naming suggestion, minor refactor opportunity
|
|
|
559
604
|
- **Files Reviewed**: [count]
|
|
560
605
|
- **Findings**: [count by severity]
|
|
561
606
|
- **Review Commit**: [git hash at time of review]
|
|
607
|
+
- **Council**: [not invoked | MULTI_FAMILY (N families) | NO_DECORRELATION — same-family subagents only]
|
|
562
608
|
- **Overall**: APPROVE | REQUEST CHANGES | NEEDS DISCUSSION
|
|
563
609
|
|
|
564
610
|
### Spec Compliance
|
|
@@ -621,7 +667,7 @@ Append to Code Review Report when invoked standalone. Suppress when called as su
|
|
|
621
667
|
```yaml
|
|
622
668
|
chain_metadata:
|
|
623
669
|
skill: "rune:review"
|
|
624
|
-
version: "1.
|
|
670
|
+
version: "1.3.0"
|
|
625
671
|
status: "[DONE | DONE_WITH_CONCERNS]"
|
|
626
672
|
domain: "[area reviewed]"
|
|
627
673
|
files_changed: [] # review doesn't change files
|
|
@@ -654,6 +700,8 @@ chain_metadata:
|
|
|
654
700
|
| Review loop exceeds 3 iterations without resolution | MEDIUM | Cap at 3 review loops. After 3rd iteration with unresolved findings → surface to user with "these findings persist after 3 fix attempts — needs human decision" |
|
|
655
701
|
| Auto-fixing something that should have been ASK | HIGH | When in doubt, ASK. AUTO-FIX only for mechanical issues (dead imports, console.log). Anything involving intent or trade-offs = ASK |
|
|
656
702
|
| Scope drift flagged on intentional refactoring | LOW | Scope drift is informational, not blocking. User can override with "intentional" — don't re-flag after override |
|
|
703
|
+
| (council) Reporting council output as consensus when decorrelation is NO_DECORRELATION | CRITICAL | Step 1.6 consume rule: report the decorrelation stamp plainly, never imply independent confirmation from same-family subagents |
|
|
704
|
+
| (council) Calling council on every review, not just high-blast-radius | MEDIUM | Step 1.6 skip condition matches the existing Step 1.5 HARD-GATE threshold exactly — no separate lower bar |
|
|
657
705
|
|
|
658
706
|
## Done When
|
|
659
707
|
|
|
@@ -663,7 +711,8 @@ chain_metadata:
|
|
|
663
711
|
- Test coverage gaps identified and documented
|
|
664
712
|
- UI anti-pattern checks ran for any frontend files in diff (or confirmed not applicable)
|
|
665
713
|
- Structured report emitted with APPROVE / REQUEST CHANGES / NEEDS DISCUSSION verdict
|
|
714
|
+
- (council) If blast radius 50+ with HIGH severity: council invoked on the diff, decorrelation stamp reported plainly, consensus/dissent folded into findings
|
|
666
715
|
|
|
667
716
|
## Cost Profile
|
|
668
717
|
|
|
669
|
-
~3000-6000 tokens input, ~1000-2000 tokens output. Sonnet default, opus for security-critical reviews. Runs once per implementation cycle.
|
|
718
|
+
~3000-6000 tokens input, ~1000-2000 tokens output. Sonnet default, opus for security-critical reviews. Runs once per implementation cycle. When Step 1.6 fires (high-blast-radius only): add council's cost profile (~1500-4000 tokens per voice × 2-5 voices) — reserved for the same tier that already requires adversarial escalation.
|
|
@@ -553,7 +553,7 @@ When `oracle.dispatched` arrives, payload contains:
|
|
|
553
553
|
- `sessionId` — caller-provided unique id
|
|
554
554
|
- `triggerSignal` — `agent.stuck` or manual
|
|
555
555
|
- `sourceSkill` — `debug` | `fix` | manual
|
|
556
|
-
- `targetModel` — concrete model name (e.g. `gpt-5-
|
|
556
|
+
- `targetModel` — concrete model name (e.g. `gpt-5.6-sol`, `gemini-3-pro`, `claude-opus-4-8`; API-only frontier: `claude-fable-5`)
|
|
557
557
|
- `bundleHash` — sha256 of the bundled context (idempotency key)
|
|
558
558
|
|
|
559
559
|
### Step D2 — Idempotency check
|
|
@@ -570,7 +570,7 @@ Create `.rune/oracle-pending/<sessionId>.json`:
|
|
|
570
570
|
"dispatchedAt": "2026-04-27T12:34:56Z",
|
|
571
571
|
"triggerSignal": "agent.stuck",
|
|
572
572
|
"sourceSkill": "debug",
|
|
573
|
-
"targetModel": "claude-opus-4-
|
|
573
|
+
"targetModel": "claude-opus-4-8",
|
|
574
574
|
"bundleHash": "sha256:9f3a...",
|
|
575
575
|
"status": "pending",
|
|
576
576
|
"timeoutAt": "2026-04-27T12:44:56Z",
|