@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: secure-coding
3
- description: "Use when threat-modeling a feature, reviewing code or a diff for security, hardening authentication/authorization, handling secrets, configuring CORS/CSP/security headers, or fixing OWASP-class vulnerabilities (broken access control, injection, SSRF, auth failures, supply-chain) in FastAPI/Python, Go, Next.js, or Flutter. Triggers: 'security review', 'threat model this', 'is this safe', 'harden auth', 'rotate secrets', 'fix this vuln', 'OWASP', 'why is this endpoint exposed', before merging an endpoint that touches auth/payments/PII/uploads."
3
+ description: "Use when securing application code: threat-modeling a feature, security-reviewing a diff, hardening auth/authz, secrets, CORS/CSP, or fixing OWASP-class bugs (access control, injection, SSRF, supply chain) in FastAPI, Next.js, Go or Flutter. NOT running the scanners (that is security-scan), NOT agent prompt-injection guardrails (that is agent-safety)."
4
4
  tags: [security, owasp, stride, auth, review]
5
5
  recommends: [deployment]
6
6
  origin: risco
@@ -22,23 +22,15 @@ Operating posture:
22
22
  - **Every finding ships a fix.** Never "consider sanitizing" — show the
23
23
  corrected code for *this* stack.
24
24
 
25
- ## When to use / When NOT to use
26
-
27
- **Use when:** adding/reviewing an endpoint touching auth, money, PII, file
28
- uploads, or external URLs; a diff needs a security pass before merge; designing
29
- a feature (threat-model before code); hardening cookies/tokens/CORS/CSP/TLS/
30
- rate limits/password hashing/MFA; handling secrets, dependency CVEs, lockfile
31
- integrity, or CI security gates.
32
-
33
- **Do NOT use for:**
34
-
35
- - **Agent / Claude-Code config security** (`.claude/`, hooks, MCP, prompt
36
- injection, sandboxing) — a *different* concern. This skill is about the
37
- **application code the user ships**. Point there (See Also) and stay in lane.
38
- - Pure infra/network firewalling with no code change — defer.
39
- - Pentest/bounty PoC against a third party — out of scope (legal); this skill
40
- defends code, it does not attack external targets.
41
- - Trivial non-security refactors — don't gate them through `verify.sh`.
25
+ Stay in lane — this is about **the application code the user ships**. Agent /
26
+ Claude-Code config security (`.claude/`, hooks, MCP, prompt injection,
27
+ sandboxing) is a *different* concern: [`agent-safety`](../agent-safety/SKILL.md),
28
+ [`building-agents`](../building-agents/SKILL.md). Running the scanners and
29
+ triaging their raw output: [`security-scan`](../security-scan/SKILL.md).
30
+ Infra/network firewalling with no code change: [`deployment`](../deployment/SKILL.md).
31
+ A pentest/bounty PoC against a third party is out of scope (legal) — this skill
32
+ defends code, it does not attack external targets. Don't gate trivial
33
+ non-security refactors through `verify.sh`.
42
34
 
43
35
  ## The 30-second model: lethal trifecta + trust boundaries
44
36
 
@@ -48,15 +40,21 @@ integrity, or CI security gates.
48
40
  - **Trust-boundary rule:** every untrusted→trusted crossing is a checkpoint with
49
41
  one owning defense: HTTP body→SQL, user string→shell/URL/HTML, JWT claim→authz
50
42
  decision, filename→fs path, upload→disk/exec.
43
+ - **Least privilege / least agency** for tokens, DB roles, CORS origins and file
44
+ perms. It is the one control that still caps blast radius *after* one of the
45
+ defenses below fails, so it is never optional.
51
46
 
52
- | Untrusted source | Dangerous sink | Defense | Reference |
47
+ | Untrusted source | Dangerous sink | Defense | Ref |
53
48
  |---|---|---|---|
54
- | Request body | SQL query | Parameterize (bound params / ORM) | `references/owasp-by-stack.md` A03 |
55
- | User URL | Outbound fetch | https-only + IP allowlist, block private ranges | `references/owasp-by-stack.md` A10 |
56
- | User HTML | DOM render | Encode by context / DOMPurify allowlist | `references/owasp-by-stack.md` A03 |
57
- | Filename | Filesystem path | Canonicalize + base-dir containment | `references/owasp-by-stack.md` A03 |
49
+ | Request body | SQL query | Parameterize (bound params / ORM) | A03 |
50
+ | User URL | Outbound fetch | https-only + IP allowlist, block private ranges | A10 |
51
+ | User HTML | DOM render | Encode by context / DOMPurify allowlist | A03 |
52
+ | Filename | Filesystem path | Canonicalize + base-dir containment | A03 |
58
53
  | JWT / claim | Authz decision | Verify signature + pinned alg + `aud`/`iss`/`exp` | `references/authn-authz.md` |
