@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: secure-coding
|
|
3
|
-
description: "Use when threat-modeling a feature, reviewing
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 |
|
|
47
|
+
| Untrusted source | Dangerous sink | Defense | Ref |
|
|
53
48
|
|---|---|---|---|
|
|
54
|
-
| Request body | SQL query | Parameterize (bound params / ORM) |
|
|
55
|
-
| User URL | Outbound fetch | https-only + IP allowlist, block private ranges |
|
|
56
|
-
| User HTML | DOM render | Encode by context / DOMPurify allowlist |
|
|
57
|
-
| Filename | Filesystem path | Canonicalize + base-dir containment |
|
|
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 |
|
|
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 |
|
|
84
|
-
|
|
85
|
-
| A01 Broken Access Control | `db.get(id)` returned to any authed user | Ownership-scoped query; 404 (not 403) on miss |
|
|
86
|
-
| A02 Cryptographic Failures | SHA-256 password hash; `random` token | Argon2id + CSPRNG (`secrets`/`crypto/rand`) |
|
|
87
|
-
| A03 Injection | f-string SQL / `shell=True` | Bound params / arg-list, no shell / canonicalize path |
|
|
88
|
-
| A04 Insecure Design | No rate limit, replayable payment | Lockout + idempotency-key `UNIQUE` constraint |
|
|
89
|
-
| A05 Security Misconfiguration | `debug=True`, `*`+credentials CORS | `debug=False`, explicit origin allowlist, headers |
|
|
90
|
-
| A06 Vulnerable Components | Ignored transitive CVE | Audit + upgrade/override/replace |
|
|
91
|
-
| A07 Auth Failures | Reusable session id, user enumeration | Lockout + rotate session id + generic error |
|
|
92
|
-
| A08 Data Integrity | `curl \| bash`, unpinned CDN script | `npm ci`/`go mod verify` + SRI |
|
|
93
|
-
| A09 Logging Failures | No authz-fail log; PII in logs | Structured log on `user_id`, redact secrets |
|
|
94
|
-
| A10 SSRF | Fetch user-supplied URL directly | https-only + IP allowlist + pin dialed IP |
|
|
95
|
-
|
|
96
|
-
Flagship: **A01 Broken Access Control / IDOR** (the #1, stack-agnostic in
|
|
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`
|
|
285
|
-
`
|
|
286
|
-
|
|
287
|
-
|
|
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
|
|
294
|
+
## Anti-patterns
|
|
292
295
|
|
|
293
|
-
|
|
|
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
|
|
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
|
|
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
|
-
##
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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.
|
package/skills/ship/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ship
|
|
3
|
-
description: "Use when
|
|
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
|
|
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
|
|
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
|
|
42
|
-
2. The **review verdict** for this branch — ship runs only on `APPROVE` or `APPROVE WITH NITS`.
|
|
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
|
|
91
|
-
project with `.rsc/.no-ship-guard`. It guarantees the
|
|
92
|
-
this skill's job (and its hard rule). If the guard
|
|
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**
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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" |
|
|
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
|
-
|