@ericrisco/rsc 0.1.47 → 0.1.49

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/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.47",
2
+ "version": "0.1.49",
3
3
  "counts": {
4
4
  "skills": 258
5
5
  },
@@ -1136,7 +1136,7 @@
1136
1136
  },
1137
1137
  {
1138
1138
  "id": "db-migrations",
1139
- "description": "Use when planning a schema change that must ship without downtime — adding a NOT NULL column, renaming/splitting/merging a column or table, changing a type, or backfilling millions of rows on a live table — and you need the expand-contract sequence plus the lock/timeout/batching discipline that keeps each step from freezing prod. Triggers: 'zero-downtime migration', 'add NOT NULL column to a huge table', 'rename a column safely', 'our deploy keeps deadlocking when we add an index', 'backfill 80M rows without replication lag', 'expand contract', 'gh-ost vs pt-osc', 'migración sin downtime', 'migració sense tallar el servei'. NOT Postgres lock internals/EXPLAIN/RLS (that is postgresdb), NOT drizzle-kit runner mechanics (that is drizzle-orm), NOT restore drills/PITR (that is backups).",
1139
+ "description": "Use when a schema change must ship without downtime — NOT NULL, rename, type change, or backfilling millions of live rows — for the expand-contract sequence and the lock/batching discipline that keeps each step from freezing prod. NOT lock internals or EXPLAIN (that is `postgresdb`), NOT drizzle-kit mechanics (that is `drizzle-orm`), NOT PITR (that is `backups`).",
1140
1140
  "tags": [
1141
1141
  "migrations",
1142
1142
  "zero-downtime",
@@ -1170,7 +1170,7 @@
1170
1170
  },
1171
1171
  {
1172
1172
  "id": "decision-records",
1173
- "description": "Use when a consequential, hard-to-reverse choice was just made or is about to be — database, framework, auth model, vendor, API style, deployment target — and future-you will ask why, with the answer buried in chat; capture it as a numbered, immutable ADR with the context that forced the choice, the real options weighed with their trade-offs, the decision, and consequences both ways. Also when a prior decision is being reversed and must be superseded without erasing history, or when onboarding keeps re-litigating a settled choice. Triggers: 'write up why we picked Postgres over DynamoDB so we stop arguing', 'record the switch to tRPC but keep the old REST decision in history', 'documenta por qué elegimos Hetzner y qué alternativas descartamos con pros y contras', 'registra la decisió i les alternatives que vam descartar'. NOT the meeting recap with action items and owners (that is meeting-notes), NOT the project-wide standing principles and stack canon (that is constitution).",
1173
+ "description": "Use when a hard-to-reverse choice (database, framework, vendor, auth model) must be frozen as an immutable numbered ADR — the context that forced it, the options weighed, the decision, consequences both ways — or superseded without erasing history. NOT a meeting recap (that is meeting-notes), NOT standing project principles (that is constitution).",
1174
1174
  "tags": [
1175
1175
  "decision-records",
1176
1176
  "adr",
@@ -1193,7 +1193,7 @@
1193
1193
  },
1194
1194
  {
1195
1195
  "id": "deep-learning",
1196
- "description": "Use when training or debugging a neural network in PyTorch — writing the training loop, chasing loss that won't drop or goes NaN, adding mixed precision (AMP), scaling across GPUs, or making a run reproducible. Covers tensors/autograd/nn.Module, torch.compile, the forward→loss→backward→step→zero_grad loop and its silent bugs, AdamW + cosine/warmup schedulers, DDP vs FSDP vs DeepSpeed ZeRO, checkpointing and seeds. PyTorch-first (JAX/TF exist). Triggers: 'my training loss isn't decreasing', 'loss becomes NaN after a few steps', 'how do I use torch.autocast and GradScaler', 'train this model across 4 GPUs', 'val accuracy is worse than train and I forgot model.eval()', 'gradients seem to add up across batches', 'build a CNN from scratch and train it', 'entrenar una xarxa neuronal des de zero amb PyTorch'. NOT adapting a pretrained LLM with LoRA/QLoRA (that is finetuning); NOT classic/tabular models like XGBoost or sklearn (that is machine-learning); NOT tokenization or NLP task metrics (that is nlp).",
1196
+ "description": "Use when training or debugging a neural net in PyTorch — the forward/loss/backward/step loop and its silent bugs, mixed precision (AMP), AdamW/LR schedules, DDP/FSDP/ZeRO, checkpoints and seeds. NOT LoRA/QLoRA on a pretrained LLM (that is `finetuning`), NOT tabular sklearn/XGBoost (that is `machine-learning`), NOT tokenization or NLP metrics (that is `nlp`).",
1197
1197
  "tags": [
1198
1198
  "pytorch",
1199
1199
  "deep-learning",
@@ -1250,7 +1250,7 @@
1250
1250
  },
1251
1251
  {
1252
1252
  "id": "digitalocean",
1253
- "description": "Use when deploying or operating a workload on DigitalOcean and you must decide where it runs and how to ship it — Droplet (raw VPS) vs App Platform (managed PaaS) vs Functions, wiring a Managed Database (Postgres/MySQL/Valkey), Spaces object storage + CDN, doctl auth, App Platform app spec YAML, cloud firewalls, VPC private networking, reserved IPs, snapshots/backups, app logs, scaling. Triggers: 'deploy to DigitalOcean', 'DigitalOcean App Platform app spec', 'DO managed postgres with connection pooling', 'doctl apps create', 'reserved IP failover', 'I need an S3-compatible bucket with a CDN' (that is Spaces — non-obvious), 'subir mi web a DigitalOcean con doctl', 'Droplet o App Platform, qué sale más barato'. NOT host-agnostic CI/CD or rollback strategy (that is deployment), NOT a Hetzner bare VPS (that is hetzner).",
1253
+ "description": "Use when deploying or operating a workload on DigitalOcean — Droplet vs App Platform vs Functions, doctl, app spec YAML, Managed Postgres/MySQL/Valkey on the VPC, S3-compatible Spaces + CDN. NOT host-agnostic CI/CD or rollback strategy (that is `deployment`), NOT a bare Hetzner VPS (that is `hetzner`), NOT another managed PaaS (that is `railway`).",
1254
1254
  "tags": [
1255
1255
  "digitalocean",
1256
1256
  "doctl",
@@ -1274,7 +1274,7 @@
1274
1274
  },
1275
1275
  {
1276
1276
  "id": "django",
1277
- "description": "Use when building, reviewing, securing, testing or shipping a Django web app — models, migrations, QuerySets, managers, FBV/CBV views, forms, the admin, settings split, and Django REST Framework (serializers, ModelViewSet, routers, permissions, throttling). Triggers: 'add a filter and fix the N+1 in this ListView', 'build a DRF ModelViewSet with owner-only permission', 'this template loops over books and queries book.author each iteration' (N+1 without the word Django), 'why does manage.py check --deploy warn about SECURE_HSTS_SECONDS', 'revisa este modelo y añade un UniqueConstraint en (tenant, slug)', 'el queryset hace N+1', any file with manage.py, settings.py, models.py, serializers.py or a 0001_initial migration. NOT async-first FastAPI services (that is fastapi).",
1277
+ "description": "Use when building, reviewing, securing, testing or shipping a Django app — models, migrations, QuerySets/managers, FBV/CBV views, forms, the admin, settings split, and Django REST Framework (serializers, ModelViewSet, permissions). NOT async FastAPI/Pydantic services (that is `fastapi`), NOT Postgres schema/index work (that is `postgresdb`).",
1278
1278
  "tags": [
1279
1279
  "python",
1280
1280
  "django",
@@ -1294,7 +1294,7 @@
1294
1294
  },
1295
1295
  {
1296
1296
  "id": "docker",
1297
- "description": "Use when authoring or auditing a Dockerfile/Containerfile, shrinking a bloated image, hardening a container that runs as root, choosing a base image, or wiring a Compose dev loop with hot reload. Triggers: 'dockerize this app', 'my node image is 1.2GB make it smaller', 'the build cache keeps busting', 'harden this Dockerfile, trivy flags it', 'docker compose watch for local dev', 'distroless vs alpine vs slim', 'la imagen pesa demasiado', 'el build de docker tarda siglos'. NOT the CI/CD pipeline or deploy-to-host (that is deployment), NOT k8s autoscaling/HPA (that is scaling), NOT app-level SQL injection or secrets-in-code (that is secure-coding).",
1297
+ "description": "Use when authoring or auditing a Dockerfile, shrinking a bloated image, hardening a container that runs as root, picking a base image, or wiring a Compose dev loop with hot reload. NOT CI builds or deploy-to-host (that is `deployment`), NOT k8s autoscaling (that is `scaling`), NOT app-level injection or secrets-in-code (that is `secure-coding`).",
1298
1298
  "tags": [
1299
1299
  "docker",
1300
1300
  "containers",
@@ -1312,7 +1312,7 @@
1312
1312
  },
1313
1313
  {
1314
1314
  "id": "document-processing",
1315
- "description": "Use when you need to get content out of a document or build a document out of data — extracting text/tables from PDFs, filling interactive PDF forms (AcroForm), generating PDF/DOCX from templates, or OCR'ing scanned/image-only files into Markdown/JSON. Triggers: 'extract the line items from this invoice PDF', 'fill this PDF form and flatten it', 'generate a contract DOCX from this template', 'this scan has no text layer', 'the PDF copies out as garbage symbols', 'merge/split/rotate these PDFs', 'OCR 300 scanned pages', 'rellena este formulario PDF y fusiónalos', 'extreu les taules d'aquesta factura escanejada'. NOT pulling schema-typed fields out of text (that is structured-extraction), NOT sending it for signature (that is e-signature), NOT reading spreadsheet cells/formulas (that is spreadsheet-ops).",
1315
+ "description": "Use when the deliverable is a document's bytes or its literal content — text/tables out of PDFs, AcroForm fill and flatten, page merge/split, PDF/DOCX from templates, OCR of image-only scans. NOT schema-typed fields pulled from text (that is structured-extraction), NOT signature routing (e-signature) or spreadsheet cells/formulas (spreadsheet-ops).",
1316
1316
  "tags": [
1317
1317
  "pdf",
1318
1318
  "ocr",
@@ -1332,7 +1332,7 @@
1332
1332
  },
1333
1333
  {
1334
1334
  "id": "domains-dns",
1335
- "description": "Use when pointing a custom domain at a host, fixing broken HTTPS, choosing apex vs www, or publishing DNS rows — registering/delegating a name, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA/SRV records, standing up auto-renewing TLS, or cutting nameservers over without downtime. Triggers: 'point example.com at Vercel', 'set up DNS for the domain', 'NET::ERR_CERT_AUTHORITY_INVALID', 'CNAME flattening / ALIAS at the apex', 'dig +trace still shows the old IP', 'need a CAA record for Let's Encrypt', 'publish the SPF/DKIM/DMARC/MX rows', 'apuntar el dominio', 'el certificado caducó y no renueva', 'configurar els registres DNS'. NOT inbox placement / warmup / spam-rate tuning (that is email-deliverability), NOT what runs behind the name (that is deployment).",
1335
+ "description": "Use when pointing a domain at a host or fixing broken HTTPS — delegating nameservers, writing A/AAAA/CNAME/ALIAS/MX/TXT/CAA rows, apex vs www, standing up auto-renewing TLS, and cutting nameservers over without downtime. NOT inbox placement or warmup (that is `email-deliverability`), NOT what runs behind the name (that is `deployment`).",
1336
1336
  "tags": [
1337
1337
  "dns",
1338
1338
  "tls",
@@ -1356,7 +1356,7 @@
1356
1356
  },
1357
1357
  {
1358
1358
  "id": "drizzle-orm",
1359
- "description": "Use when modeling data or querying with Drizzle ORM in TypeScript — schema declared in `.ts`, type-safe `select`/`insert`/relational queries, and `drizzle-kit` migrations, or when choosing Drizzle over Prisma for an edge/serverless runtime. Triggers: 'set up Drizzle ORM', 'drizzle-kit generate', 'write a pgTable schema', '$inferSelect type', 'drizzle push vs generate for production', 'my db.query.posts.findMany returns no author relation', 'drizzle-kit conflicting migration', 'switch off Prisma to something lighter for edge', 'definir l esquema amb Drizzle i tipus type-safe', 'migrar de Prisma a Drizzle para algo más ligero', 'migració amb drizzle-kit'. NOT Prisma Client or schema.prisma (that is prisma-orm), NOT a generic zero-downtime migration strategy (that is db-migrations), NOT Postgres engine/indexing/EXPLAIN (that is postgresdb).",
1359
+ "description": "Use when modeling data or querying with Drizzle ORM in TypeScript — `pgTable` schema in `.ts`, type-safe select/insert/relational queries, `drizzle-kit` migrations. NOT Prisma Client or `schema.prisma` (that is `prisma-orm`), NOT ORM-agnostic migration strategy (that is `db-migrations`), NOT Postgres engine tuning or EXPLAIN (that is `postgresdb`).",
1360
1360
  "tags": [
1361
1361
  "drizzle",
1362
1362
  "drizzle-orm",
@@ -1382,7 +1382,7 @@
1382
1382
  },
1383
1383
  {
1384
1384
  "id": "duckdb",
1385
- "description": "Use when you need fast analytical SQL over local files (Parquet/CSV/JSON/Arrow) with no server to run, when embedding OLAP in an app or notebook, when a pandas groupby/merge on multi-GB data is too slow, or when reading data straight from S3/HTTP/a lakehouse. Triggers: 'query a folder of parquet files in place', 'aggregate a 5GB CSV on my laptop', 'replace this slow pandas groupby', 'run SQL over a dataframe in Jupyter', 'read these parquet files from S3 without downloading', 'out-of-core aggregate', 'analizar un CSV enorme sin montar un servidor', 'consultar parquet en local sense base de dades'. NOT a multi-user production analytics server (that is clickhouse-analytics), NOT your app's transactional CRUD database (that is postgresdb).",
1385
+ "description": "Use when analytical SQL must run in-process with no server: Parquet/CSV/JSON/Arrow queried in place, OLAP embedded in an app or notebook, a slow pandas groupby on multi-GB data, or S3/lakehouse data read without downloading. NOT a multi-user analytics server (that is clickhouse-analytics), NOT an app's transactional CRUD store (that is postgresdb).",
1386
1386
  "tags": [
1387
1387
  "duckdb",
1388
1388
  "olap",
@@ -1404,7 +1404,7 @@
1404
1404
  },
1405
1405
  {
1406
1406
  "id": "dynamodb",
1407
- "description": "Use when designing or operating an Amazon DynamoDB table - modeling entities and relationships without joins, choosing partition/sort keys, deciding single-table vs table-per-entity, adding a GSI/LSI, picking on-demand vs provisioned capacity, or diagnosing throttling. Triggers: 'design a DynamoDB table', 'model one-to-many / many-to-many without joins', 'single-table design worth it?', 'query the same data by two different keys' (inverted GSI), 'writes throttled even though I'm under my provisioned capacity' (hot partition), 'on-demand or provisioned?', 'why does my Query only return some rows?' (1 MB page), 'diseña la tabla DynamoDB para estos patrones de acceso', 'modela aquesta relació amb clau composta a DynamoDB'. NOT relational schema / SQL / EXPLAIN / B-tree indexing (that is postgresdb), and NOT document modeling with aggregation pipelines (that is mongodb).",
1407
+ "description": "Use when modeling or operating a DynamoDB table: deriving partition/sort keys from access patterns, single-table vs table-per-entity, adding a GSI/LSI, on-demand vs provisioned capacity, or diagnosing hot-partition throttling. NOT relational schema/SQL/EXPLAIN (that is `postgresdb`), NOT aggregation-pipeline document modeling (that is `mongodb`).",
1408
1408
  "tags": [
1409
1409
  "dynamodb",
1410
1410
  "nosql",
@@ -1425,7 +1425,7 @@
1425
1425
  },
1426
1426
  {
1427
1427
  "id": "e-signature",
1428
- "description": "Use when adding legally-valid electronic-signature flows to documents — sending a PDF or template for signature, embedding signing in your own app, tracking status, verifying signing webhooks, and retrieving the signed PDF plus its audit trail via DocuSign or Dropbox Sign. Triggers: 'send this contract for signature', 'create a DocuSign envelope', 'signature_request/send', 'embed signing in our app', 'notify us when it's signed', 'set up DocuSign JWT auth', 'is a typed-name signature legally binding?', 'do we need QES for EU customers?', 'enviar a firmar', 'firma electrònica amb validesa legal'. NOT drafting the contract text itself (that is contracts) and NOT OCR/parsing/extracting fields from documents (that is document-processing).",
1428
+ "description": "Use when wiring an e-signature flow with DocuSign or Dropbox Sign — picking the SES/AES/QES legal tier, sending a PDF or template for signature, embedded signing, verifying signing webhooks, retrieving the signed PDF plus audit trail. NOT drafting contract text (that is `contracts`), NOT extracting fields from PDFs (that is `document-processing`).",
1429
1429
  "tags": [
1430
1430
  "e-signature",
1431
1431
  "docusign",
@@ -1448,7 +1448,7 @@
1448
1448
  },
1449
1449
  {
1450
1450
  "id": "e2e-testing",
1451
- "description": "Use when writing or stabilizing browser end-to-end tests with Playwright — driving real navigation, forms, logins, and multi-step user journeys through an actual browser, picking durable locators, or killing tests that pass locally but flake in CI. Covers getByRole/getByTestId locator choice, web-first assertions, storageState auth via setup projects, retries/trace config, sharding, and the 2026 flakiness playbook. Triggers: 'write a Playwright test', 'my e2e tests flake in CI but pass locally', 'set up storageState so I don't log in every test', 'getByRole vs CSS selector', 'shard Playwright across machines', 'trace on retry', 'strict mode resolved to 2 elements', 'els tests d'extrem a extrem fallen aleatòriament al pipeline', 'los tests e2e fallan a veces'. NOT in-process component/unit web tests (that is testing-web), NOT pytest suites (that is testing-py), NOT a11y audits as the goal (that is accessibility), NOT building the CI pipeline itself (that is github-actions).",
1451
+ "description": "Use when writing or stabilizing Playwright tests that drive a real browser through multi-step journeys — durable locators, web-first assertions, storageState auth, trace/retries, and flakes that only bite in CI. NOT in-process component tests (that is testing-web), NOT WCAG auditing (that is accessibility), NOT the pre-merge gate (that is verify).",
1452
1452
  "tags": [
1453
1453
  "playwright",
1454
1454
  "e2e",
@@ -1467,7 +1467,7 @@
1467
1467
  },
1468
1468
  {
1469
1469
  "id": "electron",
1470
- "description": "Use when building, hardening, or shipping a cross-platform desktop app with Electron — wiring the main/renderer/preload process model, exposing OS capabilities to a web UI over typed IPC, locking down an insecure shell, or packaging with code signing and auto-update. Triggers: 'build a desktop app for Mac/Windows/Linux from my web app', 'let my renderer read a file safely', 'audit my Electron security (nodeIntegration, sandbox, CSP)', 'my preload exposes ipcRenderer and the renderer can require(\\\"fs\\\")', 'Squirrel.Mac won\\\\'t auto-update my app', 'migrate off @electron/remote and BrowserView', 'empaquetar mi app de escritorio para Windows y Mac con firma de código y auto-actualización', 'empaquetar app d\\\\'escriptori amb auto-actualització'. NOT a smaller Rust-backed shell on the OS native webview (that is tauri).",
1470
+ "description": "Use when building, hardening, or shipping a cross-platform Electron desktop app — main/renderer/preload process model, typed contextBridge IPC, locking down nodeIntegration/contextIsolation/sandbox/CSP, or packaging with signing and auto-update. NOT a Rust-backed shell on the native webview (that is tauri), nor the web UI inside it (that is react).",
1471
1471
  "tags": [
1472
1472
  "electron",
1473
1473
  "desktop",
@@ -1488,7 +1488,7 @@
1488
1488
  },
1489
1489
  {
1490
1490
  "id": "elixir",
1491
- "description": "Use when writing or refactoring Elixir/OTP code — GenServers, supervision trees, processes, pattern matching, mix projects and releases — or when BEAM behaviour misbehaves (restart loops, mailbox growth, timeouts). Triggers: 'write a GenServer', 'design a supervision tree', 'restart strategy one_for_one', 'let it crash instead of try/rescue', 'mix release for production', non-obvious 'my process mailbox keeps growing', 'GenServer call timeout / restart loop', Catalan 'arbre de supervisio en Elixir / processos que peten', Spanish 'arbol de supervision / procesos en Elixir / que es let it crash'. NOT building a Phoenix web app, LiveView, Ecto schemas, controllers or channels (that is phoenix).",
1491
+ "description": "Use when writing or refactoring Elixir/OTP on the BEAM — GenServers, supervision trees and restart strategies, pattern matching, mix projects and releases — or when processes misbehave (restart loops, mailbox growth, call timeouts). NOT a Phoenix web app, LiveView, Ecto or channels (that is `phoenix`).",
1492
1492
  "tags": [
1493
1493
  "elixir",
1494
1494
  "otp",
@@ -1508,7 +1508,7 @@
1508
1508
  },
1509
1509
  {
1510
1510
  "id": "email-connector",
1511
- "description": "Use when wiring an app to send transactional or bulk email through an HTTP provider (Resend, Twilio SendGrid, Postmark) from server code: welcome/reset/receipt sends, a provider-agnostic sendEmail() seam, React Email or dynamic templates, idempotent retries, 100-cap batch with partial-failure handling, transactional-vs-broadcast stream split, and the bounce/complaint webhook feeding a suppression list. Triggers: 'send a password-reset email with Resend', 'emails double-send when the queue retries', 'switch us off SendGrid onto Postmark without rewriting every call site', 'send a digest to 4000 users and handle the ones that fail', 'el webhook de bounces no actualiza la lista de supresión', 'montar el envío de correos con Resend'. NOT writing the subject line or the open/click growth system (that is newsletter).",
1511
+ "description": "Use when wiring server code to send transactional or bulk email via Resend, SendGrid, or Postmark: a provider-agnostic sendEmail() seam, idempotent retries, 100-cap batches with partial failures, transactional-vs-broadcast streams, bounce webhooks feeding a suppression list. NOT SPF/DKIM/DMARC inbox reputation (that is `email-deliverability`).",
1512
1512
  "tags": [
1513
1513
  "email",
1514
1514
  "transactional-email",
@@ -1530,7 +1530,7 @@
1530
1530
  },
1531
1531
  {
1532
1532
  "id": "email-deliverability",
1533
- "description": "Use when mail you send lands in spam, gets rejected, or fails the Gmail/Yahoo bulk-sender checks, and you must fix authentication and reputation rather than the sending code — setting SPF/DKIM/DMARC DNS records so they align, raising a cold domain through warmup, keeping the spam-complaint rate under threshold, adding one-click unsubscribe, getting the brand logo and verified checkmark to show via BIMI, and reading why 'soft' rejections and Postmaster Tools say you are throttled. Triggers: 'our emails go to spam since the Gmail update', 'DMARC fails even though SPF passes', 'how do I warm up a new sending domain', 'do I need one-click unsubscribe to send to gmail', '550 5.7.26 unauthenticated', 'how do I get our logo and the blue checkmark to show in Gmail with BIMI', 'los correos caen en spam y el SPF está bien', 'configurar BIMI i el logo verificat al correu'. NOT putting transactional/bulk email on the wire through a provider API (that is email-connector).",
1533
+ "description": "Use when mail already sent lands in spam, is rejected, or fails the Gmail/Yahoo bulk-sender checks and the fix is authentication and reputation, not the sending code: SPF/DKIM/DMARC alignment, domain warmup, spam-complaint rate, one-click unsubscribe, BIMI. NOT putting mail on the wire through a provider API (that is `email-connector`).",
1534
1534
  "tags": [
1535
1535
  "email",
1536
1536
  "deliverability",
@@ -1552,7 +1552,7 @@
1552
1552
  },
1553
1553
  {
1554
1554
  "id": "embeddings-search",
1555
- "description": "Use when results from a semantic search feel irrelevant, when you don't know which embedding model or chunk size to use, when you need to add a reranker or hybrid scoring, or when you have no way to tell whether a retrieval change actually helped. Symptoms: search returns garbage, exact-match queries miss, paraphrases work but keywords don't, recall looks fine but the top result is wrong, every chunking idea is a guess. Triggers: 'which embedding model and chunk size should I use', 'my semantic search returns irrelevant results', 'how do I measure if retrieval is good — recall@k, nDCG', 'add a reranker and hybrid BM25+vector', 'exact product codes return nothing but paraphrases work fine', 'recall@k is fine but the top answer is wrong', 'quin model d'embeddings i quina mida de chunk faig servir per a aquests documents', 'la cerca semàntica retorna resultats irrellevants'. NOT operating the vector store (that is vector-db).",
1555
+ "description": "Use when choosing an embedding model, chunk size, or query form, when semantic search returns irrelevant results, when adding hybrid BM25+vector or a reranker, or when a retrieval change needs a number (recall@k, nDCG, MRR). NOT operating the store — index tuning, quantization (that is `vector-db`) — nor the retrieve-to-answer loop (that is `rag`).",
1556
1556
  "tags": [
1557
1557
  "embeddings",
1558
1558
  "semantic-search",
@@ -1575,7 +1575,7 @@
1575
1575
  },
1576
1576
  {
1577
1577
  "id": "error-handling",
1578
- "description": "Use when designing how code recognizes, recovers from, and reports failure — typed error taxonomies, retry/backoff/timeout policy, circuit breakers, React/Next error boundaries, and the user message + operator log line. Triggers: 'add retries with backoff', 'set up an error boundary', 'standardize our error types', 'stop swallowing exceptions', 'this catch block hides the real error', 'degrade gracefully when the payment provider is down', 'why does our retry storm take the database down', 'maneja los errores sin tragarte la excepción', 'afegeix reintents amb backoff i tallacircuits'. NOT finding why one specific thing broke (that is debug), NOT wiring logs/metrics/traces (that is observability), NOT the on-the-wire error envelope contract (that is api-design).",
1578
+ "description": "Use when designing the reaction to a class of failures — typed error taxonomies, retry/backoff/timeout policy, circuit breakers, React/Next error boundaries, and the user-message vs operator-log split. NOT diagnosing one specific crash (that is debug), NOT logs/metrics/traces (that is observability), NOT the wire error envelope (that is api-design).",
1579
1579
  "tags": [
1580
1580
  "error-handling",
1581
1581
  "resilience",
@@ -1598,7 +1598,7 @@
1598
1598
  },
1599
1599
  {
1600
1600
  "id": "expo",
1601
- "description": "Use when building, shipping, or hot-patching a React Native app with Expo — EAS Build/Submit/Update, eas.json build profiles, config plugins, prebuild/CNG, the managed workflow, confirming you are on the New Architecture (mandatory since SDK 55). Triggers: 'eas build', 'eas.json', 'config plugin', 'expo prebuild', 'my OTA update isn't showing up on devices', 'runtime version mismatch', 'how do I add a permission without editing Xcode/Gradle', 'newArchEnabled is gone from app.json', 'com publico una actualització OTA sense passar per la store', 'subir la app a la Play Store con EAS'. NOT generic React Native components/navigation/native-module authoring (that is react-native), NOT a Dart mobile app (that is flutter).",
1601
+ "description": "Use when shipping a React Native app with Expo — EAS Build/Submit/Update, eas.json profiles and channels, config plugins, prebuild/CNG, runtime-version policy, OTA updates that never land, SDK upgrades, the New Architecture. NOT RN UI, navigation or native-module authoring (that is `react-native`), NOT a Dart app (that is `flutter`).",
1602
1602
  "tags": [
1603
1603
  "expo",
1604
1604
  "eas",
@@ -1641,7 +1641,7 @@
1641
1641
  },
1642
1642
  {
1643
1643
  "id": "fal",
1644
- "description": "Use when calling a fal.ai model endpoint by id to generate an image, audio, or video from JS/Python/curl, wiring the queue (subscribe vs submit), receiving results by webhook and verifying the ED25519 signature, estimating per-call cost, or migrating off the deprecated @fal-ai/serverless-client. Use when a fal request is stuck IN_QUEUE, a webhook never arrives or its signature won't verify, or a long video gen drops mid-call. Triggers: 'generate an image with fal-ai/flux/dev', 'subscribe vs submit on fal', 'my fal webhook signature won't verify', 'fal request stuck IN_QUEUE', 'how much will 500 Seedream images cost on fal', 'migrate off @fal-ai/serverless-client', 'genera esta imagen con fal y guarda la URL', 'munta el webhook de fal i verifica la firma'. NOT picking the model or art direction (that is ai-media).",
1644
+ "description": "Use when calling a fal.ai endpoint by id to generate image, audio, or video from JS/Python/curl: subscribe vs submit, queue states, ED25519 webhook signature verification, per-call cost, or migrating off @fal-ai/serverless-client. NOT which model or art direction (that is ai-media); NOT the same models on another platform (that is replicate).",
1645
1645
  "tags": [
1646
1646
  "fal",
1647
1647
  "fal-ai",
@@ -1680,7 +1680,7 @@
1680
1680
  },
1681
1681
  {
1682
1682
  "id": "finance-ops",
1683
- "description": "Use when running the money side of a small company on a cash basis — building a rolling cash-flow forecast, reconciling the bank against the books, fixing a messy expense-category map, or driving a month-end close. Triggers: 'are we going to run out of cash', 'what's our runway', '13-week cash flow forecast', 'reconcile the bank statement', 'these two numbers don't match', 'the ending balance doesn't roll into next week', 'everything landed in Other expenses', 'close the books for May', 'what's our burn this month', 'tancar el mes', 'conciliar el banc amb els llibres', 'cuánto runway nos queda al ritmo actual'. NOT recording journal entries or payroll/depreciation postings (that is bookkeeping).",
1683
+ "description": "Use when running the money side of a small company on a cash basis: a 13-week rolling cash-flow forecast and runway, bank-vs-books reconciliation, mapping spend to tax-return expense categories, or a month-end close. NOT posting journal entries, payroll or depreciation (that is `bookkeeping`); NOT multi-year projections (that is `financial-model`).",
1684
1684
  "tags": [
1685
1685
  "cash-flow",
1686
1686
  "reconciliation",
@@ -1702,7 +1702,7 @@
1702
1702
  },
1703
1703
  {
1704
1704
  "id": "financial-model",
1705
- "description": "Use when a founder needs the multi-period, driver-based projection that raises a round and steers the company — build 3-year projections, model revenue + costs + headcount + runway, size the raise, or stress-test scenarios. Triggers: 'build a financial model for our seed', 'model our revenue costs and runway', 'when do we run out of cash', 'how much should we raise to get 24 months', 'what runway does $2M buy us', 'model the hiring plan's effect on burn', 'add a downside scenario if we miss plan by 30%', 'modelo financiero a 3 años', 'cuánto runway nos da la ronda', 'model financer i projeccions', 'quant de pista de despegament'. The engine is assumptions → revenue/cost/headcount builds → cash/burn/runway/scenarios, 18–36 months forward, every output a formula. NOT the next-13-weeks liquidity reconciled to the actual bank balance (that is finance-ops), NOT the isolated LTV/CAC math (that is unit-economics, which this model consumes as drivers), NOT the slide story (that is pitch-deck).",
1705
+ "description": "Use when a founder needs the driver-based 18–36 month projection: revenue, cost and headcount builds resolving to net burn, runway, cash-out date, scenarios and the raise size. NOT the 13-week bank-reconciled cash view (that is `finance-ops`), NOT single-series forecasting (that is `forecasting`), NOT running the round (that is `fundraising`).",
1706
1706
  "tags": [
1707
1707
  "financial-model",
1708
1708
  "projections",
@@ -1725,7 +1725,7 @@
1725
1725
  },
1726
1726
  {
1727
1727
  "id": "finetuning",
1728
- "description": "Use when adapting an open-weight model to a target FORM or BEHAVIOR — tone, output format, domain style, a reasoning/tool-calling pattern — via LoRA/QLoRA or full fine-tuning with TRL SFTTrainer and then preference optimization (DPO/ORPO/KTO/GRPO); when choosing fine-tune vs prompt vs RAG vs longer context; when setting LoRA r/alpha/target_modules; when eval loss climbs while train loss keeps falling; when the adapter seems to have learned nothing; when inference outputs garbage after a run. Triggers: 'fine-tune Llama/Qwen on our data', 'LoRA vs QLoRA vs full fine-tune', 'SFT then DPO/ORPO/KTO/GRPO', 'set target_modules / rank / alpha', 'my fine-tune overfits', 'the model forgot everything after training', 'write a GRPO reward function', 'ajustar un modelo amb LoRA amb les nostres dades'. NOT adding facts or fresh knowledge to a model (that is rag); NOT the fast single-GPU Unsloth backend or GGUF export (that is unsloth); NOT serving the trained model (that is vllm).",
1728
+ "description": "Use when adapting an open-weight model to a target form or behavior — tone, output format, reasoning pattern — via LoRA/QLoRA or full fine-tuning with TRL SFTTrainer, then preference optimization (DPO/ORPO/KTO/GRPO), and for fine-tune vs prompt vs RAG. NOT adding facts to a model (that is rag); NOT the single-GPU Unsloth backend or GGUF export (that is unsloth).",
1729
1729
  "tags": [
1730
1730
  "finetuning",
1731
1731
  "lora",
@@ -1749,7 +1749,7 @@
1749
1749
  },
1750
1750
  {
1751
1751
  "id": "firebase",
1752
- "description": "Use when building on Firebase — modeling Firestore data, writing or auditing Security Rules, wiring Auth, shipping Cloud Functions, storing files in Cloud Storage, or choosing modular Web/Admin SDK imports — and when symptoms appear like 'my database is open to the internet', 'rules reject my query', a counter stuck at 1 write/sec, custom-claim RBAC, or a callable function. Triggers: 'firestore.rules audit', 'denormalize for read paths', 'collection vs subcollection', 'my query works in the emulator but fails in prod with requires-an-index', 'set a custom admin claim', 'regles de seguretat de Firestore', 'mi base de datos está abierta a internet'. NOT managed-Postgres BaaS with SQL and RLS (that is supabase).",
1752
+ "description": "Use when building on Firebase — Firestore data modeling, Security Rules, Auth and custom claims, Cloud Functions, Storage, modular Web/Admin SDK imports — including symptoms like a database open to the internet, a query rejected by rules, or a doc stuck at ~1 write/sec. NOT managed-Postgres BaaS with SQL and RLS (that is supabase).",
1753
1753
  "tags": [
1754
1754
  "firebase",
1755
1755
  "firestore",
@@ -1783,7 +1783,7 @@
1783
1783
  },
1784
1784
  {
1785
1785
  "id": "fly-io",
1786
- "description": "Use when deploying or operating an app on Fly.io — writing fly.toml, running Machines across regions near users, attaching Volumes, managing secrets, or scaling (autostop/autostart, scale count, fly-replay). Triggers: 'fly deploy', 'fly launch', 'desplega a Fly.io a prop dels usuaris', 'users in Sydney see high latency but my app only runs in iad', 'scale my Fly app to zero overnight', 'attach a persistent disk to my Fly machine', 'forward writes to the primary region'. NOT a generic git-push PaaS (that is railway), NOT frontend edge hosting (that is vercel).",
1786
+ "description": "Use when deploying or operating an app on Fly.io — writing fly.toml, placing Machines in regions near users, attaching Volumes, managing secrets, or picking a scaling lever (autostop/autostart, scale count, fly-replay). NOT choosing which host to deploy on (that is `deployment`), NOT a git-push PaaS with no regions model (that is `railway`).",
1787
1787
  "tags": [
1788
1788
  "fly-io",
1789
1789
  "deployment",
@@ -1822,7 +1822,7 @@
1822
1822
  },
1823
1823
  {
1824
1824
  "id": "fundraising",
1825
- "description": "Use when planning or running an equity fundraising round and you need the STRATEGY and PROCESS, not a single document — sizing the raise to a milestone, choosing post-money SAFE vs priced round, building and tiering the investor target list around intro paths, sequencing outreach to manufacture momentum, managing the diligence pipeline, or reading a term sheet to know what to push on before you sign. Triggers: 'plan our seed raise', 'how much should we raise and on what terms', 'SAFE or priced round, what cap', 'build my investor target list', 'how many investors do I actually need to contact', 'we have meetings but no term sheet — the raise stalled', 'got a term sheet, what do I push back on', 'planifica la ronda seed', 'cuántos inversores tengo que contactar', '¿SAFE o ronda con precio?', 'planifiquem la ronda d'inversió'. NOT the slide-deck narrative (that is pitch-deck) and NOT the cap-table/valuation math (that is financial-model).",
1825
+ "description": "Use when planning or running an equity round as a process: sizing the raise to a milestone, choosing post-money SAFE vs priced round, tiering investors by intro path, sequencing outreach for momentum, or reading a term sheet before signing. NOT the slide narrative (that is `pitch-deck`), NOT the cap-table or valuation math (that is `financial-model`).",
1826
1826
  "tags": [
1827
1827
  "fundraising",
1828
1828
  "seed",
@@ -1845,7 +1845,7 @@
1845
1845
  },
1846
1846
  {
1847
1847
  "id": "game-design",
1848
- "description": "Use when designing or repairing the design of a game — inventing mechanics and systems, shaping the core loop, layering moment-to-moment / session / meta loops, planning progression, economy, currencies and sinks/faucets, tuning difficulty curves and balance, killing dominant strategies, adding 'juice'/game feel as design intent, prototyping and playtesting, or controlling scope, and especially when the ask is 'why isn't my game fun'. Triggers: 'design the core loop', 'my game isn't fun / doesn't hook', 'balance the economy', 'the difficulty curve feels off', 'players found a dominant strategy', 'how do I scope this', 'reward / progression loop', 'diseñar la mecánica', 'el juego no engancha', 'com equilibro l'economia'. NOT writing engine code, scripts or shaders (route to godot/unity/unreal), NOT narrative, dialogue or story beats (route to game-storytelling), NOT level layout or encounter placement (route to level-design).",
1848
+ "description": "Use when designing or repairing what makes a game fun — verbs and mechanics, moment-to-moment/session/meta loops, progression, economy faucets and sinks, difficulty curves, dominant strategies, juice, prototyping and scope. NOT engine code (that is `godot`), NOT story (that is `game-storytelling`), NOT level layout (that is `level-design`).",
1849
1849
  "tags": [
1850
1850
  "game-design",
1851
1851
  "mechanics",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "description": "Eric Risco's agent-skills catalog as a granular, self-recommending CLI installer.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: db-migrations
3
- description: "Use when planning a schema change that must ship without downtime — adding a NOT NULL column, renaming/splitting/merging a column or table, changing a type, or backfilling millions of rows on a live table — and you need the expand-contract sequence plus the lock/timeout/batching discipline that keeps each step from freezing prod. Triggers: 'zero-downtime migration', 'add NOT NULL column to a huge table', 'rename a column safely', 'our deploy keeps deadlocking when we add an index', 'backfill 80M rows without replication lag', 'expand contract', 'gh-ost vs pt-osc', 'migración sin downtime', 'migració sense tallar el servei'. NOT Postgres lock internals/EXPLAIN/RLS (that is postgresdb), NOT drizzle-kit runner mechanics (that is drizzle-orm), NOT restore drills/PITR (that is backups)."
3
+ description: "Use when a schema change must ship without downtime — NOT NULL, rename, type change, or backfilling millions of live rows — for the expand-contract sequence and the lock/batching discipline that keeps each step from freezing prod. NOT lock internals or EXPLAIN (that is `postgresdb`), NOT drizzle-kit mechanics (that is `drizzle-orm`), NOT PITR (that is `backups`)."
4
4
  tags: [migrations, zero-downtime, expand-contract, schema, backfill, ddl]
5
5
  recommends: [postgresdb, drizzle-orm, backups, sql, planetscale]
6
6
  origin: risco
@@ -17,20 +17,9 @@ point without an outage.
17
17
 
18
18
  This skill is engine-neutral. It owns the *choreography* — what order to run things in and how to keep each
19
19
  DDL from freezing the table — across Postgres, MySQL, and serverless variants, whatever runner you use
20
- (Flyway, Alembic, golang-migrate, drizzle-kit, raw SQL). It does not teach one ORM's CLI and it is not a
21
- Postgres tuning manual.
20
+ (Flyway, Alembic, golang-migrate, drizzle-kit, raw SQL).
22
21
 
23
- ## When to use / When NOT to use
24
-
25
- **Use when:**
26
-
27
- - Sequencing a multi-step zero-downtime change: NOT NULL, rename, split/merge, type change, drop, add FK.
28
- - Backfilling a new column/table over millions of rows without blocking writes or blowing up replica lag.
29
- - Making one DDL statement safe: `lock_timeout`, `CONCURRENTLY`, running outside a transaction.
30
- - Coordinating deploy order so app version N and N+1 are both schema-compatible at every step.
31
- - Deciding rollback strategy (forward-fix vs reversible) and choosing an OSC tool for a big MySQL ALTER.
32
-
33
- **Route elsewhere:**
22
+ ## Route elsewhere
34
23
 
35
24
  - Postgres lock internals, EXPLAIN/ANALYZE, indexing strategy, RLS, PgBouncer → `../postgresdb/SKILL.md`.
36
25
  - Writing schema and running drizzle-kit specifically → `../drizzle-orm/SKILL.md`.
@@ -246,9 +235,3 @@ the same static checks against the example migrations shipped in `references/`.
246
235
  | Contracting before the cooling period | Kills your no-schema-change rollback path | wait until the new app has run clean; then drop |
247
236
  | Relying on `down()` in prod | Data already changed; rollback is unsafe or wrong | forward-only; expand-contract IS the rollback (Step 6) |
248
237
  | Coupling app + schema in one deploy | Breaks N/N-1; an instant where some app can't run | separate deploys; schema is a superset of both (Step 3) |
249
-
250
- ## References
251
-
252
- - `references/expand-contract-playbook.md` — ordered deploy/migrate checklists per change (NOT NULL, rename, split, type change, drop, add FK).
253
- - `references/backfill-and-batching.md` — batched backfill scripts (Postgres + MySQL), replica-lag throttle, checksum verification, snapshot+CDC for huge tables.
254
- - `references/tools-and-runners.md` — 2026 tool matrix, gh-ost vs pt-osc, per-runner transaction opt-out, Squawk CI config.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: decision-records
3
- description: "Use when a consequential, hard-to-reverse choice was just made or is about to be — database, framework, auth model, vendor, API style, deployment target — and future-you will ask why, with the answer buried in chat; capture it as a numbered, immutable ADR with the context that forced the choice, the real options weighed with their trade-offs, the decision, and consequences both ways. Also when a prior decision is being reversed and must be superseded without erasing history, or when onboarding keeps re-litigating a settled choice. Triggers: 'write up why we picked Postgres over DynamoDB so we stop arguing', 'record the switch to tRPC but keep the old REST decision in history', 'documenta por qué elegimos Hetzner y qué alternativas descartamos con pros y contras', 'registra la decisió i les alternatives que vam descartar'. NOT the meeting recap with action items and owners (that is meeting-notes), NOT the project-wide standing principles and stack canon (that is constitution)."
3
+ description: "Use when a hard-to-reverse choice (database, framework, vendor, auth model) must be frozen as an immutable numbered ADR — the context that forced it, the options weighed, the decision, consequences both ways — or superseded without erasing history. NOT a meeting recap (that is meeting-notes), NOT standing project principles (that is constitution)."
4
4
  tags: [decision-records, adr, architecture-decisions, rationale, decision-log, madr, knowledge-ops]
5
5
  recommends: [meeting-notes, constitution, sop-builder, knowledge-ops, codebase-onboarding, plan, specify]
6
6
  origin: risco
@@ -182,7 +182,7 @@ A decision log is only useful if it stays navigable.
182
182
  - **Index every ADR at creation.** One row: `| 0007 | Choose Postgres | accepted | 2026-06-02 |`. No orphans.
183
183
  - **Keep supersession links live** in both directions.
184
184
  - **Review cadence.** Periodically sweep `accepted` ADRs — anything reality has overtaken gets a superseding record, not a silent edit.
185
- - **Index the log from the entry point** — `02-DOCS/wiki/index.md` (the Knowledge map; root `CLAUDE.md` keeps only a short pointer) — so onboarding finds it. The broader wiki and onboarding doc are owned by `knowledge-ops` and `codebase-onboarding`; this skill owns only the ADRs the wiki links to. The meeting that spawned a decision routes to [`meeting-notes`](../meeting-notes/SKILL.md); the repeatable how-to it implies routes to [`sop-builder`](../sop-builder/SKILL.md).
185
+ - **Index the log from the entry point** — `02-DOCS/wiki/index.md` — so onboarding finds it. The broader wiki and onboarding doc belong to [`knowledge-ops`](../knowledge-ops/SKILL.md) and [`codebase-onboarding`](../codebase-onboarding/SKILL.md); this skill owns only the ADRs the wiki links to. The meeting that spawned a decision routes to [`meeting-notes`](../meeting-notes/SKILL.md); the repeatable how-to it implies routes to [`sop-builder`](../sop-builder/SKILL.md).
186
186
 
187
187
  ## Anti-patterns
188
188
 
@@ -199,4 +199,4 @@ A decision log is only useful if it stays navigable.
199
199
 
200
200
  ## Verify
201
201
 
202
- Lint a produced ADR (or a whole decisions dir) with `scripts/verify.sh <path>`: it checks for a recognized status, a date, the required sections, ≥2 options, and a valid filename — read-only, no network. See [`references/templates.md`](references/templates.md) for the skeletons it expects.
202
+ Lint a produced ADR (or a whole decisions dir) with `scripts/verify.sh <path>`: it checks for a recognized status, a date, the required sections, ≥2 options, and a valid filename — read-only, no network. It expects the skeletons above.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: deep-learning
3
- description: "Use when training or debugging a neural network in PyTorch — writing the training loop, chasing loss that won't drop or goes NaN, adding mixed precision (AMP), scaling across GPUs, or making a run reproducible. Covers tensors/autograd/nn.Module, torch.compile, the forward→loss→backward→step→zero_grad loop and its silent bugs, AdamW + cosine/warmup schedulers, DDP vs FSDP vs DeepSpeed ZeRO, checkpointing and seeds. PyTorch-first (JAX/TF exist). Triggers: 'my training loss isn't decreasing', 'loss becomes NaN after a few steps', 'how do I use torch.autocast and GradScaler', 'train this model across 4 GPUs', 'val accuracy is worse than train and I forgot model.eval()', 'gradients seem to add up across batches', 'build a CNN from scratch and train it', 'entrenar una xarxa neuronal des de zero amb PyTorch'. NOT adapting a pretrained LLM with LoRA/QLoRA (that is finetuning); NOT classic/tabular models like XGBoost or sklearn (that is machine-learning); NOT tokenization or NLP task metrics (that is nlp)."
3
+ description: "Use when training or debugging a neural net in PyTorch — the forward/loss/backward/step loop and its silent bugs, mixed precision (AMP), AdamW/LR schedules, DDP/FSDP/ZeRO, checkpoints and seeds. NOT LoRA/QLoRA on a pretrained LLM (that is `finetuning`), NOT tabular sklearn/XGBoost (that is `machine-learning`), NOT tokenization or NLP metrics (that is `nlp`)."
4
4
  tags: [pytorch, deep-learning, neural-networks, training-loop, autograd, mixed-precision, amp, ddp, fsdp, optimizer, lr-scheduler, reproducibility, torch-compile]
5
5
  recommends: [machine-learning, finetuning, nlp, python]
6
6
  origin: risco
@@ -27,8 +27,7 @@ is stable 2.x API. The one namespace shift to know: AMP now lives under `torch.a
27
27
  | Adapting a *pretrained LLM* — LoRA/QLoRA, SFT, trl/peft | [`finetuning`](../finetuning/SKILL.md) |
28
28
  | Classic/tabular — sklearn, XGBoost/LightGBM, feature engineering | [`machine-learning`](../machine-learning/SKILL.md) |
29
29
  | Tokenization, NLP task modeling, task metrics (F1/BLEU/ROUGE) | [`nlp`](../nlp/SKILL.md) |
30
-
31
- If the model is a from-scratch or vision/custom net and you're writing the loop yourself, stay.
30
+ | Envs, packaging, tests and hygiene around the model code | [`python`](../python/SKILL.md) |
32
31
 
33
32
  ## 1. PyTorch essentials
34
33
 
@@ -83,7 +82,7 @@ for epoch in range(epochs):
83
82
  scheduler.step() # if a per-step scheduler
84
83
  ```
85
84
 
86
- The load-bearing traps (all in the gotchas table, worth stating once here):
85
+ The load-bearing traps, stated next to the lines they govern:
87
86
 
88
87
  - **Missing `optimizer.zero_grad()` → gradients silently add up across batches.** PyTorch
89
88
  *accumulates* `.grad` by design (so you can split a batch); if you never zero it, every step
@@ -200,7 +199,6 @@ Save optimizer + scheduler + scaler too, or a resumed run restarts its LR schedu
200
199
  `pytorch.org/docs/stable/notes/randomness.html`, quoted: *"Completely reproducible results are not
201
200
  guaranteed across PyTorch releases, individual commits, or different platforms. Furthermore,
202
201
  results may not be reproducible between CPU and GPU executions, even when using identical seeds."*
203
- So bit-exact reproducibility is a debugging/regression tool, not a guarantee.
204
202
 
205
203
  ```python
206
204
  import random, numpy as np, torch
@@ -217,47 +215,12 @@ DataLoader workers need their own seeding (each worker forks its RNG), via `work
217
215
  a seeded `generator` — full snippet in `references/training-recipes.md`. Determinism costs speed
218
216
  (`benchmark=False` disables autotuned convs).
219
217
 
220
- ## Guardrails (the silent killers)
221
-
222
- - **`optimizer.zero_grad()` every step.** Skipping it makes `.grad` accumulate across batches — no
223
- error, just diverging training. (`torch.optim` / optimization tutorial, pytorch.org)
224
- - **`model.eval()` + `torch.no_grad()` at inference/validation.** Forgetting `eval()` leaves Dropout
225
- dropping and BatchNorm using *batch* stats instead of running stats, so val numbers are wrong;
226
- forgetting `no_grad()` builds the autograd graph and can OOM. Flip back to `model.train()` after.
227
- (`nn.Module.eval` / autograd docs, pytorch.org)
228
- - **Determinism is not guaranteed across releases/platforms/CPU-vs-GPU** even with identical seeds —
229
- seed for debugging, don't promise bit-exactness. (randomness note, pytorch.org)
230
- - **fp16 without `GradScaler`** underflows to zero grads → no learning. bf16 doesn't need it.
231
- - **`CrossEntropyLoss`/`BCEWithLogitsLoss` take raw logits** — no Softmax/Sigmoid before them.
232
- - **Everything on the same device** — a stray CPU tensor in a CUDA graph is a hard error you'll hit
233
- fast; `.to(device)` inputs, targets, and model.
234
- - **NaN loss?** LR too high, no AMP scaler on fp16, unclipped exploding grads, or a `log(0)`/`0/0`
235
- in a custom loss. Bisect: drop LR 10×, clip grads, switch fp16→bf16.
236
-
237
- ## Related skills
238
-
239
- - [`finetuning`](../finetuning/SKILL.md) — adapting a **pretrained LLM** (LoRA/QLoRA, SFT via
240
- trl/peft). If the base is a foundation model you're specializing, that's the skill; this one is
241
- general/from-scratch net training.
242
- - [`machine-learning`](../machine-learning/SKILL.md) — classic/**tabular** (sklearn, GBDT). No
243
- autograd, no GPU loop.
244
- - [`nlp`](../nlp/SKILL.md) — tokenization, NLP **task** modeling and metrics. Route text task
245
- specifics there; route the raw training loop back here.
246
- - [`python`](../python/SKILL.md) — env/packaging, testing, general Python hygiene under the model
247
- code.
248
-
249
- ## Checklist
250
-
251
- - [ ] Right skill: from-scratch/vision/custom net in PyTorch (not finetuning / ML / nlp).
252
- - [ ] Loop has all five steps in order; `zero_grad(set_to_none=True)` every step.
253
- - [ ] Loss takes raw logits (no double activation); running loss uses `.item()`.
254
- - [ ] `model.train()` for training; `model.eval()` **and** `torch.no_grad()` for val/inference.
255
- - [ ] AMP: `torch.autocast` on forward+loss only; fp16 → `GradScaler`, bf16 → skip it; clip after
256
- `unscale_`.
257
- - [ ] AdamW (decoupled decay) with no weight decay on bias/Norm params; LR warmup→cosine, `step()`
258
- cadence matches scheduler units.
259
- - [ ] Multi-GPU chosen by fit: DDP (fits) vs FSDP2 (doesn't) vs ZeRO (offload/config).
260
- - [ ] Checkpoint saves model+optimizer+scheduler+scaler `state_dict`s; resume loads model first.
261
- - [ ] Seeds set; determinism flags on if needed, with the "not guaranteed across releases/devices"
262
- caveat acknowledged.
263
- - [ ] Version claims hedged to "PyTorch 2.x (~2.13, verify)"; no hard minor pin presented as current.
218
+ ## Anti-patterns (the silent killers)
219
+
220
+ | Anti-pattern | What actually happens | Instead |
221
+ |---|---|---|
222
+ | Validating with the model left in `train()` mode, or without `torch.no_grad()` | Dropout keeps dropping and BatchNorm uses *batch* stats instead of running stats, so val numbers are wrong; the graph is still built and can OOM | `model.eval()` + `torch.no_grad()` around val/inference, `model.train()` after (`nn.Module.eval` / autograd docs, pytorch.org) |
223
+ | Reading a NaN loss as a modeling problem | It is almost always LR too high, fp16 with no `GradScaler`, unclipped exploding grads, or a `log(0)`/`0/0` in a custom loss | Bisect: drop LR 10×, clip grads, switch fp16→bf16 |
224
+ | fp16 with the scaler left off | Gradients underflow to zero → no learning, and no error to tell you | Keep `GradScaler` on fp16, or move to bf16 where underflow can't happen |
225
+ | A stray CPU tensor in a CUDA graph | Hard device-mismatch error mid-step, usually on the targets or a hand-built mask rather than the inputs | `.to(device)` inputs, targets **and** model |
226
+ | Promising bit-exact reproducibility from seeds | Not guaranteed across releases, commits, platforms, or CPU-vs-GPU even with identical seeds | Seed for debugging/regression and say so; state the caveat with the result (randomness note, pytorch.org) |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: digitalocean
3
- description: "Use when deploying or operating a workload on DigitalOcean and you must decide where it runs and how to ship it — Droplet (raw VPS) vs App Platform (managed PaaS) vs Functions, wiring a Managed Database (Postgres/MySQL/Valkey), Spaces object storage + CDN, doctl auth, App Platform app spec YAML, cloud firewalls, VPC private networking, reserved IPs, snapshots/backups, app logs, scaling. Triggers: 'deploy to DigitalOcean', 'DigitalOcean App Platform app spec', 'DO managed postgres with connection pooling', 'doctl apps create', 'reserved IP failover', 'I need an S3-compatible bucket with a CDN' (that is Spaces — non-obvious), 'subir mi web a DigitalOcean con doctl', 'Droplet o App Platform, qué sale más barato'. NOT host-agnostic CI/CD or rollback strategy (that is deployment), NOT a Hetzner bare VPS (that is hetzner)."
3
+ description: "Use when deploying or operating a workload on DigitalOcean — Droplet vs App Platform vs Functions, doctl, app spec YAML, Managed Postgres/MySQL/Valkey on the VPC, S3-compatible Spaces + CDN. NOT host-agnostic CI/CD or rollback strategy (that is `deployment`), NOT a bare Hetzner VPS (that is `hetzner`), NOT another managed PaaS (that is `railway`)."
4
4
  tags: [digitalocean, doctl, app-platform, droplets, spaces, deployment, paas, vps]
5
5
  recommends: [deployment, postgresdb, docker, domains-dns, monitoring, scaling, hetzner]
6
6
  origin: risco
@@ -8,10 +8,11 @@ origin: risco
8
8
 
9
9
  # DigitalOcean — Droplet vs App Platform, doctl, Managed DBs, Spaces
10
10
 
11
- You own one decision: **where on DO does this run, and how do I ship it.** Everything
12
- else (CI/CD shape, Dockerfile, DB schema, DNS) belongs to a sibling — see the boundary at
13
- the bottom. Pick the compute model first, then wire data and storage, then the ops that
14
- bite in production.
11
+ You own one decision: **where on DO does this run, and how do I ship it.** Host-agnostic
12
+ CI/CD, release gating and rollback strategy are `../deployment/SKILL.md`; authoring the
13
+ Dockerfile is `docker`; DNS records and the registrar are `domains-dns`; a bare Hetzner box
14
+ and its economics are `hetzner`. Pick the compute model first, then wire data and storage,
15
+ then the ops that bite in production.
15
16
 
16
17
  ```text
17
18
  workload → [Droplet | App Platform | Functions] → Managed DB (VPC private host) → Spaces (S3 + CDN)
@@ -34,11 +35,9 @@ Droplet only when you need the box itself.
34
35
  Rules of thumb:
35
36
 
36
37
  - **Stateless web service or API from a Git repo → App Platform.** It builds, deploys, gives
37
- you TLS and a URL, and re-deploys on push. No box to patch.
38
+ you TLS and a URL, and re-deploys on push. No box to patch. **Static frontend → App
39
+ Platform static site (free, up to 3)**; don't run a Droplet to serve HTML.
38
40
  - **You need root, a long-lived stateful daemon, custom ports, or a cron host → Droplet.**
39
- App Platform components are not meant to be a persistent pet process.
40
- - **Static frontend → App Platform static site (free, up to 3).** Don't run a Droplet to
41
- serve HTML.
42
41
  - **A managed Postgres/MySQL/Valkey → always the Managed Database product**, never a DB you
43
42
  hand-install on a Droplet, unless you have a hard reason.
44
43
 
@@ -156,8 +155,8 @@ doctl compute droplet create web-1 \
156
155
  to spin up and destroy, but a powered-off Droplet still bills for storage — destroy, don't
157
156
  just power off, to stop charges.
158
157
 
159
- Cloud-init recipes, firewall inbound/outbound rule sets, snapshot cadence, and reserved-IP
160
- failover live in `references/droplet-ops.md`.
158
+ Cloud-init recipes, firewall inbound/outbound rule sets, snapshot cadence + restore,
159
+ reserved-IP failover, and the VPC + private-DB layout live in `references/droplet-ops.md`.
161
160
 
162
161
  ## Managed Databases
163
162
 
@@ -206,11 +205,6 @@ browser fetches it cross-origin, and a **lifecycle** rule to expire/transition o
206
205
 
207
206
  ## Basic ops + cost gotchas
208
207
 
209
- ```bash
210
- doctl apps logs <app-id> --type run --follow # app logs
211
- doctl apps update <app-id> --spec .do/app.yaml # change instance_count/size by editing the spec
212
- ```
213
-
214
208
  - Set **alerts** on the app/DB (deploy failures, CPU, restart count) so you hear about
215
209
  trouble before users do. App-level dashboards/alerting *strategy* is `../monitoring`.
216
210
  - **Scale** by editing `instance_count`/`instance_size_slug` in the spec and re-applying;
@@ -231,21 +225,3 @@ doctl apps update <app-id> --spec .do/app.yaml # change instance_count/size by e
231
225
  | Hand-installing Postgres on a Droplet "to save money" | You now own patching, backups, failover | Managed Database unless you have a hard reason |
232
226
  | Powering off a Droplet to stop billing | Powered-off Droplets still bill for storage | Snapshot then destroy |
233
227
  | Leaving a reserved IP unassigned | It's billed when not attached | Release it |
234
-
235
- ## References
236
-
237
- - `references/app-spec.md` — full multi-component app spec (web + worker + static_site + job
238
- + database), health checks, ingress routes, instance-size slug table, env/SECRET scoping,
239
- autoscaling and alerts.
240
- - `references/droplet-ops.md` — cloud-init examples, cloud firewall inbound/outbound rule
241
- recipes, snapshot/backup cadence + restore, reserved IP failover, VPC + private DB layout.
242
-
243
- ## Boundary
244
-
245
- This skill is DigitalOcean-specific deployment. For neighbors:
246
-
247
- - Host-agnostic CI/CD pipelines, release gating, rollback strategy → `../deployment/SKILL.md`.
248
- - Postgres schema/query/index work (not "provision a DO cluster") → `../postgresdb/SKILL.md`.
249
- - Authoring the Dockerfile itself → `docker`. DNS records/registrar → `domains-dns`.
250
- - Uptime/metrics dashboards + alerting strategy → `monitoring`. Host-agnostic capacity →
251
- `scaling`. A Hetzner bare VPS and its economics → `hetzner`.