59
- | Upload | Disk / exec | Type+size, sniff magic bytes, store outside web root | `references/owasp-by-stack.md` A01 |
54
+ | Upload | Disk / exec | Type+size, sniff magic bytes, store outside web root | A01 |
55
+
56
+ A-numbers index `references/owasp-by-stack.md`: same section, vulnerable→fixed
57
+ code in all three stacks.
60
58
 
61
59
  ## Review workflow (PR-sized)
62
60
 
@@ -65,35 +63,27 @@ integrity, or CI security gates.
65
63
  3. **Map sinks to OWASP.** Match each changed sink to a category → `references/owasp-by-stack.md`.
66
64
  4. **Rank by exploitability.** Reachable? User-controlled? Meaningful sink? Report the highest-impact reachable findings first, not a checklist.
67
65
  5. **Propose fixes as diffs** in the repo's actual stack (Good/Bad, copy-pasteable).
68
- 6. **Run `scripts/verify.sh`** in the repo root; resolve every high/critical before merge.
69
-
70
- ## Core principles (non-negotiable)
71
-
72
- 1. Validate at the boundary with a schema (Pydantic v2 / Zod `.strict()` / Go struct+validator) — never trust shape.
73
- 2. Parameterize every query; ORM or driver bind params, never string-built SQL.
74
- 3. Authorize on the **server**, per-object, on **every** request; deny by default.
75
- 4. Encode on output by context (HTML / attribute / JS / URL); never build HTML from user strings.
76
- 5. Secrets only from env/secret-manager — never in repo, logs, or client bundles.
77
- 6. Fail closed: generic errors to the client, detail to logs (no stack traces, no PII).
78
- 7. Pin + lock dependencies; a reachable CVE is a release blocker.
79
- 8. Least privilege / least agency for tokens, DB roles, CORS origins, file perms.
66
+ 6. **Run `scripts/verify.sh`** in the repo root; resolve every high/critical before merge — a reachable CVE is a release blocker, not a follow-up ticket.
80
67
 
81
68
  ## OWASP Top 10 — fastest fix per category
82
69
 
