@rune-kit/rune 2.8.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +68 -34
  3. package/agents/adversary.md +27 -0
  4. package/agents/architect.md +19 -29
  5. package/agents/asset-creator.md +18 -4
  6. package/agents/audit.md +25 -4
  7. package/agents/autopsy.md +19 -4
  8. package/agents/ba.md +35 -0
  9. package/agents/brainstorm.md +31 -4
  10. package/agents/browser-pilot.md +21 -4
  11. package/agents/coder.md +21 -29
  12. package/agents/completion-gate.md +20 -4
  13. package/agents/constraint-check.md +18 -4
  14. package/agents/context-engine.md +22 -4
  15. package/agents/context-pack.md +32 -0
  16. package/agents/cook.md +41 -4
  17. package/agents/db.md +19 -4
  18. package/agents/debug.md +33 -4
  19. package/agents/dependency-doctor.md +20 -4
  20. package/agents/deploy.md +27 -4
  21. package/agents/design.md +22 -4
  22. package/agents/doc-processor.md +27 -0
  23. package/agents/docs-seeker.md +19 -4
  24. package/agents/docs.md +31 -0
  25. package/agents/fix.md +37 -4
  26. package/agents/git.md +29 -0
  27. package/agents/hallucination-guard.md +20 -4
  28. package/agents/incident.md +21 -4
  29. package/agents/integrity-check.md +18 -4
  30. package/agents/journal.md +19 -4
  31. package/agents/launch.md +32 -4
  32. package/agents/logic-guardian.md +26 -11
  33. package/agents/marketing.md +23 -4
  34. package/agents/mcp-builder.md +26 -0
  35. package/agents/neural-memory.md +30 -0
  36. package/agents/onboard.md +22 -4
  37. package/agents/perf.md +21 -4
  38. package/agents/plan.md +29 -4
  39. package/agents/preflight.md +22 -4
  40. package/agents/problem-solver.md +20 -4
  41. package/agents/rescue.md +23 -4
  42. package/agents/research.md +19 -4
  43. package/agents/researcher.md +19 -29
  44. package/agents/retro.md +32 -0
  45. package/agents/review-intake.md +20 -4
  46. package/agents/review.md +32 -4
  47. package/agents/reviewer.md +20 -28
  48. package/agents/safeguard.md +19 -4
  49. package/agents/sast.md +18 -4
  50. package/agents/scaffold.md +41 -0
  51. package/agents/scanner.md +19 -28
  52. package/agents/scope-guard.md +18 -4
  53. package/agents/scout.md +23 -4
  54. package/agents/sentinel-env.md +26 -0
  55. package/agents/sentinel.md +33 -4
  56. package/agents/sequential-thinking.md +20 -4
  57. package/agents/session-bridge.md +24 -4
  58. package/agents/skill-forge.md +22 -4
  59. package/agents/skill-router.md +26 -4
  60. package/agents/slides.md +24 -0
  61. package/agents/surgeon.md +19 -4
  62. package/agents/team.md +30 -4
  63. package/agents/test.md +36 -4
  64. package/agents/trend-scout.md +17 -4
  65. package/agents/verification.md +20 -4
  66. package/agents/video-creator.md +20 -4
  67. package/agents/watchdog.md +19 -4
  68. package/agents/worktree.md +17 -4
  69. package/commands/rune.md +168 -168
  70. package/compiler/__tests__/analytics.test.js +370 -0
  71. package/compiler/adapters/openclaw.js +2 -2
  72. package/compiler/analytics.js +385 -0
  73. package/compiler/bin/rune.js +68 -2
  74. package/compiler/dashboard.js +883 -0
  75. package/compiler/transforms/branding.js +1 -1
  76. package/contexts/dev.md +34 -34
  77. package/contexts/research.md +43 -43
  78. package/contexts/review.md +55 -55
  79. package/extensions/ai-ml/PACK.md +88 -88
  80. package/extensions/ai-ml/skills/ai-agents.md +172 -172
  81. package/extensions/ai-ml/skills/code-sandbox.md +187 -187
  82. package/extensions/ai-ml/skills/deep-research.md +146 -146
  83. package/extensions/ai-ml/skills/embedding-search.md +66 -66
  84. package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -74
  85. package/extensions/ai-ml/skills/llm-architect.md +125 -125
  86. package/extensions/ai-ml/skills/llm-integration.md +64 -64
  87. package/extensions/ai-ml/skills/prompt-patterns.md +72 -72
  88. package/extensions/ai-ml/skills/rag-patterns.md +66 -66
  89. package/extensions/ai-ml/skills/web-extraction.md +114 -114
  90. package/extensions/analytics/PACK.md +92 -92
  91. package/extensions/analytics/skills/ab-testing.md +72 -72
  92. package/extensions/analytics/skills/dashboard-patterns.md +83 -83
  93. package/extensions/analytics/skills/data-validation.md +68 -68
  94. package/extensions/analytics/skills/funnel-analysis.md +81 -81
  95. package/extensions/analytics/skills/sql-patterns.md +57 -57
  96. package/extensions/analytics/skills/statistical-analysis.md +79 -79
  97. package/extensions/analytics/skills/tracking-setup.md +71 -71
  98. package/extensions/backend/PACK.md +104 -104
  99. package/extensions/backend/skills/api-patterns.md +84 -84
  100. package/extensions/backend/skills/async-pipeline.md +193 -193
  101. package/extensions/backend/skills/auth-patterns.md +97 -97
  102. package/extensions/backend/skills/background-jobs.md +133 -133
  103. package/extensions/backend/skills/caching-patterns.md +108 -108
  104. package/extensions/backend/skills/cli-generation.md +133 -133
  105. package/extensions/backend/skills/database-patterns.md +87 -87
  106. package/extensions/backend/skills/middleware-patterns.md +104 -104
  107. package/extensions/chrome-ext/PACK.md +93 -93
  108. package/extensions/chrome-ext/skills/cws-preflight.md +143 -143
  109. package/extensions/chrome-ext/skills/cws-publish.md +104 -104
  110. package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -251
  111. package/extensions/chrome-ext/skills/ext-messaging.md +139 -139
  112. package/extensions/chrome-ext/skills/ext-storage.md +133 -133
  113. package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -164
  114. package/extensions/content/PACK.md +96 -96
  115. package/extensions/content/skills/blog-patterns.md +88 -88
  116. package/extensions/content/skills/cms-integration.md +131 -131
  117. package/extensions/content/skills/content-scoring.md +107 -107
  118. package/extensions/content/skills/i18n.md +83 -83
  119. package/extensions/content/skills/mdx-authoring.md +137 -137
  120. package/extensions/content/skills/reference.md +1014 -1014
  121. package/extensions/content/skills/seo-patterns.md +67 -67
  122. package/extensions/content/skills/video-repurpose.md +153 -153
  123. package/extensions/devops/PACK.md +101 -101
  124. package/extensions/devops/skills/chaos-testing.md +67 -67
  125. package/extensions/devops/skills/ci-cd.md +75 -75
  126. package/extensions/devops/skills/docker.md +58 -58
  127. package/extensions/devops/skills/edge-serverless.md +163 -163
  128. package/extensions/devops/skills/infra-as-code.md +158 -158
  129. package/extensions/devops/skills/kubernetes.md +110 -110
  130. package/extensions/devops/skills/monitoring.md +57 -57
  131. package/extensions/devops/skills/server-setup.md +64 -64
  132. package/extensions/devops/skills/ssl-domain.md +42 -42
  133. package/extensions/ecommerce/PACK.md +116 -116
  134. package/extensions/ecommerce/skills/cart-system.md +79 -79
  135. package/extensions/ecommerce/skills/inventory-mgmt.md +102 -102
  136. package/extensions/ecommerce/skills/order-management.md +126 -126
  137. package/extensions/ecommerce/skills/payment-integration.md +472 -472
  138. package/extensions/ecommerce/skills/shopify-dev.md +69 -69
  139. package/extensions/ecommerce/skills/subscription-billing.md +93 -93
  140. package/extensions/ecommerce/skills/tax-compliance.md +117 -117
  141. package/extensions/gamedev/PACK.md +142 -142
  142. package/extensions/gamedev/skills/asset-pipeline.md +74 -74
  143. package/extensions/gamedev/skills/audio-system.md +129 -129
  144. package/extensions/gamedev/skills/camera-system.md +87 -87
  145. package/extensions/gamedev/skills/ecs.md +98 -98
  146. package/extensions/gamedev/skills/game-loops.md +72 -72
  147. package/extensions/gamedev/skills/input-system.md +199 -199
  148. package/extensions/gamedev/skills/multiplayer.md +180 -180
  149. package/extensions/gamedev/skills/particles.md +105 -105
  150. package/extensions/gamedev/skills/physics-engine.md +89 -89
  151. package/extensions/gamedev/skills/scene-management.md +146 -146
  152. package/extensions/gamedev/skills/threejs-patterns.md +90 -90
  153. package/extensions/gamedev/skills/webgl.md +71 -71
  154. package/extensions/mobile/PACK.md +106 -106
  155. package/extensions/mobile/skills/app-store-connect.md +152 -152
  156. package/extensions/mobile/skills/app-store-prep.md +66 -66
  157. package/extensions/mobile/skills/deep-linking.md +109 -109
  158. package/extensions/mobile/skills/flutter.md +60 -60
  159. package/extensions/mobile/skills/ios-build-pipeline.md +142 -142
  160. package/extensions/mobile/skills/native-bridge.md +66 -66
  161. package/extensions/mobile/skills/ota-updates.md +97 -97
  162. package/extensions/mobile/skills/push-notifications.md +111 -111
  163. package/extensions/mobile/skills/react-native.md +82 -82
  164. package/extensions/saas/PACK.md +116 -116
  165. package/extensions/saas/skills/billing-integration.md +200 -200
  166. package/extensions/saas/skills/feature-flags.md +130 -130
  167. package/extensions/saas/skills/multi-tenant.md +103 -103
  168. package/extensions/saas/skills/onboarding-flow.md +139 -139
  169. package/extensions/saas/skills/subscription-flow.md +95 -95
  170. package/extensions/saas/skills/team-management.md +144 -144
  171. package/extensions/security/PACK.md +99 -99
  172. package/extensions/security/skills/api-security.md +140 -140
  173. package/extensions/security/skills/compliance.md +68 -68
  174. package/extensions/security/skills/owasp-audit.md +64 -64
  175. package/extensions/security/skills/pentest-patterns.md +77 -77
  176. package/extensions/security/skills/secret-mgmt.md +65 -65
  177. package/extensions/security/skills/supply-chain.md +65 -65
  178. package/extensions/trading/PACK.md +80 -80
  179. package/extensions/trading/skills/chart-components.md +55 -55
  180. package/extensions/trading/skills/experiment-loop.md +125 -125
  181. package/extensions/trading/skills/fintech-patterns.md +47 -47
  182. package/extensions/trading/skills/indicator-library.md +58 -58
  183. package/extensions/trading/skills/quant-analysis.md +111 -111
  184. package/extensions/trading/skills/realtime-data.md +58 -58
  185. package/extensions/trading/skills/trade-logic.md +104 -104
  186. package/extensions/ui/PACK.md +130 -130
  187. package/extensions/ui/skills/a11y-audit.md +91 -91
  188. package/extensions/ui/skills/animation-patterns.md +127 -106
  189. package/extensions/ui/skills/component-patterns.md +100 -75
  190. package/extensions/ui/skills/design-decision.md +108 -108
  191. package/extensions/ui/skills/design-system.md +68 -68
  192. package/extensions/ui/skills/landing-patterns.md +155 -155
  193. package/extensions/ui/skills/palette-picker.md +173 -173
  194. package/extensions/ui/skills/react-health.md +90 -90
  195. package/extensions/ui/skills/type-system.md +125 -125
  196. package/extensions/ui/skills/web-vitals.md +153 -153
  197. package/extensions/zalo/PACK.md +145 -145
  198. package/extensions/zalo/skills/zalo-oa-mcp.md +317 -317
  199. package/extensions/zalo/skills/zalo-oa-messaging.md +429 -429
  200. package/extensions/zalo/skills/zalo-oa-setup.md +236 -236
  201. package/extensions/zalo/skills/zalo-oa-webhook.md +189 -189
  202. package/extensions/zalo/skills/zalo-personal-messaging.md +194 -194
  203. package/extensions/zalo/skills/zalo-personal-setup.md +153 -153
  204. package/extensions/zalo/skills/zalo-rate-guard.md +219 -219
  205. package/hooks/auto-format/index.cjs +48 -48
  206. package/hooks/context-watch/index.cjs +95 -68
  207. package/hooks/hooks.json +111 -111
  208. package/hooks/metrics-collector/index.cjs +86 -42
  209. package/hooks/post-session-reflect/index.cjs +189 -153
  210. package/hooks/pre-compact/index.cjs +95 -95
  211. package/hooks/run-hook.cmd +1 -1
  212. package/hooks/secrets-scan/index.cjs +100 -100
  213. package/hooks/session-start/index.cjs +71 -65
  214. package/hooks/typecheck/index.cjs +65 -65
  215. package/package.json +63 -63
  216. package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -21
  217. package/references/ui-pro-max-data/charts.csv +26 -26
  218. package/references/ui-pro-max-data/colors.csv +161 -161
  219. package/references/ui-pro-max-data/styles.csv +68 -68
  220. package/references/ui-pro-max-data/typography.csv +74 -74
  221. package/references/ui-pro-max-data/ui-reasoning.csv +162 -162
  222. package/references/ui-pro-max-data/ux-guidelines.csv +99 -99
  223. package/skills/adversary/SKILL.md +283 -283
  224. package/skills/asset-creator/SKILL.md +157 -157
  225. package/skills/audit/SKILL.md +148 -2
  226. package/skills/autopsy/SKILL.md +335 -259
  227. package/skills/autopsy/references/repo-analysis-patterns.md +113 -0
  228. package/skills/ba/SKILL.md +72 -2
  229. package/skills/brainstorm/SKILL.md +342 -341
  230. package/skills/browser-pilot/SKILL.md +168 -168
  231. package/skills/constraint-check/SKILL.md +165 -165
  232. package/skills/context-engine/SKILL.md +404 -404
  233. package/skills/cook/SKILL.md +917 -834
  234. package/skills/cook/references/output-format.md +33 -0
  235. package/skills/db/SKILL.md +273 -272
  236. package/skills/debug/SKILL.md +465 -443
  237. package/skills/dependency-doctor/SKILL.md +265 -235
  238. package/skills/deploy/SKILL.md +274 -231
  239. package/skills/design/DESIGN-REFERENCE.md +365 -365
  240. package/skills/design/SKILL.md +589 -482
  241. package/skills/doc-processor/SKILL.md +254 -254
  242. package/skills/docs/SKILL.md +374 -373
  243. package/skills/docs-seeker/SKILL.md +177 -177
  244. package/skills/fix/SKILL.md +330 -308
  245. package/skills/git/SKILL.md +339 -339
  246. package/skills/graft/SKILL.md +352 -0
  247. package/skills/graft/references/challenge-framework.md +98 -0
  248. package/skills/graft/references/mode-decision.md +44 -0
  249. package/skills/hallucination-guard/SKILL.md +219 -219
  250. package/skills/incident/SKILL.md +254 -251
  251. package/skills/integrity-check/SKILL.md +169 -169
  252. package/skills/journal/SKILL.md +240 -238
  253. package/skills/launch/SKILL.md +344 -342
  254. package/skills/logic-guardian/SKILL.md +251 -251
  255. package/skills/marketing/SKILL.md +290 -245
  256. package/skills/mcp-builder/SKILL.md +425 -423
  257. package/skills/mcp-builder/references/auto-discovery-pattern.md +169 -0
  258. package/skills/neural-memory/SKILL.md +362 -362
  259. package/skills/onboard/SKILL.md +404 -403
  260. package/skills/perf/SKILL.md +346 -346
  261. package/skills/plan/SKILL.md +433 -370
  262. package/skills/plan/references/feature-map.md +84 -0
  263. package/skills/preflight/SKILL.md +415 -396
  264. package/skills/problem-solver/SKILL.md +380 -284
  265. package/skills/rescue/SKILL.md +474 -450
  266. package/skills/retro/SKILL.md +5 -1
  267. package/skills/review/SKILL.md +612 -535
  268. package/skills/review-intake/SKILL.md +249 -249
  269. package/skills/safeguard/SKILL.md +200 -200
  270. package/skills/sast/SKILL.md +190 -190
  271. package/skills/scaffold/SKILL.md +328 -286
  272. package/skills/scope-guard/SKILL.md +180 -162
  273. package/skills/scout/SKILL.md +263 -263
  274. package/skills/sentinel/SKILL.md +382 -353
  275. package/skills/sentinel-env/SKILL.md +254 -254
  276. package/skills/sequential-thinking/SKILL.md +234 -234
  277. package/skills/session-bridge/SKILL.md +543 -397
  278. package/skills/skill-forge/SKILL.md +581 -539
  279. package/skills/skill-router/{skill.md → SKILL.md} +30 -2
  280. package/skills/surgeon/SKILL.md +215 -215
  281. package/skills/team/SKILL.md +556 -514
  282. package/skills/test/SKILL.md +614 -587
  283. package/skills/trend-scout/SKILL.md +145 -145
  284. package/skills/verification/SKILL.md +326 -325
  285. package/skills/video-creator/SKILL.md +201 -201
  286. package/skills/watchdog/SKILL.md +168 -168
  287. package/skills/worktree/SKILL.md +140 -140
