@ericrisco/rsc 0.1.44 → 0.1.46

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 (97) hide show
  1. package/README.md +1 -1
  2. package/manifest.json +110 -86
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -1
  5. package/scripts/skill-migration-brief.md +192 -0
  6. package/skills/ab-testing/SKILL.md +9 -41
  7. package/skills/accessibility/SKILL.md +5 -7
  8. package/skills/ads/SKILL.md +9 -24
  9. package/skills/agent-eval/SKILL.md +7 -33
  10. package/skills/agent-safety/SKILL.md +7 -14
  11. package/skills/ai-media/SKILL.md +4 -20
  12. package/skills/analytics/SKILL.md +8 -30
  13. package/skills/analyze/SKILL.md +24 -59
  14. package/skills/angular/SKILL.md +12 -25
  15. package/skills/api-connector-builder/SKILL.md +10 -27
  16. package/skills/api-design/SKILL.md +7 -14
  17. package/skills/article-writing/SKILL.md +1 -18
  18. package/skills/astro/SKILL.md +36 -54
  19. package/skills/author-skill/SKILL.md +41 -74
  20. package/skills/author-skill/evals/README.md +5 -5
  21. package/skills/author-skill/evals/cases.yaml +8 -8
  22. package/skills/author-skill/references/description-recipe.md +25 -26
  23. package/skills/author-skill/references/eval-authoring.md +1 -1
  24. package/skills/author-skill/references/rsc-conventions.md +1 -1
  25. package/skills/automation-flows/SKILL.md +6 -16
  26. package/skills/automation-strategy/SKILL.md +1 -9
  27. package/skills/aws-essentials/SKILL.md +6 -23
  28. package/skills/backups/SKILL.md +1 -7
  29. package/skills/bash-scripting/SKILL.md +5 -9
  30. package/skills/bookkeeping/SKILL.md +2 -10
  31. package/skills/brand-identity/SKILL.md +18 -44
  32. package/skills/brand-voice/SKILL.md +4 -31
  33. package/skills/building-agents/SKILL.md +20 -103
  34. package/skills/business-intelligence/SKILL.md +9 -23
  35. package/skills/calendar-scheduling/SKILL.md +11 -27
  36. package/skills/case-studies/SKILL.md +3 -8
  37. package/skills/chatbot/SKILL.md +6 -23
  38. package/skills/chrome-extension/SKILL.md +2 -2
  39. package/skills/clarify/SKILL.md +31 -72
  40. package/skills/clickhouse-analytics/SKILL.md +2 -8
  41. package/skills/client-onboarding/SKILL.md +17 -19
  42. package/skills/cloudflare/SKILL.md +3 -8
  43. package/skills/code-review/SKILL.md +10 -23
  44. package/skills/codebase-onboarding/SKILL.md +2 -7
  45. package/skills/cold-outreach/SKILL.md +9 -30
  46. package/skills/community/SKILL.md +4 -31
  47. package/skills/competitor-watch/SKILL.md +20 -35
  48. package/skills/compliance/SKILL.md +10 -37
  49. package/skills/compose-multiplatform/SKILL.md +1 -1
  50. package/skills/constitution/SKILL.md +8 -35
  51. package/skills/content-engine/SKILL.md +4 -16
  52. package/skills/context-budget/SKILL.md +3 -21
  53. package/skills/continuous-learning/SKILL.md +10 -40
  54. package/skills/contracts/SKILL.md +6 -17
  55. package/skills/coolify/SKILL.md +8 -14
  56. package/skills/cost-tracking/SKILL.md +3 -11
  57. package/skills/course-builder/SKILL.md +17 -45
  58. package/skills/course-storytelling/SKILL.md +26 -94
  59. package/skills/cpp/SKILL.md +26 -71
  60. package/skills/csharp-dotnet/SKILL.md +19 -38
  61. package/skills/customer-support/SKILL.md +14 -37
  62. package/skills/dashboard/SKILL.md +6 -26
  63. package/skills/data-cleaning/SKILL.md +37 -63
  64. package/skills/data-policy/SKILL.md +8 -21
  65. package/skills/data-scraper/SKILL.md +1 -7
  66. package/skills/debug/SKILL.md +10 -49
  67. package/skills/deployment/SKILL.md +38 -101
  68. package/skills/design/SKILL.md +34 -116
  69. package/skills/fable-operator/SKILL.md +1 -4
  70. package/skills/fastapi/SKILL.md +50 -82
  71. package/skills/flutter/SKILL.md +41 -89
  72. package/skills/go/SKILL.md +57 -105
  73. package/skills/harness/SKILL.md +52 -68
  74. package/skills/implement/SKILL.md +1 -1
  75. package/skills/init/SKILL.md +152 -168
  76. package/skills/marketing/SKILL.md +30 -80
  77. package/skills/nextjs/SKILL.md +42 -116
  78. package/skills/orient/SKILL.md +25 -20
  79. package/skills/parallel/SKILL.md +9 -75
  80. package/skills/plan/SKILL.md +92 -165
  81. package/skills/postgresdb/SKILL.md +52 -72
  82. package/skills/presentations/SKILL.md +49 -87
  83. package/skills/python/SKILL.md +23 -72
  84. package/skills/react/SKILL.md +19 -67
  85. package/skills/review/SKILL.md +22 -52
  86. package/skills/rust/SKILL.md +40 -115
  87. package/skills/sdd-init/SKILL.md +9 -14
  88. package/skills/secure-coding/SKILL.md +80 -109
  89. package/skills/ship/SKILL.md +25 -55
  90. package/skills/tasks/SKILL.md +37 -61
  91. package/skills/typescript/SKILL.md +22 -55
  92. package/skills/verify/SKILL.md +8 -32
  93. package/skills/viral-score/SKILL.md +122 -0
  94. package/skills/viral-score/evals/README.md +16 -0
  95. package/skills/viral-score/evals/cases.yaml +50 -0
  96. package/skills/vue-nuxt/SKILL.md +45 -106
  97. package/skills/worktrees/SKILL.md +24 -69
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: react
3
- description: "Use when building or reviewing a client-side React SPA on Vite (React 19.2 — components, where state lives, hooks, client routing, server data fetching) and NOT server-rendered React. Triggers: 'React + Vite app', 'I'm fetching in useEffect and it fires twice', 'where should this filter state live', 'my list flickers and the wrong row updates', 'set up React Router', 'cómo hago data fetching en React sin useEffect', 'per què es re-renderitza tot l'arbre'. NOT App Router / server components / SSR (that is nextjs); NOT mobile screens (that is react-native)."
3
+ description: "Use when building or reviewing a client-side React SPA bundled by Vite (React 19.2): components, where state lives, hooks, server data fetching, client routing, re-render and effect bugs. NOT App Router / server components / SSR (that is `nextjs`); NOT native screens (that is `react-native`)."
4
4
  tags: [react, vite, spa, frontend, hooks, tanstack-query]
5
5
  recommends: [typescript, design, testing-web]
6
6
  origin: risco
@@ -10,42 +10,23 @@ origin: risco
10
10
 
