@ericrisco/rsc 0.1.52 → 0.1.54

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.
@@ -10,9 +10,9 @@ should_trigger:
10
10
  - prompt: "Is this cold prospect list legal to email under GDPR before we send?"
11
11
  why: "The compliance gate (GDPR LIA, no scraped basis) is part of making a list ship-ready — non-obvious that lead-gen owns it, not a generic legal skill."
12
12
  - prompt: "Necesito una lista de clientes potenciales para nuestro SaaS de logística."
13
- why: "Spanish core trigger — build a target list of prospects."
13
+ why: "Spanish-language phrasing of the core case — build a target list of prospects."
14
14
  - prompt: "A qui de la nostra base truquem primer, tenim 300 comptes sense prioritzar?"
15
- why: "Catalan trigger — scoring/tiering an unprioritized account list."
15
+ why: "Catalan-language phrasing — scoring/tiering an unprioritized account list."
16
16
 
17
17
  should_not_trigger:
18
18
  - prompt: "Write the cold email sequence for these prospects."
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linkedin-carousels
3
- description: "Use when designing a LinkedIn carousel / document post — the swipeable multi-page PDF that is currently the highest-reach native LinkedIn format — and you need the slide-by-slide build: a scroll-stopping cover hook, a narrative arc that pulls swipe-to-swipe, one focused idea per slide with real visual hierarchy, a payoff + CTA closer, and a PDF export spec. Triggers: 'turn this into a LinkedIn carousel', 'design a swipeable PDF document post', 'my carousel cover gets no swipes fix the hook', 'how many slides and what goes on each', 'restructure one-idea-per-slide', 'what size to export so it isnt cropped on mobile', 'convierte este post en un carrusel de LinkedIn', 'dissenya'm un carrusel de LinkedIn amb portada i CTA'. NOT the account/format plan (that is linkedin-strategy), NOT a text or single-image feed post (that is linkedin-content), NOT a spoken-to talk deck (that is presentations)."
3
+ description: "Use when building a LinkedIn carousel / document post — the swipeable multi-page PDF — slide by slide: cover hook, narrative arc, one idea per slide, CTA closer, PDF export spec. NOT the account/format plan (that is `linkedin-strategy`), NOT a text or single-image feed post (that is `linkedin-content`), NOT a 16:9 talk deck (that is `presentations`)."
4
4
  tags: [linkedin, carousel, document-post, slide-design, visual-hierarchy, hook, b2b, social-content]
5
5
  recommends: [linkedin-content, linkedin-strategy, brand-identity, brand-voice, presentations, social-publisher]
6
6
  origin: risco
@@ -8,16 +8,10 @@ origin: risco
8
8
 
9
9
  # LinkedIn carousels
10
10
 
11
- Design **one swipeable LinkedIn document post**: the cover hook, the narrative arc, one idea
12
- per slide, and the PDF export spec. You hand back a slide-by-slide build ready to assemble in
13
- Canva/Figma/Slides and a one-page design+export checklist.
14
-
15
- You do not own the account plan (which formats, cadence, niche — that is
16
- [`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md)), the words of a text or
17
- single-image feed post ([`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md)), the
18
- programmatic upload ([`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md)), or a spoken-to
19
- 16:9 talk deck ([`../presentations/SKILL.md`](../presentations/SKILL.md)). Your artifact is the
20
- *slides of a phone-read PDF* — sequence and visual hierarchy are the product.
11
+ Design **one swipeable LinkedIn document post**. Your artifact is the *slides of a phone-read
12
+ PDF* — sequence and visual hierarchy are the product — handed back as a slide-by-slide build
13
+ ready to assemble in Canva/Figma/Slides plus a one-page design+export checklist. Everything
14
+ adjacent (the account plan, the feed copy, the upload, a talk deck) routes out; see Handoffs.
21
15
 
22
16
  ## What you produce
23
17
 
@@ -27,34 +21,30 @@ programmatic upload ([`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md)), or
27
21
  - **A design + export spec** — canvas, safe area, type floor, PDF checklist (one page).
28
22
  - **A one-line caption starter** — the first feed line; full caption tuning routes out.
29
23
 
30
- Precondition: you need a topic, an angle, or a source (post/article). If the ask is the
31
- account plan or a block of feed copy, you are in the wrong skill — see the handoffs table.
24
+ Precondition: a topic, an angle, or a source (post/article). If the ask is the account plan or
25
+ a block of feed copy, you are in the wrong skill — see the handoffs table.
32
26
 
33
27
  ## Ground first
34
28
 
35
- A carousel inherits an identity; it does not invent one. Before you design slides:
36
-
37
- - **Read the brand kit** if it exists in `02-DOCS/` (colors, type, logo) —
38
- [`../brand-identity/SKILL.md`](../brand-identity/SKILL.md) owns it. Don't pick arbitrary fonts.
39
- - **Read the voice** so the headlines sound like the account —
40
- [`../brand-voice/SKILL.md`](../brand-voice/SKILL.md) owns it.
41
- - **Pull the angle** from any plan/post already produced by
42
- [`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) or
43
- [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md). Reuse the hook, don't reopen it.
44
-
45
- If none exist, name your assumptions on slide 0 of the spec and proceed — don't stall.
29
+ A carousel inherits an identity; it does not invent one. Read the brand kit (colors, type,
30
+ logo — owned by [`../brand-identity/SKILL.md`](../brand-identity/SKILL.md)) and the voice
31
+ ([`../brand-voice/SKILL.md`](../brand-voice/SKILL.md)) from `02-DOCS/` if they exist; don't pick
32
+ arbitrary fonts. Pull the angle from any plan or post already produced by
33
+ [`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) or
34
+ [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) — reuse the hook, don't reopen
35
+ it. If none exist, name your assumptions on slide 0 of the spec and proceed; don't stall.
46
36
 