83
- | OWASP 2021 | The mistake you'll actually see | Stack-correct fix in one phrase | Deep ref |
84
- |---|---|---|---|
85
- | A01 Broken Access Control | `db.get(id)` returned to any authed user | Ownership-scoped query; 404 (not 403) on miss | `references/owasp-by-stack.md` A01 |
86
- | A02 Cryptographic Failures | SHA-256 password hash; `random` token | Argon2id + CSPRNG (`secrets`/`crypto/rand`) | `references/owasp-by-stack.md` A02 |
87
- | A03 Injection | f-string SQL / `shell=True` | Bound params / arg-list, no shell / canonicalize path | `references/owasp-by-stack.md` A03 |
88
- | A04 Insecure Design | No rate limit, replayable payment | Lockout + idempotency-key `UNIQUE` constraint | `references/owasp-by-stack.md` A04 |
89
- | A05 Security Misconfiguration | `debug=True`, `*`+credentials CORS | `debug=False`, explicit origin allowlist, headers | `references/owasp-by-stack.md` A05 |
90
- | A06 Vulnerable Components | Ignored transitive CVE | Audit + upgrade/override/replace | `references/owasp-by-stack.md` A06 |
91
- | A07 Auth Failures | Reusable session id, user enumeration | Lockout + rotate session id + generic error | `references/owasp-by-stack.md` A07 |
92
- | A08 Data Integrity | `curl \| bash`, unpinned CDN script | `npm ci`/`go mod verify` + SRI | `references/owasp-by-stack.md` A08 |
93
- | A09 Logging Failures | No authz-fail log; PII in logs | Structured log on `user_id`, redact secrets | `references/owasp-by-stack.md` A09 |
94
- | A10 SSRF | Fetch user-supplied URL directly | https-only + IP allowlist + pin dialed IP | `references/owasp-by-stack.md` A10 |
95
-
96
- Flagship: **A01 Broken Access Control / IDOR** (the #1, stack-agnostic in shape).
70
+ | OWASP 2021 | The mistake you'll actually see | Stack-correct fix in one phrase |
71
+ |---|---|---|
72
+ | A01 Broken Access Control | `db.get(id)` returned to any authed user | Ownership-scoped query; 404 (not 403) on miss |
73
+ | A02 Cryptographic Failures | SHA-256 password hash; `random` token | Argon2id + CSPRNG (`secrets`/`crypto/rand`) |
74
+ | A03 Injection | f-string SQL / `shell=True` | Bound params / arg-list, no shell / canonicalize path |
75
+ | A04 Insecure Design | No rate limit, replayable payment | Lockout + idempotency-key `UNIQUE` constraint |
76
+ | A05 Security Misconfiguration | `debug=True`, `*`+credentials CORS | `debug=False`, explicit origin allowlist, headers |
77
+ | A06 Vulnerable Components | Ignored transitive CVE | Audit + upgrade/override/replace |
78
+ | A07 Auth Failures | Reusable session id, user enumeration | Lockout + rotate session id + generic error |
79
+ | A08 Data Integrity | `curl \| bash`, unpinned CDN script | `npm ci`/`go mod verify` + SRI |
80
+ | A09 Logging Failures | No authz-fail log; PII in logs | Structured log on `user_id`, redact secrets |
81
+ | A10 SSRF | Fetch user-supplied URL directly | https-only + IP allowlist + pin dialed IP |
82
+
83
+ Flagship: **A01 Broken Access Control / IDOR** (the #1, stack-agnostic in
84
+ shape). Authorize on the **server**, per object, on **every** request, deny by
85
+ default: the id in the URL is attacker-controlled, so the ownership predicate in
86
+ the query is the only thing standing between a stranger and the row.
97
87
 
98
88
  ```python
99
89
  # Python — FastAPI 3.12 + SQLAlchemy 2.0
@@ -162,6 +152,9 @@ in `references/owasp-by-stack.md`.
162
152
 
163
153
  ## Input validation & output encoding
164
154
 
155
+ Validate at the boundary with a schema and never trust the shape that arrived —
156
+ an unbounded or extra field is how the next sink gets its payload.
157
+
165
158
  ```python
166
159
  # BAD — raw body, unbounded, unknown fields silently accepted.
167
160
  data = await request.json()
@@ -181,7 +174,9 @@ const data = Schema.parse(await req.json()); // BAD: `body as any`
181
174
 
182
175
  **XSS:** React auto-escapes — the bug is `dangerouslySetInnerHTML`. **Stored**
183
176
  (persisted then served), **reflected** (echoed from the request), and **DOM**
184
- (client writes user data into the DOM) XSS all need encoding/sanitizing.
177
+ (client writes user data into the DOM) XSS all need encoding/sanitizing. Encode
178
+ on output *by context* (HTML / attribute / JS / URL); never build HTML by
179
+ concatenating user strings.
185
180
 
186
181
  ```tsx
187
182
  // BAD — raw user HTML into the DOM.
@@ -272,6 +267,8 @@ export default { async headers() { return [{ source: "/:path*", headers }]; } };
272
267
 
273
268
  - **Rate limiting:** per-IP **and** per-identity, stricter on auth/OTP/search.
274
269
  In-memory limiters are **not** multi-instance safe — use Redis behind >1 replica.
270
+ - **Fail closed:** generic error to the client, detail to the logs. A stack
271
+ trace or DB message in a response is free reconnaissance for the attacker.
275
272
  - **Logging without PII:** redact tokens/passwords/PAN/email; log `user_id`,
276
273
  not email; structured (`slog`/`structlog`); never log auth-route bodies.
277
274
 
@@ -279,18 +276,24 @@ export default { async headers() { return [{ source: "/:path*", headers }]; } };
279
276
 
280
277
  - Env/secret-manager, never repo; `.env` gitignored. Only `NEXT_PUBLIC_*` is
281
278
  public — **BAD: a secret read in a Client Component ships to the browser.**
279
+ In a [`harness`](../harness/SKILL.md) project they live in
280
+ `01-TOOLS/<PROVIDER>/.env` (gitignored) — same rule, never in the repo.
282
281
  - Pin + commit the lockfile; install with `npm ci` / `pnpm i --frozen-lockfile`
283
282
  / `go mod verify` / `pip install --require-hashes`.
284
- - Audit per stack: `pip-audit`, `npm audit --omit=dev` / `osv-scanner`,
285
- `govulncheck`, `dart pub outdated`.
286
- - On exposure: **rotate the credential first, then scrub history** (`gitleaks`
287
- to confirm). SBOM via `syft`; provenance via `cosign`/SLSA.
283
+ - Audit per stack: `pip-audit`, `npm audit --omit=dev --audit-level=high` or
284
+ `osv-scanner scan source -L pnpm-lock.yaml` (lockfile-aware, multi-ecosystem,
285
+ v2 CLI), `govulncheck ./...` (reachability-aware — only vulns you call),
286
+ `dart pub outdated`. Fix by upgrading to the fixed version; constrain or
287
+ override transitive pins.
288
+ - On exposure: **rotate the credential first, then scrub history** (`gitleaks
289
+ dir . --redact` for the working tree, `gitleaks git .` for history, to
290
+ confirm). SBOM via `syft`; provenance via `cosign`/SLSA.
288
291
 
289
292
  Full runbook: `references/secrets-and-supply-chain.md`.
290
293
 
291
- ## Anti-patterns / rationalizations → STOP
294
+ ## Anti-patterns
292
295
 
293
- | Rationalization | Reality |
296
+ | What gets said | Reality |
294
297
  |---|---|
295
298
  | "It's behind auth, so IDOR doesn't matter." | Authenticated ≠ authorized. Check object ownership on every request. |
296
299
  | "The frontend already validates / hides the button." | Client checks are UX. Re-authorize and re-validate on the server. Server Actions and API routes are public. |
@@ -300,66 +303,34 @@ Full runbook: `references/secrets-and-supply-chain.md`.
300
303
  | "npm audit shows criticals but they're transitive." | Transitive is still in your bundle. Pin/override or replace. |
301
304
  | "I'll log the payload to debug, remove it later." | "Later" never comes; PII/secrets leak to logs. Redact now. |
302
305
  | "We'll add rate limiting after launch." | Auth/OTP endpoints get brute-forced on day one. |
303
- | "It's an internal URL fetch, SSRF isn't a risk." | Internal is exactly the SSRF target (metadata, RDS). Allowlist hosts, block private IPs. |
306
+ | "It's an internal URL fetch, SSRF isn't a risk." | Internal is exactly the SSRF target (metadata, RDS). Allowlist hosts and block `169.254.169.254`, `10/8`, `172.16/12`, `192.168/16`, `127/8`, `::1`, `fc00::/7`, `fe80::/10`. |
304
307
  | "Error stack to the client speeds debugging." | It leaks internals to attackers. Generic to client, detail to logs. |
305
308
  | "Secrets in `.env.example` are placeholders; real ones in CI YAML are fine." | Use the secret store; never inline real secrets in CI files. |
306
309
  | "Argon2 is overkill, SHA-256 is fast." | Fast = brute-forceable. Use Argon2id (or bcrypt cost≥12). |
307
310
 
308
311
  ## verify.sh — the gate
309
312
 
310
- `scripts/verify.sh` runs gitleaks + semgrep + the per-stack CVE audit
313
+ `scripts/verify.sh` runs gitleaks + semgrep (`--config=auto --severity ERROR`
314
+ gates; WARNING is informational) + the per-stack CVE audit
311
315
  (pip-audit/osv-scanner/govulncheck). It is **the user's to run in their own repo
312
316
  root** — it auto-detects the stack, skips (does not fail) when a tool is
313
317
  missing, and exits non-zero **only** on real high/critical findings. The CI
314
318
  equivalent is in `references/secrets-and-supply-chain.md`.
315
319
 
316
- ## Quick reference
317
-
318
- | Concern | Tool / flag | One-liner |
319
- |---|---|---|
320
- | Secret scan | `gitleaks dir . --redact` | Working tree; add `gitleaks git .` for history; rotate-then-scrub on a hit |
321
- | SAST | `semgrep --config=auto --severity ERROR` | ERROR gates; WARNING informational |
322
- | Python CVEs | `pip-audit` | Upgrade to fix version; constraints for transitive |
323
- | Node CVEs | `npm audit --omit=dev --audit-level=high` | Or `osv-scanner scan source -L …` (multi-ecosystem) |
324
- | Node CVEs (lockfile) | `osv-scanner scan source -L pnpm-lock.yaml` | Lockfile-aware, broad ecosystem coverage (v2 CLI) |
325
- | Go CVEs | `govulncheck ./...` | Reachability-aware (only vulns you call) |
326
- | Password hash | Argon2id | `time_cost=3, memory_cost=65536, parallelism=4` |
327
- | Cookie flags | `Set-Cookie` | `__Host-name; HttpOnly; Secure; SameSite=Lax; Path=/` |
328
- | CSP starter | header | `default-src 'self'; object-src 'none'; frame-ancestors 'none'` |
329
- | CORS rule | allowlist | Explicit origins; never `*` with credentials |
330
- | SSRF blocklist | IP ranges | `169.254.169.254`, `10/8`, `172.16/12`, `192.168/16`, `127/8`, `::1`, `fc00::/7`, `fe80::/10` |
331
-
332
- ## Project grounding (02-DOCS + CLAUDE.md)
333
-
334
- When this skill runs in a project with a `02-DOCS/` layer (the
335
- [`harness`](../harness/SKILL.md) Karpathy wiki), record this
336
- project's security decisions there and index them from the root `CLAUDE.md`, so the next
337
- agent inherits the conventions instead of re-deriving them.
338
-
339
- 1. **Find the article** `02-DOCS/wiki/stack/security.md`, indexed in `02-DOCS/wiki/index.md` (the
340
- Knowledge map index; root `CLAUDE.md` points to it).
341
- 2. **If missing or stale**, create/update it with the project's real choices — the threat model, the auth model, the secrets backend, the CI security gates, and any accepted risks —
342
- then index it in `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a
343
- short pointer to it).
344
- 3. **Read it first on every use** and stay consistent; when a convention changes, update the
345
- article (bump its `Updated` date) in the same change.
346
-
347
- No `02-DOCS/` layer? Skip silently (optionally suggest `harness`). Unlike the
348
- brand study, technical conventions are *recorded, not gated* — never block the task on this.
349
-
350
- ## See Also
351
-
352
- - **Stack skills** — `../fastapi/SKILL.md`, `../nextjs/SKILL.md`, `../go/SKILL.md`,
353
- `../flutter/SKILL.md`, `../postgresdb/SKILL.md` (and `../design/SKILL.md`,
354
- `../deployment/SKILL.md`): they defer security to this skill. If a stack skill
355
- doesn't exist yet, treat this as the canonical security reference it points to.
356
- - **Agent / Claude-Code config security** — a separate concern (`.claude/`,
357
- hooks, MCP, prompt injection, sandboxing). Covered by `../building-agents/SKILL.md`
358
- and agent-config-security tooling; explicitly **out of scope** here.
359
- - **`../harness/SKILL.md`** — secrets land in
360
- `01-TOOLS/<PROVIDER>/.env` (gitignored); reinforces never-in-repo.
361
- - **References** — go to `references/threat-modeling.md` to model a feature before
362
- coding; `references/owasp-by-stack.md` for vulnerable→fixed code in any of the
363
- three stacks; `references/authn-authz.md` to design login/sessions/tokens/MFA;
364
- `references/secrets-and-supply-chain.md` for secret handling, dependency
365
- pinning, and the CI gate.
320
+ ## Project grounding
321
+
322
+ In a project with a `02-DOCS/` layer ([`harness`](../harness/SKILL.md)), this
323
+ project's security decisions live in `02-DOCS/wiki/stack/security.md`, indexed
324
+ from `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a
325
+ pointer to it). Read it first and stay consistent; if it is missing or stale,
326
+ create/update it with the real choices — threat model, auth model, secrets
327
+ backend, CI security gates, accepted risks — index it, and bump its `Updated`
328
+ date in the same change. No `02-DOCS/` layer? Skip silently (optionally suggest
329
+ `harness`): technical conventions are *recorded, not gated* — never block the
330
+ task on this.
331
+
332
+ Stack skills ([`fastapi`](../fastapi/SKILL.md), [`nextjs`](../nextjs/SKILL.md),
333
+ [`go`](../go/SKILL.md), [`flutter`](../flutter/SKILL.md),
334
+ [`postgresdb`](../postgresdb/SKILL.md), [`deployment`](../deployment/SKILL.md))
335
+ defer security to this skill; where one doesn't exist yet, this is the canonical
336
+ reference it would point to.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ship
3
- description: "Use when the work is complete and verified and it is time to CLOSE the development branch — the final phase of the rsc SDD chain, after review approves the diff. Triggers: 'ship it', 'close the branch', 'open the PR', 'merge this', 'merge into main', 'create the pull request', 'how do I land this work', 'finish this feature', 'haz el merge', 'abre el PR', 'cierra la rama', 'súbelo a main', 'clean up the branch', 'I'm done, what now'. HARD RULE it enforces: git authorship is ALWAYS Eric — never a Co-Authored-By or 'generated with' footer in any commit or PR. NOT running lint/type/test (that is `verify`), NOT reading the diff adversarially (that is `review`), NOT deploy/release mechanics to a server (that is `deployment`). Honors the harness accompaniment dial."
3
+ description: "Use when a verified, review-approved branch has to land — the close of the rsc SDD chain: safety checks, then three landing options (merge, PR, park/discard), authorship always Eric and never an AI trailer. NOT running the gates (that is `verify`), NOT reading the diff for defects (that is `review`), NOT shipping to a server (that is `deployment`)."
4
4
  tags: [sdd, ship, release, pr]
5
5
  recommends: []
6
6
  profiles: [core, full]
@@ -11,22 +11,21 @@ origin: risco
11
11
 
12
12
  Ship is the **last gate** of the rsc SDD chain: `constitution → specify → clarify → plan → tasks → analyze → implement → verify → review → **ship**`. Everything upstream proved the work is *correct* and *green*; ship is the act of **landing it** — turning an approved branch into merged history, a pull request, or a clean parked branch — without breaking the trunk and without ever forging the author.
13
13
 
14
- This skill owns one decision and its safe execution: **how does this work integrate?** It does not write code, run test gates, or read the diff for defects — those phases already happened. It takes a diff that *earned* its verdict and gets it home.
14
+ This skill owns one decision and its safe execution: **how does this work integrate?** It does not write code, run test gates, or read the diff for defects — those phases already happened. It is the **close, not the open**: creating the isolated branch or worktree is `../worktrees/SKILL.md`, taking the gates green is `../verify/SKILL.md`, reading the diff adversarially is `../review/SKILL.md`, and putting merged code onto a server is `../deployment/SKILL.md`.
15
15
 
16
16
  ## The hard rule: git authorship is Eric, never Claude
17
17
 
18
18
  > **Every commit and every PR ships under Eric's name. No `Co-Authored-By: Claude`. No `Co-Authored-By` for any AI. No "🤖 Generated with Claude Code" footer. No "made by an agent" line in the PR body. Nothing that attributes the work to a tool.**
19
19
 
20
- This is non-negotiable and it is the reason this skill exists as its own phase. The work is Eric's; the agent is a tool he used, like an editor or a compiler. You do not credit the compiler in the commit message.
20
+ This is absolute — not a preference to weigh against convenience — because a commit is a permanent, published claim about who wrote something. The work is Eric's; the agent is a tool he used, like an editor or a compiler, and you do not credit the compiler in the commit message. Once a forged trailer is pushed it is in everyone's history and only a rewrite removes it.
21
21
 
22
22
  Concretely, before any commit or PR:
23
23
 
24
- - **Never** append a trailer, footer, or body line crediting Claude, an AI, an agent, or "Claude Code".
25
24
  - **Never** pass `--author` to set a non-Eric author. The repo's configured `user.name` / `user.email` (Eric's) is the author and committer.
26
25
  - If `git config user.email` is unset or clearly not Eric's, **stop and ask** which identity to commit under — do not guess, and do not substitute an agent identity.
27
26
  - If you find a Claude/AI trailer in a commit you are about to push (e.g. left over from an upstream tool), **strip it** before the branch lands.
28
27
 
29
- Verify it after writing the commit:
28
+ Verify it after writing the commit. A non-empty match is a blocker: amend and re-check before the branch goes anywhere.
30
29
 
31
30
  ```bash