11
11
  > Build or review a fast, typed, correctly-architected **client-side** React single-page app bundled by Vite. No server runtime, no RSC tree, no framework router. Server-rendered React (App Router, server actions, SSR/SSG) is not this skill — that is `../nextjs/SKILL.md`.
12
12
 
13
- ## When to use
14
-
15
- > **⚠️ SDD new-feature gate — read this first.** If this skill fired on a **new, non-trivial feature or behaviour change** and there is **no approved spec + plan** under `02-DOCS/wiki/sdd/`, STOP — do **not** write feature code yet. Hand off to `../specify/SKILL.md` first: it runs brainstorm → spec → plan → tasks before any code, then routes back here once the plan is approved. Build here directly only for a genuinely one-line / low-risk change. Method: `../sdd/SKILL.md`.
16
-
17
- - A repo with `vite.config.{ts,js}` + `react` + `react-dom` and **no** `next` and **no** `@react-router/dev` framework mode — i.e. an SPA.
18
- - Creating components and deciding **where state lives** (local / lifted / URL / context / store).
19
- - Wiring **server data**: queries, mutations, caching, optimistic UI.
20
- - Client routing (React Router v7 *library mode* or TanStack Router).
21
- - Fixing re-render storms, stale closures, effect misuse, `key` bugs.
22
- - Setting up the Vite project (aliases, env vars, code-splitting, `dist/` build).
23
-
24
- ## When NOT to use — route it
25
-
26
- - Server Components, server actions, App Router, SSR/SSG, `app/` dir → `../nextjs/SKILL.md`.
27
- - React Native / Expo / native screens → `../react-native/SKILL.md`.
28
- - The type system itself (generics, discriminated unions, `tsconfig` theory) with no React shape → `../typescript/SKILL.md`.
29
- - Visual design, tokens, spacing, button states → `../design/SKILL.md`.
30
- - Writing the Vitest/RTL or E2E suite as the task → `../testing-web/SKILL.md`.
31
- - Hosting the built `dist/` → `../deployment/SKILL.md`.
13
+ **SDD gate — before writing feature code.** If this skill fired on a **new, non-trivial feature or behaviour change** and there is **no approved spec + plan** under `02-DOCS/wiki/sdd/`, stop and hand off to `../specify/SKILL.md`: it runs brainstorm → spec → plan → tasks, then routes back here once the plan is approved. Build directly only for a genuinely one-line / low-risk change. Method: `../sdd/SKILL.md`.
32
14
 
33
15
  ## First: confirm it's a Vite SPA, not a framework
34
16
 
35
- Do this before giving any advice — it stops you applying SSR/RSC patterns to a client SPA.
36
-
37
- 1. Read `package.json`. `vite` + `react` present? Good.
38
- 2. Is `next` present? → stop, use `../nextjs/SKILL.md`.
39
- 3. Is `@react-router/dev` present (React Router **framework mode** = SSR, Remix successor)? → that is out of scope, closer to nextjs-shape. Library mode (`react-router` / `react-router-dom` alone) stays here.
40
- 4. Pick the data layer (**TanStack Query**, always — see §6) and the router (React Router v7 library mode *or* TanStack Router).
17
+ Read `package.json` before giving any advice — it stops you applying SSR/RSC patterns to a client SPA.
41
18
 
42
19
  | Signal in package.json | Read it as | Where |
43
20
  | ------------------------------------- | --------------------- | ---------------------- |
44
21
  | `vite` + `react`, no `next` | Vite SPA | here |
45
22
  | `next` | metaframework / RSC | `../nextjs/SKILL.md` |
46
- | `@react-router/dev` | RR framework mode/SSR | `../nextjs/SKILL.md`-shape |
47
- | `react-router` only | RR library mode | here (§7) |
48
- | `expo` / `react-native` | native | `../react-native/SKILL.md` |
23
+ | `@react-router/dev` | RR framework mode/SSR (Remix successor) | `../nextjs/SKILL.md`-shape |
24
+ | `react-router` only | RR library mode | here (Routing) |
25
+ | `expo` / `react-native` | native | `../react-native/SKILL.md` |
26
+
27
+ Then pick the data layer (**TanStack Query**, always) and the router (React Router v7 library mode *or* TanStack Router).
28
+
29
+ Adjacent jobs route out: the type system itself (generics, discriminated unions, `tsconfig` theory) with no React shape → `../typescript/SKILL.md`; visual design, tokens, spacing, button states → `../design/SKILL.md`; writing the Vitest/RTL or E2E suite as the task → `../testing-web/SKILL.md`; hosting the built `dist/` → `../deployment/SKILL.md`.
49
30
 
50
31
  ## Component & state architecture
51
32
 
@@ -62,7 +43,7 @@ Do this before giving any advice — it stops you applying SSR/RSC patterns to a
62
43
  | URL search params | it should be shareable / bookmarkable / survive reload |
63
44
  | Context | wide read, **low** write frequency (theme, auth user) |
64
45
  | External store (Zustand) + selectors | wide read, **high** write frequency, or deep prop-drilling |
65
- | TanStack Query cache | it's **server** data (anything fetched) — see §6 |
46
+ | TanStack Query cache | it's **server** data (anything fetched) |
66
47
 
67
48
  ```tsx
68
49
  // Bad: syncing a derived value into state with an effect → stale + extra render
@@ -76,7 +57,7 @@ const fullName = `${first} ${last}`;
76
57
  ## Hooks discipline (React 19.2)
77
58
 
78
59
  - `useState` for one or two independent values; `useReducer` when the next state depends on the previous one or several fields move together.
79
- - **The `useEffect` rule:** effects exist to *synchronize with a non-React external system* (a subscription, a DOM node, a non-React widget). They are **not** for transforming data and **not** for fetching server data (§6). If you can compute it in render or in an event handler, do that instead.
60
+ - **The `useEffect` rule:** effects exist to *synchronize with a non-React external system* (a subscription, a DOM node, a non-React widget). They are **not** for transforming data and **not** for fetching server data. If you can compute it in render or in an event handler, do that instead.
80
61
  - **`useEffectEvent`** (stable in 19.2): extract the non-reactive part of an effect so it reads the latest props/state without being a dependency. Fixes the stale-closure / over-firing class of effect bug.
81
62
 
82
63
  ```tsx