47
37
  ## The swipe economics (why every rule below exists)
48
38
 
49
39
  Document/carousel posts are the **highest-engagement native format on LinkedIn right now** —
50
- they hit roughly 6.6% average engagement, ahead of every other format, and top-performing
51
- pages post far more of them than median pages. The reason is **dwell time**: a swipeable deck
52
- holds the thumb on screen, and dwell — not likes — is the algorithm's main distribution signal.
40
+ roughly 6.6% average engagement, ahead of every other format, and top-performing pages post far
41
+ more of them than median pages. The reason is **dwell time**: a swipeable deck holds the thumb
42
+ on screen, and dwell — not likes — is the algorithm's main distribution signal.
53
43
 
54
44
  So the whole bet is the **cover slide and the swipe-to-slide-2 bounce**. If the cover doesn't
55
- earn the first swipe, your slide count, your arc, and your CTA never get seen. Every swipe
56
- after that extends dwell and signals quality, which is why each slide must *pull to the next*.
57
- Design backwards from that: stop the thumb, then keep it moving.
45
+ earn the first swipe, your slide count, your arc, and your CTA never get seen; every swipe after
46
+ that extends dwell and signals quality, which is why each slide must *pull to the next*. Design
47
+ backwards from that: stop the thumb, then keep it moving.
58
48
 
59
49
  ## The cover slide
60
50
 
@@ -150,11 +140,10 @@ Good — one idea, one dominant element, breathing room
150
140
  ## Slide count
151
141
 
152
142
  Aim for the **6–12 band, ~8 as the sweet spot** (cover + ~6 content + CTA). Reported peaks
153
- cluster around 7–8 slides; 8–12 is where the highest dwell/save/share rates show up. Fewer
154
- than 5 underdelivers (not worth the swipe); more than ~15 loses people before the payoff.
155
-
156
- Treat the band as a guide, not a law: **length follows the arc, not a quota.** If the arc
157
- resolves in 7 slides, ship 7 — don't pad to hit a number.
143
+ cluster around 7–8 slides; 8–12 is where the highest dwell/save/share rates show up. Fewer than
144
+ 5 underdelivers (not worth the swipe); more than ~15 loses people before the payoff. Treat the
145
+ band as a guide, not a law: **length follows the arc, not a quota** — if the arc resolves in 7
146
+ slides, ship 7, don't pad to hit a number.
158
147
 
159
148
  ## The CTA closer
160
149
 