32
31
  git log -1 --format='%an <%ae>%n%n%b' | grep -iE 'co-authored-by.*(claude|anthropic|ai)|generated with|claude code' \
@@ -34,40 +33,21 @@ git log -1 --format='%an <%ae>%n%n%b' | grep -iE 'co-authored-by.*(claude|anthro
34
33
  || echo "authorship clean"
35
34
  ```
36
35
 
37
- A non-empty match is a blocker. Amend the commit and re-check before the branch goes anywhere.
38
-
39
36
  ## Read these first
40
37
 
41
- 1. `02-DOCS/wiki/harness/user-profile.md` — the **accompaniment dial** (L0..L3). It sets how much you narrate and how you frame the three options, never whether you run the safety checklist. See "Accompaniment dial" below.
42
- 2. The **review verdict** for this branch — ship runs only on `APPROVE` or `APPROVE WITH NITS`. If the last verdict was `CHANGES REQUESTED`, the loop goes back to `implement`, not forward to ship.
38
+ 1. `02-DOCS/wiki/harness/user-profile.md` — the accompaniment dial (L0..L3). It sets narration only, never whether you run the safety checklist.
39
+ 2. The **review verdict** for this branch — ship runs only on `APPROVE` or `APPROVE WITH NITS`. `CHANGES REQUESTED` loops back to `implement`, not forward to ship. If there is no verdict on record, say so and treat it as a red flag: do not ship a diff that skipped `../review/SKILL.md` — offer to run it first.
43
40
  3. `02-DOCS/wiki/sdd/decisions.md` and the spec/plan slug — so the commit message and PR body describe *what shipped against which spec*, not a vague "various changes".
44
41
 
45
- If there is no review verdict on record, say so and treat it as a red flag — do not ship a diff that skipped the `review` phase. Offer to run review first.
46
-
47
- ## When to use / When NOT to use
48
-
49
- Use when:
50
-
51
- - A branch has passed `verify` (green gates) and `review` (approved), and the work needs to land.
52
- - The user says "ship it", "merge this", "open the PR", "close the branch", "I'm done" — and the code is actually ready.
53
- - A finished branch needs to be parked or discarded cleanly (abandoned spike, superseded approach).
54
-
55
- Do NOT use when (route elsewhere):
56
-
57
- - Lint / type-check / tests / audit still need to run and go green → that is the **verify** phase (`../verify/SKILL.md`). Ship assumes green.
58
- - The diff hasn't been read for defects, or a review comment needs processing → that is **review** (`../review/SKILL.md`).
59
- - The merge is clean but now it must reach a *server* — provisioning, release pipeline, environment promotion, rollback → that is **deployment** (`../deployment/SKILL.md`). Ship lands the code in the repo; deployment puts it in production.
60
- - You need to *create* the isolated branch/worktree to start work → that is **worktrees** (`../worktrees/SKILL.md`). Ship is the close, not the open.
61
-
62
42
  ## The pre-ship safety checklist
63
43
 
64
44
  Run this before presenting the landing options. Any unchecked item is a stop — surface it, don't ship around it.
65
45
 
66
46
  - [ ] **Review verdict is APPROVE / APPROVE WITH NITS** (not CHANGES REQUESTED, not absent).
67
47
  - [ ] **Working tree is clean** — `git status --short` is empty. No stray edits sneaking into the merge.
68
- - [ ] **On a feature branch, not the trunk** — `git rev-parse --abbrev-ref HEAD` is not `main`/`master`. If work landed directly on the trunk, that is its own problem; flag it.
48
+ - [ ] **On a feature branch, not the trunk** — `git rev-parse --abbrev-ref HEAD` is not `main`/`master`. If work landed directly on the trunk, that is its own problem; flag it, don't paper over it.
69
49
  - [ ] **Rebased / up to date with the base** — branch is on top of latest `main`; conflicts resolved locally, not punted to the merge.
70
- - [ ] **No secrets in the diff** — scan the staged/branch diff for keys, tokens, `.env` values (`git diff main... | grep -iE 'api[_-]?key|secret|password|token|BEGIN .*PRIVATE KEY'`). A hit is a blocker.
50
+ - [ ] **No secrets in the diff** — scan the staged/branch diff for keys, tokens, `.env` values (`git diff main... | grep -iE 'api[_-]?key|secret|password|token|BEGIN .*PRIVATE KEY'`). A hit is a blocker; remove it, and rotate it if it was ever pushed.
71
51
  - [ ] **Authorship is Eric** — `git config user.email` is Eric's; no AI trailer in any commit on the branch (run the grep above across `main..HEAD`).
72
52
  - [ ] **Commit history is intelligible** — squashed or organized so the history reads as deliberate, not "wip wip fix fix".
73
53
 
@@ -87,9 +67,10 @@ When rsc is installed for Claude Code, a `PreToolUse` hook (`.rsc/ship-guard.mjs
87
67
  phase at the one deterministic moment it matters: it **denies** any Bash command that switches to
