@rune-kit/rune 2.25.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 +5 -1
- package/package.json +1 -1
- package/skills/council/SKILL.md +1 -1
- 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/review/SKILL.md +25 -2
- package/skills/sentinel-env/SKILL.md +34 -4
package/README.md
CHANGED
|
@@ -103,7 +103,11 @@ 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)
|
|
107
111
|
|
|
108
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.
|
|
109
113
|
|
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": {
|
package/skills/council/SKILL.md
CHANGED
|
@@ -58,7 +58,7 @@ None at the skill level — council dispatches via `Bash` (external CLI bridge)
|
|
|
58
58
|
### Fed By ←
|
|
59
59
|
|
|
60
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
|
|
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
62
|
|
|
63
63
|
## Workflow
|
|
64
64
|
|
|
@@ -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:**
|
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
|
|
@@ -559,6 +559,29 @@ className="focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
|
|
|
559
559
|
- iOS target: solid-background cards (iOS 26 Liquid Glass deprecates this visual language) — should use translucent/blur surfaces
|
|
560
560
|
- Android target: hardcoded hex colors instead of `MaterialTheme.colorScheme` tokens → not adaptive to dynamic color
|
|
561
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
|
+
|
|
562
585
|
## Weighted Composite Scoring
|
|
563
586
|
|
|
564
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.
|
|
@@ -667,7 +690,7 @@ Append to Code Review Report when invoked standalone. Suppress when called as su
|
|
|
667
690
|
```yaml
|
|
668
691
|
chain_metadata:
|
|
669
692
|
skill: "rune:review"
|
|
670
|
-
version: "1.
|
|
693
|
+
version: "1.5.0"
|
|
671
694
|
status: "[DONE | DONE_WITH_CONCERNS]"
|
|
672
695
|
domain: "[area reviewed]"
|
|
673
696
|
files_changed: [] # review doesn't change files
|
|
@@ -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
|
|