@@ -1,130 +1,130 @@
1
- ---
2
- name: "feature-flags"
3
- pack: "@rune/saas"
4
- description: "Feature flag management — gradual rollouts, kill switches, A/B testing, user-segment targeting, and stale flag cleanup. Supports self-hosted (Unleash, custom Redis) and managed (LaunchDarkly, Statsig, Flagsmith)."
5
- model: sonnet
6
- tools: [Read, Edit, Write, Grep, Glob, Bash]
7
- ---
8
-
9
- # feature-flags
10
-
11
- Feature flag management — gradual rollouts, kill switches, A/B testing, user-segment targeting, and stale flag cleanup. Supports self-hosted (Unleash, custom Redis) and managed (LaunchDarkly, Statsig, Flagsmith).
12
-
13
- #### Flag Types
14
-
15
- | Type | Use Case | Example |
16
- |---|---|---|
17
- | Boolean | Simple on/off for a feature | `new_dashboard_ui` |
18
- | Percentage rollout | Gradual release 1% → 100% | `redesigned_editor: 25%` |
19
- | User segment | Specific users/orgs first | `beta_users`, `enterprise_plan` |
20
- | A/B test | Compare variants with metrics | `checkout_flow: variant_a / variant_b` |
21
- | Kill switch | Instant disable on failure | `payment_processor_v2` |
22
- | Environment | Dev/staging/prod separation | Auto by `NODE_ENV` |
23
-
24
- #### Rollout Pattern: Canary → Gradual → GA
25
-
26
- ```
27
- 1% (internal + beta users) → 10% → 25% → 50% → 100% → cleanup flag after 30 days at 100%
28
- ```
29
-
30
- #### Workflow
31
-
32
- **Step 1 — Identify feature boundary**
33
- Before writing code, define the flag: name (kebab-case, descriptive), default value (false = safe default), targeting rules (who sees it first), and planned cleanup date. Document in your flag provider dashboard.
34
-
35
- **Step 2 — Create flag with targeting rules**
36
- In Unleash/LaunchDarkly/Flagsmith: create flag with gradual rollout strategy. Start at 0%. Add a "beta users" segment for internal testing before any percentage rollout. Set environment-specific defaults: always-on in dev, gradual in staging, starts at 0% in prod.
37
-
38
- **Step 3 — Implement client/server evaluation**
39
- Client: evaluate flag in a React hook, never inline. Server: evaluate in middleware or at request start, attach result to request context. Never evaluate flags inside hot loops — cache the result for the request lifetime.
40
-
41
- **Step 4 — Add analytics event tracking**
42
- Every flag evaluation on a user-facing feature should fire an analytics event: `feature_flag_evaluated` with `{ flag, variant, userId, tenantId }`. This enables funnel analysis by variant and measures the rollout's impact on key metrics.
43
-
44
- **Step 5 — Schedule flag cleanup**
45
- Flags that have been at 100% for >30 days are stale. Run a weekly lint job: grep all flag keys used in code, compare against provider's flag list, flag mismatches (code uses a flag that was deleted → runtime error, or flag exists but never referenced → cleanup candidate). Remove stale flags from both code and provider in the same PR.
46
-
47
- #### Example
48
-
49
- ```typescript
50
- // Custom Redis-based flag evaluation (self-hosted, zero SaaS dependency)
51
- import { Redis } from 'ioredis';
52
- const redis = new Redis(process.env.REDIS_URL!);
53
-
54
- interface FlagConfig {
55
- enabled: boolean;
56
- percentage?: number; // 0-100 for gradual rollout
57
- allowedUsers?: string[]; // canary user IDs
58
- allowedPlans?: string[]; // plan-based targeting
59
- }
60
-
61
- const evaluateFlag = async (
62
- flagKey: string,
63
- ctx: { userId: string; tenantId: string; plan: string }
64
- ): Promise<boolean> => {
65
- const raw = await redis.get(`flag:${flagKey}`);
66
- if (!raw) return false; // default off = safe
67
- const config: FlagConfig = JSON.parse(raw);
68
- if (!config.enabled) return false;
69
- if (config.allowedUsers?.includes(ctx.userId)) return true;
70
- if (config.allowedPlans?.includes(ctx.plan)) return true;
71
- if (config.percentage !== undefined) {
72
- // Deterministic: same user always gets same bucket
73
- const hash = parseInt(ctx.userId.slice(-8), 16) % 100;
74
- return hash < config.percentage;
75
- }
76
- return config.enabled;
77
- };
78
-
79
- // React hook — evaluate once per render cycle, never in loops
80
- function useFlag(flagKey: string): boolean {
81
- const { user } = useAuth();
82
- const { data: enabled = false } = useQuery({
83
- queryKey: ['flag', flagKey, user?.id],
84
- queryFn: () => fetchFlag(flagKey),
85
- staleTime: 30_000, // cache 30s — flags don't change every millisecond
86
- });
87
- return enabled;
88
- }
89
-
90
- // Server middleware — evaluate at request boundary, attach to context
91
- const flagMiddleware = (flagKey: string) => async (req: Request, res: Response, next: NextFunction) => {
92
- req.flags = req.flags ?? {};
93
- req.flags[flagKey] = await evaluateFlag(flagKey, {
94
- userId: req.user!.id,
95
- tenantId: req.tenantId!,
96
- plan: req.user!.plan,
97
- });
98
- next();
99
- };
100
-
101
- // Usage in route — flag already evaluated, no async needed
102
- app.get('/api/checkout', flagMiddleware('new_checkout_v2'), (req, res) => {
103
- if (req.flags['new_checkout_v2']) {
104
- return checkoutV2Handler(req, res);
105
- }
106
- return checkoutV1Handler(req, res);
107
- });
108
-
109
- // Stale flag detection — run weekly in CI
110
- import { execSync } from 'child_process';
111
-
112
- const findStaleFlags = async () => {
113
- const flagsInCode = execSync('grep -r "useFlag\\|evaluateFlag" src/ --include="*.ts" -h')
114
- .toString()
115
- .match(/(?:useFlag|evaluateFlag)\(['"]([^'"]+)['"]/g)
116
- ?.map(m => m.match(/['"]([^'"]+)['"]/)?.[1])
117
- .filter(Boolean) ?? [];
118
-
119
- const flagsInProvider = await redis.keys('flag:*').then(keys => keys.map(k => k.replace('flag:', '')));
120
- const stale = flagsInProvider.filter(f => !flagsInCode.includes(f));
121
- const missing = flagsInCode.filter(f => !flagsInProvider.includes(f));
122
- return { stale, missing };
123
- };
124
- ```
125
-
126
- **Sharp edges for flags:**
127
- - Never evaluate flags on hot paths (e.g., inside `Array.map` over 1000 items) — cache the flag state at the top of the function.
128
- - In tests: mock flag evaluation at the provider level, not by conditionally skipping flag checks. Every code path should be testable with flags on and off.
129
- - Flag dependency chains (flag A enables flag B) — avoid. If you need compound logic, evaluate both flags independently and combine in application code. Provider-level dependencies are invisible in code review.
130
- - Percentage rollout is not the same as A/B test — percentage rollout has no control group. For A/B tests, always keep a 50/50 split or a defined control group.
1
+ ---
2
+ name: "feature-flags"
3
+ pack: "@rune/saas"
4
+ description: "Feature flag management — gradual rollouts, kill switches, A/B testing, user-segment targeting, and stale flag cleanup. Supports self-hosted (Unleash, custom Redis) and managed (LaunchDarkly, Statsig, Flagsmith)."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # feature-flags
10
+
11
+ Feature flag management — gradual rollouts, kill switches, A/B testing, user-segment targeting, and stale flag cleanup. Supports self-hosted (Unleash, custom Redis) and managed (LaunchDarkly, Statsig, Flagsmith).
12
+
13
+ #### Flag Types
14
+
15
+ | Type | Use Case | Example |
16
+ |---|---|---|
17
+ | Boolean | Simple on/off for a feature | `new_dashboard_ui` |
18
+ | Percentage rollout | Gradual release 1% → 100% | `redesigned_editor: 25%` |
19
+ | User segment | Specific users/orgs first | `beta_users`, `enterprise_plan` |
20
+ | A/B test | Compare variants with metrics | `checkout_flow: variant_a / variant_b` |
21
+ | Kill switch | Instant disable on failure | `payment_processor_v2` |
22
+ | Environment | Dev/staging/prod separation | Auto by `NODE_ENV` |
23
+
24
+ #### Rollout Pattern: Canary → Gradual → GA
25
+
26
+ ```
27
+ 1% (internal + beta users) → 10% → 25% → 50% → 100% → cleanup flag after 30 days at 100%
28
+ ```
29
+
30
+ #### Workflow
31
+
32
+ **Step 1 — Identify feature boundary**
33
+ Before writing code, define the flag: name (kebab-case, descriptive), default value (false = safe default), targeting rules (who sees it first), and planned cleanup date. Document in your flag provider dashboard.
34
+
35
+ **Step 2 — Create flag with targeting rules**
36
+ In Unleash/LaunchDarkly/Flagsmith: create flag with gradual rollout strategy. Start at 0%. Add a "beta users" segment for internal testing before any percentage rollout. Set environment-specific defaults: always-on in dev, gradual in staging, starts at 0% in prod.
37
+
38
+ **Step 3 — Implement client/server evaluation**
39
+ Client: evaluate flag in a React hook, never inline. Server: evaluate in middleware or at request start, attach result to request context. Never evaluate flags inside hot loops — cache the result for the request lifetime.
40
+
41
+ **Step 4 — Add analytics event tracking**
42
+ Every flag evaluation on a user-facing feature should fire an analytics event: `feature_flag_evaluated` with `{ flag, variant, userId, tenantId }`. This enables funnel analysis by variant and measures the rollout's impact on key metrics.
43
+
44
+ **Step 5 — Schedule flag cleanup**
45
+ Flags that have been at 100% for >30 days are stale. Run a weekly lint job: grep all flag keys used in code, compare against provider's flag list, flag mismatches (code uses a flag that was deleted → runtime error, or flag exists but never referenced → cleanup candidate). Remove stale flags from both code and provider in the same PR.
46
+
47
+ #### Example
48
+
49
+ ```typescript
50
+ // Custom Redis-based flag evaluation (self-hosted, zero SaaS dependency)
51
+ import { Redis } from 'ioredis';
52
+ const redis = new Redis(process.env.REDIS_URL!);
53
+
54
+ interface FlagConfig {
55
+ enabled: boolean;
56
+ percentage?: number; // 0-100 for gradual rollout
57
+ allowedUsers?: string[]; // canary user IDs
58
+ allowedPlans?: string[]; // plan-based targeting
59
+ }
60
+
61
+ const evaluateFlag = async (
62
+ flagKey: string,
63
+ ctx: { userId: string; tenantId: string; plan: string }
64
+ ): Promise<boolean> => {
65
+ const raw = await redis.get(`flag:${flagKey}`);
66
+ if (!raw) return false; // default off = safe
67
+ const config: FlagConfig = JSON.parse(raw);
68
+ if (!config.enabled) return false;
69
+ if (config.allowedUsers?.includes(ctx.userId)) return true;
70
+ if (config.allowedPlans?.includes(ctx.plan)) return true;
71
+ if (config.percentage !== undefined) {
72
+ // Deterministic: same user always gets same bucket
73
+ const hash = parseInt(ctx.userId.slice(-8), 16) % 100;
74
+ return hash < config.percentage;
75
+ }
76
+ return config.enabled;
77
+ };
78
+
79
+ // React hook — evaluate once per render cycle, never in loops
80
+ function useFlag(flagKey: string): boolean {
81
+ const { user } = useAuth();
82
+ const { data: enabled = false } = useQuery({
83
+ queryKey: ['flag', flagKey, user?.id],
84
+ queryFn: () => fetchFlag(flagKey),
85
+ staleTime: 30_000, // cache 30s — flags don't change every millisecond
86
+ });
87
+ return enabled;
88
+ }
89
+
90
+ // Server middleware — evaluate at request boundary, attach to context
91
+ const flagMiddleware = (flagKey: string) => async (req: Request, res: Response, next: NextFunction) => {
92
+ req.flags = req.flags ?? {};
93
+ req.flags[flagKey] = await evaluateFlag(flagKey, {
94
+ userId: req.user!.id,
95
+ tenantId: req.tenantId!,
96
+ plan: req.user!.plan,
97
+ });
98
+ next();
99
+ };
100
+
101
+ // Usage in route — flag already evaluated, no async needed
102
+ app.get('/api/checkout', flagMiddleware('new_checkout_v2'), (req, res) => {
103
+ if (req.flags['new_checkout_v2']) {
104
+ return checkoutV2Handler(req, res);
105
+ }
106
+ return checkoutV1Handler(req, res);
107
+ });
108
+
109
+ // Stale flag detection — run weekly in CI
110
+ import { execSync } from 'child_process';
111
+
112
+ const findStaleFlags = async () => {
113
+ const flagsInCode = execSync('grep -r "useFlag\\|evaluateFlag" src/ --include="*.ts" -h')
114
+ .toString()
115
+ .match(/(?:useFlag|evaluateFlag)\(['"]([^'"]+)['"]/g)
116
+ ?.map(m => m.match(/['"]([^'"]+)['"]/)?.[1])
117
+ .filter(Boolean) ?? [];
118
+
119
+ const flagsInProvider = await redis.keys('flag:*').then(keys => keys.map(k => k.replace('flag:', '')));
120
+ const stale = flagsInProvider.filter(f => !flagsInCode.includes(f));
121
+ const missing = flagsInCode.filter(f => !flagsInProvider.includes(f));
122
+ return { stale, missing };
123
+ };
124
+ ```
125
+
126
+ **Sharp edges for flags:**
127
+ - Never evaluate flags on hot paths (e.g., inside `Array.map` over 1000 items) — cache the flag state at the top of the function.
128
+ - In tests: mock flag evaluation at the provider level, not by conditionally skipping flag checks. Every code path should be testable with flags on and off.
129
+ - Flag dependency chains (flag A enables flag B) — avoid. If you need compound logic, evaluate both flags independently and combine in application code. Provider-level dependencies are invisible in code review.
130
+ - Percentage rollout is not the same as A/B test — percentage rollout has no control group. For A/B tests, always keep a 50/50 split or a defined control group.
@@ -1,103 +1,103 @@
1
- ---
2
- name: "multi-tenant"
3
- pack: "@rune/saas"
4
- description: "Multi-tenancy patterns — database isolation strategies, tenant context middleware, data partitioning, cross-tenant query prevention, tenant-aware background jobs, and GDPR data export."
5
- model: sonnet
6
- tools: [Read, Edit, Write, Grep, Glob, Bash]
7
- ---
8
-
9
- # multi-tenant
10
-
11
- Multi-tenancy patterns — database isolation strategies, tenant context middleware, data partitioning, cross-tenant query prevention, tenant-aware background jobs, and GDPR data export.
12
-
13
- #### Isolation Strategy Comparison
14
-
15
- | Strategy | Cost | Isolation | Migration Difficulty | When to Use |
16
- |---|---|---|---|---|
17
- | Shared DB, tenant column | Low | Weak (app-enforced) | Easy | Early-stage, <1000 tenants |
18
- | Shared DB + PostgreSQL RLS | Low | Strong (DB-enforced) | Easy | Best default for most SaaS |
19
- | Schema-per-tenant | Medium | Strong | Medium | When tenants need schema customization |
20
- | DB-per-tenant | High | Perfect | Hard | Enterprise, compliance (HIPAA, SOC2) |
21
-
22
- #### Workflow
23
-
24
- **Step 1 — Detect current isolation strategy**
25
- Use Grep to find tenant-related code: `tenantId`, `organizationId`, `workspaceId`, `x-tenant-id` header, RLS policies, schema-per-tenant patterns, database switching logic. Read the database schema and middleware to classify the isolation strategy in use.
26
-
27
- **Step 2 — Audit isolation boundaries**
28
- Check for: queries without tenant filter (data leak risk), missing tenant context in middleware, no RLS policies on shared tables, admin endpoints that bypass tenant isolation, background jobs processing cross-tenant data without scoping. Flag each with severity.
29
-
30
- **Step 3 — Emit tenant-safe patterns**
31
- Based on detected strategy, emit: tenant middleware (extract from JWT/header, set on request context), RLS policies for shared-schema approach, scoped repository pattern that injects tenant filter on every query, and tenant-aware test fixtures.
32
-
33
- **Step 4 — Tenant-aware background jobs**
34
- Every background job MUST carry `tenantId`. Use BullMQ job data to pass tenant context, then initialize a scoped repository inside the job processor. Never process tenant data in a job without an explicit `tenantId` guard.
35
-
36
- **Step 5 — Tenant data export (GDPR portability)**
37
- Implement `/api/tenants/:id/export` that collects all data rows belonging to a tenant across all tables, serializes to JSON or CSV, and streams the result as a download. Log the export event in the audit trail with timestamp and requesting user.
38
-
39
- #### Example
40
-
41
- ```typescript
42
- // Tenant middleware — extract from JWT, inject into request context
43
- const tenantMiddleware = async (req: Request, res: Response, next: NextFunction) => {
44
- const tenantId = req.user?.tenantId ?? req.headers['x-tenant-id'] as string;
45
- if (!tenantId) return res.status(403).json({ error: { code: 'TENANT_REQUIRED', message: 'Tenant context missing' } });
46
- req.tenantId = tenantId;
47
- next();
48
- };
49
-
50
- // Scoped repository — every query automatically filtered by tenant
51
- class ScopedRepository<T extends { tenantId: string }> {
52
- constructor(private model: PrismaModel<T>, private tenantId: string) {}
53
-
54
- async findMany(where: Partial<Omit<T, 'tenantId'>> = {}) {
55
- return this.model.findMany({ where: { ...where, tenantId: this.tenantId } });
56
- }
57
-
58
- async create(data: Omit<T, 'tenantId' | 'id' | 'createdAt' | 'updatedAt'>) {
59
- return this.model.create({ data: { ...data, tenantId: this.tenantId } as any });
60
- }
61
- }
62
-
63
- // PostgreSQL RLS — DB-enforced isolation, safest approach
64
- -- Enable RLS on every shared table
65
- ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
66
-
67
- -- Set tenant context before query (from app middleware)
68
- SET LOCAL app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';
69
-
70
- -- Policy reads from session variable — automatic for all queries
71
- CREATE POLICY tenant_isolation ON projects
72
- USING (tenant_id = current_setting('app.tenant_id')::uuid);
73
-
74
- -- Set in Prisma $executeRaw before each query block:
75
- -- await prisma.$executeRaw`SELECT set_config('app.tenant_id', ${tenantId}, true)`;
76
-
77
- // BullMQ — tenant-aware background job
78
- const emailQueue = new Queue('emails');
79
-
80
- // Producer: always pass tenantId in job data
81
- await emailQueue.add('send-invoice', { tenantId, invoiceId, recipientEmail });
82
-
83
- // Consumer: initialize scoped context from job data
84
- const worker = new Worker('emails', async (job) => {
85
- const { tenantId, invoiceId } = job.data;
86
- const invoices = new ScopedRepository(prisma.invoice, tenantId);
87
- const invoice = await invoices.findMany({ id: invoiceId });
88
- // process...
89
- });
90
-
91
- // GDPR export — stream all tenant data
92
- app.get('/api/tenants/:id/export', requireOwner, async (req, res) => {
93
- const { id: tenantId } = req.params;
94
- const [projects, members, invoices] = await Promise.all([
95
- prisma.project.findMany({ where: { tenantId } }),
96
- prisma.member.findMany({ where: { tenantId } }),
97
- prisma.invoice.findMany({ where: { tenantId } }),
98
- ]);
99
- await prisma.auditLog.create({ data: { tenantId, action: 'DATA_EXPORT', actorId: req.user.id } });
100
- res.setHeader('Content-Disposition', `attachment; filename="export-${tenantId}.json"`);
101
- res.json({ exportedAt: new Date(), projects, members, invoices });
102
- });
103
- ```
1
+ ---
2
+ name: "multi-tenant"
3
+ pack: "@rune/saas"
4
+ description: "Multi-tenancy patterns — database isolation strategies, tenant context middleware, data partitioning, cross-tenant query prevention, tenant-aware background jobs, and GDPR data export."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # multi-tenant
10
+
11
+ Multi-tenancy patterns — database isolation strategies, tenant context middleware, data partitioning, cross-tenant query prevention, tenant-aware background jobs, and GDPR data export.
12
+
13
+ #### Isolation Strategy Comparison
14
+
15
+ | Strategy | Cost | Isolation | Migration Difficulty | When to Use |
16
+ |---|---|---|---|---|
17
+ | Shared DB, tenant column | Low | Weak (app-enforced) | Easy | Early-stage, <1000 tenants |
18
+ | Shared DB + PostgreSQL RLS | Low | Strong (DB-enforced) | Easy | Best default for most SaaS |
19
+ | Schema-per-tenant | Medium | Strong | Medium | When tenants need schema customization |
20
+ | DB-per-tenant | High | Perfect | Hard | Enterprise, compliance (HIPAA, SOC2) |
21
+
22
+ #### Workflow
23
+
24
+ **Step 1 — Detect current isolation strategy**
25
+ Use Grep to find tenant-related code: `tenantId`, `organizationId`, `workspaceId`, `x-tenant-id` header, RLS policies, schema-per-tenant patterns, database switching logic. Read the database schema and middleware to classify the isolation strategy in use.
26
+
27
+ **Step 2 — Audit isolation boundaries**
28
+ Check for: queries without tenant filter (data leak risk), missing tenant context in middleware, no RLS policies on shared tables, admin endpoints that bypass tenant isolation, background jobs processing cross-tenant data without scoping. Flag each with severity.
29
+
30
+ **Step 3 — Emit tenant-safe patterns**
31
+ Based on detected strategy, emit: tenant middleware (extract from JWT/header, set on request context), RLS policies for shared-schema approach, scoped repository pattern that injects tenant filter on every query, and tenant-aware test fixtures.
32
+
33
+ **Step 4 — Tenant-aware background jobs**
34
+ Every background job MUST carry `tenantId`. Use BullMQ job data to pass tenant context, then initialize a scoped repository inside the job processor. Never process tenant data in a job without an explicit `tenantId` guard.
35
+
36
+ **Step 5 — Tenant data export (GDPR portability)**
37
+ Implement `/api/tenants/:id/export` that collects all data rows belonging to a tenant across all tables, serializes to JSON or CSV, and streams the result as a download. Log the export event in the audit trail with timestamp and requesting user.
38
+
39
+ #### Example
40
+
41
+ ```typescript
42
+ // Tenant middleware — extract from JWT, inject into request context
43
+ const tenantMiddleware = async (req: Request, res: Response, next: NextFunction) => {
44
+ const tenantId = req.user?.tenantId ?? req.headers['x-tenant-id'] as string;
45
+ if (!tenantId) return res.status(403).json({ error: { code: 'TENANT_REQUIRED', message: 'Tenant context missing' } });
46
+ req.tenantId = tenantId;
47
+ next();
48
+ };
49
+
50
+ // Scoped repository — every query automatically filtered by tenant
51
+ class ScopedRepository<T extends { tenantId: string }> {
52
+ constructor(private model: PrismaModel<T>, private tenantId: string) {}
53
+
54
+ async findMany(where: Partial<Omit<T, 'tenantId'>> = {}) {
55
+ return this.model.findMany({ where: { ...where, tenantId: this.tenantId } });
56
+ }
57
+
58
+ async create(data: Omit<T, 'tenantId' | 'id' | 'createdAt' | 'updatedAt'>) {
59
+ return this.model.create({ data: { ...data, tenantId: this.tenantId } as any });
60
+ }
61
+ }
62
+
63
+ // PostgreSQL RLS — DB-enforced isolation, safest approach
64
+ -- Enable RLS on every shared table
65
+ ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
66
+
67
+ -- Set tenant context before query (from app middleware)
68
+ SET LOCAL app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';
69
+
70
+ -- Policy reads from session variable — automatic for all queries
71
+ CREATE POLICY tenant_isolation ON projects
72
+ USING (tenant_id = current_setting('app.tenant_id')::uuid);
73
+
74
+ -- Set in Prisma $executeRaw before each query block:
75
+ -- await prisma.$executeRaw`SELECT set_config('app.tenant_id', ${tenantId}, true)`;
76
+
77
+ // BullMQ — tenant-aware background job
78
+ const emailQueue = new Queue('emails');
79
+
80
+ // Producer: always pass tenantId in job data
81
+ await emailQueue.add('send-invoice', { tenantId, invoiceId, recipientEmail });
82
+
83
+ // Consumer: initialize scoped context from job data
84
+ const worker = new Worker('emails', async (job) => {
85
+ const { tenantId, invoiceId } = job.data;
86
+ const invoices = new ScopedRepository(prisma.invoice, tenantId);
87
+ const invoice = await invoices.findMany({ id: invoiceId });
88
+ // process...
89
+ });
90
+
91
+ // GDPR export — stream all tenant data
92
+ app.get('/api/tenants/:id/export', requireOwner, async (req, res) => {
93
+ const { id: tenantId } = req.params;
94
+ const [projects, members, invoices] = await Promise.all([
95
+ prisma.project.findMany({ where: { tenantId } }),
96
+ prisma.member.findMany({ where: { tenantId } }),
97
+ prisma.invoice.findMany({ where: { tenantId } }),
98
+ ]);
99
+ await prisma.auditLog.create({ data: { tenantId, action: 'DATA_EXPORT', actorId: req.user.id } });
100
+ res.setHeader('Content-Disposition', `attachment; filename="export-${tenantId}.json"`);
101
+ res.json({ exportedAt: new Date(), projects, members, invoices });
102
+ });
103
+ ```