88
68
  `main`/`master` or merges while the current feature branch has **uncommitted changes** or **commits
89
69
  that were never pushed**. The denial reason names the branch and routes you here. The guard is
90
- local-only (no network), **fail-open** (any ambiguity allows the command), and can be disabled per
91
- project with `.rsc/.no-ship-guard`. It guarantees the commit → push step; opening the PR is still
92
- this skill's job (and its hard rule). If the guard blocks you, do not work around it — run ship.
70
+ local-only (no network), **fail-open** (any ambiguity — detached HEAD, no repo, git error — allows
71
+ the command), and can be disabled per project with `.rsc/.no-ship-guard`. It guarantees the
72
+ commit → push step; opening the PR is still this skill's job (and its hard rule). If the guard
73
+ blocks you, do not work around it — run ship.
93
74
 
94
75
  ## The three landing options — always present exactly three
95
76
 
@@ -116,6 +97,8 @@ Stacked PR / feature-track support still fits inside the three landing options:
116
97
 
117
98
  ## Executing each option
118
99
 
100
+ **Nothing below runs before the user picks an option.** Merging, pushing, opening a PR and deleting a branch are outward or irreversible — they change shared history or publish to a forge, and no later phase undoes them. A recommendation is not a yes; wait for one.
101
+
119
102
  ### Option 1 — direct merge
