@ericrisco/rsc 0.1.44 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/manifest.json +110 -86
- package/package.json +1 -1
- package/scripts/lib/domains.js +1 -1
- package/scripts/skill-migration-brief.md +104 -0
- package/skills/ab-testing/SKILL.md +9 -41
- package/skills/accessibility/SKILL.md +5 -7
- package/skills/ads/SKILL.md +9 -24
- package/skills/agent-eval/SKILL.md +7 -33
- package/skills/agent-safety/SKILL.md +7 -14
- package/skills/ai-media/SKILL.md +4 -20
- package/skills/analytics/SKILL.md +8 -30
- package/skills/analyze/SKILL.md +24 -59
- package/skills/angular/SKILL.md +12 -25
- package/skills/api-connector-builder/SKILL.md +10 -27
- package/skills/api-design/SKILL.md +7 -14
- package/skills/article-writing/SKILL.md +1 -18
- package/skills/astro/SKILL.md +36 -54
- package/skills/author-skill/SKILL.md +41 -74
- package/skills/author-skill/evals/README.md +5 -5
- package/skills/author-skill/evals/cases.yaml +8 -8
- package/skills/author-skill/references/description-recipe.md +25 -26
- package/skills/author-skill/references/eval-authoring.md +1 -1
- package/skills/author-skill/references/rsc-conventions.md +1 -1
- package/skills/automation-flows/SKILL.md +6 -16
- package/skills/automation-strategy/SKILL.md +1 -9
- package/skills/aws-essentials/SKILL.md +6 -23
- package/skills/backups/SKILL.md +1 -7
- package/skills/bash-scripting/SKILL.md +5 -9
- package/skills/bookkeeping/SKILL.md +2 -10
- package/skills/brand-identity/SKILL.md +18 -44
- package/skills/brand-voice/SKILL.md +4 -31
- package/skills/building-agents/SKILL.md +20 -103
- package/skills/business-intelligence/SKILL.md +9 -23
- package/skills/calendar-scheduling/SKILL.md +11 -27
- package/skills/case-studies/SKILL.md +3 -8
- package/skills/chatbot/SKILL.md +6 -23
- package/skills/chrome-extension/SKILL.md +2 -2
- package/skills/clarify/SKILL.md +31 -72
- package/skills/clickhouse-analytics/SKILL.md +2 -8
- package/skills/client-onboarding/SKILL.md +17 -19
- package/skills/cloudflare/SKILL.md +3 -8
- package/skills/code-review/SKILL.md +10 -23
- package/skills/codebase-onboarding/SKILL.md +2 -7
- package/skills/cold-outreach/SKILL.md +9 -30
- package/skills/community/SKILL.md +4 -31
- package/skills/competitor-watch/SKILL.md +20 -35
- package/skills/compliance/SKILL.md +10 -37
- package/skills/compose-multiplatform/SKILL.md +1 -1
- package/skills/constitution/SKILL.md +8 -35
- package/skills/content-engine/SKILL.md +4 -16
- package/skills/context-budget/SKILL.md +3 -21
- package/skills/continuous-learning/SKILL.md +10 -40
- package/skills/contracts/SKILL.md +6 -17
- package/skills/coolify/SKILL.md +8 -14
- package/skills/cost-tracking/SKILL.md +3 -11
- package/skills/course-builder/SKILL.md +17 -45
- package/skills/course-storytelling/SKILL.md +26 -94
- package/skills/cpp/SKILL.md +26 -71
- package/skills/csharp-dotnet/SKILL.md +19 -38
- package/skills/customer-support/SKILL.md +14 -37
- package/skills/dashboard/SKILL.md +6 -26
- package/skills/data-cleaning/SKILL.md +37 -63
- package/skills/data-policy/SKILL.md +8 -21
- package/skills/data-scraper/SKILL.md +1 -7
- package/skills/debug/SKILL.md +10 -49
- package/skills/deployment/SKILL.md +38 -101
- package/skills/design/SKILL.md +34 -116
- package/skills/fable-operator/SKILL.md +1 -4
- package/skills/fastapi/SKILL.md +50 -82
- package/skills/flutter/SKILL.md +41 -89
- package/skills/go/SKILL.md +57 -105
- package/skills/harness/SKILL.md +52 -68
- package/skills/implement/SKILL.md +1 -1
- package/skills/init/SKILL.md +152 -168
- package/skills/marketing/SKILL.md +30 -80
- package/skills/nextjs/SKILL.md +42 -116
- package/skills/orient/SKILL.md +25 -20
- package/skills/parallel/SKILL.md +9 -75
- package/skills/plan/SKILL.md +92 -165
- package/skills/postgresdb/SKILL.md +52 -72
- package/skills/presentations/SKILL.md +49 -87
- package/skills/python/SKILL.md +23 -72
- package/skills/react/SKILL.md +19 -67
- package/skills/review/SKILL.md +22 -52
- package/skills/rust/SKILL.md +40 -115
- package/skills/sdd-init/SKILL.md +9 -14
- package/skills/secure-coding/SKILL.md +80 -109
- package/skills/ship/SKILL.md +25 -55
- package/skills/tasks/SKILL.md +37 -61
- package/skills/typescript/SKILL.md +22 -55
- package/skills/verify/SKILL.md +8 -32
- package/skills/viral-score/SKILL.md +122 -0
- package/skills/viral-score/evals/README.md +16 -0
- package/skills/viral-score/evals/cases.yaml +50 -0
- package/skills/vue-nuxt/SKILL.md +45 -106
- package/skills/worktrees/SKILL.md +24 -69
package/skills/react/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: react
|
|
3
|
-
description: "Use when building or reviewing a client-side React SPA
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
48
|
-
| `expo` / `react-native` | native
|
|
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)
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
-
-
|
|
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.
|
|
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
|
-
|
|
|
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
|
|
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/`.
|
package/skills/review/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
|
-
description: "Use when
|
|
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: **
|
|
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/
|
|
24
|
-
2. `02-DOCS/wiki/sdd/
|
|
25
|
-
3. `02-DOCS/wiki/
|
|
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
|
-
|
|
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:
|
package/skills/rust/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rust
|
|
3
|
-
description: "Use when writing, reviewing, testing, or shipping Rust
|
|
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>` +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
281
|
+
## Anti-patterns
|
|
329
282
|
|
|
330
|
-
|
|
|
283
|
+
| Anti-pattern | Reality / Do instead |
|
|
331
284
|
| --- | --- |
|
|
332
|
-
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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.
|
package/skills/sdd-init/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sdd-init
|
|
3
|
-
description: "Use when calibrating an existing repo
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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** —
|
|
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
|
|
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
|