@@ -184,17 +173,17 @@ Gotchas: open the exported PDF and verify fonts — free Canva silently substi
184
173
  ```
185
174
 
186
175
  Portrait 4:5 takes the most vertical space in a mobile feed, which raises dwell and the chance
187
- of a scroll-stop. PDF is the only export that preserves fonts and layout reliably across
188
- devices — exporting slides as images loses crispness and breaks selectable text. The full
176
+ of a scroll-stop; PDF is the only export that preserves fonts and layout reliably across
177
+ devices, since slides exported as images lose crispness and break selectable text. The full
189
178
  checklist in prose is in [`references/carousel-patterns.md`](references/carousel-patterns.md).
190
179
 
191
180
  ## The feed caption handoff
192
181
 
193
182
  A document post still has a **text caption above it**, and its first 1–2 lines (before the
194
183
  "…more" fold) must restate the cover hook — that line is what people read before deciding to
195
- expand and swipe. Give a one-line starter that echoes the cover, then route full feed-copy
196
- length/tone tuning to [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md). You own
197
- the slides; that sibling owns the caption.
184
+ expand and swipe. Give a one-line starter that echoes the cover; you own the slides, and
185
+ [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) owns the caption's full
186
+ length/tone tuning.
198
187
 
199
188
  ## Handoffs
200
189
 
@@ -202,7 +191,7 @@ the slides; that sibling owns the caption.
202
191
  |---|---|
203
192
  | The account/format plan, cadence, niche, who to target | [`../linkedin-strategy/SKILL.md`](../linkedin-strategy/SKILL.md) |
204
193
  | A text or single-image feed post (just the copy) | [`../linkedin-content/SKILL.md`](../linkedin-content/SKILL.md) |
205
- | 1:1 DMs / connection-request outreach sequences | `linkedin-outreach` |
194
+ | 1:1 DMs / connection-request outreach sequences | [`../linkedin-outreach/SKILL.md`](../linkedin-outreach/SKILL.md) |
206
195
  | To publish/schedule the deck via the LinkedIn API | [`../linkedin-api/SKILL.md`](../linkedin-api/SKILL.md) |
207
196
  | A spoken-to 16:9 talk deck (speaker support) | [`../presentations/SKILL.md`](../presentations/SKILL.md) |
208
197
  | The color / type / logo brand kit the deck is styled in | [`../brand-identity/SKILL.md`](../brand-identity/SKILL.md) |
@@ -236,4 +225,4 @@ scripts/verify.sh path/to/carousel-spec.md
236
225
  It checks (read-only) for a labeled cover slide, a slide count in 5–15 (warns outside 6–12), a
237
226
  CTA slide with an explicit ask, a portrait/square canvas, a ≥28 px type floor, "PDF" as the
238
227
  export target, and warns on any crammed (3+ bullet) interior slide. An empty or clean target
239
- passes. The deeper rigor is the capability eval in `evals/`.
228
+ passes.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: llm-pipeline
3
- description: "Use when wiring several LLM calls into one production flow, when random 429s/timeouts/provider outages take a feature down, when adding a fallback model or a gateway in front of OpenAI/Claude, or when the bill explodes from uncached or flagship-everywhere calls. Triggers: 'chain these LLM calls', 'step 1 extracts step 2 summarizes', 'add a fallback if GPT fails use Claude', 'route easy requests to Haiku escalate to Opus', 'put a LiteLLM gateway in front', 'our bill tripled because we re-call the model on every request', 'semantic cache the responses', 'encadenar llamadas y cachear respuestas', 'cadena de crides a l'LLM amb fallback'. NOT prompt wording (that is prompt-engineering)."
3
+ description: "Use when wiring several LLM calls into a production flow: typed contracts between steps, a router/gateway so 429s, timeouts and outages fail over instead of taking you down, and cost control via caching, model tiers and abort caps. NOT single-prompt wording (that is `prompt-engineering`), NOT a model-driven tool loop (that is `building-agents`)."
4
4
  tags: [llm-orchestration, llm-gateway, fallbacks, prompt-caching, cost-control, litellm, reliability]
5
5
  recommends: [prompt-engineering, structured-extraction, building-agents, cost-tracking, agent-eval, rag, observability, parallel]
6
6
  origin: risco
@@ -10,14 +10,7 @@ origin: risco
10
10
 
11
11
  Wire multiple LLM calls into a reliable, controllable production pipeline. You chain steps where one call's validated output feeds the next, put a router in front of providers so an outage fails over instead of taking you down, and engineer the cross-cutting concerns: timeouts, bounded retries, fallbacks, caching, and cost caps.
12
12
 
13
- ## Core stance
14
-
15
- Treat the LLM as an unreliable network dependency, not a local function call. Everything below follows from that.
16
-
17
- - **Every call gets a hard timeout and bounded retries.** Providers have outages, rate limits, and latency tails; a naked call can hang your whole request behind one slow upstream.
18
- - **Steps pass validated structured objects, not free text.** The output of step N is a contract you assert on before it becomes step N+1's input — a parse failure is caught at the seam, not three steps later.
19
- - **A router sits in front of every provider.** One model's 429/500/timeout fails over to another; no single provider is a single point of failure.
20
- - **Cache and tier before you tune prompts.** The cheapest, fastest, most reliable call is the one you never made. Prompt wording is the last lever, not the first.
13
+ Treat the LLM as an unreliable network dependency, not a local function call. Every rule below follows from that: providers have outages, rate limits, and latency tails, so no single provider is a single point of failure and no call is allowed to run unbounded.
21
14
 
22
15
  ## Do you even need a pipeline?
23
16
 
@@ -116,6 +109,8 @@ except Exception:
116
109
 
117
110
  ## Caching: two distinct layers
118
111
 
112
+ The cheapest, fastest, most reliable call is the one you never made — so cache and tier before you tune prompts. Wording is the last lever, not the first.
113
+
119
114
  | Layer | What it matches | Safety | Enable when |
120
115
  | --- | --- | --- | --- |
121
116
  | Prefix / prompt cache (provider-native) | Exact prefix of the prompt | Always safe (same input → same cached compute) | Always; put the stable prefix first |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: medium-publishing
3
- description: "Use when getting a finished article onto Medium correctly — choosing profile vs publication, cross-posting from your own site without an SEO duplicate-content hit, the import tool, the 5-tag limit, and Medium's pseudo-Markdown formatting gotchas. Triggers: 'cross-post to Medium', 'republish on Medium with a canonical link', 'submit my draft to a Medium publication', 'why are my code blocks broken on Medium', 'can I still automate Medium via the API', 'the Medium import tool failed with a 403', 'importar mi artículo a Medium sin penalización SEO', 'publicar a Medium amb enllaç canònic'. NOT drafting, structuring, or headlining the article itself (that is medium-writing)."
3
+ description: "Use when a finished article has to land on Medium correctly — profile vs publication, cross-posting under a canonical link so your own site keeps the SEO credit, import-tool failures, the 5-tag cap, pseudo-Markdown gotchas. NOT writing or headlining the piece (that is `medium-writing`), NOT cadence or earnings (that is `medium-strategy`)."
4
4
  tags: [medium, publishing, cross-posting, canonical-url, seo, import-tool]
5
5
  recommends: [medium-writing, medium-strategy, seo-geo, social-publisher]
6
6
  origin: risco
@@ -10,13 +10,11 @@ origin: risco
10
10
 
11
11
  Land a finished article on Medium with correct metadata: right destination, no duplicate-content penalty, valid tags, intact formatting. Writing the article is `../medium-writing/SKILL.md`; deciding when/where/how-often to publish and how it grows is `../medium-strategy/SKILL.md`. This skill is only the mechanics of getting bytes onto the platform with the right metadata attached.
12
12
 
13
- ## Reality check — read this first (2025-2026)
13
+ ## Reality check (2025-2026)
14
14
 
15
15
  **The Medium API is closed to new integrations.** As of 2025-01-01 Medium issues no new integration tokens and accepts no new integrations; only tokens minted before that date still work. Why it matters: any plan that starts with "register a Medium API token and automate it" is dead on arrival for anyone who didn't already have one. Integration platforms confirm this — the Medium app in Make is marked legacy, and n8n's Medium credentials can no longer be newly configured.
16
16
 
17
- So the default publishing path is **web editor + import tool + canonical link**, done through the UI. Treat the API as a legacy escape hatch (last section), not the plan.
18
-
19
- Do not promise API automation. If the user asks "can I get a token and automate this?", the honest answer is almost always no — explain why and route them to the import path below.
17
+ So the default publishing path is **web editor + import tool + canonical link**, done through the UI. Treat the API as a legacy escape hatch (last section), not the plan — when asked "can I get a token and automate this?", the honest answer is almost always no, so say why and route to the import path below rather than promising automation.
20
18
 
21
19
  ## Decision table — pick your path
22
20
 
@@ -30,11 +28,9 @@ Branch on where the article already lives and what you control:
30
28
  | You hold a pre-2025 API token and want automation | Legacy `POST /v1/users/{id}/posts` | `canonicalUrl` field in the request body |
31
29
  | Destination is a publication you write for | Submit draft to the publication | Same as above; submission is separate from canonical |
32
30
 
33
- When in doubt and the piece exists elsewhere, prefer the import tool — it gets canonical *and* backdate right in one step.
34
-
35
31
  ## Cross-post without an SEO penalty (the core job)
36
32
 
37
- When the article already lives on your own site, you must tell search engines the original is canonical, or Google sees two copies and may credit Medium or neither. Two mechanisms, in order of preference:
33
+ When the article already lives on your own site, you must tell search engines that the **origin** — your page, never the Medium URL — is canonical, or Google sees two copies and may credit Medium or neither. Two mechanisms, in order of preference:
38
34
 
39
35
  **1. Import tool (preferred).** Pasting the original URL into Medium's import field does three things at once:
40
36
  - imports the rendered content into a new draft,
@@ -54,8 +50,6 @@ Good: Use the import tool (canonical + backdate set automatically), or if
54
50
  BEFORE publishing.
55
51
  ```