120
103
 
121
104
  ```bash
@@ -176,7 +159,7 @@ Never stack to hide review risk. Stack because each slice is independently revie
176
159
  ### Option 3 — park or discard
177
160
 
178
161
  - **Park:** leave the branch, push it so it's not lost (`git push -u origin feature/<slug>`), and log *why it's parked* to `02-DOCS/wiki/sdd/decisions.md`. Do not merge.
179
- - **Discard:** deletion is **destructive** — require an explicit confirmation that quotes the branch name (e.g. the literal `yes, delete feature/<slug>`) before `git branch -D`. Anything ambiguous means keep it. Log the discard and the reason so the dead-end is remembered, not re-attempted.
162
+ - **Discard:** deletion is **destructive and unrecoverable** once the branch is gone from both sides, so it takes an explicit confirmation that quotes the branch name (the literal `yes, delete feature/<slug>`) before `git branch -D`. Anything ambiguous means keep it. Log the discard and the reason so the dead-end is remembered, not re-attempted.
180
163
 
181
164
  **If the work lived in a worktree, clean it up provenance-aware.** After the merge/park/discard, only
182
165
  remove a worktree **rsc created** (under `.worktrees/`/`worktrees/` or the `../<repo>-<slug>` dir),
@@ -196,25 +179,24 @@ The commit is the durable record. Make it describe the change and tie it to the
196
179
 
197
180
  ## Model tier — `light` (opt-in routing)
198
181
 
199
- This phase's default model tier is **`light`** — closing the branch (PR / merge / cleanup) is mechanical. 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. The Eric-only authorship rule is independent of the model and never relaxes.
182
+ Closing the branch (PR / merge / cleanup) is mechanical, so this phase's default tier is **`light`**. Routing is **off** unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`; when it is on, follow `../sdd/references/model-routing.md` for resolving and announcing the switch rather than from memory. Routing off or no profile → honor the session model silently, and skip routing on a one-line change. The Eric-only authorship rule is independent of the model and never relaxes.
200
183
 
