@rune-kit/rune 2.24.0 → 2.26.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 +19 -11
- package/package.json +1 -1
- package/skills/adversary/SKILL.md +40 -2
- package/skills/brainstorm/SKILL.md +22 -1
- package/skills/brainstorm/references/design-it-twice.md +5 -2
- package/skills/council/SKILL.md +222 -0
- package/skills/council/references/dispatch-protocol.md +77 -0
- package/skills/design/DESIGN-REFERENCE.md +1 -1
- package/skills/design/MOTION-CRAFT.md +263 -0
- package/skills/design/SKILL.md +4 -1
- package/skills/perf/SKILL.md +11 -1
- package/skills/problem-solver/SKILL.md +21 -2
- package/skills/review/SKILL.md +57 -3
- package/skills/sentinel-env/SKILL.md +34 -4
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<strong>Less skills. Deeper connections.</strong><br>
|
|
7
7
|
A lean, interconnected skill ecosystem for AI coding assistants.<br>
|
|
8
|
-
|
|
8
|
+
66 skills · 206 connections · 45 signals · 13 platforms · MIT
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
Most skill ecosystems are either **too many isolated skills** (540+ that don't talk to each other) or **rigid pipelines** (A → B → C, if B fails everything stops).
|
|
25
25
|
|
|
26
|
-
Rune is a **mesh** —
|
|
26
|
+
Rune is a **mesh** — 66 skills with 206 connections + 45 signals across a 5-layer architecture. Skills call each other bidirectionally, forming resilient workflows that adapt when things go wrong.
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
Pipeline: A → B → C → D (B fails = stuck)
|
|
@@ -103,7 +103,15 @@ Plus **9 domain packs** (product, sales, data-science, support, growth, media, p
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
-
## What's New (v2.
|
|
106
|
+
## What's New (v2.26.0 — Motion Craft)
|
|
107
|
+
|
|
108
|
+
> **v2.26.0 (2026-07-18):** Rune's UI mesh gains a deep **motion authority**. New reference `skills/design/MOTION-CRAFT.md` is the canonical source for animation decisions: the *should-it-animate* frequency gate (never animate keyboard/100+-per-day actions), easing decision tree with strong custom curves, per-element duration budgets (UI under 300ms, modals/drawers exempt to 500ms), physicality rules (never `scale(0)`, origin-aware popovers, press feedback), spring physics (damping/response, velocity handoff, momentum projection, rubber-banding), interruptibility (transitions vs keyframes, `@starting-style`), motion performance, reduced-motion, and a reverse-lookup vocabulary glossary. `design` (v0.8.0) loads it whenever a domain involves motion and gains an advisory motion-audit mode. `review` (v1.5.0) adds **Motion Craft Checks** — an advisory lens that fires only on motion diffs, flagging `ease-in` on UI, `scale(0)` entrances, animation on high-frequency actions, layout-property animation, and more, citing MOTION-CRAFT for exact fixes. `perf` (v0.6.0) adds **Step 5.5 Motion Performance** — GPU-property, Framer-Motion-shorthand, and recalc-storm detection ranked in the Cost Impact Hierarchy. Advisory-first throughout (no new HARD-GATEs) — enrichment only, mesh unchanged at 66 skills.
|
|
109
|
+
|
|
110
|
+
### Previous (v2.25.0 — Council)
|
|
111
|
+
|
|
112
|
+
> **v2.25.0 (2026-07-11):** New **`council`** L3 primitive — genuinely decorrelated multi-perspective gathering. Detects installed AI CLIs, fans a question across distinct model families (external CLIs when present, subagents when not), gates off-topic/malformed voices, normalizes claims (anchor-first + shared-evidence arbiter fallback — prose similarity never counts), and arbitrates inline with an honest `NO_DECORRELATION` stamp when fewer than 2 real model families answered. Wired into 4 consumers: `adversary` (CRITICAL-tier plans), `review` (high-blast-radius diffs), `brainstorm` (Design-It-Twice candidate judgment), `problem-solver` (high-stakes conclusions) — every integration explicitly refuses to launder a same-family run as independent confirmation. **Live-dogfooded same day**: a real 3-voice self-test (external `grok` dispatch + 2 subagent fallbacks) independently converged on a real gap — "confirmed" `model_family` was CLI-brand identity, not verified backend identity, letting two CLIs redirected to the same backend (BYOK/proxy override) defeat the gate — fixed before shipping. **Pro**: `council-cockpit` (separate repo) adds a read-only live panel + cost-aware allocation + history-informed family weighting, PRE-council advisory only — Free never depends on Pro. Mesh now 66 skills / 208 connections / 45 signals.
|
|
113
|
+
|
|
114
|
+
### Previous (v2.24.0 — Market Refresh)
|
|
107
115
|
|
|
108
116
|
> **v2.24.0 (2026-07-11):** Model lineup + platform adapters refreshed to the mid-2026 landscape. Claude tiers across the mesh → **Opus 4.8 / Sonnet 5 / Haiku 4.5**; the **Codex** adapter → **GPT-5.6** `sol` / `terra` / `luna` (verified against codex-cli 0.144.1), with a suggested tier→`model_reasoning_effort` mapping in the generated `AGENTS.md`. **Fable 5** is positioned as an API-only frontier **oracle** target rather than a routing tier (opus stays the ceiling). **Windsurf → Devin Desktop** rebrand documented (emission kept on `.windsurf/`, which Devin still reads). New **goal-first advisory** in `plan`/`ba` to leverage native `/goal` + Managed Agents Outcomes. CI 1572/1572.
|
|
109
117
|
|
|
@@ -249,7 +257,7 @@ cook ───emit:phase.complete────→ session-bridge
|
|
|
249
257
|
|
|
250
258
|
## What Rune Is (and Isn't)
|
|
251
259
|
|
|
252
|
-
Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same
|
|
260
|
+
Rune started as a **Claude Code plugin** and now compiles to **every major AI IDE**. Same 66 skills, same mesh connections, same workflows — zero knowledge loss across platforms.
|
|
253
261
|
|
|
254
262
|
| | Rune Provides | Claude Code Provides |
|
|
255
263
|
|---|---|---|
|
|
@@ -341,14 +349,14 @@ npx @rune-kit/rune init --platform windsurf
|
|
|
341
349
|
npx @rune-kit/rune init --platform antigravity
|
|
342
350
|
```
|
|
343
351
|
|
|
344
|
-
This compiles all
|
|
352
|
+
This compiles all 66 skills into your IDE's rules format. Same knowledge, same workflows.
|
|
345
353
|
|
|
346
354
|
### Platform Comparison
|
|
347
355
|
|
|
348
356
|
| Feature | Claude Code | Cursor / Windsurf / Others |
|
|
349
357
|
|---------|-------------|---------------------------|
|
|
350
|
-
| Skills available |
|
|
351
|
-
| Mesh connections |
|
|
358
|
+
| Skills available | 66/66 | 66/66 |
|
|
359
|
+
| Mesh connections | 206 sync + 45 signals (programmatic) | 206 sync + 45 signals (rule references) |
|
|
352
360
|
| Workflows & HARD-GATEs | Full | Full |
|
|
353
361
|
| Extension packs | 14 | 14 |
|
|
354
362
|
| Subagent parallelism | Native | Sequential fallback |
|
|
@@ -702,7 +710,7 @@ See [docs/MULTI-PLATFORM.md](docs/MULTI-PLATFORM.md) for the full architecture.
|
|
|
702
710
|
| Doc | What's inside |
|
|
703
711
|
|-----|---------------|
|
|
704
712
|
| [`docs/GETTING_STARTED.md`](docs/GETTING_STARTED.md) | Your first 5 minutes with Rune — install to first `/rune cook` |
|
|
705
|
-
| [`docs/SKILLS.md`](docs/SKILLS.md) | All
|
|
713
|
+
| [`docs/SKILLS.md`](docs/SKILLS.md) | All 66 skills, searchable by intent and layer |
|
|
706
714
|
| [`docs/SIGNALS.md`](docs/SIGNALS.md) | Canonical signal inventory — 25 events, emit/listen graph |
|
|
707
715
|
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | 5-layer mesh architecture reference |
|
|
708
716
|
| [`docs/VISION.md`](docs/VISION.md) | Philosophy — what Rune is and isn't |
|
|
@@ -715,10 +723,10 @@ See [docs/MULTI-PLATFORM.md](docs/MULTI-PLATFORM.md) for the full architecture.
|
|
|
715
723
|
## Numbers
|
|
716
724
|
|
|
717
725
|
```
|
|
718
|
-
Core Skills:
|
|
726
|
+
Core Skills: 66 (L0: 1 │ L1: 5 │ L2: 30 │ L3: 30)
|
|
719
727
|
Extension Packs: 14 free + 9 pro + 4 business
|
|
720
|
-
Mesh Connections:
|
|
721
|
-
Mesh Signals:
|
|
728
|
+
Mesh Connections: 206 sync calls (rune doctor)
|
|
729
|
+
Mesh Signals: 45 signals · 55 emit/listen edges (rune doctor)
|
|
722
730
|
Connections/Skill: 3.2 avg
|
|
723
731
|
Platforms: 8 (Claude Code, Cursor, Windsurf, Antigravity, Codex, OpenCode, OpenClaw, Generic)
|
|
724
732
|
Compiler: ~1400 LOC (parser + 8 transforms + 8 adapters + CLI)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rune-kit/rune",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "65-skill mesh for AI coding assistants — runtime auto-discipline via native hooks (Claude/Cursor/Windsurf/Antigravity), 5-layer architecture, 204 connections + 43 signals, multi-platform compiler. converge (L3) scans spec vs code so dead-button/frontend-only implementations can't ship.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -3,7 +3,7 @@ name: adversary
|
|
|
3
3
|
description: "Pre-implementation red-team analysis. Use when a plan is high-risk, critical path, or expensive to reverse. Challenges plans before code is written — finds edge cases, security holes, scalability bottlenecks, error propagation risks, and integration conflicts. Catches flaws at plan time (10x cheaper than post-implementation)."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.5.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: opus
|
|
9
9
|
group: quality
|
|
@@ -41,6 +41,7 @@ Every finding MUST reference the specific plan section, file, or assumption it c
|
|
|
41
41
|
- `hallucination-guard` (L3): verify that APIs, packages, or patterns referenced in the plan actually exist
|
|
42
42
|
- `context-engine` (L3): (oracle-mode) emit `context.preview` before bundle build to gate token cost
|
|
43
43
|
- `session-bridge` (L3): (oracle-mode) detach protocol when target model is opus-class for non-blocking dispatch
|
|
44
|
+
- `council` (L3): Step 0.6 — decorrelated multi-perspective critique for CRITICAL-tier plans (one-way-door decisions, auth/payment/crypto/user-data), mode=critique
|
|
44
45
|
|
|
45
46
|
## Called By (inbound)
|
|
46
47
|
|
|
@@ -58,6 +59,7 @@ Every finding MUST reference the specific plan section, file, or assumption it c
|
|
|
58
59
|
- `adversary` → `perf` — scalability concern raised → perf quantifies the bottleneck
|
|
59
60
|
- `adversary` → `scout` — integration risk flagged → scout finds affected code
|
|
60
61
|
- `adversary` → `plan` — CRITICAL findings → plan revises before implementation
|
|
62
|
+
- `adversary` → `council` — CRITICAL-tier plan (one-way-door decision, or auth/payment/crypto/user-data) → decorrelated critique before red-teaming
|
|
61
63
|
|
|
62
64
|
## Execution
|
|
63
65
|
|
|
@@ -93,6 +95,35 @@ produces concrete remediations (likely HARDEN), but maps to REVISE if it exposes
|
|
|
93
95
|
structural flaw in the chosen approach; Socratic's surfaced assumptions and Pre-mortem's
|
|
94
96
|
narratives become findings.
|
|
95
97
|
|
|
98
|
+
### Step 0.6: Decorrelated Multi-Perspective Gathering (council, CRITICAL-tier only)
|
|
99
|
+
|
|
100
|
+
adversary's own single pass is one model's opinion, however rigorous. For the subset of plans
|
|
101
|
+
where being wrong is expensive enough to justify it, call `rune:council` (mode=critique) before
|
|
102
|
+
Steps 1-5, instead of (or in addition to) solo analysis.
|
|
103
|
+
|
|
104
|
+
**Trigger — call council when ANY of:**
|
|
105
|
+
- Step 0.5 selected the **Dialectic** or **Pre-mortem** lens (one-way-door architecture/vendor
|
|
106
|
+
decisions, irreversible migrations — exactly the cases where a second architecture's blind
|
|
107
|
+
spots differing from yours has the highest expected value)
|
|
108
|
+
- The plan touches auth, payment, crypto, or user data at a severity that would otherwise
|
|
109
|
+
trigger mandatory `sentinel` escalation (Step 2)
|
|
110
|
+
- The user explicitly asks for a second opinion or "gut check" before committing
|
|
111
|
+
|
|
112
|
+
**Do NOT call council for**: Quick Challenge mode plans, plans under 3 files with no
|
|
113
|
+
auth/payment/data logic, or routine feature work — council is opt-in overhead, not a default
|
|
114
|
+
tax on every adversary run (see council's own Sharp Edges: never auto-fires on every plan).
|
|
115
|
+
|
|
116
|
+
**Request**: `{ question: <steelmanned thesis + the specific risk being tested>, mode: "critique",
|
|
117
|
+
n: 3, diversity: { prefer_model_families: true }, evidence_required: [reasoning, citation] }`.
|
|
118
|
+
The question MUST be self-contained — council's voices have no access to this conversation.
|
|
119
|
+
|
|
120
|
+
**Consume**: fold `CouncilResult.agreement.consensus_claims` into the relevant dimension's
|
|
121
|
+
findings below, tagged `[council-verified]`. Fold `agreement.dissent` into that dimension's
|
|
122
|
+
findings too, but tagged `[council-dissent]` — dissent is information, not something to
|
|
123
|
+
resolve by picking a side. If `decorrelation: NO_DECORRELATION`, do not describe the result as
|
|
124
|
+
a second opinion in the report — say plainly that no independent model family was reachable and
|
|
125
|
+
the additional voices were same-family subagents.
|
|
126
|
+
|
|
96
127
|
### Step 1: Edge Case Analysis
|
|
97
128
|
|
|
98
129
|
Challenge the plan's handling of boundary conditions.
|
|
@@ -212,6 +243,9 @@ Synthesize all findings into an actionable report.
|
|
|
212
243
|
- 3+ HIGH findings → **REVISE**
|
|
213
244
|
- HIGH findings with clear remediations → **HARDEN** (add remediations to plan, then proceed)
|
|
214
245
|
- Only MEDIUM/LOW findings → **PROCEED** (note findings for implementation awareness)
|
|
246
|
+
- If council was invoked (Step 0.6) and returned `needs_decision: true` → the verdict cannot be
|
|
247
|
+
PROCEED regardless of adversary's own findings; surface the unresolved dissent to the user
|
|
248
|
+
instead of silently picking a side
|
|
215
249
|
|
|
216
250
|
After reporting:
|
|
217
251
|
- If verdict is REVISE: return to `plan` with findings attached as constraints
|
|
@@ -225,6 +259,7 @@ After reporting:
|
|
|
225
259
|
- **Plan analyzed**: [path to plan file]
|
|
226
260
|
- **Dimensions checked**: [which of the 5 were relevant]
|
|
227
261
|
- **Reasoning lens**: [Red Team | Pre-mortem | Evidence Audit | Dialectic | Socratic — and why]
|
|
262
|
+
- **Council**: [not invoked | MULTI_FAMILY (N families) | NO_DECORRELATION — same-family subagents only]
|
|
228
263
|
- **Findings**: [count by severity]
|
|
229
264
|
- **Verdict**: REVISE | HARDEN | PROCEED
|
|
230
265
|
|
|
@@ -347,6 +382,8 @@ See `references/oracle-mode.md` for the full protocol and integration with `debu
|
|
|
347
382
|
| (cross-model) External CLI invoked without authorization or in non-interactive run | CRITICAL | Per-call user authorization required; non-interactive → skip + announce. See `references/cross-model-escalation.md` |
|
|
348
383
|
| (cross-model) Bundle interpolated into shell args — embedded `$(...)` executes | CRITICAL | Always pass via stdin from a temp file; read-only sandbox. Never inline `-p "<bundle>"` |
|
|
349
384
|
| (cross-model) Rubber-stamping the external reviewer's verdict | MEDIUM | Reply is data, not ruling — reconcile against the artifact; classify each finding |
|
|
385
|
+
| (council) Reporting council output as consensus when decorrelation is NO_DECORRELATION | CRITICAL | Step 0.6 consume rule: report the decorrelation stamp plainly, never imply independent validation from same-family subagents |
|
|
386
|
+
| (council) Calling council on every plan, not just CRITICAL-tier | MEDIUM | Step 0.6 trigger list is explicit — Dialectic/Pre-mortem lens, auth/payment/crypto/user-data, or explicit user request only |
|
|
350
387
|
|
|
351
388
|
## Done When
|
|
352
389
|
|
|
@@ -358,6 +395,7 @@ See `references/oracle-mode.md` for the full protocol and integration with `debu
|
|
|
358
395
|
- Findings formatted for consumption by cook Phase 3 (if PROCEED) or plan (if REVISE)
|
|
359
396
|
- Strength Notes section acknowledges well-designed aspects of the plan
|
|
360
397
|
- (oracle-mode) If dispatched: response cited file:line for each claim, or `oracle.failed` emitted with rejection reason
|
|
398
|
+
- (council) If CRITICAL-tier trigger matched: council invoked before Steps 1-5, its decorrelation stamp reported plainly, consensus/dissent folded into the relevant dimension findings
|
|
361
399
|
|
|
362
400
|
## Returns
|
|
363
401
|
|
|
@@ -371,6 +409,6 @@ See `references/oracle-mode.md` for the full protocol and integration with `debu
|
|
|
371
409
|
|
|
372
410
|
## Cost Profile
|
|
373
411
|
|
|
374
|
-
~4000-8000 tokens input (plan + codebase context), ~2000-3000 tokens output. Opus model for adversarial depth. Runs once per feature plan — high cost justified by preventing wasted implementation cycles.
|
|
412
|
+
~4000-8000 tokens input (plan + codebase context), ~2000-3000 tokens output. Opus model for adversarial depth. Runs once per feature plan — high cost justified by preventing wasted implementation cycles. When Step 0.6 fires (CRITICAL-tier only): add council's cost profile (~1500-4000 tokens per voice × 2-5 voices) — reserved for the minority of plans where being wrong is expensive.
|
|
375
413
|
|
|
376
414
|
**Scope guardrail:** adversary reviews THE PLAN only — never audits existing codebase quality or rewrites code.
|
|
@@ -3,7 +3,7 @@ name: brainstorm
|
|
|
3
3
|
description: "Creative ideation and solution exploration. Generates multiple approaches with trade-offs, uses structured frameworks (SCAMPER, First Principles), and hands off to plan for structuring."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.8.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: opus
|
|
9
9
|
group: creation
|
|
@@ -57,6 +57,7 @@ Activated when exploring alternative *interface shapes* for a deepening candidat
|
|
|
57
57
|
3. Diversity score MUST be >= 0.4 before presenting (re-spawn once if below)
|
|
58
58
|
4. Recommendation MUST be opinionated with a concrete hedge condition — "it depends" is BLOCKED
|
|
59
59
|
5. Hybrid synthesis (Step 4.5) is opt-in when 2 designs have complementary strengths
|
|
60
|
+
6. council (Step 3.75) is opt-in per the narrow trigger — MUST NOT fire on every design-it-twice run, and its output MUST NOT replace the diversity-score mechanism, only supplement it
|
|
60
61
|
|
|
61
62
|
Full doctrine: [references/design-it-twice.md](references/design-it-twice.md).
|
|
62
63
|
|
|
@@ -110,6 +111,7 @@ Direct API call ≠ Wrapper/middleware layer ≠ Reverse engineering ≠ Browser
|
|
|
110
111
|
- `trend-scout` (L3): market context and trends for product-oriented brainstorming
|
|
111
112
|
- `problem-solver` (L3): structured reasoning frameworks (SCAMPER, First Principles, 6 Hats)
|
|
112
113
|
- `sequential-thinking` (L3): evaluating approaches with many variables
|
|
114
|
+
- `council` (L3): Step 3.75 — decorrelated judgment on which Design-It-Twice candidate is strongest, narrow trigger only (Design-It-Twice Mode)
|
|
113
115
|
|
|
114
116
|
## Called By (inbound)
|
|
115
117
|
|
|
@@ -126,6 +128,7 @@ Direct API call ≠ Wrapper/middleware layer ≠ Reverse engineering ≠ Browser
|
|
|
126
128
|
|
|
127
129
|
- `brainstorm` ↔ `plan` — bidirectional: brainstorm generates options → plan structures the chosen one, plan needs exploration → brainstorm ideates
|
|
128
130
|
- `brainstorm` → `ba` — a standalone brainstorm that picks an approach for a NEW feature with no requirements spec routes to `ba` first (WHAT before HOW). Suppressed when `ba` is the caller — that direction is `ba` → `brainstorm` and re-invoking would loop.
|
|
131
|
+
- `brainstorm` → `council` — Design-It-Twice candidates ready + high-stakes trigger fires (Step 3.75) → decorrelated judgment on the strongest candidate before Step 4 Recommend
|
|
129
132
|
|
|
130
133
|
## Reasoning Frameworks
|
|
131
134
|
|
|
@@ -300,6 +303,21 @@ diversity = 1 - mean(pairwise_jaccard(feature_vectors))
|
|
|
300
303
|
|
|
301
304
|
Emit `diversity_score` in chain_metadata.
|
|
302
305
|
|
|
306
|
+
### Step 3.75 — Decorrelated Judgment (council, Design-It-Twice Mode only)
|
|
307
|
+
|
|
308
|
+
The diversity score (Step 3.5) measures how DIFFERENT the candidate designs are — it says nothing about which one is BEST. For high-stakes interface decisions, that judgment call benefits from a second architecture's opinion, the same discipline `adversary` Step 0.6 and `review` Step 1.6 already apply.
|
|
309
|
+
|
|
310
|
+
**Trigger — call council (mode=judge) when ANY of:**
|
|
311
|
+
- N=4 was spawned (dependency category `remote-owned` or `true-external`, per Step 2.5 — the same tier that already signals higher stakes)
|
|
312
|
+
- Diversity landed in the 0.4–0.59 marginal band (Step 3.5) and a re-spawn still didn't clearly separate the candidates
|
|
313
|
+
- User explicitly asks for a second opinion on which design to pick
|
|
314
|
+
|
|
315
|
+
**Do NOT call council for**: routine Design-It-Twice runs where diversity ≥ 0.6 on an in-process/local-substitutable dependency (N=3) — this is opt-in overhead on top of the existing subagent fan-out, not a default tax on every design-it-twice run.
|
|
316
|
+
|
|
317
|
+
**Request**: `{ question: <the candidate designs' interface/usage/tradeoffs YAML + "which design is strongest given constraints X">, mode: "judge", n: 3, diversity: { prefer_model_families: true }, evidence_required: [reasoning] }`. Self-contained — council's voices never saw the subagent spawns.
|
|
318
|
+
|
|
319
|
+
**Consume**: fold `agreement.consensus_claims` into Step 4's recommendation as a `[council-verified]` signal alongside (never instead of) the diversity score. Fold `agreement.dissent` into the hedge condition, tagged `[council-dissent]`. If `decorrelation: NO_DECORRELATION`, say so plainly — do NOT describe the judgment as independent confirmation from same-family subagents; the diversity-score mechanism remains the primary signal in that case.
|
|
320
|
+
|
|
303
321
|
### Step 4 — Recommend
|
|
304
322
|
|
|
305
323
|
Select ONE approach as the recommendation. State:
|
|
@@ -419,6 +437,7 @@ If the user rejects the recommendation, return to Step 2 with adjusted constrain
|
|
|
419
437
|
9. [Design-It-Twice Mode] MUST spawn parallel subagents with one constraint pinned per agent — fake diversity (one agent producing N options) is BLOCKED
|
|
420
438
|
10. [Design-It-Twice Mode] MUST emit `diversity_score` and re-spawn (once) if below 0.4 floor
|
|
421
439
|
11. [Design-It-Twice Mode] MUST NOT produce "it depends" recommendations — pick one design with a concrete hedge condition
|
|
440
|
+
12. [Design-It-Twice Mode] MUST NOT call council on every run — narrow trigger only (N=4 remote/external dependency, marginal diversity band, or explicit user request); when invoked, council output supplements (never replaces) the diversity-score mechanism
|
|
422
441
|
|
|
423
442
|
## Output Format
|
|
424
443
|
|
|
@@ -486,6 +505,7 @@ Known failure modes for this skill. Check these before declaring done.
|
|
|
486
505
|
| [Design-It-Twice] Diversity score below 0.4 ignored | HIGH | Step 3.5 gate — re-spawn once; if still low, present with explicit "low-diversity" warning |
|
|
487
506
|
| [Design-It-Twice] "It depends" recommendation | HIGH | Step 4 — must pick one with a hedge; if genuinely tied, propose hybrid (Step 4.5) and recommend that |
|
|
488
507
|
| [Design-It-Twice] Forgetting to include CONTEXT.md domain terms in subagent prompt | MEDIUM | Step 2.5 spawn template requires domain glossary be passed through |
|
|
508
|
+
| [Design-It-Twice] Reporting council output as consensus when decorrelation is NO_DECORRELATION | CRITICAL | Step 3.75 consume rule: report the decorrelation stamp plainly, never imply independent confirmation from same-family subagents |
|
|
489
509
|
|
|
490
510
|
## Done When
|
|
491
511
|
|
|
@@ -496,6 +516,7 @@ Known failure modes for this skill. Check these before declaring done.
|
|
|
496
516
|
- Constraints for plan phase listed explicitly
|
|
497
517
|
- Step 5 spec-presence gate evaluated — routed to `ba` (new feature, no spec, standalone) or `plan` (spec exists, or ba/cook was the caller, or no new behavior)
|
|
498
518
|
- Next skill (`ba` or `plan`) called with the approved approach and constraints
|
|
519
|
+
- (Design-It-Twice) If council's high-stakes trigger matched (Step 3.75): council invoked before Step 4, decorrelation stamp reported plainly, consensus/dissent folded into the recommendation alongside the diversity score
|
|
499
520
|
|
|
500
521
|
## Cost Profile
|
|
501
522
|
|
|
@@ -107,8 +107,9 @@ diversity = 1 - mean(similarity(d_i, d_j) for all i < j)
|
|
|
107
107
|
- **Locality** — where change concentrates
|
|
108
108
|
- **Seam placement** — internal vs external; ports yes/no
|
|
109
109
|
- **Common-case trivial-ness** — how much work for the default path
|
|
110
|
-
3. **
|
|
111
|
-
4. **
|
|
110
|
+
3. **Council check (SKILL.md Step 3.75)** — if the narrow high-stakes trigger fired, note `[council-verified]`/`[council-dissent]` tags inline in the comparison table or as a short addendum. If `decorrelation: NO_DECORRELATION`, say so plainly — never present it as a second opinion.
|
|
111
|
+
4. **Opinionated recommendation** — strongest design + concrete hedge condition for when to pick a different one
|
|
112
|
+
5. **Hybrid synthesis (optional Step 4.5)** — if 2 designs have complementary strengths, propose a 4th synthesized option that combines them
|
|
112
113
|
|
|
113
114
|
Skip the comparison table if N=2 (just 1 contrast, prose handles it).
|
|
114
115
|
|
|
@@ -152,4 +153,6 @@ chain_metadata:
|
|
|
152
153
|
constraints_used: [C1, C2, C4]
|
|
153
154
|
recommendation: <design-id>
|
|
154
155
|
hybrid_proposed: <true|false>
|
|
156
|
+
council_invoked: <true|false>
|
|
157
|
+
council_decorrelation: <MULTI_FAMILY|NO_DECORRELATION|null>
|
|
155
158
|
```
|
|
@@ -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 (council writes this in Step 1, OR `sentinel-env --agents` may have pre-written it during env pre-flight — same council-owned schema, council's Step 1.1 cache-reuse path picks up whichever wrote first)
|
|
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
|
|
@@ -196,7 +196,7 @@ xl: 1280px — desktop
|
|
|
196
196
|
| Trigger | Animation | Duration | Easing |
|
|
197
197
|
|---------|-----------|----------|--------|
|
|
198
198
|
| Hover on card | scale(1.02) + shadow lift | 200ms | ease-out |
|
|
199
|
-
| Button click | scale(0.97) | 100ms | ease-
|
|
199
|
+
| Button click | scale(0.97) | 100ms | ease-out |
|
|
200
200
|
| Page transition | fade + slide 8px | 200ms | ease |
|
|
201
201
|
| Data loading | Skeleton shimmer | loop | linear |
|
|
202
202
|
| Toast notification | slide-in from edge | 300ms | cubic-bezier(0.34, 1.56, 0.64, 1) |
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Motion Craft Reference
|
|
2
|
+
|
|
3
|
+
The motion authority for Rune's UI mesh. `design`, `cook`, `review`, and `perf` cite these exact values instead of approximating. The premise: in a world where every product works, motion is a differentiator — but **most interfaces are over-animated, not under-animated**. Default to less. Every value below is a fact you can defend, not a preference.
|
|
4
|
+
|
|
5
|
+
Load this when generating, reviewing, or auditing any animation/motion code.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Should it animate at all?
|
|
10
|
+
|
|
11
|
+
Decide before writing a single line. The gate is **frequency** — how often a user sees the motion.
|
|
12
|
+
|
|
13
|
+
| Frequency | Decision |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| 100+ times/day (keyboard shortcuts, command palette toggle, core nav) | **No animation. Ever.** |
|
|
16
|
+
| Tens/day (hover effects, list navigation, frequent toggles) | Remove or drastically reduce |
|
|
17
|
+
| Occasional (modals, drawers, toasts, settings) | Standard animation |
|
|
18
|
+
| Rare / first-time (onboarding, empty states, success, celebration) | Delight is welcome here |
|
|
19
|
+
|
|
20
|
+
**Never animate keyboard-initiated actions.** They repeat hundreds of times a day; motion makes them feel slow, delayed, and disconnected from the input. A command palette with zero open/close animation is the *correct* experience, not a missing feature.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Purpose — why does this animate?
|
|
25
|
+
|
|
26
|
+
Every animation must answer this in one word. If it can't, delete it.
|
|
27
|
+
|
|
28
|
+
- **Feedback** — confirming the interface heard the user (press scale, hold-to-confirm fill)
|
|
29
|
+
- **Spatial consistency** — showing where something came from or went (a toast enters and exits the same edge; a panel grows from its trigger)
|
|
30
|
+
- **State indication** — making a state change legible (morphing button, expanding accordion)
|
|
31
|
+
- **Preventing a jarring change** — content that would otherwise teleport, appear, or vanish with no bridge
|
|
32
|
+
- **Explanation** — motion that demonstrates how a feature works (marketing/onboarding only)
|
|
33
|
+
- **Delight** — allowed *only* at the rare/first-time frequency tier
|
|
34
|
+
|
|
35
|
+
"It looks cool" is not on this list. On a frequently-seen element, it's a regression.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Easing
|
|
40
|
+
|
|
41
|
+
Decision order:
|
|
42
|
+
|
|
43
|
+
- Entering or exiting → **`ease-out`** (starts fast, feels responsive)
|
|
44
|
+
- Moving / morphing on screen → **`ease-in-out`**
|
|
45
|
+
- Hover / color change → **`ease`**
|
|
46
|
+
- Constant motion (marquee, progress, spinner) → **`linear`**
|
|
47
|
+
- Default → **`ease-out`**
|
|
48
|
+
|
|
49
|
+
**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching most closely. `ease-out` at 200ms *feels* faster than `ease-in` at 200ms.
|
|
50
|
+
|
|
51
|
+
Built-in CSS easings are too weak — they lack the punch that reads as intentional. Use strong custom curves:
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
--ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1); /* punchy ease-out for deliberate UI interactions */
|
|
55
|
+
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */
|
|
56
|
+
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer/sheet curve */
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
These are the *punchy* variants for deliberate, high-attention motion. The baseline `--ease-out: cubic-bezier(0.16, 1, 0.3, 1)` in `DESIGN-REFERENCE.md` remains the general-purpose default; reach for `--ease-out-strong` when an interaction needs extra snap. Don't hand-roll curves from scratch — start from a known strong variant and tune.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 4. Duration
|
|
64
|
+
|
|
65
|
+
| Element | Duration |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| Button press feedback | 100–160ms |
|
|
68
|
+
| Tooltips, small popovers | 125–200ms |
|
|
69
|
+
| Dropdowns, selects | 150–250ms |
|
|
70
|
+
| Modals, drawers | 200–500ms |
|
|
71
|
+
| Marketing / explanatory | Can be longer |
|
|
72
|
+
|
|
73
|
+
**Rule: UI animations stay under 300ms — with one exception: modals/drawers, whose larger surface area justifies 200–500ms.** A 180ms dropdown feels more responsive than a 400ms one. Perceived performance is real: a faster spinner makes load *feel* faster at identical actual time; instant tooltips after the first (skip delay + animation) make a whole toolbar feel faster.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 5. Physicality
|
|
78
|
+
|
|
79
|
+
- **Never `scale(0)`.** Nothing in the real world appears from nothing. Start from `scale(0.9–0.97)` + `opacity: 0`. Even a barely-visible initial scale makes the entrance read as natural.
|
|
80
|
+
```css
|
|
81
|
+
/* Bad */ .entering { transform: scale(0); }
|
|
82
|
+
/* Good */ .entering { transform: scale(0.95); opacity: 0; }
|
|
83
|
+
```
|
|
84
|
+
- **Origin-aware popovers.** A popover/dropdown/tooltip scales in from its trigger, not its center. `transform-origin: center` is wrong for almost every trigger-anchored surface.
|
|
85
|
+
```css
|
|
86
|
+
.popover { transform-origin: var(--radix-popover-content-transform-origin); } /* Radix */
|
|
87
|
+
.popover { transform-origin: var(--transform-origin); } /* Base UI */
|
|
88
|
+
```
|
|
89
|
+
**Modals are exempt** — they aren't anchored to a trigger; keep them centered.
|
|
90
|
+
- **Press feedback.** `transform: scale(0.97)` on `:active`, `transition: transform 160ms ease-out`. Subtle (0.95–0.98). Applies to any pressable element. `scale()` scales children too (font, icons) — a feature here.
|
|
91
|
+
- **Tooltips: skip delay on subsequent hovers.** Delay the first to prevent accidental activation; once one is open, adjacent tooltips open instantly (`transition-duration: 0ms`). Faster feel without losing the guard.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 6. Interruptibility
|
|
96
|
+
|
|
97
|
+
CSS **transitions** can be interrupted and retargeted mid-flight; **keyframes** restart from zero. For anything triggered rapidly (toasts being added, toggles) or gesture-driven, transitions (or springs) are smoother.
|
|
98
|
+
|
|
99
|
+
```css
|
|
100
|
+
/* Interruptible — good for dynamic UI */
|
|
101
|
+
.toast { transition: transform 400ms ease; }
|
|
102
|
+
|
|
103
|
+
/* Not interruptible — avoid for dynamic UI */
|
|
104
|
+
@keyframes slideIn { from { transform: translateY(100%); } to { transform: translateY(0); } }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Animate entry without JS using `@starting-style`:
|
|
108
|
+
|
|
109
|
+
```css
|
|
110
|
+
.toast {
|
|
111
|
+
opacity: 1; transform: translateY(0);
|
|
112
|
+
transition: opacity 400ms ease, transform 400ms ease;
|
|
113
|
+
@starting-style { opacity: 0; transform: translateY(100%); }
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Legacy fallback where browser support is short: `useEffect(() => setMounted(true), [])` + a `data-mounted` attribute.
|
|
118
|
+
|
|
119
|
+
`translate` percentages are relative to the element's own size — `translateY(100%)` moves by the element's height regardless of dimensions. Prefer over hardcoded px.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 7. Springs (physics-based motion)
|
|
124
|
+
|
|
125
|
+
Springs feel natural because they simulate physics and have no fixed duration — they settle on parameters. Reach for them for anything a user can touch: drag with momentum, "alive" elements, interruptible gestures, decorative mouse-tracking.
|
|
126
|
+
|
|
127
|
+
Think in two designer-friendly parameters, not the raw physics triplet:
|
|
128
|
+
|
|
129
|
+
- **Damping ratio** — controls overshoot. `1.0` = critically damped, smooth settle, no bounce. `< 1.0` overshoots and oscillates. Lower = bouncier.
|
|
130
|
+
- **Response** — how quickly the value reaches target, in seconds. Lower = snappier. This is *not* "duration."
|
|
131
|
+
|
|
132
|
+
**Defaults:**
|
|
133
|
+
- Start most UI at **damping `1.0`** — graceful and non-distracting.
|
|
134
|
+
- Add bounce (**damping ~`0.8`**) **only when the gesture itself carried momentum** (a flick, a throw, a drag release). Overshoot on a menu that just faded in feels wrong; overshoot on a card you flicked feels right. Keep bounce subtle (0.1–0.3).
|
|
135
|
+
|
|
136
|
+
**Concrete values:**
|
|
137
|
+
|
|
138
|
+
| Interaction | Damping | Response |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| Move / reposition | `1.0` | `0.4` |
|
|
141
|
+
| Rotation | `0.8` | `0.4` |
|
|
142
|
+
| Drawer / sheet | `0.8` | `0.3` |
|
|
143
|
+
|
|
144
|
+
Web mapping (Motion / Framer Motion) — `bounce` + `duration` maps closely to damping + response:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { animate } from 'motion';
|
|
148
|
+
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 }); // critically damped default
|
|
149
|
+
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 }); // momentum → slight bounce
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For mouse-tracking, interpolate with `useSpring` rather than tying the value directly to pointer position (direct = artificial, no momentum) — and only when the motion is decorative.
|
|
153
|
+
|
|
154
|
+
Springs **maintain velocity when interrupted** — keyframes restart from zero — so they're ideal for gestures a user may reverse mid-motion.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 8. Fluid gesture interactions
|
|
159
|
+
|
|
160
|
+
The through-line of a fluid interface: motion starts from the current on-screen value, inherits the user's velocity, projects momentum forward, and can be grabbed and reversed at any instant.
|
|
161
|
+
|
|
162
|
+
- **Respond on pointer-down, not release.** Highlight the instant it's pressed; waiting for `click` feels dead. Feedback must be continuous *during* the gesture, not only at the end.
|
|
163
|
+
- **1:1 tracking** — the dragged element stays glued to the finger and respects the grab offset (don't snap to center on grab). Use Pointer Events + `setPointerCapture` so tracking continues when the pointer leaves bounds.
|
|
164
|
+
- **Velocity handoff** — when the gesture ends, the animation continues at the finger's exact release velocity so there's no seam between drag and animate. Some spring APIs want relative velocity: `gestureVelocity / (target − current)`.
|
|
165
|
+
- **Momentum projection** — don't snap to the nearest boundary from the release point; project the resting position from velocity (like scroll deceleration), then snap to the target nearest that projection:
|
|
166
|
+
```js
|
|
167
|
+
function project(v /* px/s */, decel = 0.998) { return (v / 1000) * decel / (1 - decel); }
|
|
168
|
+
const target = nearestSnapPoint(currentPosition + project(releaseVelocity));
|
|
169
|
+
```
|
|
170
|
+
- **Momentum dismissal** — don't require crossing a distance threshold; compute velocity (`Math.abs(distance) / elapsedMs`) and dismiss if `> ~0.11`. A flick should be enough.
|
|
171
|
+
- **Rubber-banding** — at a boundary, resist progressively instead of stopping hard (a hard stop reads as "frozen"):
|
|
172
|
+
```js
|
|
173
|
+
function rubberband(overshoot, dim, c = 0.55) { return (overshoot * dim * c) / (dim + c * Math.abs(overshoot)); }
|
|
174
|
+
```
|
|
175
|
+
- **Multi-touch protection** — ignore extra touch points once a drag begins (`if (isDragging) return`) to prevent jumps.
|
|
176
|
+
- **Decompose 2D motion into independent X and Y springs** — a single spring on a 2D distance desyncs when the axes have different velocities.
|
|
177
|
+
- **Interrupt from the presentation value** — on interrupt, read the element's live on-screen transform and animate from there; starting from the logical/target value causes a visible jump.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 9. Motion performance
|
|
182
|
+
|
|
183
|
+
Full detail lives in `perf`; the rules that gate motion:
|
|
184
|
+
|
|
185
|
+
- **Only animate `transform` and `opacity`** — they skip layout and paint and run on the GPU. `width`/`height`/`margin`/`padding`/`top`/`left` trigger all three rendering steps.
|
|
186
|
+
- **Avoid `transition: all`** — it animates unintended properties, often off-GPU.
|
|
187
|
+
- **Framer Motion shorthands (`x`/`y`/`scale`) are NOT hardware-accelerated** — they run on the main thread via rAF and drop frames under load. Use the full transform string: `animate={{ transform: "translateX(100px)" }}`.
|
|
188
|
+
- **Don't drive child transforms via a CSS variable on the parent** — it recalculates styles for all children. Set `transform` directly on the element.
|
|
189
|
+
- **CSS animations beat JS under load** — they run off the main thread while the browser loads/scripts/paints. Use CSS for predetermined motion, JS/springs for dynamic/interruptible.
|
|
190
|
+
- **WAAPI** gives JS control at CSS performance (hardware-accelerated, interruptible, no library).
|
|
191
|
+
- **`will-change`** hints imminent motion — use where motion is about to happen, don't leave it on permanently.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 10. Accessibility
|
|
196
|
+
|
|
197
|
+
Reduced motion means *fewer and gentler*, not zero — keep opacity/color transitions that aid comprehension, drop movement/position/parallax.
|
|
198
|
+
|
|
199
|
+
```css
|
|
200
|
+
@media (prefers-reduced-motion: reduce) {
|
|
201
|
+
.element { transition: opacity 200ms ease; transform: none !important; }
|
|
202
|
+
}
|
|
203
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
204
|
+
.toolbar { background: white; backdrop-filter: none; } /* frostier/solid translucent surfaces */
|
|
205
|
+
}
|
|
206
|
+
@media (hover: hover) and (pointer: fine) {
|
|
207
|
+
.element:hover { transform: scale(1.05); } /* gate hover motion — touch fires false hovers on tap */
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```jsx
|
|
212
|
+
const reduce = useReducedMotion();
|
|
213
|
+
const closedX = reduce ? 0 : '-100%';
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Also avoid full-viewport moving backgrounds and slow looping oscillations (~0.2 Hz); ease dark↔light theme changes rather than jumping brightness.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 11. Polish
|
|
221
|
+
|
|
222
|
+
- **Asymmetric enter/exit** — slow where the user is deciding, fast where the system responds. A hold-to-delete fills over 2s linear; the release snaps back in 200ms ease-out.
|
|
223
|
+
- **Stagger group entrances** — 30–80ms between items; longer feels slow. Decorative, so never block interaction while it plays.
|
|
224
|
+
```css
|
|
225
|
+
.item { opacity: 0; transform: translateY(8px); animation: fadeIn 300ms ease-out forwards; }
|
|
226
|
+
.item:nth-child(2) { animation-delay: 50ms; }
|
|
227
|
+
.item:nth-child(3) { animation-delay: 100ms; }
|
|
228
|
+
@keyframes fadeIn { to { opacity: 1; transform: translateY(0); } }
|
|
229
|
+
```
|
|
230
|
+
- **Blur to mask imperfect crossfades** — when two states visibly overlap despite tuning easing/duration, add `filter: blur(2px)` during the transition to blend them into one perceived transformation. Keep blur < 20px (heavy blur is expensive, especially Safari).
|
|
231
|
+
- **`clip-path: inset(t r b l)`** is a powerful animation tool — each value eats in from that side. Uses: reveal-on-scroll (`inset(0 0 100% 0)` → `inset(0 0 0 0)`), hold-to-delete overlay, seamless tab color transitions (duplicate the list, clip the active copy), comparison sliders.
|
|
232
|
+
- **Cohesion** — match motion to the component's personality and the rest of the product. A playful component can be bouncier; a dashboard stays crisp and fast. Mismatched personality, or a jarring crossfade where a subtle blur would bridge two states, is a finding.
|
|
233
|
+
- **Translucent chrome** — build nav/toolbars/sheets as translucent layers (`backdrop-filter: blur()` + semi-transparent bg) with content scrolling under, not opaque bars. Never stack a light translucent surface on another. Materialize (animate blur + scale together on enter) rather than a plain opacity fade.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 12. Feel-checking (when code alone can't decide)
|
|
238
|
+
|
|
239
|
+
Motion feel often can't be judged from source. When uncertain, recommend a feel-check instead of guessing:
|
|
240
|
+
|
|
241
|
+
- **Slow motion** — bump duration 2–5× or use DevTools animation inspector. Check colors crossfade cleanly, easing doesn't stop abruptly, `transform-origin` is right, coordinated properties stay in sync.
|
|
242
|
+
- **Frame-by-frame** — Chrome DevTools Animations panel reveals timing drift between coordinated properties.
|
|
243
|
+
- **Real devices** for gestures (drawers, swipe) — desktop emulation lies about touch feel.
|
|
244
|
+
- **Fresh eyes next day** — imperfections invisible during development surface later.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 13. Vocabulary (reverse-lookup glossary)
|
|
249
|
+
|
|
250
|
+
When a user describes a motion effect without knowing its name, map the sensation to the term. Lead with the closest match; note alternates only when two genuinely compete.
|
|
251
|
+
|
|
252
|
+
**Entrances/Exits** — Fade in/out · Slide in · Scale in · **Pop in** (slight overshoot) · **Reveal** (uncover via clip-path/mask) · Enter/Exit.
|
|
253
|
+
**Sequencing** — Keyframes · Interpolation/Tween · **Stagger** (cascade with per-item delay) · Orchestration · Delay · Duration · Fill mode · Stepped animation.
|
|
254
|
+
**Transforms** — Translate · Scale · Rotate · Skew · 3D tilt/Flip · Perspective · Transform origin · **Origin-aware** (grows from its trigger, not center).
|
|
255
|
+
**State transitions** — Crossfade · Continuity transition · **Morph** (one shape into another) · **Shared element transition** (element travels + transforms between positions) · **Layout animation** (animates to new size/pos instead of snapping) · Accordion/Collapse · Direction-aware transition.
|
|
256
|
+
**Scroll** — Scroll reveal · Scroll-driven animation · Parallax · Page transition · View transition.
|
|
257
|
+
**Feedback** — Hover effect · **Press/Tap feedback** (subtle scale-down) · **Hold to confirm** (fill while held) · Drag · Drag to reorder · Swipe to dismiss · **Rubber-banding** (resist + snap-back past a boundary) · Shake/Wiggle (error) · Ripple.
|
|
258
|
+
**Easing** — Easing · Ease-out (default for UI) · Ease-in (usually avoid) · Ease-in-out · Linear (spinners/marquees only) · Cubic-bezier · Asymmetric easing.
|
|
259
|
+
**Springs** — Spring · Stiffness/Tension · Damping · Mass · Bounce · Perceptual duration · Momentum · Velocity · Interruptible animation.
|
|
260
|
+
**Ambient** — Marquee · Loop · Alternate (yoyo) · Orbit · Pulse · Float · Idle animation.
|
|
261
|
+
**Polish** — Blur · Clip-path · Mask (soft fadeable edges) · Before/after slider · Line drawing · Text morph · Skeleton/Shimmer · **Number ticker** · **Tabular numbers** (fixed-width digits, essential for counters) · Typewriter.
|
|
262
|
+
**Performance** — Frame rate (FPS) · Jank · Dropped frame · Compositing · will-change · Layout thrashing.
|
|
263
|
+
**Principles** — Purposeful animation · Anticipation · Follow-through · Squash & stretch · Perceived performance · Frequency of use · Spatial consistency · Hardware acceleration · Reduced motion.
|
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.8.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: creation
|
|
@@ -57,6 +57,9 @@ Load the design knowledge base before reasoning:
|
|
|
57
57
|
2. If no user override → `Read` the baseline: `skills/design/DESIGN-REFERENCE.md` (shipped with Rune)
|
|
58
58
|
3. The loaded reference provides: font pairings, chart selection, component architecture, color principles, UX checklist, interaction patterns, anti-pattern signatures
|
|
59
59
|
4. Apply reference knowledge throughout Steps 3-5 (domain reasoning, token generation, checklist)
|
|
60
|
+
5. **When the domain involves any UI motion** (transitions, animations, gestures, springs, micro-interactions) → also `Read` `skills/design/MOTION-CRAFT.md`. It is the canonical motion authority: the should-it-animate frequency gate, easing/duration budgets, physicality (never `scale(0)`, origin-aware popovers), spring physics, gesture handoff, motion performance, reduced-motion, and a reverse-lookup vocabulary. Cite its exact values in the design-system motion section; never approximate curves or durations. **When MOTION-CRAFT.md and DESIGN-REFERENCE.md disagree on a motion detail (easing, duration, interaction timing), MOTION-CRAFT.md wins** — it is the deeper, motion-specific source; DESIGN-REFERENCE.md's Animation Timing / Interaction Patterns tables are the general baseline.
|
|
61
|
+
|
|
62
|
+
> **Advisory motion-audit mode**: When asked to "improve the animations" / "make this feel better" / "what could be animated here", operate read-only — survey the motion surface against MOTION-CRAFT.md, report frequency-gated findings (and deliberately rejected candidates), and hand precise recipes to `cook`/`fix`. Suggesting motion everywhere is worse than useless; expect to reject most candidates.
|
|
60
63
|
|
|
61
64
|
> **Why two layers**: The baseline ships "good enough" universal design knowledge. Users who care about aesthetics create their own `design-dna.md` with curated palettes, font pairings, and style preferences. The design skill works well with either — it just works _better_ with a curated reference.
|
|
62
65
|
|
package/skills/perf/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: perf
|
|
|
3
3
|
description: "Performance regression gate. Detects N+1 queries, sync-in-async, missing indexes, memory leaks, and bundle bloat before they reach production. Ranks findings by Cost Impact Hierarchy (architecture > data transfer > compute > DB > caching) so fix priority maps to actual unit-cost reduction."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.6.0"
|
|
7
7
|
layer: L2
|
|
8
8
|
model: sonnet
|
|
9
9
|
group: quality
|
|
@@ -212,6 +212,16 @@ Do NOT downgrade this gate.
|
|
|
212
212
|
If no URL available (dev-only environment): log `INFO: no URL for Lighthouse — run manually before deploy`
|
|
213
213
|
If Lighthouse MCP not installed: log `INFO: Lighthouse MCP not available — run lighthouse [url] --output json manually`
|
|
214
214
|
|
|
215
|
+
### Step 5.5 — Motion / Animation Performance (frontend only)
|
|
216
|
+
|
|
217
|
+
Apply only when the diff touches motion. Strong signals: `@keyframes`, `motion.`, `animate={`, `useSpring`, `will-change`, `transition:` / `transition-`. Weak signals: bare `transition` / `animation` / `transform` — these also match non-motion code and prose (state-machine "transition", `AnimationController`, static layout transforms), so treat them as a trigger only when they co-occur with a strong signal (mirrors `review`'s Motion Craft Checks). These are frame-budget regressions — a dropped frame is a 16.7ms miss at 60fps. Findings are **WARN** by default (jank degrades feel, rarely blocks a merge); escalate to **BLOCK** only when motion runs on a hot, always-on path (scroll handler, drag loop, list with many animated rows). Rank within the Cost Impact Hierarchy under *compute* (main-thread work) — note this is client-side compute: the cost is UX/conversion degradation and engineering-hour ROI, not a cloud-billing multiplier like Tier 3's infra examples. For remediation values and the full rationale, point to `skills/design/MOTION-CRAFT.md §9`.
|
|
218
|
+
|
|
219
|
+
- **Non-GPU property animation** — animating `width`/`height`/`margin`/`padding`/`top`/`left` (or `transition: all`) triggers layout + paint every frame. Finding: `MOTION_LAYOUT_THRASH — [file:line] — animating [prop] forces layout/paint per frame — use transform/opacity`
|
|
220
|
+
- **Framer Motion shorthands under load** — `x`/`y`/`scale` props run on the main thread via rAF and drop frames while the page is busy. Finding: `MOTION_NOT_HW_ACCEL — [file:line] — FM x/y/scale shorthand — use full transform string for GPU compositing`
|
|
221
|
+
- **CSS-variable-driven child transforms** — updating a variable on a parent to drive children recalculates styles for every child (recalc storm). Finding: `MOTION_RECALC_STORM — [file:line] — parent CSS var drives child transform — set transform on the element directly`
|
|
222
|
+
- **`will-change` misuse** — missing on imminent, frequently-triggered motion, or left on permanently (permanent layer promotion wastes GPU memory). Finding: `MOTION_WILL_CHANGE — [file:line] — [missing on hot path | left on permanently]`
|
|
223
|
+
- **Keyframes on rapidly-triggered / interruptible motion** — restart-from-zero causes visible jank under rapid retriggering; CSS transitions or springs retarget smoothly. Finding: `MOTION_NON_INTERRUPTIBLE — [file:line] — keyframes on rapidly-triggered element`
|
|
224
|
+
|
|
215
225
|
### Step 6 — Framework-Specific Checks
|
|
216
226
|
|
|
217
227
|
**React:**
|
|
@@ -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.5.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.
|
|
@@ -532,6 +559,29 @@ className="focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
|
|
|
532
559
|
- iOS target: solid-background cards (iOS 26 Liquid Glass deprecates this visual language) — should use translucent/blur surfaces
|
|
533
560
|
- Android target: hardcoded hex colors instead of `MaterialTheme.colorScheme` tokens → not adaptive to dynamic color
|
|
534
561
|
|
|
562
|
+
## Motion Craft Checks
|
|
563
|
+
|
|
564
|
+
Apply **only** when the diff touches motion. Strong signals: `@keyframes`, `motion.`, `animate={`, `useSpring`, `cubic-bezier`, `@starting-style`, `transition:` / `transition-`. Weak signals: bare `transform` or `ease-` — these also match static layout transforms (`translate(-50%,-50%)`) and non-motion identifiers, so treat them as a trigger only when they co-occur with a strong signal. Skip entirely for diffs with no motion code.
|
|
565
|
+
|
|
566
|
+
These are **advisory** — default severity MEDIUM. Escalate to HIGH only for *feel-breaking* regressions (the first five triggers below). Motion is a taste call: when feel can't be judged from source, say so and recommend a slow-motion / frame-by-frame check rather than asserting a defect. **For every finding, cite the exact remediation value from `skills/design/MOTION-CRAFT.md`** — do not restate its tables here.
|
|
567
|
+
|
|
568
|
+
**Escalation triggers — flag on sight (first five = HIGH, feel-breaking):**
|
|
569
|
+
- `ease-in` on any UI interaction (delays the moment the user watches most)
|
|
570
|
+
- `scale(0)` or pure-fade entrances with no initial transform (nothing appears from nothing → initial scale + opacity per MOTION-CRAFT §5)
|
|
571
|
+
- Animation on a keyboard shortcut / command-palette toggle / 100+/day action (should have none)
|
|
572
|
+
- Animating layout properties (`width`/`height`/`margin`/`padding`/`top`/`left`) instead of `transform`/`opacity` (off-GPU, drops frames)
|
|
573
|
+
- UI animation duration over the budget with no stated reason (duration budget + modals/drawers exemption per MOTION-CRAFT §4)
|
|
574
|
+
- `transition: all` — unbounded property animation (MEDIUM)
|
|
575
|
+
- `transform-origin: center` on a trigger-anchored popover/dropdown/tooltip (should scale from trigger; modals exempt) (MEDIUM)
|
|
576
|
+
- Keyframes on toasts/toggles/anything triggered rapidly (can't retarget → use CSS transitions or springs) (MEDIUM)
|
|
577
|
+
- Framer Motion `x`/`y`/`scale` shorthands on motion that runs while the page is busy (not hardware-accelerated → full `transform` string) (MEDIUM)
|
|
578
|
+
- Driving a child transform via a CSS variable on the parent (style-recalc storm) (MEDIUM)
|
|
579
|
+
- Missing `prefers-reduced-motion` handling on movement, or ungated `:hover` motion (missing `@media (hover: hover) and (pointer: fine)`) (MEDIUM)
|
|
580
|
+
- Symmetric enter/exit timing on a press-and-release or hold interaction (deliberate phase should be slower, response snappier) (LOW–MEDIUM)
|
|
581
|
+
- Everything-at-once entrance where a stagger belongs (stagger interval per MOTION-CRAFT §11) (LOW)
|
|
582
|
+
|
|
583
|
+
**Remedial preference (propose the earliest that applies):** delete the animation (high-frequency / no purpose) → reduce it → fix the easing → fix origin/physicality → make it interruptible → move it to the GPU → asymmetric timing → polish (blur/stagger/`@starting-style`/spring). Pull the exact curve, duration, or spring config from MOTION-CRAFT.md. Findings feed the existing Report + Fix-First triage — do not create a parallel output path.
|
|
584
|
+
|
|
535
585
|
## Weighted Composite Scoring
|
|
536
586
|
|
|
537
587
|
When a review is part of a recurring quality-gate cycle (e.g., sprint review, pre-release gate), produce a **composite quality score** alongside the findings list. This makes review output numeric and comparable across runs.
|
|
@@ -577,6 +627,7 @@ LOW — style inconsistency, naming suggestion, minor refactor opportunity
|
|
|
577
627
|
- **Files Reviewed**: [count]
|
|
578
628
|
- **Findings**: [count by severity]
|
|
579
629
|
- **Review Commit**: [git hash at time of review]
|
|
630
|
+
- **Council**: [not invoked | MULTI_FAMILY (N families) | NO_DECORRELATION — same-family subagents only]
|
|
580
631
|
- **Overall**: APPROVE | REQUEST CHANGES | NEEDS DISCUSSION
|
|
581
632
|
|
|
582
633
|
### Spec Compliance
|
|
@@ -639,7 +690,7 @@ Append to Code Review Report when invoked standalone. Suppress when called as su
|
|
|
639
690
|
```yaml
|
|
640
691
|
chain_metadata:
|
|
641
692
|
skill: "rune:review"
|
|
642
|
-
version: "1.
|
|
693
|
+
version: "1.5.0"
|
|
643
694
|
status: "[DONE | DONE_WITH_CONCERNS]"
|
|
644
695
|
domain: "[area reviewed]"
|
|
645
696
|
files_changed: [] # review doesn't change files
|
|
@@ -672,6 +723,8 @@ chain_metadata:
|
|
|
672
723
|
| 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" |
|
|
673
724
|
| 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 |
|
|
674
725
|
| Scope drift flagged on intentional refactoring | LOW | Scope drift is informational, not blocking. User can override with "intentional" — don't re-flag after override |
|
|
726
|
+
| (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 |
|
|
727
|
+
| (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 |
|
|
675
728
|
|
|
676
729
|
## Done When
|
|
677
730
|
|
|
@@ -681,7 +734,8 @@ chain_metadata:
|
|
|
681
734
|
- Test coverage gaps identified and documented
|
|
682
735
|
- UI anti-pattern checks ran for any frontend files in diff (or confirmed not applicable)
|
|
683
736
|
- Structured report emitted with APPROVE / REQUEST CHANGES / NEEDS DISCUSSION verdict
|
|
737
|
+
- (council) If blast radius 50+ with HIGH severity: council invoked on the diff, decorrelation stamp reported plainly, consensus/dissent folded into findings
|
|
684
738
|
|
|
685
739
|
## Cost Profile
|
|
686
740
|
|
|
687
|
-
~3000-6000 tokens input, ~1000-2000 tokens output. Sonnet default, opus for security-critical reviews. Runs once per implementation cycle.
|
|
741
|
+
~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.
|
|
@@ -3,11 +3,11 @@ name: sentinel-env
|
|
|
3
3
|
description: "Environment-aware pre-flight check. Use when starting work in a new environment, switching machines, or when 'works on my machine' bugs surface. Validates OS, runtime versions, installed tools, port availability, env vars, and disk space BEFORE coding starts. Like sentinel but for the environment, not the code."
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.5.0"
|
|
7
7
|
layer: L3
|
|
8
8
|
model: haiku
|
|
9
9
|
group: validation
|
|
10
|
-
tools: "Bash, Read, Glob, Grep"
|
|
10
|
+
tools: "Bash, Read, Write, Glob, Grep"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# sentinel-env
|
|
@@ -28,7 +28,7 @@ This is the environment counterpart to `sentinel` (which checks code security) a
|
|
|
28
28
|
|
|
29
29
|
## Calls (outbound)
|
|
30
30
|
|
|
31
|
-
None — sentinel-env
|
|
31
|
+
None — sentinel-env never calls another Rune skill and never modifies the developer's machine/environment (no install/update). In `--agents` mode it writes ONE Rune-owned advisory cache file, `.rune/runtimes.json` (a Rune state file, not the dev's environment — same category as the files `journal`/`session-bridge`/`council` write).
|
|
32
32
|
|
|
33
33
|
## Called By (inbound)
|
|
34
34
|
|
|
@@ -36,6 +36,13 @@ None — sentinel-env is a pure read-only utility. It checks and reports, never
|
|
|
36
36
|
- `scaffold` (L1): post-bootstrap environment validation
|
|
37
37
|
- `onboard` (L2): developer onboarding verification
|
|
38
38
|
- User: `/rune env-check` direct invocation
|
|
39
|
+
- User: `/rune env-check --agents` — also run AI-Agent Fleet Detection (Step 6.6)
|
|
40
|
+
|
|
41
|
+
## Data Flow
|
|
42
|
+
|
|
43
|
+
### Feeds Into →
|
|
44
|
+
|
|
45
|
+
- `council` (L3): `.rune/runtimes.json` — when `--agents` mode ran during env pre-flight, it PRE-WARMS council's Step 1 DETECT cache (council reuses a same-session cache instead of re-probing the bridge). Schema + model-family map are OWNED by `skills/council/references/dispatch-protocol.md` §Detect — sentinel-env writes that exact shape, it does not define its own.
|
|
39
46
|
|
|
40
47
|
## Execution
|
|
41
48
|
|
|
@@ -135,6 +142,7 @@ Detect and verify tools the project depends on:
|
|
|
135
142
|
|
|
136
143
|
**Verdict:**
|
|
137
144
|
- Tool found via any tier → PASS (log which tier + version)
|
|
145
|
+
- Tool resolves to DIFFERENT paths across tiers (e.g. a `~/.local/bin` copy AND a PATH copy, or two PATH entries) → **WARN [ENV-AMBIG]**: `'<tool>' resolves to N paths — the shell picks <selected>, but <other> also exists`. This is the "wrong binary runs" class of "works on my machine" (stale/shadowed installs, two Pythons, two npms). Don't stop at the first tier hit for the ambiguity check — after a PASS, cheaply confirm PATH (`where`/`which -a`) doesn't disagree with the winning tier. List all paths + versions so the developer can prune the shadow.
|
|
138
146
|
- Tool NOT found → **BLOCK** with per-OS install guidance:
|
|
139
147
|
```
|
|
140
148
|
[ENV-XXX] Required tool '<tool>' not found (9-tier lookup exhausted)
|
|
@@ -193,6 +201,18 @@ Quick system health check:
|
|
|
193
201
|
- **macOS**: Check Xcode CLI tools if native modules detected (`node-gyp` in dependencies)
|
|
194
202
|
- **Linux**: Check file watcher limit if large project (`fs.inotify.max_user_watches`)
|
|
195
203
|
|
|
204
|
+
### Step 6.6: AI-Agent Fleet Detection (opt-in — `--agents` flag, or when called by `council`)
|
|
205
|
+
|
|
206
|
+
Skip this step unless `--agents` was passed or `council` requested it. It answers ONE question: *"how many distinct AI model families can this machine reach for a decorrelated council run?"* — and pre-warms council's cache so its first invocation skips a redundant probe.
|
|
207
|
+
|
|
208
|
+
**Schema and model-family map are OWNED by `skills/council/references/dispatch-protocol.md` §Detect. Do NOT redefine them here — read that file and write its exact shape.** This keeps a single source of truth; if that reference changes, this step follows it.
|
|
209
|
+
|
|
210
|
+
1. **Resolve the bridge**: look for `1devtool-agent` on PATH, then the per-user location for the current OS (`~/.1devtool/bin/1devtool-agent` POSIX, `%USERPROFILE%\.1devtool\bin\1devtool-agent.cmd` Windows). Resolve relative to the current user's home — never hardcode a specific user's path.
|
|
211
|
+
2. **Bridge found** → `1devtool-agent list --json`; for each `status: detected` entry map its id → `model_family` per the council reference table (claude→anthropic, codex→openai, gemini/agy→google, grok→xai, qwen→alibaba, wrapper CLIs cline/amp/opencode/aider→`unknown`). Reading `~/.1devtool/state/cli-registry.json` directly is an acceptable faster path (it is the pre-scanned manifest `list` returns).
|
|
212
|
+
3. **Bridge NOT found** → `detected: []`, `bridge_path: null`. This is normal, not an error. Optionally 9-tier detect raw AI-agent binaries for the human report only ("5 AI CLIs installed but no 1devtool bridge → council will run subagent-only"), but do NOT put unbridged binaries in `detected[]` — council can't dispatch to them.
|
|
213
|
+
4. **Write** `.rune/runtimes.json` in the council-owned schema (`checked_at` session marker, `bridge_path`, `detected[]`). Only bridge-reachable `status: detected` entries belong in `detected[]`.
|
|
214
|
+
5. **Count distinct CONFIRMED families** (exclude `unknown` wrappers) → report `MULTI_FAMILY-capable` (≥2) or `NO_DECORRELATION-only` (<2). **Honesty (inherit council's caveat): "confirmed" = the CLI is vendor-dedicated by product design, NOT that a given response was verified to come from that vendor's backend** (BYOK / base-URL / gateway override can collapse families invisibly). Report the count as advisory availability, never as a decorrelation guarantee — that call is council's at run time.
|
|
215
|
+
|
|
196
216
|
### Step 7: Report
|
|
197
217
|
|
|
198
218
|
Produce a structured environment report:
|
|
@@ -227,11 +247,17 @@ For each finding, include a specific remediation command the developer can copy-
|
|
|
227
247
|
- [ENV-005] Git 2.44.0 ✓
|
|
228
248
|
- [ENV-006] Docker 25.0.3 ✓
|
|
229
249
|
- [ENV-007] Disk space: 42 GB available ✓
|
|
250
|
+
|
|
251
|
+
### AI AGENTS (only when --agents / council mode ran)
|
|
252
|
+
- Bridge: 1devtool-agent ✓ (or: not found → council runs subagent-only)
|
|
253
|
+
- Reachable: codex (openai), grok (xai), gemini (google), agy (google) — 3 distinct confirmed families
|
|
254
|
+
- council capability: MULTI_FAMILY-capable (advisory — not a decorrelation guarantee; backend override can collapse families)
|
|
255
|
+
- Wrote .rune/runtimes.json (pre-warms council DETECT)
|
|
230
256
|
```
|
|
231
257
|
|
|
232
258
|
## Constraints
|
|
233
259
|
|
|
234
|
-
1. MUST
|
|
260
|
+
1. MUST NOT install, update, or modify anything on the developer's MACHINE/environment (no `apt install`, no version switches, no PATH edits). Writing the Rune-owned advisory cache `.rune/runtimes.json` in `--agents` mode is the ONE exception and is not a machine modification — it is Rune state, same as `journal`/`council` run files.
|
|
235
261
|
2. MUST NOT log environment variable VALUES — only check key presence (security)
|
|
236
262
|
3. MUST provide copy-paste remediation commands for every BLOCK and WARN finding
|
|
237
263
|
4. MUST handle cross-platform differences (Windows/macOS/Linux) gracefully
|
|
@@ -249,6 +275,9 @@ For each finding, include a specific remediation command the developer can copy-
|
|
|
249
275
|
| Over-checking — flagging optional tools as required | MEDIUM | Only check tools evidenced by config files, not speculative |
|
|
250
276
|
| Missing hard dependency — project wraps external CLI but tool not checked | HIGH | Step 3.6: scan for `shutil.which`, `subprocess.run`, `child_process.exec` → verify tool exists on PATH |
|
|
251
277
|
| Hard dep found but wrong version — tool exists but API changed | MEDIUM | Log version for manual review. Version compatibility is project-specific — don't guess |
|
|
278
|
+
| `--agents` writes `.rune/runtimes.json` with a schema that drifts from council's | HIGH | Schema + family map are OWNED by `skills/council/references/dispatch-protocol.md` §Detect — sentinel-env writes that shape, never its own; if the reference changes, this step follows it |
|
|
279
|
+
| Reporting an installed-but-unbridged AI CLI as council-reachable | HIGH | Step 6.6.3: only bridge-reachable `status: detected` entries go in `detected[]`; unbridged binaries are report-only text, never dispatchable state |
|
|
280
|
+
| Claiming a distinct-family count as a decorrelation guarantee | MEDIUM | Step 6.6.5: report as advisory availability only; "confirmed" ≠ backend-verified (BYOK/gateway override) — the real decorrelation call is council's at run time |
|
|
252
281
|
|
|
253
282
|
## Done When
|
|
254
283
|
|
|
@@ -259,6 +288,7 @@ For each finding, include a specific remediation command the developer can copy-
|
|
|
259
288
|
- Disk space verified adequate
|
|
260
289
|
- Structured report with READY / READY WITH WARNINGS / BLOCKED verdict
|
|
261
290
|
- Every BLOCK/WARN finding has a copy-paste remediation command
|
|
291
|
+
- In `--agents` mode only: `.rune/runtimes.json` written in council's canonical schema and a distinct-family count reported (advisory, not a decorrelation guarantee)
|
|
262
292
|
|
|
263
293
|
## Cost Profile
|
|
264
294
|
|