56
52
 
57
- The canonical must point to the **origin** (your site), not to the Medium URL. The whole point is that your site keeps the SEO credit.
58
-
59
53
  ## When the import fails
60
54
 
61
55
  The importer is an HTTP crawler hitting your URL; failures are crawl failures, not Medium bugs. Map the symptom to the cause:
@@ -98,9 +92,7 @@ Good: Convert the table to a labeled list, or screenshot/export it as an
98
92
  Two outcomes, often confused:
99
93
 
100
94
  - **Self-publish** puts the story on your own profile immediately.
101
- - **Submit to a publication** routes the draft to that publication's editors. It stays **pending** — not live — until an editor publishes it, and you must already be an accepted writer for that publication. Submitting does not auto-publish.
102
-
103
- Why it sits pending: a publication is editorially controlled; submission is a request, not a publish action. If you're not yet a writer there, request access first; submitting otherwise goes nowhere.
95
+ - **Submit to a publication** routes the draft to that publication's editors, where it stays **pending** — not live — until one of them publishes it. A publication is editorially controlled, so submission is a request, not a publish action, and it only works if you are already an accepted writer there. If you're not, request access first; submitting otherwise goes nowhere.
104
96
 
105
97
  ## Legacy API — only if you hold a pre-2025 token
106
98
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: modal
3
- description: "Use when running Python functions or GPU workloads serverlessly on Modal — defining a modal.App with inline container Images, scaling @app.function onto CPU/GPU, persisting weights or datasets in a Volume, scheduling jobs, serving an HTTP/ASGI endpoint, or choosing modal run vs serve vs deploy. Triggers: 'run my embedding job on an H100 without managing servers', 'cache HuggingFace weights across runs', 'my @app.function GPU string errors', 'gpu=modal.gpu.A100() fails', 'modal serve isn't hot-reloading my fastapi_endpoint', 'cold start too slow / keep a warm pool', 'ejecutar Python en GPU serverless', 'desplegar un model de difusió amb GPU sense servidor i exposar-lo per HTTP'. NOT managed prediction endpoints with no infra code (that is replicate) and NOT persistent SSH-able GPU pods rented by the hour (that is runpod)."
3
+ description: "Use when running Python or GPU workloads serverlessly on Modal — modal.App, inline container Images, gpu= on @app.function, Volumes for weight caching, Cron schedules, ASGI endpoints, modal run vs serve vs deploy. NOT managed prediction APIs with no container of your own (that is replicate); NOT SSH-able GPU boxes rented by the hour (that is runpod)."
4
4
  tags: [modal, serverless, gpu, python, ai-infra, deployment, cron, web-endpoint]
5
5
  recommends: [replicate, runpod, fastapi, docker, python, llm-pipeline]
6
6
  origin: risco