201
184
  ## Accompaniment dial (L0..L3)
202
185
 
203
- Read the level from `02-DOCS/wiki/harness/user-profile.md`. It changes the narration, **never** the safety checklist or the authorship rule.
204
-
205
- - **L0** — run the checklist silently, state the recommended option in one line, execute on a yes. `Clean, rebased, authorship Eric. Recommend PR (main is protected). Open it?`
206
- - **L1** — the three options as one-liners with the recommendation and its *why*.
207
- - **L2** — the full options table, the checklist results, and why the recommended option fits this repo's workflow.
208
- - **L3** — the above plus teaching: what a fast-forward vs `--no-ff` merge does to history, why a protected `main` wants a PR, what squashing trades away — framed for a non-technical owner ("a PR is asking permission before changing the shared copy"). Still never skips a checklist item.
186
+ Read the level from `02-DOCS/wiki/harness/user-profile.md`. It changes what you show, **never** the safety checklist or the authorship rule. No profile → default to L2 and proceed; don't stall the ship to ask for a dial setting.
209
187
 
210
- When no profile exists, default to L2 and proceed — don't stall the ship to ask for a dial setting.
188
+ | Level | What ship shows |
189
+ | --- | --- |
190
+ | **L0** | Checklist run silently, recommended option in one line, execute on a yes: `Clean, rebased, authorship Eric. Recommend PR (main is protected). Open it?` |
191
+ | **L1** | The three options as one-liners, with the recommendation and its *why*. |
192
+ | **L2** | The full options table, the checklist results, and why the recommended option fits this repo's workflow. |
193
+ | **L3** | L2 plus teaching, framed for a non-technical owner: what fast-forward vs `--no-ff` does to history, why a protected `main` wants a PR ("asking permission before changing the shared copy"), what squashing trades away. |
211
194
 