@@ -96,7 +77,7 @@ useEffect(() => {
96
77
  function Profile({ id }: { id: string }) {
97
78
  const user = use(fetch(`/api/users/${id}`).then(r => r.json())); // ❌
98
79
  }
99
- // Good: the promise is owned by a cache (see §6 useSuspenseQuery)
80
+ // Good: the promise is owned by a cache (useSuspenseQuery, below)
100
81
  ```
101
82
 
102
83
  - **`useTransition` / `useDeferredValue`** keep the UI responsive: mark a slow state update non-urgent so typing/clicks stay live.
@@ -132,7 +113,8 @@ const remove = useMutation({
132
113
  ```
133
114
 
134
115
  - **`useSuspenseQuery`** + `<Suspense fallback>` + an error boundary moves loading/error out of the component body and is the correct source for `use()`-style reads.
135
- - **Optimistic delete**: `onMutate` snapshots + writes the expected state, `onError` rolls back, `onSettled` invalidates. Full pattern (infinite, prefetch, query-key factory, optimistic) → `references/data-and-state.md`.
116
+ - **Optimistic delete**: `onMutate` snapshots + writes the expected state, `onError` rolls back, `onSettled` invalidates.
117
+ - Query-key factory, `invalidateQueries` vs `setQueryData`, infinite queries, prefetch, Zustand store + selectors, the full Bad→Good set → `references/data-and-state.md`.
136
118
 
137
119
  ## Routing (client-side)
138
120
 
@@ -151,7 +133,7 @@ const router = createBrowserRouter([
151
133
  - Use **lazy routes** so each route is its own chunk.
152
134
  - **TanStack Router** is the type-safe alternative — fully typed params/search, first-class loaders. Pick it when route/search typing matters.
153
135
  - `@react-router/dev` **framework mode** is SSR → treat like nextjs, out of scope.
154
- - Protected routes, `useSearchParams`-as-state, nested loaders → `references/routing.md`.
136
+ - Nested/lazy routes, client loaders, protected-route wrapper, `useSearchParams`-as-state → `references/routing.md`.
155
137
 
156
138
  ## Performance
157
139
 
@@ -162,7 +144,7 @@ const router = createBrowserRouter([
162
144
  - **Code-split** routes and heavy components with `lazy()` + `<Suspense>`; Vite splits automatically on dynamic `import()`.
163
145
  - **React Compiler on** ⇒ delete manual `useMemo`/`useCallback`/`React.memo` — it auto-memoizes; leaving them in is dead noise.
164
146
  - **Narrow store selectors** so a slice change doesn't re-render the whole subtree.
165
- - Targets: LCP < 2.5s, INP < 200ms, CLS < 0.1. Deep dive → `references/performance.md`.
147
+ - Targets: LCP < 2.5s, INP < 200ms, CLS < 0.1. Profiler workflow, React Compiler Vite setup, bundle analysis, re-render map → `references/performance.md`.
166
148
 
167
149
  ## TypeScript + Vite project setup
168
150
 
@@ -174,7 +156,7 @@ const router = createBrowserRouter([
174
156
 
175
157
  ## Anti-patterns → STOP
176
158
 
177
- | Rationalization | Reality | Do instead |
159
+ | Anti-pattern | Reality | Do instead |
178
160
  | ----------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------- |
179
161
  | "Fetch in `useEffect`, it's simpler" | Waterfalls, races, double-fire, no cache/dedupe | `useQuery` / `useSuspenseQuery` |
180
162
  | "Create the promise inline for `use()`" | New promise every render → suspends forever | Promise owned by a cache (TanStack Query) |
@@ -185,40 +167,10 @@ const router = createBrowserRouter([
185
167
  | "`VITE_API_SECRET` is fine, it's an env var" | It ships in the browser bundle — fully public | Proxy the secret through a backend |
186
168
  | "`useEffect` to compute a derived value" | Extra render + a stale window | Recompute in render |
187
169
 
188
- ## Quick reference
189
-
190
- | Task | API / tool | Note |
191
- | ----------------------------------- | ------------------------------------------- | ------------------------------------- |
192
- | Server data | `useQuery` | `queryKey` + `queryFn` + `staleTime` |
193
- | Mutation | `useMutation` + `invalidateQueries` | or optimistic `onMutate/onError/onSettled` |
194
- | Read a promise in render | `use()` + `<Suspense>` + error boundary | promise from a cache, never inline |
195
- | High-frequency global state | Zustand + selector | narrow the selector |
196
- | Wide, low-write state | `<Context value>` | theme/auth, not server data |
197
- | Keep UI responsive | `useTransition` / `useDeferredValue` | mark slow update non-urgent |
198
- | Non-reactive logic inside an effect | `useEffectEvent` | reads latest, not a dependency |
199
- | Client route | `createBrowserRouter` + `RouterProvider` | lazy routes for splitting |
200
- | Code-split | `lazy()` + `<Suspense>` | Vite splits on dynamic `import()` |
201
- | Build | `vite build` | emits `dist/` |
202
-
203
170
  ## Verify
204
171
 
205
- `scripts/verify.sh` runs from the project root: **ESLint → `tsc --noEmit` → Vitest → `vite build`**, in that order. Each tool is detected and **skipped with a warning (never a failure) if absent** — a repo without a tool can't fail a command it doesn't have. The final `vite build` writes `dist/`; the lint/type/test steps are read-only. No installs, no network mutations, safe to re-run. It exits non-zero only on a real tool failure, and exits 0 on a clean/empty target.
206
-
207
- ## References
208
-
209
- - `references/data-and-state.md` — TanStack Query v5 in depth (query-key factory, mutations, `invalidateQueries` vs `setQueryData`, optimistic with `onMutate/onError/onSettled`, `useSuspenseQuery`, infinite, prefetch), Zustand store + selectors, context patterns, full Bad→Good set.
210
- - `references/routing.md` — React Router v7 library mode (nested/lazy routes, client loaders, protected-route wrapper, `useSearchParams` as state) and the TanStack Router type-safe alternative; framework mode → defer.
211
- - `references/performance.md` — Profiler workflow, `key` bugs, `react-virtual`, code-splitting, React Compiler Vite setup, bundle analysis, narrow-selector re-render map.
172
+ `scripts/verify.sh` runs from the project root: **ESLint → `tsc --noEmit` → Vitest → `vite build`**, in that order. Each tool is detected and **skipped with a warning (never a failure) if absent**. The final `vite build` writes `dist/`; the lint/type/test steps are read-only. No installs, no network mutations, safe to re-run. It exits non-zero only on a real tool failure, and exits 0 on a clean/empty target.
212
173
 
213
174
  ## Project grounding (02-DOCS)
214
175
 
215
176
  If the workspace has `02-DOCS/`, record stack-specific React conventions (chosen router, store, query defaults) in `02-DOCS/wiki/stack/react.md` and index it from `CLAUDE.md`. Recorded, not gated — skip silently if there is no `02-DOCS/`.
216
-
217
- ## See also
218
-
219
- - `../nextjs/SKILL.md` — the SSR / RSC / App Router sibling (server-rendered React).
220
- - `../react-native/SKILL.md` — native mobile React.
221
- - `../typescript/SKILL.md` — the type system itself.
222
- - `../design/SKILL.md` — visual design, tokens, component states.
223
- - `../testing-web/SKILL.md` — Vitest/RTL test authoring.
224
- - `../deployment/SKILL.md` — hosting the built `dist/`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: review
3
- description: "Use when giving OR receiving an adversarial code review in the rsc SDD chain — after implement/verify and before ship. Triggers: 'review this PR', 'review my diff', 'revisa este código', 'critique this change', 'be my reviewer', 'tear this apart', 'what's wrong with this code', plus the receiving side: 'the reviewer said X', 'address this review comment', 'they want me to change Y', 'do I have to do what the review says', 'PR feedback', 'someone left comments on my PR'. Covers the full loop: producing a rigorous, evidence-backed review keyed to the spec/plan/constitution under 02-DOCS/wiki/sdd/, AND processing incoming feedback with technical rigor — verify each finding against the code before agreeing, push back on the wrong ones, fix the real ones. NOT running lint/type/test gates (that's the verify phase) and NOT merging/closing the branch (that's ship). No performative agreement; points to ship when the diff survives review."
3
+ description: "Use when a green diff needs adversarial judgment in the rsc SDD chain, between verify and ship — give a review keyed to the spec/plan/constitution in 02-DOCS/wiki/sdd/, or receive one and verify each comment before agreeing. NOT running lint/type/tests (that is `verify`), NOT the spec-less standalone pass (that is `code-review`), NOT merging (that is `ship`)."
4
4
  tags: [sdd, review, code-review]
5
5
  recommends: [ship]
6
6
  profiles: [core, full]
@@ -16,37 +16,18 @@ This skill owns **two roles that share one discipline**:
16
16
  - **Giving** a review — read a diff adversarially, find the real defects, rank them by impact, ship the verdict as evidence not opinion.
17
17
  - **Receiving** a review — take incoming feedback (human or machine), verify each point against the code *before* acting, fix the real findings, and push back — with proof — on the wrong ones.
18
18
 
19
- The discipline is the same in both directions: **claims are backed by evidence, never by deference.** A reviewer who waves through a bug to be agreeable, and an author who edits working code because a comment *sounded* authoritative, are making the same mistake. This skill exists to kill both.
19
+ The discipline is the same in both directions: **every finding and every rebuttal carries evidence — no agreement without verification, no objection without a defect.** A reviewer who waves through a bug to be agreeable, and an author who edits working code because a comment *sounded* authoritative, are making the same mistake. Everything below is in service of that one rule.
20
+
21
+ **Route out** when the ask isn't this gate: a deep OWASP / threat-model pass belongs to `../secure-coding/SKILL.md` (review folds its findings in, but the dedicated pass is owned there); a "is this idiomatic React/FastAPI/Go/SQL" question belongs to the stack skill (`../nextjs/SKILL.md`, `../fastapi/SKILL.md`, `../go/SKILL.md`, `../postgresdb/SKILL.md`, `../flutter/SKILL.md`) — pull the idiom there, then judge against it here.
20
22
 
21
23
  ## Read these first
22
24
 
23
- 1. `02-DOCS/wiki/harness/user-profile.md` — the **accompaniment dial** (L0..L3). It sets how much you narrate, not how rigorous you are. Rigor is fixed; verbosity is dialled. See "Accompaniment dial" below.
24
- 2. `02-DOCS/wiki/sdd/specs/<slug>.md` and `02-DOCS/wiki/sdd/plans/<slug>.md` — what the diff was *supposed* to do. A review with no spec is a review of vibes.
25
- 3. `02-DOCS/wiki/sdd/constitution.md` — the project's non-negotiables (stack canon, quality bars, conventions). Constitution violations are findings even when the code "works".
25
+ 1. `02-DOCS/wiki/sdd/specs/<slug>.md` and `02-DOCS/wiki/sdd/plans/<slug>.md` — what the diff was *supposed* to do. A review with no spec is a review of vibes.
26
+ 2. `02-DOCS/wiki/sdd/constitution.md` — the project's non-negotiables (stack canon, quality bars, conventions). Constitution violations are findings even when the code "works".
27
+ 3. `02-DOCS/wiki/harness/user-profile.md` — the accompaniment dial (see "Narration dial" below).
26
28
 
27
29
  If there is no spec/plan (someone jumped straight to code), say so and review against the constitution + the diff's own stated intent. Don't pretend a spec exists.
28
30
 
29
- ## When to use / When NOT to use
30
-
31
- Use when:
32
-
33
- - A diff, branch, or PR is ready and you want it torn apart before it ships.
34
- - You are the author and a review landed — comments from a teammate, a bot, or another agent — and you need to decide what to actually change.
35
- - A "review comment" feels wrong, vague, or technically questionable and you're tempted to just comply.
36
-
37
- Do NOT use when (route elsewhere):
38
-
39
- - You need lint / type-check / test / audit to run and go green → that's the **verify** phase, run before review.
40
- - The diff has survived review and you want to PR / merge / close the branch → that's the **ship** phase (and ship enforces Eric-only git authorship).
41
- - It's specifically a *security* threat-model / OWASP pass → `../secure-coding/SKILL.md` (review folds in its findings, but the deep pass is owned there).
42
- - The "review" is really framework-level "is this idiomatic React/FastAPI/Go" — pull the relevant stack skill (`../nextjs/SKILL.md`, `../fastapi/SKILL.md`, `../go/SKILL.md`, `../postgresdb/SKILL.md`, `../flutter/SKILL.md`) for the idiom, then judge against it here.
43
-
44
- ## The one rule
45
-
46
- > **Every finding and every rebuttal carries evidence. No agreement without verification, no objection without a defect.**
47
-
48
- Everything below is in service of that rule.
49
-
50
31
  ## Confidence filtering
51
32
 
52
33
  A review's value is its signal-to-noise. Every finding you report costs the author triage time, so report only what you can stand behind:
@@ -56,14 +37,7 @@ A review's value is its signal-to-noise. Every finding you report costs the auth
56
37
  - **Common false positives to skip:** patterns guarded two functions up; "unsafe" calls on values that are provably constant/internal; missing checks the framework already enforces; style the linter owns; defects behind a flag that's off everywhere (note as `nit`, not blocker); test-only or generated code held to prod standards.
57
38
  - **No severity inflation.** A `should-fix` dressed as a `blocker` burns the same trust as a missed bug. Rank by actual blast radius and reachability — if everything is a blocker, nothing is.
58
39
 
59
- ## Executable review
60
-
61
- This skill is the **discipline**; the **`rsc-review` bundle is its automated counterpart**. When you want the doctrine above run for you over a real diff:
62
-
63
- - **`/code-review [pr]`** — fans the diff out to the per-language reviewer fleet (`code-reviewer`, `web-reviewer`, `python-reviewer`, `go-reviewer`, `sql-reviewer`, `flutter-reviewer`) in parallel and aggregates one ranked verdict.
64
- - **`/security-scan`** — two-layer security pass: automated scanners plus the `security-reviewer` agent, merged into one exploitability-ranked report.
65
-
66
- Those commands and agents enforce the same evidence bar and the same confidence filtering described here — they are this skill, executed.
40
+ This skill is the discipline; the **`rsc-review` bundle is it executed** — `/code-review [pr]` fans a diff out to the per-language reviewer fleet and aggregates one ranked verdict, `/security-scan` merges automated scanners with the `security-reviewer` agent into an exploitability-ranked report. Both enforce the same evidence bar and confidence filtering described here.
67
41
 
68
42
  ---
69
43
 
@@ -114,14 +88,12 @@ A finding without a `repro` or a mechanism is a *suspicion*. Label it `[question
114
88
 
115
89
  ### Verify before you flag
116
90
 
117
- The reviewer is held to the same evidence bar as the author. Before writing a `blocker`:
91
+ The reviewer is held to the same evidence bar as the author, because a reviewer who cries blocker on a non-bug burns the same trust as an author who ships one. Before writing a `blocker`:
118
92
 
119
93
  - **Read the surrounding code**, not just the diff hunk. The check you think is missing may live two functions up.
120
94
  - **Trace the value**, don't pattern-match. "This looks like SQL injection" is not a finding; "this f-string interpolates `request.args['q']` straight into `execute()`" is.
121
95
  - **Confirm the path is reachable.** A defect behind a flag that's off in every environment is a `nit`, not a `blocker` — say so.
122
96
 
123
- A reviewer who cries blocker on a non-bug burns the same trust as an author who ships one.
124
-
125
97
  ### The verdict
126
98
 
127
99
  End every review you give with one of three, and nothing mushy in between:
@@ -184,21 +156,6 @@ When you've processed the review, summarize for the reviewer (and the decisions
184
156
 
185
157
  ---
186
158
 
187
- ## Model tier — `heavy` (opt-in routing)
188
-
189
- This phase's default model tier is **`heavy`** — adversarial diff reading is where the strongest model pays off most. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../sdd/references/model-routing.md` — announce the switch per the accompaniment dial when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
190
-
191
- ## Accompaniment dial (L0..L3)
192
-
193
- Read the level from `02-DOCS/wiki/harness/user-profile.md`. **It changes the narration, never the rigor** — every level runs the same passes and the same evidence bar.
194
-
195
- - **L0** — verdict + the blocker list, terse. `CHANGES REQUESTED: 1 blocker (auth, documents.py:42), 1 nit. Fix the auth scope and re-run verify.`
196
- - **L1** — each finding gets its one-line *why*.
197
- - **L2** — full finding format (where/why/repro/fix); explain why each blocker blocks.
198
- - **L3** — the above plus teaching: name the defect class (IDOR, N+1, TOCTOU), why the boundary matters, and how to not reintroduce it. For non-technical authors, translate the impact ("any logged-in person could read everyone else's documents").
199
-
200
- When no profile exists, default to L2 and proceed — don't stall a review to ask for a dial setting.
201
-
202
159
  ## Anti-patterns → STOP
203
160
 
204
161
  | Rationalization | Reality |
@@ -212,6 +169,19 @@ When no profile exists, default to L2 and proceed — don't stall a review to as
212
169
  | "I'll approve it, the issues are minor" | If they're truly minor, label them nits and approve. If they block, don't approve. No mushy middle. |
213
170
  | "No spec, so I'll just eyeball it" | Say there's no spec and review against the constitution + stated intent. Don't fake a baseline. |
214
171
 
172
+ ## Narration dial
173
+
174
+ Read the level from `02-DOCS/wiki/harness/user-profile.md`. It changes what a review *shows*, never its rigor — every level runs the same passes and the same evidence bar. No profile → default L2 and proceed; don't stall a review to ask for a dial setting.
175
+
176
+ - **L0** — verdict + the blocker list, terse. `CHANGES REQUESTED: 1 blocker (auth, documents.py:42), 1 nit. Fix the auth scope and re-run verify.`
177
+ - **L1** — each finding gets its one-line *why*.
178
+ - **L2** — full finding format (where/why/repro/fix); explain why each blocker blocks.
179
+ - **L3** — the above plus teaching: name the defect class (IDOR, N+1, TOCTOU), why the boundary matters, and how to not reintroduce it. For non-technical authors, translate the impact ("any logged-in person could read everyone else's documents").
180
+
181
+ ## Model tier — `heavy` (opt-in routing)
182
+
183
+ This phase's default model tier is **`heavy`** — adversarial diff reading is where the strongest model pays off most. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`. When on: resolve this phase's tier (`models.overrides` wins over `models.phases`), map it to a model via `models.tiers`, and apply per `../sdd/references/model-routing.md` — announce the switch per the narration dial when it differs from the session model, and dispatch any `Task`/`parallel` subagents on that model. Routing off or no profile → honor the session model silently. Never fake a switch a tool can't make; skip routing on a one-line change.
184
+
215
185
  ## Where this writes
216
186
 
217
187
  Review is mostly a conversation, but two artifacts persist into the harness wiki so the knowledge model grows:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rust
3
- description: "Use when writing, reviewing, testing, or shipping Rust code or async services - ownership/borrowing (move vs borrow vs clone, Arc/Rc/RefCell, lifetimes), error modeling with Result/?/thiserror/anyhow, async on tokio (.await, JoinSet, select!, spawn_blocking, Send-across-await), axum 0.8 HTTP handlers/extractors/State, cargo test (unit/integration/doctests), and sqlx + cargo-audit security. Triggers: \"write an axum/tokio service\", \"is this idiomatic Rust\", \"add tests\", compiler error text like \"value moved here\", \"borrow of moved value\", \"future cannot be sent between threads safely\", \"does not live long enough\", \"cannot borrow as mutable\", \"el borrow checker no em deixa\", \"petición async en Rust\", .rs files, Cargo.toml, thiserror, sqlx. NOT desktop/GUI shells (that is tauri)."
3
+ description: "Use when writing, reviewing, testing, or shipping Rust — ownership and the borrow checker (move/borrow/clone, Arc/RefCell, lifetimes), errors with Result/`?`/thiserror/anyhow, async on tokio, axum 0.8 services, cargo test, and sqlx + cargo-audit hardening. NOT the same service in Go (that is `go`), NOT a desktop webview shell (that is `tauri`)."
4
4
  tags: [rust, tokio, axum, async, backend, service]
5
5
  recommends: [go, postgresdb, secure-coding, deployment]
6
6
  origin: risco
@@ -21,60 +21,12 @@ The thing an agent gets wrong in Rust is almost never syntax — it is *ownershi
21
21
  compile errors about moves, borrows, and `Send + Sync` across `.await`. Front-load that mental model;
22
22
  the rest follows.
23
23
 
24
- ## When to use / When NOT to use
25
-
26
- **Use when:**
27
-
28
- - Authoring, reviewing, or testing any `.rs` file, `Cargo.toml`, or workspace.
29
- - Fighting the borrow checker: "value moved here", "cannot borrow as mutable", "does not live long
30
- enough", or choosing between `Rc`/`Arc`/`RefCell`/`Mutex`.
31
- - Modeling errors: `Result<T, E>`, `?`, a `thiserror` enum vs `anyhow::Result`, `#[from]` conversions,
32
- mapping a domain error to an HTTP status.
33
- - Async on tokio: `#[tokio::main]`, `.await`, `JoinSet`, `select!`, channels, `spawn_blocking`,
34
- cancellation, the "future is not `Send`" / "held across await" error class.
35
- - Building an axum service: routers, extractors, `State`, `IntoResponse`, tower middleware, shutdown.
36
-
37
- **When NOT to use (delegate):**
38
-
39
- - Language-agnostic threat modeling / authz / OWASP review -> `secure-coding` (this skill keeps the
40
- Rust-specific controls: sqlx parametrization, `#![forbid(unsafe_code)]`, `cargo audit`).
41
- - Dockerfile / CI pipeline / shipping infra -> `deployment` (this skill carries only a multi-stage
42
- Docker note + release-profile flags).
43
- - Pure SQL schema/index tuning -> `postgresdb` (this skill covers only the Rust-side sqlx query).
44
- - Native GUI / desktop shell with a webview -> `tauri`; cross-platform app work is out of scope.
45
- - A Go / Python / Node service -> `go` / `fastapi` / `nodejs` respectively.
46
-
47
- Rust error modeling and the HTTP error->status contract live **here**, in Rust terms — they are not a
48
- separate skill. `go` is the structural twin: same shape, GC + goroutines instead of ownership + futures.
49
-
50
- ## Decision rules
51
-
52
- Apply these on every Rust edit:
53
-
54
- 1. Prefer borrowing (`&T`) over moving over cloning; `.clone()` to silence the borrow checker is a
55
- smell, not a fix — it hides the real ownership question.
56
- 2. Take `&str`/`&[T]` in function params, return owned `String`/`Vec<T>`; borrow on the way in, own on
57
- the way out. It is the most flexible and the cheapest.
58
- 3. `?` over `.unwrap()`/`.expect()` everywhere off the test path; an `.unwrap()` on untrusted input is a
59
- remote panic.
60
- 4. Model errors with a `thiserror` enum when callers branch on the failure mode; reach for `anyhow` only
61
- at the application boundary (`main`, top-level handlers) where you just need context + a backtrace.
62
- 5. Never hold a `MutexGuard` (or any non-`Send` value) across an `.await` — it deadlocks or fails to
63
- compile with "future cannot be sent between threads".
64
- 6. Bound concurrency with a `JoinSet` (or a semaphore); a loop of bare `tokio::spawn` is unbounded
65
- fan-out that can exhaust the runtime.
66
- 7. `spawn_blocking` for CPU-bound or synchronous-blocking work; blocking inside an async task starves
67
- the executor's worker threads.
68
- 8. Validate at the boundary, parse into a typed domain model; "parse, don't validate" — make illegal
69
- states unrepresentable.
70
- 9. Parametrize every sqlx query with bind args; `format!` into SQL is injection.
71
- 10. `#![forbid(unsafe_code)]` at the crate root unless you have a measured, reviewed, documented reason;
72
- `clippy -D warnings` and `cargo fmt --check` are build gates, not suggestions.
73
-
74
24
  ## Ownership & borrowing (essentials)
75
25
 
76
26
  This is the skill's center of gravity. Three moves: **move** (transfer ownership), **borrow** (`&`/`&mut`,
77
- no transfer), **clone** (a real copy, real cost).
27
+ no transfer), **clone** (a real copy, real cost) — and prefer them in that order, borrow first.
28
+ Take `&str`/`&[T]` in function params, return owned `String`/`Vec<T>`: borrow on the way in, own on the
29
+ way out is both the most flexible and the cheapest.
78
30
 
79
31
  ```rust
80
32
  fn print_name(name: &str) { println!("{name}"); } // borrows; caller keeps ownership
@@ -127,8 +79,8 @@ Lifetimes, `'static`, `Cow`, and the full smart-pointer tree -> `references/owne
127
79
 
128
80
  ## Errors
129
81
 
130
- Error modeling is owned here. The model: `Result<T, E>` + `?`, typed enums for libraries, `anyhow` at
131
- the edge, one mapping from a domain enum to an HTTP status.
82
+ Error modeling is owned here, in Rust terms — it is not a separate skill. The model: `Result<T, E>` +
83
+ `?`, typed enums for libraries, `anyhow` at the edge, one mapping from a domain enum to an HTTP status.
132
84
 
133
85
  ```rust
134
86
  use thiserror::Error;
@@ -172,9 +124,7 @@ impl IntoResponse for UserError {
172
124
  }
173
125
  ```
174
126
 
175
- **Anti-patterns:** `.unwrap()`/`.expect()` on the request path (a panic = 500 + a stack trace, or a
176
- crashed worker); stringly-typed errors you `match` on by message; `Box<dyn Error>` smeared everywhere so
177
- nothing can branch on the failure. Full repo->service->handler skeleton -> `references/axum-service.md`.
127
+ Full repo->service->handler skeleton -> `references/axum-service.md`.
178
128
 
179
129
  ## Async (tokio, essentials)
180
130
 
@@ -242,7 +192,9 @@ let app = Router::new()
242
192
  .with_state(state);
243
193
  ```
244
194
 
245
- Full skeleton — tower middleware (`TraceLayer`, timeout, request-id), graceful shutdown via
195
+ Validate at the boundary and parse into a typed domain model — "parse, don't validate" makes illegal
196
+ states unrepresentable, so the handler body never re-checks. Full skeleton — tower middleware
197
+ (`TraceLayer`, timeout, request-id), graceful shutdown via
246
198
  `axum::serve(...).with_graceful_shutdown(...)`, and JSON helpers -> `references/axum-service.md`.
247
199
 
248
200
  ## Project layout
@@ -302,7 +254,8 @@ sqlx::query_as!(User, "SELECT id, name FROM users WHERE id = $1", id).fetch_one(
302
254
  `#![forbid(unsafe_code)]` at the crate root; run `cargo audit` (RustSec advisories) and `cargo deny` (license
303
255
  + ban + advisory policy) in CI; never `.unwrap()` on untrusted input — a malicious request becomes a
304
256
  panic. Read secrets from env or a secret manager, never hardcode or log them. Deeper authz / threat
305
- modeling -> `secure-coding`.
257
+ modeling -> [`secure-coding`](../secure-coding/SKILL.md). Pure SQL schema/index/plan tuning ->
258
+ [`postgresdb`](../postgresdb/SKILL.md); this skill covers only the Rust-side sqlx query.
306
259
 
307
260
  ## Production
308
261
 
@@ -323,73 +276,45 @@ strip = true # strip symbols
323
276
 
324
277
  Expose `/healthz` (static 200 liveness) and `/readyz` (pings the DB pool, 503 on failure). Docker:
325
278
  multi-stage build, `cargo build --release`, copy the binary onto a distroless/slim base. Full
326
- Containerfile + CI -> `deployment`.
279
+ Containerfile + CI -> [`deployment`](../deployment/SKILL.md).
327
280
 
328
- ## Anti-patterns / rationalizations -> STOP
281
+ ## Anti-patterns
329
282
 
330
- | Rationalization | Reality / Do instead |
283
+ | Anti-pattern | Reality / Do instead |
331
284
  | --- | --- |
332
- | ".clone() to make the borrow checker happy" | It hides the real ownership question; borrow, or restructure who owns what. |
333
- | ".unwrap() here, it can't fail" | It can, and a panic on the request path is a 500/crash; use `?` + a typed error. |
334
- | "`Box<dyn Error>` everywhere is simpler" | Nothing can branch on the failure; use a `thiserror` enum the caller can match. |
335
- | "`#[async_trait]` on every async trait" | Edition 2024 has native async fn in traits; drop the macro for most cases. |
336
- | "lock the Mutex, then .await" | Guard held across await = deadlock / not-`Send`; drop it first or use `tokio::sync::Mutex`. |
337
- | "`block_on` inside this async fn" | Nesting a runtime panics/deadlocks; restructure to `.await`. |
338
- | "just `tokio::spawn` in the loop" | Unbounded fan-out exhausts the runtime; bound it with `JoinSet`/semaphore. |
339
- | "`unsafe` to get past the borrow checker" | `unsafe` turns a compile error into UB; the checker was right — restructure. |
340
- | "`format!` the id into the SQL, it's trusted" | Injection; bind parameters with `$1` always. |
341
- | "skip clippy, it's just style" | clippy catches correctness (`.unwrap()` on `Option`, await-holds-lock); gate on `-D warnings`. |
342
- | "`Arc<Mutex<T>>` for everything shared" | If data flows one way it is a channel; reach for `mpsc` first. |
343
- | "`String` params everywhere" | Take `&str`; you force needless allocations and lose flexibility. |
344
-
345
- ## Quick reference
346
-
347
- | Task | Command / idiom |
285
+ | `.clone()` to make the borrow checker happy | It hides the real ownership question; borrow, or restructure who owns what. |
286
+ | `.unwrap()` / `.expect()` off the test path | A panic on the request path is a 500 or a crashed worker; use `?` + a typed error. |
287
+ | Matching an error by its message string | Messages are prose and they change; match the enum variant. |
288
+ | `Box<dyn Error>` everywhere because it is simpler | Nothing can branch on the failure; use a `thiserror` enum the caller can match. |
289
+ | `#[async_trait]` on every async trait | Edition 2024 has native async fn in traits; drop the macro for most cases. |
290
+ | `block_on` inside an async fn | Nesting a runtime panics or deadlocks; restructure to `.await`. |
291
+ | A bare `tokio::spawn` per loop iteration | Unbounded fan-out exhausts the runtime; bound it with `JoinSet`/semaphore. |
292
+ | `Arc<Mutex<T>>` for everything shared | If data flows one way it is a channel; reach for `mpsc` first. |
293
+ | `unsafe` to get past the borrow checker | `unsafe` turns a compile error into UB; the checker was right — restructure. |
294
+ | Skipping clippy as "just style" | clippy catches correctness (`.unwrap()` on `Option`, await-holds-lock); gate on `-D warnings`. |
295
+
296
+ ## Gates & commands
297
+
298
+ | Task | Command |
348
299
  | --- | --- |
349
300
  | Format check | `cargo fmt --all -- --check` |
350
301
  | Lint (gate) | `cargo clippy --all-targets -- -D warnings` |
351
302
  | Test | `cargo test` / `cargo nextest run` |
352
303
  | Doctests | `cargo test --doc` |
353
304
  | Audit deps | `cargo audit` / `cargo deny check` |
354
- | Propagate error | `let v = thing()?;` |
355
- | Library error | `#[derive(Error)]` enum + `#[from]` |
356
- | App error | `anyhow::Result<T>` + `.context(...)` |
357
- | Async test | `#[tokio::test] async fn ...` |
358
- | Bound concurrency | `JoinSet` + `join_next().await` |
359
- | Off-runtime work | `spawn_blocking(move || ...).await?` |
360
305
  | Local gate | `./scripts/verify.sh` (run in your crate root) |
361
306
 
362
- ## Project grounding (02-DOCS + CLAUDE.md)
363
-
364
- When this skill runs in a project with a `02-DOCS/` layer (the
365
- [`harness`](../harness/SKILL.md) Karpathy wiki), record this project's service decisions there and index
366
- them from the root `CLAUDE.md`, so the next agent inherits the conventions instead of re-deriving them.
367
-
368
- 1. **Find the article** `02-DOCS/wiki/stack/rust.md`, indexed in `02-DOCS/wiki/index.md` (the
369
- Knowledge map index; root `CLAUDE.md` points to it).
370
- 2. **If missing or stale**, create/update it with the project's real choices — the crate/workspace layout,
371
- the runtime (tokio), the HTTP framework (axum 0.8), the error strategy (thiserror enum + IntoResponse
372
- mapping), the DB layer (sqlx + pool), and tracing/concurrency defaults — then index it in
373
- `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short pointer to it).
374
- 3. **Read it first on every use** and stay consistent; when a convention changes, update the article
375
- (bump its `Updated` date) in the same change.
376
-
377
- No `02-DOCS/` layer? Skip silently (optionally suggest `harness`). Technical conventions are *recorded,
378
- not gated* — never block the task on this.
379
-
380
- ## See Also
381
-
382
- Sibling skills (all resolve under `skills/`):
307
+ Format and lint are build gates, not suggestions.
383
308
 
384
- - [`go`](../go/SKILL.md) - the structural twin: same write/review/test/ship service shape, GC + goroutines + multi-return errors instead of ownership + futures + `Result`.
385
- - [`secure-coding`](../secure-coding/SKILL.md) - threat modeling and language-agnostic authz/abuse/OWASP review (this skill keeps the Rust-specific controls).
386
- - [`postgresdb`](../postgresdb/SKILL.md) - SQL schema/index/query-plan tuning (this skill covers only the Rust-side sqlx query).
387
- - [`deployment`](../deployment/SKILL.md) - Docker multi-stage, CI, shipping infra (this skill ships only the Docker note + release-profile flags).
388
- - [`harness`](../harness/SKILL.md) - the `02-DOCS/` workspace wiki where per-project Rust conventions are recorded (see "Project grounding").
309
+ ## Project grounding (02-DOCS)
389
310
 
390
- Local references (read when):
311
+ In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), the service decisions
312
+ live in `02-DOCS/wiki/stack/rust.md`, indexed from `02-DOCS/wiki/index.md`. Read it first and stay
313
+ consistent; if it is missing or stale, write the project's real choices there — crate/workspace layout,
314
+ runtime (tokio), HTTP framework (axum 0.8), error strategy (thiserror enum + `IntoResponse` mapping), DB
315
+ layer (sqlx + pool), tracing and concurrency defaults — bump its `Updated` date, and index it. No
316
+ `02-DOCS/`? Skip silently. Conventions are *recorded, not gated* — never block the task on this.
391
317
 
392
- - `references/ownership.md` - move/borrow/clone deep dive, the borrow-checker error catalog, lifetimes, `'static`, `Cow`, smart-pointer tree, interior mutability.
393
- - `references/async-tokio.md` - runtime model, `JoinSet`/`select!`/channels, cancellation, `spawn_blocking`, `Send + Sync` across await, bounded-concurrency + retry helper.
394
- - `references/axum-service.md` - full skeleton: router, extractors, `State`, error->response, tower middleware, graceful shutdown, JSON helpers.
395
- - `references/testing.md` - unit/integration/doctests, `#[tokio::test]`, `tests/` HTTP tests against the Router, fakes/trait mocks, `cargo nextest`, `insta` snapshots.
318
+ [`go`](../go/SKILL.md) is the structural twin: same write/review/test/ship service shape, GC +
319
+ goroutines + multi-return errors instead of ownership + futures + `Result`. A desktop shell around a
320
+ webview is [`tauri`](../tauri/SKILL.md), not this.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sdd-init
3
- description: "Use when calibrating an existing repo before running the rsc SDD flow: detecting stack, package manager, test runners, lint/type/build commands, monorepo signals, artifact store, execution mode, review budget, strict TDD capability, and project skill registry. Triggers: 'calibrate this repo for SDD', 'run sdd init', 'detect my test runner before implementing', 'set up SDD config', 'prepare this repo for spec-driven development', 'configure per-phase model routing', 'assign models per SDD phase', 'set up cheaper model for implementation'. NOT first-contact user/workspace bootstrap (init), NOT 01-TOOLS/02-DOCS scaffolding (harness), NOT writing a feature spec (specify)."
3
+ description: "Use when calibrating an existing repo for the rsc SDD chain: detect stack, package manager, test runners and apply/verify commands, refresh the skill registry, and write the runtime config at 02-DOCS/wiki/sdd/config.yaml. NOT the dispatcher that routes SDD phases (that is `sdd`), NOT first-contact user/workspace bootstrap (that is `init`)."
4
4
  tags: [sdd, init, config, testing, registry]
5
5
  recommends: [sdd, specify, implement, verify]
6
6
  profiles: [core, full]
@@ -9,13 +9,13 @@ origin: risco
9
9
 
10
10
  # sdd-init — calibrate the repo before the SDD chain
11
11
 
12
- `sdd-init` is step zero for technical SDD work. It does not profile the user and it does not scaffold the harness. `init` owns first contact; `harness` owns `01-TOOLS/` and `02-DOCS/`. This skill reads the repo, detects how it should be built and tested, refreshes the cheap skill registry, and writes one durable config:
12
+ Step zero for technical SDD work: read the repo, detect how it should be built and tested, refresh the cheap skill registry, and write one durable config:
13
13
 
14
14
  ```text
15
15
  02-DOCS/wiki/sdd/config.yaml
16
16
  ```
17
17
 
18
- That config is the runtime contract later phases read before choosing commands, TDD strictness, artifact paths, review budget or skill briefs.
18
+ That config is the runtime contract later phases read before choosing commands, TDD strictness, artifact paths, review budget or skill briefs. `sdd` dispatches the phases that consume it, `init` owns first contact and user profiling, `harness` owns `01-TOOLS/` and `02-DOCS/` scaffolding. This skill only calibrates.
19
19
 
20
20
  ## Inputs
21
21
 
@@ -30,7 +30,7 @@ If `02-DOCS/` does not exist, create only the `02-DOCS/wiki/sdd/` path needed fo
30
30
 
31
31
  ## Preflight Choices
32
32
 
33
- Ask only when the answer changes behavior. At L0/L1 infer defaults and show them; at L2/L3 explain the trade-off.
33
+ Ask only when the answer changes behavior; let the accompaniment level in the profile set how much you explain each trade-off.
34
34
 
35
35
  | Setting | Default | Options |
36
36
  | --- | --- | --- |
@@ -38,7 +38,7 @@ Ask only when the answer changes behavior. At L0/L1 infer defaults and show them
38
38
  | `artifact_store` | `02-DOCS/wiki/sdd` | Keep RSC artifacts in `02-DOCS`; do not create an `openspec/` parallel tree. |
39
39
  | `review_budget.line_budget` | `400` | Lower for solo tight review; higher only with explicit approval. |
40
40
  | `delivery_strategy.default` | `ask-on-risk` | `ask-on-risk`, `single-pr`, `autochain`, `exception`. |
41
- | `models.enabled` | `false` | Per-phase model routing is opt-in. Leave off unless the user asks for it; flipping it on is their call. |
41
+ | `models.enabled` | `false` | Per-phase model routing is opt-in; leave off unless the user asks for it. |
42
42
  | `models.provider` | `anthropic` | Which provider the tiers resolve to. Set from the detected assistant when obvious, else `anthropic`. See `../sdd/references/model-routing.md` for other providers. |
43
43
 
44
44
  ## Detection
@@ -70,7 +70,7 @@ This writes:
70
70
  .rsc/skill-registry.md
71
71
  ```
72
72
 
73
- Later phases use this as a cheap index: id, trigger, tags, path, installed/available, hash. Do not load every skill into context. Select the few matching the phase and stack, then digest them into compact rules for subagents.
73
+ Later phases use it as a cheap index — id, trigger, tags, path, installed/available, hash — to select the few skills matching the phase and stack and digest them into compact rules for subagents.
74
74
 
75
75
  ## Equip the repo — install the skills this stack needs
76
76
 
@@ -145,14 +145,9 @@ models:
145
145
  overrides: {} # per-phase tier overrides set by the user
146
146
  ```
147
147
 
148
- The `models` block is the **per-phase model routing** profile: each phase declares a tier
149
- (`heavy`/`balanced`/`light`) and the tiers resolve to concrete models for `provider`. It ships
150
- **off** (`enabled: false`) — write it so the user can opt in, never switch models behind their
151
- back. The full protocol (how phases apply it, the per-assistant switch mechanism, the
152
- provider→model table) lives in `../sdd/references/model-routing.md`. Keep this block byte-for-byte
153
- in sync with that reference.
148
+ The `models` block is the **per-phase model routing** profile: each phase declares a tier (`heavy`/`balanced`/`light`) and the tiers resolve to concrete models for `provider`. The full protocol — how phases apply it, the per-assistant switch mechanism, the provider→model table — lives in `../sdd/references/model-routing.md`. Keep this block byte-for-byte in sync with that reference.
154
149
 
155
- Preserve user edits if the file exists: update detected facts and leave comments/custom policy fields intact when possible. **Never flip `models.enabled` or drop `models.overrides` on re-calibration** — those are user choices; preserve them verbatim and only refresh `tiers`/`provider` if the user asks. If preservation is risky, write a proposed replacement next to it as `config.proposed.yaml` and ask.
150
+ Preserve user edits if the file exists: update detected facts and leave comments/custom policy fields intact when possible. **Never flip `models.enabled` or drop `models.overrides` on re-calibration** — a calibration pass that silently switches the user's models is the one failure they cannot detect from the diff, so preserve both verbatim and only refresh `tiers`/`provider` if the user asks. If preservation is risky, write a proposed replacement next to it as `config.proposed.yaml` and ask.
156
151
 
157
152
  ## Result Envelope
158
153
 
@@ -186,7 +181,7 @@ End with the standard SDD result envelope:
186
181
  | "I'll skip config and remember the commands in chat." | Chat is not source of truth. Write `config.yaml`. |
187
182
  | "No test command detected, but I'll still say strict TDD is active." | Strict TDD needs a runner. Record the gap. |
188
183
  | "Load all skills so the agent has context." | That pollutes context. Use registry -> selected skills -> compact rules. |
189
- | "This is the same as init." | No. `init` profiles user/workspace; `sdd-init` calibrates technical SDD runtime. |
184
+ | "This is the same as init." | No. `init` profiles user/workspace, `sdd` dispatches the phases, `sdd-init` calibrates the technical SDD runtime they both rely on. |
190
185
  | "Create openspec/ because Gentle does." | RSC uses `02-DOCS/wiki/sdd/` as source of truth. |
191
186
 
192
187
  ## Next