@@ -19,27 +19,19 @@ Pinned stack: **modal 1.4.3** (released 2026-05-18), Python **3.10–3.14** (`>=
19
19
  Install with `pip install modal` then `modal setup` to authenticate. Everything below uses
20
20
  the Modal 1.0+ API; several pre-1.0 forms were removed and are called out as Bad→Good.
21
21
 
22
- ## When to use
22
+ ## Not this skill
23
23
 
24
- - Running a Python function or batch job on remote CPU/GPU with no server to manage.
25
- - GPU inference, fine-tuning, or model serving (vLLM, diffusion, embeddings) on T4…B200.
26
- - Building a container image inline (`Image.debian_slim().uv_pip_install(...)`) for a remote job.
27
- - Persisting model weights or datasets across runs with a Modal `Volume`.
28
- - Scheduled jobs (nightly report, periodic scrape) via `modal.Cron` / `modal.Period`.
29
- - Exposing a function over HTTP (`fastapi_endpoint`, `asgi_app`, `wsgi_app`, `web_server`).
30
- - Fan-out parallelism with `.map()` / `.starmap()` / `.spawn()`.
24
+ Modal owns the serverless-container-as-decorators surface and its CLI lifecycle; the *contents*
25
+ of your function belong elsewhere.
31
26
 
32
- ## When NOT to use
33
-
34
- - Calling a **managed prediction API** with no container of your own → **`replicate`** / `together-fireworks` / `fal`.
35
- - Renting a **persistent, SSH-able GPU box** by the hour/week → **`runpod`**.
36
- - Generic **FastAPI design** (routing, Pydantic, deps) independent of host → **`fastapi`**.
37
- - Writing a **Dockerfile** for a registry / k8s / Compose → **`docker`**.
38
- - General **Python language/runtime** questions → **`python`**.
39
- - **RAG / LLM pipeline** orchestration logic itself → **`llm-pipeline`**.
40
-
41
- Modal owns the serverless-container-as-decorators surface and its CLI lifecycle; the
42
- *contents* of your function (FastAPI app, RAG chain) belong to the siblings above.
27
+ | The job | Goes to |
28
+ |---|---|
29
+ | Calling a **managed prediction API** with no container of your own | [`replicate`](../replicate/SKILL.md) / [`together-fireworks`](../together-fireworks/SKILL.md) / [`fal`](../fal/SKILL.md) |
30
+ | Renting a **persistent, SSH-able GPU box** by the hour/week | [`runpod`](../runpod/SKILL.md) |
31
+ | **FastAPI design** (routing, Pydantic, deps) independent of host | [`fastapi`](../fastapi/SKILL.md) |
32
+ | Writing a **Dockerfile** for a registry / k8s / Compose | [`docker`](../docker/SKILL.md) |
33
+ | General **Python language/runtime** questions | [`python`](../python/SKILL.md) |
34
+ | **RAG / LLM pipeline** orchestration logic itself | [`llm-pipeline`](../llm-pipeline/SKILL.md) |
43
35
 
44
36
  ## Decision: which entrypoint?
45
37
 
@@ -109,8 +101,8 @@ image = (
109
101
  )
110
102
  ```
111
103
 
112
- `→ references/images-gpu-cookbook.md` for vLLM / torch+CUDA / diffusers recipes and the
113
- download-once weight-cache pattern.
104
+ → [`references/images-gpu-cookbook.md`](references/images-gpu-cookbook.md) for vLLM / torch+CUDA
105
+ / diffusers recipes and the download-once weight-cache pattern.
114
106
 
115
107
  ## GPU — it's a string now
116
108
 
@@ -193,8 +185,7 @@ def pull():
193
185
  ```
194
186
 
195
187
  Never bake a token into the image (`.run_commands("export TOKEN=...")`) — it's recorded in
196
- layer history. Use a Secret. `→ references/images-gpu-cookbook.md` for the weight-cache and
197
- HF/OpenAI secret patterns.
188
+ layer history. Use a Secret. The cookbook above also carries the HF/OpenAI secret patterns.
198
189
 
199
190
  ## Web endpoints
200
191
 
@@ -228,8 +219,8 @@ def web():
228
219
 
229
220
  Develop with `modal serve app.py` (hot-reload); ship with `modal deploy app.py` (stable URL).
230
221
  For custom domains, proxy-auth tokens, batching (`@modal.batched`), and concurrency tuning →
231
- `references/web-and-scaling.md`. For the FastAPI app's *own* design (routes, Pydantic, deps),
232
- that's the `fastapi` sibling — this skill only mounts it.
222
+ [`references/web-and-scaling.md`](references/web-and-scaling.md). For the FastAPI app's *own*
223
+ design (routes, Pydantic, deps), that's [`fastapi`](../fastapi/SKILL.md) — this skill only mounts it.
233
224
 
234
225
  ## Scheduled jobs
235
226
 
@@ -268,9 +259,9 @@ def main():
268
259
  they complete (faster when latencies vary). Combine with `retries=` on the function so a
269
260
  single bad input doesn't sink the batch.
270
261
 
271
- ## Anti-patterns → STOP
262
+ ## Anti-patterns
272
263
 
273
- | Rationalization | Reality → STOP |
264
+ | Anti-pattern | Do instead |
274
265
  |---|---|
275
266
  | "I'll use `gpu=modal.gpu.A100()` like the old docs" | Removed in 1.0. Use the string `gpu="A100-80GB"`. |
276
267
  | "Attach a GPU, it might speed up this CPU job" | GPU is billed per second alive. CPU-only job → no `gpu=`. |
@@ -279,30 +270,21 @@ single bad input doesn't sink the batch.
279
270
  | "`modal run` it, the endpoint/schedule will stay up" | `run` is ephemeral; it exits. Use `modal deploy` for anything persistent. |
280
271
  | "Order the decorators however — Modal figures it out" | `@app.function` outermost, web decorator innermost. Wrong order errors. |
281
272
  | "Bake the HF token into the image with `run_commands`" | Leaks into layer history. Use `modal.Secret.from_name(...)`. |
282
- | "Just call the model via a managed API through Modal" | If you write no container, that's a managed-API job → `replicate`. |
283
- | "I need a box to SSH into for a week" | That's a persistent rental → `runpod`, not Modal's scale-to-zero. |
273
+ | "Just call the model via a managed API through Modal" | If you write no container, that's a managed-API job → [`replicate`](../replicate/SKILL.md). |
274
+ | "I need a box to SSH into for a week" | That's a persistent rental → [`runpod`](../runpod/SKILL.md), not Modal's scale-to-zero. |
284
275
  | "Set `min_containers` high so it's always fast" | Idle warm containers cost money 24/7. Tune `scaledown_window` first. |
285
276
 
286
- ## verify.sh
277
+ ## Verify
287
278
 
288
- `scripts/verify.sh [TARGET]` statically lints the nearest emitted Modal `*.py`: it requires a
289
- `modal.App(`, **fails** if the removed `modal.gpu.` object form appears, checks that any web
290
- decorator sits under an `@app.function`, and that any `Volume` uses
279
+ [`scripts/verify.sh`](scripts/verify.sh) `[TARGET]` statically lints the nearest emitted Modal
280
+ `*.py`: it requires a `modal.App(`, **fails** if the removed `modal.gpu.` object form appears,
281
+ checks that any web decorator sits under an `@app.function`, and that any `Volume` uses
291
282
  `from_name(..., create_if_missing=...)`. It runs `python -c "import modal"` only if modal is
292
- installed (skip-pass otherwise) and needs **no Modal credentials**. On an empty/clean target it
293
- exits 0.
294
-
295
- ## Project grounding (02-DOCS + CLAUDE.md)
296
-
297
- In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), record this
298
- app's real Modal choices — GPU types, image base, Volume names, schedule, endpoint shape — in
299
- `02-DOCS/wiki/stack/modal.md` and index it in `02-DOCS/wiki/index.md` (the Knowledge map; root
300
- `CLAUDE.md` keeps only a short pointer to it). Read it
301
- first on every use; create/update it with the real decisions. No `02-DOCS/`? Skip silently.
283
+ installed (skip-pass otherwise), needs **no Modal credentials**, and exits 0 on an empty target.
302
284
 
303
- ## See Also
285
+ ## Project grounding (02-DOCS)
304
286
 
305
- - [`fastapi`](../fastapi/SKILL.md) — the design of the FastAPI app you mount behind `@modal.asgi_app()`.
306
- - References: [`references/images-gpu-cookbook.md`](references/images-gpu-cookbook.md), [`references/web-and-scaling.md`](references/web-and-scaling.md).
307
- - Verify gate: [`scripts/verify.sh`](scripts/verify.sh).
308
- - Siblings (catalog): `replicate` / `together-fireworks` / `fal` (managed prediction APIs), `runpod` (persistent GPU pods), `docker` (Dockerfiles), `python` (language), `llm-pipeline` (orchestration).
287
+ In a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), read
288
+ `02-DOCS/wiki/stack/modal.md` first, then record this app's real Modal choices there — GPU types,
289
+ image base, Volume names, schedule, endpoint shape — and index it in `02-DOCS/wiki/index.md`. No
290
+ `02-DOCS/`? Skip silently.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: n8n
3
- description: "Use when operating a live n8n instance programmatically — driving its public REST API (`{host}/api/v1`, `X-N8N-API-KEY`) or the n8n-mcp server to create, validate, test, activate/deactivate, list, update, or delete workflows and read executions, on self-host or `*.app.n8n.cloud`. Triggers: 'deploy this workflow to my n8n over the API', 'push my workflow JSON to n8n with curl', 'I set active:true in the create call but it stays inactive', 'connect n8n-mcp / n8n_create_workflow to Claude', 'validate and autofix my n8n workflow', 'run an n8n workflow by id from a script', 'delete a workflow via the API', 'automatiza n8n por API', 'desplega el workflow a n8n.cloud'. NOT designing the flow's trigger-step-error logic or hand-building the importable workflow JSON (that is automation-flows), NOT operating Make / Zapier / Power Automate (their own skills), NOT a typed API client with auth+pagination+retry (api-connector-builder), NOT the inbound webhook receiver in your own app (webhooks)."
3
+ description: "Use when operating a live n8n instance programmatically — its REST API (`{host}/api/v1`, `X-N8N-API-KEY`) or the n8n-mcp server — to create, validate, test, activate, update, delete workflows and read executions on self-host or n8n.cloud. NOT designing the flow or its importable JSON (that is `automation-flows`), NOT another platform (`make`, `zapier`)."
4
4
  tags: [n8n, automation, workflows, mcp, rest-api, no-code, integrations]
5
5
  recommends: [automation-flows, automation-strategy, api-connector-builder, webhooks]
6
6
  profiles: [full]
@@ -9,15 +9,11 @@ origin: risco
9
9
 
10
10
  # n8n — operate a live instance over its REST API and MCP
11
11
 
12
- You already have a workflow *design* (the trigger→steps→error shape and the importable JSON).
13
- This skill is the **operate** half: push that JSON into a running n8n, validate it, test it, flip it
14
- live, watch its executions, and tear it down safely — all without touching the UI. n8n has the
15
- richest programmatic story of the no-code platforms: a first-party REST API **plus** a mature MCP
16
- server, both hitting the same instance.
17
-
18
- Designing the flow itself (what fires it, how errors are handled, dedup) is **automation-flows** —
19
- this skill *consumes* that skill's `references/n8n-workflow-json.md` payload; it does not re-teach
20
- design.
12
+ You already have a workflow *design* — the trigger→steps→error shape and the importable JSON, which is
13
+ `automation-flows`' job and this skill does not re-teach it. This skill is the **operate** half: push
14
+ that JSON into a running n8n, validate it, test it, flip it live, watch its executions, and tear it
15
+ down safely — all without touching the UI. n8n has the richest programmatic story of the no-code
16
+ platforms: a first-party REST API **plus** a mature MCP server, both hitting the same instance.
21
17
 
22
18
  ## Connect first — REST API vs MCP
23
19
 
@@ -31,10 +27,10 @@ Two ways to drive the same instance. Pick by who is at the wheel.
31
27
  | Surface | identical on self-host **and** `*.app.n8n.cloud` | same underlying API; hosted trial at `dashboard.n8n-mcp.com` |
32
28
 
33
29
  **Version reality (verify at author time — n8n moves fast):**
34
- - API base is `{host}/api/v1`. Self-host ships interactive Swagger at `/api/v1/docs`; **cloud does not**.
30
+ - API base is `{host}/api/v1`. Self-host ships interactive Swagger at `/api/v1/docs`; **cloud does not**
31
+ — on cloud, work from the docs.
35
32
  - `POST /workflows/{id}/execute` (run-by-id) is **recent** (n8n PR #20234). Older instances lack it —
36
- there, the only programmatic run is POSTing to a Webhook node's URL. It also takes **no custom input**.
37
- - The API surface is the SAME whether you self-host or run on `*.app.n8n.cloud`; only the host differs.
33
+ there, the only programmatic run is POSTing to a Webhook node's URL.
38
34
 
39
35
  ### `.env`
40
36
 
@@ -66,7 +62,8 @@ shapes rather than guessing — that is the single biggest reason to drive via M
66
62
  ## Dynamic lifecycle
67
63
 
68
64
  create → **validate** → test → **activate** → manage → delete. A concrete sketch per step, curl and
69
- n8n-mcp side by side.
65
+ n8n-mcp side by side; every endpoint with pagination and scopes is in
66
+ `references/rest-api-cheatsheet.md`.
70
67
 
71
68
  ### 1. Validate (do this BEFORE create)
72
69
 
@@ -107,8 +104,9 @@ n8n_test_workflow { "id": "$ID" }
107
104
 
108
105
  ### 4. Activate — the step people miss
109
106
 
110
- `active` in the create/update body is **read-only and silently ignored**. Setting `"active": true`
111
- does nothing. You MUST call the separate endpoint:
107
+ `active` (and `tags`) in the create/update body are **read-only and silently ignored**. Setting
108
+ `"active": true` does nothing, which is why the workflow you just created never fires. You MUST call
109
+ the separate endpoint:
112
110
 
113
111
  ```bash
114
112
  curl -sS -X POST "$N8N_API_URL/api/v1/workflows/$ID/activate" -H "X-N8N-API-KEY: $N8N_API_KEY"
@@ -137,7 +135,8 @@ omitted. n8n-mcp's `n8n_update_partial_workflow` applies a targeted, atomic batc
137
135
 
138
136
  ### 6. Delete — irreversible
139
137
 
140
- `DELETE` is a **hard delete: no trash, no undo.** Export the current JSON first, every time.
138
+ `DELETE` is a **hard delete: no trash, no undo**, so export the current JSON first, every time — this
139
+ one is absolute because nothing downstream can recover the workflow if you skip it.
141
140
 
142
141
  ```bash
143
142
  curl -sS "$N8N_API_URL/api/v1/workflows/$ID" -H "X-N8N-API-KEY: $N8N_API_KEY" > backup-$ID.json # export FIRST
@@ -147,7 +146,7 @@ curl -sS -X DELETE "$N8N_API_URL/api/v1/workflows/$ID" -H "X-N8N-API-KEY: $N8N_A
147
146
  n8n_get_workflow { "id": "$ID" } → save output → n8n_delete_workflow { "id": "$ID" }
148
147
  ```
149
148
 
150
- **Irreversibility rule:** never DELETE (or full-replace) a live workflow without exporting it first.
149
+ The same applies to a full replace: export before `PUT`.
151
150
 
152
151
  ## Governance
153
152
 
@@ -156,50 +155,23 @@ If you expose n8n-mcp to an agent, lock down destructive tools with the `DISABLE
156
155
  key itself too — n8n supports scoped keys (e.g. grant `workflow:read` without `workflow:execute`).
157
156
  Details in `references/n8n-mcp-tools.md`.
158
157
 
159
- ## Honesty / gotchas
160
-
161
- - **`active` and `tags` are read-only on create/update** — ignored in the body; activate/tag via the
162
- dedicated endpoints. This is the #1 "why is my workflow inactive" bug.
163
- - **`/workflows/{id}/execute` takes no custom input** — for runtime params, trigger a Webhook workflow
164
- by POSTing to its URL. Also, it is a newer endpoint; older instances 404 it.
165
- - **`DELETE` and `PUT` are destructive** — hard delete (no trash) and full replace. Export first; prefer
166
- `n8n_update_partial_workflow` for edits.
167
- - **`typeVersion` must exist on the target** — a node version the instance doesn't have fails only at
168
- activate/execute time, not at create. Validate (MCP) or test before activating.
169
- - **Credential secrets are not fully manageable over the public API** — you can reference existing
170
- credentials by id/name and create some, but you cannot read secrets back; provision credentials in
171
- the UI / n8n-mcp `n8n_manage_credentials` and reference them.
172
- - **Cloud has no Swagger** — `/api/v1/docs` is self-host only; on cloud, work from the docs.
173
- - **Activation can succeed via API where the UI would block it** — the public API historically skips a
174
- few UI-side activation validations, so a workflow can be "active" yet non-functional. Test it live.
175
-
176
- ## Related skills
177
-
178
- - **automation-flows** — *design* the flow and produce the importable JSON this skill deploys. Design
179
- vs operate boundary: they decide the trigger/steps/error path; you push it live. Start there if the
180
- flow's logic isn't settled yet.
181
- - **automation-strategy** — whether n8n is even the right platform (cost model, self-host vs cloud) vs
182
- Make/Zapier/Power Automate.
183
- - **make / zapier / power-automate** — the other platforms' operate stories (Zapier and Power Automate
184
- can't create flows via API — n8n's edge).
185
- - **api-connector-builder** — a generic typed HTTP client with auth/pagination/backoff, when the answer
186
- is real code, not a workflow.
187
- - **webhooks** — building the inbound receiver *in your own app*; here webhooks are just n8n triggers.
188
-
189
- ## Checklist
190
-
191
- - [ ] `.env` has `N8N_API_URL`/`N8N_HOST` + `N8N_API_KEY` (key from Settings → n8n API); key not logged.
192
- - [ ] Payload has `name`, `nodes` (one trigger), `connections`, `settings` per automation-flows' schema;
193
- node credentials referenced by store, never inlined.
194
- - [ ] Validated (`n8n_validate_workflow` / `n8n_autofix_workflow`) before create when MCP is available.
195
- - [ ] Created via `POST /workflows` or `n8n_create_workflow`; captured the returned `id`.
196
- - [ ] Tested — `execute`-by-id (no input) or POST to the webhook URL (with input) — and the run succeeded.
197
- - [ ] Activated via `POST /workflows/{id}/activate` (NOT `active:true` in the body).
198
- - [ ] Before any DELETE or full-replace: exported the current JSON (hard delete, no trash).
199
- - [ ] If MCP is exposed to an agent: `DISABLED_TOOLS` set and/or the API key scoped.
200
-
201
- ## References
202
-
203
- - `references/rest-api-cheatsheet.md` — every workflow/execution endpoint with curl, pagination, scopes.
204
- - `references/n8n-mcp-tools.md` — the management + node-knowledge tool list, the auth split, `DISABLED_TOOLS`.
205
- - `references/auth-and-selfhost.md` — API-key creation, self-host vs cloud, Swagger, installing/pointing n8n-mcp.
158
+ ## Anti-patterns
159
+
160
+ | Anti-pattern | Why it bites | Do instead |
161
+ |---|---|---|
162
+ | `"active": true` (or `tags`) in the create/update body | Read-only fields, silently ignored — the #1 "why is my workflow inactive" bug | `POST /workflows/{id}/activate`; set tags via their own endpoint |
163
+ | Passing runtime input to `/workflows/{id}/execute` | It accepts no custom input, and older instances 404 the endpoint entirely | Trigger a Webhook workflow by POSTing to its URL |
164
+ | `PUT` with a partial body, or `DELETE` on a live workflow | Full replace drops every node you omitted; delete is a hard delete with no trash | GET and export first; prefer `n8n_update_partial_workflow` for edits |
165
+ | A `typeVersion` the target instance doesn't have | Create succeeds; it fails only at activate/execute time | Validate via MCP, or test, before activating |
166
+ | Inlining node credential secrets, or expecting to read them back | Credentials are not fully manageable over the public API — it can create some, but never returns a secret | Provision in the UI or via `n8n_manage_credentials`, then reference existing credentials by id/name |
167
+ | Treating a successful API activation as proof it works | The public API historically skips a few UI-side activation validations, so a workflow can be "active" yet non-functional | Run it live after activating and check the execution |
168
+
169
+ ## Route elsewhere
170
+
171
+ | If the ask is | Skill |
172
+ |---|---|
173
+ | Designing the flow (trigger/steps/error path, dedup) and producing the importable JSON | `automation-flows` — start there if the logic isn't settled |
174
+ | Whether n8n is the right platform at all — cost model, self-host vs cloud | `automation-strategy` |
175
+ | Operating a different platform | `make`, `zapier`, `power-automate` (neither Zapier nor Power Automate can create flows via API — n8n's edge) |
176
+ | A generic typed HTTP client with auth/pagination/backoff — real code, not a workflow | `api-connector-builder` |
177
+ | The inbound webhook receiver in your own app (here webhooks are only n8n triggers) | `webhooks` |