212
195
  ## Anti-patterns → STOP
213
196
 
214
197
  | Rationalization | Reality |
215
198
  | --- | --- |
216
- | "I'll add `Co-Authored-By: Claude` to be transparent" | No. The work is Eric's. No AI trailer, ever — this is the whole point of the phase. |
217
- | "A 'Generated with Claude Code' footer is just polite" | It forges the record. Strip it. The commit credits Eric and only Eric. |
199
+ | "I'll add `Co-Authored-By: Claude` / a 'Generated with Claude Code' footer to be transparent" | It forges the record. The work is Eric's — no AI trailer, ever. Strip it. |
218
200
  | "Review didn't formally approve but it's obviously fine" | No verdict = not ready. Ship runs on APPROVE only. Route back to review. |
219
201
  | "The tree has a couple of stray edits, they're harmless" | A dirty tree means the merge is not the reviewed diff. Clean it or stash it first. |
220
202
  | "`main` is protected but I'll just force-merge, I'm sure" | Protected means PR. Don't bypass the gate the repo deliberately set. |
@@ -223,14 +205,6 @@ When no profile exists, default to L2 and proceed — don't stall the ship to as
223
205
  | "Squash everything, history doesn't matter" | Squash noise, preserve meaning. The history is the next reader's spec. |
224
206
  | "There's a key in the diff but it's a test key" | A secret in the diff is a blocker regardless. Pull it out before landing. |
225
207
 
226
- ## Red flags — abort and re-plan
227
-
228
- - Any commit on the branch carries an AI `Co-Authored-By` or "generated with" line → strip it; do not push until clean.
229
- - `git config user.email` is unset or not Eric's → stop and ask which identity to commit under.
230
- - No review verdict, or the last verdict was `CHANGES REQUESTED` → not shippable; route to review/implement.
231
- - The branch is `main`/`master` itself → work landed on the trunk directly; flag it, don't paper over it.
232
- - A secret matches in the branch diff → blocker; remove it (and rotate it if it was ever pushed).
233
-
234
208
  ## Where this writes
235
209
 
236
210
  Ship is mostly git actions, but the outcome is recorded so the knowledge model stays whole:
@@ -269,12 +243,8 @@ End with:
269
243
 
270
244
  ## Next in the chain
271
245
 
272
- Ship is the end of the SDD loop for a feature. Two onward paths:
273
-
274
- - The code is merged but must reach a **server / release** — provisioning, pipeline, environment promotion, rollback → hand off to **deployment** (`../deployment/SKILL.md`). Ship lands it in the repo; deployment puts it in front of users.
275
- - The next feature starts the loop again at **specify** (`../specify/SKILL.md`) — or at **constitution** if the project's principles changed. The `sdd` dispatcher (`../sdd/SKILL.md`) routes whichever comes next.
246
+ Ship is the end of the SDD loop for a feature. Two onward paths: the merged code still has to reach a **server / release** → hand off to **deployment** (`../deployment/SKILL.md`); or the next feature restarts the loop at **specify** (`../specify/SKILL.md`), or at **constitution** if the project's principles changed. The `sdd` dispatcher (`../sdd/SKILL.md`) routes whichever comes next.
276
247
 
277
248
  ## Orientación (siempre)
278
249
 
279
250
  Cierra cada turno con el **bloque-brújula** (📍 dónde estás · ✅ qué hiciste · 🧭 por qué · ➡️ siguiente, terminando en pregunta), calibrado al dial de `02-DOCS/wiki/harness/user-profile.md`. **Nunca termines en seco.** Protocolo completo: skill `orient` → `skills/orient/references/orientation-contract.md`. (Defiere a `suggest` el "¿instalo la skill que falta?".)
280
-