@ericrisco/rsc 0.1.51 → 0.1.53

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hetzner
3
- description: "Use when provisioning or hardening a Hetzner Cloud VPS to host real apps: picking a plan/location (CX/CPX/CAX/CCX), bringing a box up reproducibly with the hcloud CLI + cloud-init, locking down SSH, and wiring the Hetzner Cloud Firewall before handing off a Docker/Coolify-ready host. Triggers: 'set up a Hetzner server', 'provision a Hetzner VPS', 'harden SSH on my box', 'which Hetzner plan for a small API', 'configure the Hetzner Cloud Firewall', the non-obvious 'my ipv6-only box is cheaper but docker pull can't reach the registry', and the Spanish/Catalan 'monta un servidor en Hetzner', 'configura el firewall de Hetzner'. NOT deploying apps through Coolify (that is coolify)."
3
+ description: "Use when provisioning or hardening a Hetzner Cloud VPS: picking the plan/location (CX/CPX/CAX/CCX), bringing it up reproducibly with hcloud + cloud-init, locking down SSH, and wiring the Cloud Firewall to hand off a Docker-ready host. NOT app deploys on Coolify (that is `coolify`), NOT Dockerfiles (that is `docker`), NOT another provider (that is `digitalocean`)."
4
4
  tags: [hetzner, vps, cloud-firewall, hcloud, ssh-hardening, cloud-init, self-hosting]
5
5
  recommends: [coolify, docker, secure-coding, domains-dns, backups, monitoring]
6
6
  origin: risco
@@ -14,17 +14,8 @@ include 20 TB of egress. The risk is that "cheap" becomes "unhardened and
14
14
  unmonitored" — a root-SSH box on a public IPv4 with password auth on. Your job
15
15
  is to make the cheap box **safe** and **reproducible**: every server comes up
16
16
  from a committed cloud-init file and a Cloud Firewall ruleset, never from
17
- click-ops in the console.
18
-
19
- Operating posture:
20
-
21
- - **Reproducible by default.** A box you can't recreate from a file isn't a box,
22
- it's a pet. Bake the non-root user, SSH key, and sshd hardening into first
23
- boot via cloud-init — not into a post-login checklist you'll forget.
24
- - **Two firewalls, edge first.** The Hetzner Cloud Firewall stops packets before
25
- they reach the VM; host `ufw` is defense-in-depth. You need both.
26
- - **Honest about the trade-off.** No managed DB, no uptime SLA, phone support
27
- only for dedicated-server customers. Name it; don't pretend it's AWS.
17
+ click-ops in the console. A box you can't recreate from a file isn't a box, it's
18
+ a pet.
28
19
 
29
20
  ## Decide before you create
30
21
 
@@ -139,8 +130,8 @@ hcloud firewall add-rule app-edge --direction in --protocol tcp --port 22 --s
139
130
 
140
131
  ## SSH hardening rules
141
132
 
142
- - **Key-only auth.** `PasswordAuthentication no`. A password is brute-forceable;
143
- an ed25519 key is not.
133
+ - **Key-only auth.** `PasswordAuthentication no`, `PubkeyAuthentication yes`. A
134
+ password is brute-forceable; an ed25519 key is not.
144
135
  - **No root login.** `PermitRootLogin no` + a sudo user. Root over SSH is the
145
136
  single most-targeted login on the internet.
146
137
  - **fail2ban on.** Bans IPs after repeated failures — cheap insurance for the
@@ -149,16 +140,6 @@ hcloud firewall add-rule app-edge --direction in --protocol tcp --port 22 --s
149
140
  harden anything. Do it if you like clean logs, but never *instead* of key-only
150
141
  + a firewall.
151
142
 
152
- Bad → Good:
153
-
154
- ```diff
155
- - PermitRootLogin yes
156
- - PasswordAuthentication yes
157
- + PermitRootLogin no
158
- + PasswordAuthentication no
159
- + PubkeyAuthentication yes
160
- ```
161
-
162
143
  ## Hand off to Docker / Coolify
163
144
 
164
145
  "Host ready" means all of the following are true:
@@ -169,28 +150,29 @@ Bad → Good:
169
150
  - [ ] `ufw status` enabled with the same allowlist.
170
151
  - [ ] `unattended-upgrades` and `fail2ban` running.
171
152
 
172
- Then route the install/deploy: Coolify (the self-hosted PaaS) and container
173
- build/run are not this skill's job. Use the `coolify` skill for the Coolify
174
- install + app deploy flow, and the `docker` skill for Dockerfiles, compose, and
175
- image hardening. This skill stops at a clean, hardened host.
153
+ This skill stops at a clean, hardened host. Then route the install/deploy:
154
+ [`coolify`](../coolify/SKILL.md) for the Coolify install + app deploy flow,
155
+ [`docker`](../docker/SKILL.md) for Dockerfiles, compose, and image hardening.
176
156
 
177
157
  ## Day-2
178
158
 
179
159
  - **Snapshot ≠ backup.** A snapshot is a one-off manual image you can boot from;
180
160
  the **Backups** add-on is automated, rotating, ~20% of the server price. A
181
- snapshot you took once in March is not a backup strategy — for that, see the
182
- `backups` skill.
161
+ snapshot you took once in March is not a backup strategy — for that, see
162
+ [`backups`](../backups/SKILL.md).
183
163
  - **Resize grows, never shrinks.** You can scale the disk up; you cannot scale it
184
164
  back down. Size conservatively or you're stuck paying for it.
185
165
  - **Volumes** for data you want to outlive/detach from the server. Keep databases
186
166
  and uploads on a volume so a server rebuild doesn't take the data with it.
187
167
  - **Reverse DNS** must match for outbound mail to be accepted — set the PTR in the
188
- console/`hcloud` if the box sends email. Forward DNS records belong to the
189
- `domains-dns` skill.
190
- - **No SLA, no managed DB.** There's no uptime guarantee and no managed-database
191
- product — you run Postgres yourself, you monitor it yourself. For monitoring
192
- and alerting, see the `monitoring` skill; treat the box as something you must
193
- watch, not something Hetzner watches for you.
168
+ console/`hcloud` if the box sends email. Forward DNS records belong to
169
+ [`domains-dns`](../domains-dns/SKILL.md).
170
+ - **No SLA, no managed DB.** There's no uptime guarantee, no managed-database
171
+ product, and phone support only for dedicated-server customers — you run
172
+ Postgres yourself, you monitor it yourself. For monitoring and alerting, see
173
+ [`monitoring`](../monitoring/SKILL.md); treat the box as something you must
174
+ watch, not something Hetzner watches for you. Name the trade-off to the user;
175
+ don't pretend it's AWS.
194
176
 
195
177
  ## Anti-patterns
196
178
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: knowledge-ops
3
- description: "Use when an already-running 02-DOCS/ wiki needs day-to-day gardening — too many half-finished articles, a 600-line page that covers three unrelated topics, a note with no obvious home, orphan pages nothing links to, stale answers and superseded articles piling up, or two articles that contradict each other. This is the judgment layer over harness's automation: decide what is worth capturing (default: nothing), where it goes, how to title/split/merge it, build the See Also web, and prune to _archive (never delete). Triggers: 'limpia mi wiki', 'organiza mis notas', 'esto dónde va en la wiki', 'ordena la base de conocimiento', 'split this bloated article', 'where should this note live', 'scores.json says these three are orphans — fix the cross-links', 'prune the stale and superseded stuff'. NOT building, bootstrapping or sweeping the wiki engine (that is harness)."
3
+ description: "Use when an already-running 02-DOCS/ wiki needs gardening judgment — what is worth capturing (default: nothing), where a loose note belongs, whether to split a bloated article or merge near-duplicates, how to link orphans back in, and what retires to _archive (never delete). NOT building or sweeping the wiki engine itself (that is `harness`)."
4
4
  tags: [knowledge-ops, wiki, 02-docs, knowledge-base, curation, cross-linking, pruning, knowledge-meta]
5
5
  recommends: [harness, decision-records, meeting-notes, sop-builder, research-ops, codebase-onboarding]
6
6
  origin: risco
@@ -8,7 +8,7 @@ origin: risco
8
8
 
9
9
  # Knowledge Ops — Garden the 02-DOCS Wiki
10
10
 
11
- *The engine is already running. `harness` built the `02-DOCS/` wiki and owns its automation — the inbox sweep, deterministic lint, scoring, gap detection, the self-improve loop. Your job is the four judgments that automation deliberately leaves to a human: what is worth capturing, how it should be shaped, how it connects, and what should retire. You are the gardener, not the machinery.*
11
+ *The engine is already running. `harness` built the `02-DOCS/` wiki and owns its automation — the inbox sweep, deterministic lint, scoring, gap detection, the self-improve loop — and changing that machinery or filling the wiki in bulk stays with it. Yours are the four judgments automation deliberately leaves to a human: what is worth capturing, how it should be shaped, how it connects, and what should retire. You are the gardener, not the machinery.*
12
12
 
13
13
  ## Before you touch anything
14
14
 
@@ -29,7 +29,7 @@ Every gardening request is one of four operations. Identify which before you act
29
29
  | **Link** | Pages are unreachable or under-connected | `scores.json` orphan_penalty (=5), low inbound count | `## See Also` in articles, `index.md` |
30
30
  | **Prune** | Answers are stale, articles superseded, conflicts unresolved, gaps bloated | freshness, conflict annotations, old `[FILLED]` gaps | `_archive/`, `log.md`, `gaps.md` |
31
31
 
32
- Whichever you do, record a line in `wiki/log.md`. `log.md` is the OKF reserved change-log: new entries are **prepended at the top (newest first)**, ISO 8601 dates, and past entries are never edited or deleted. (`gaps.md` is the append-only file — new entries go at the bottom.) The exact entry shapes per operation live in `references/gardening-playbook.md`.
32
+ Whichever you do, record a line in `wiki/log.md` under the reserved-file rule in **Safety rails** below. The exact entry shapes per operation live in `references/gardening-playbook.md`.
33
33
 
34
34
  ## Capture — the bar is high
35
35
 
@@ -44,7 +44,7 @@ Walk the altitude ladder, lowest rung first; stop at the first that fits:
44
44
 
45
45
  Worked examples for each rung are in `references/gardening-playbook.md`.
46
46
 
47
- **Topic choice.** Reuse an existing topic before inventing one, and keep `wiki/` exactly **one level of subdirs** deep (the protocol's rule).
47
+ **Topic choice.** Reuse an existing topic before inventing one, and keep `wiki/` exactly **one level of subdirs** deep (the protocol's rule).
48
48
 
49
49
  - Bad: `wiki/payments/stripe/webhooks/retries.md` (four levels deep).
50
50
  - Good: `wiki/payments/stripe-webhook-retries.md` (existing `payments/` topic, one level).
@@ -81,20 +81,19 @@ Every article earns **≥1 inbound link** or is a conscious leaf you can justify
81
81
 
82
82
  - **Conflicts:** you **flag** contradictions and the **human arbitrates**. Once they choose a winner, you may apply the resolution and annotate the loser — but you never auto-pick. Why: silently choosing a side is data corruption.
83
83
  - **Stale answers:** mark a page or archived copy with a `> ⚠ Stale — superseded YYYY-MM-DD` note rather than rewriting history.
84
- - **Compaction is bounded:** only `[FILLED YYYY-MM-DD]` gaps **older than 90 days** may be compacted, and only via a single marker prepended to `log.md`. `gaps.md` is append-only and `log.md` is newest-first (both immutable past entries) — gaps get a `[FILLED]` stamp, never a deletion.
85
- - **Log every prune** by prepending to `wiki/log.md` (newest first). Exact entry shapes per operation: `references/gardening-playbook.md`.
84
+ - **Compaction is bounded:** only `[FILLED YYYY-MM-DD]` gaps **older than 90 days** may be compacted, and only via a single marker prepended to `log.md` — a gap gets a `[FILLED]` stamp, never a deletion.
85
+ - **Log every prune** by prepending to `wiki/log.md`.
86
86
 
87
- A full worked prune session (stale + superseded + conflict + gap compaction, all logged) is in the playbook.
87
+ A full worked prune session (stale + superseded + conflict + gap compaction, all logged) is in `references/gardening-playbook.md`.
88
88
 
89
- ## Safety rails (inherited from the protocol)
89
+ ## Safety rails
90
90
 
91
- These are hard constraints, not suggestions. They come from `../harness/references/wiki-protocol.md` — do not override them.
91
+ Inherited from `../harness/references/wiki-protocol.md` — do not override them; each one guards against a loss you cannot undo.
92
92
 
93
93
  1. **Preserve before overwrite.** Never overwrite an article without first copying the old version to `wiki/<topic>/_archive/<article>__YYYY-MM-DD.md`.
94
94
  2. **Never auto-resolve a conflict.** Flag it; the human arbitrates.
95
- 3. **`gaps.md` is append-only; `log.md` is newest-first.** New `gaps.md` entries are appended at the bottom; new `log.md` entries are prepended at the top (the OKF reserved-file rule). Past entries in either are immutable — gaps get `[FILLED YYYY-MM-DD]`, nothing is ever edited out.
96
- 4. **Log every change** by prepending to `wiki/log.md` (newest first).
97
- 5. **Never touch an article whose `timestamp` is < 24h ago** without explicit user say-so. Why: it is likely still being worked. (`timestamp` is the OKF last-meaningful-edit field, ISO 8601 — see `../harness/references/wiki-protocol.md` "## Conventions".)
95
+ 3. **The reserved files only grow.** Log every change in `wiki/log.md`, the OKF reserved change-log: new entries are **prepended at the top (newest first)**, ISO 8601 dates. `gaps.md` is the append-only one — new entries go at the bottom. Past entries in either are immutable: a gap gets a `[FILLED YYYY-MM-DD]` stamp, nothing is ever edited out or deleted.
96
+ 4. **Never touch an article whose `timestamp` is < 24h ago** without explicit user say-so. Why: it is likely still being worked. (`timestamp` is the OKF last-meaningful-edit field, ISO 8601 — see `../harness/references/wiki-protocol.md` "## Conventions".)
98
97
 
99
98
  ## Anti-patterns
100
99
 
@@ -117,9 +116,7 @@ Route elsewhere when the request is not gardening an existing wiki:
117
116
  |---------|----------|
118
117
  | Build/bootstrap the wiki, run an inbox sweep, scaffold `01-TOOLS`, generate root `CLAUDE.md` | `harness` (`../harness/SKILL.md`) — it owns the engine |
119
118
  | Turn a meeting transcript into a recap with action items + owners | `meeting-notes` (`../meeting-notes/SKILL.md`) |
120
- | Author a decision record (ADR) with alternatives, status, review cadence | `decision-records` (you may *file* one; that skill *authors* the discipline) |
119
+ | Author a decision record (ADR) with alternatives, status, review cadence | `decision-records` (`../decision-records/SKILL.md`) — you may *file* one; that skill *authors* the discipline |
121
120
  | Document a repeatable procedure step by step | `sop-builder` (`../sop-builder/SKILL.md`) |
122
- | First-pass walkthrough of an unfamiliar codebase | `codebase-onboarding` |
123
- | Run a literature / source-gathering research project | `research-ops` |
124
-
125
- The sharp line: changing the wiki's machinery or filling it in bulk is `harness`. Exercising judgment over what an already-running wiki should contain and how it should be shaped is `knowledge-ops`.
121
+ | First-pass walkthrough of an unfamiliar codebase | `codebase-onboarding` (`../codebase-onboarding/SKILL.md`) |
122
+ | Run a literature / source-gathering research project | `research-ops` (`../research-ops/SKILL.md`) |
@@ -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: 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` |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: notion-connector
3
- description: "Use when wiring an app, cron job, or service to Notion as an ops backend over the official HTTP API — pushing rows into a Notion database, mirroring a database into your app, syncing both ways without duplicates, building or reading page block content, or fixing an integration that broke after the 2025-09-03 data-source split. Triggers: 'store our tasks in a Notion database from code', 'use Notion as a CRM/content calendar', 'sync our CRM into Notion nightly without duplicates', 'why does POST /v1/databases/:id/query return 404 now', 'integration broke after a teammate added a second data source', 'guardar registros en una base de datos de Notion desde código', 'desar files a una base de dades de Notion'. NOT a generic call-any-REST-API integration (that is api-connector-builder), NOT receiving/verifying inbound Notion webhook events (that is webhooks)."
3
+ description: "Use when wiring server code or a cron job to Notion as an ops backend over its HTTP API: pushing or mirroring database rows, two-way sync without duplicates, page blocks, or an integration broken by the 2025-09-03 data-source split. NOT generic REST wiring (that is `api-connector-builder`), NOT inbound Notion webhook events (that is `webhooks`)."
4
4
  tags: [notion, ops-backend, api-integration, databases, sync]
5
5
  recommends: [api-connector-builder, webhooks, automation-flows, spreadsheet-ops, secure-coding]
6
6
  origin: risco
@@ -14,24 +14,7 @@ write page blocks. This skill owns the **outbound** Notion API surface only —
14
14
  the database/data-source data model, property-type write shapes, and the
15
15
  rate-limit/pagination discipline that the API forces on you.
16
16
 
17
- ## The one rule
18
-
19
- **Pin the `Notion-Version` per request, and resolve the data source before you
20
- query.** Post `2025-09-03` a database is a *container* of one or more data
21
- sources — not a queryable table. Skip either half and you get silent 404s on
22
- code that worked last year.
23
-
24
- - Current major version: **`2025-09-03`** (the SDK default). Latest: **`2026-03-11`**.
25
- - Behavior differs across versions; an unpinned client drifts when the default moves.
26
-
27
- ## When to use / when NOT to use
28
-
29
- **Use when:** treating a Notion database as a CRM / task tracker / content
30
- calendar from code; pushing or pulling rows on a schedule; two-way sync that
31
- must not duplicate on re-run; building or reading page block content; migrating
32
- a `2022-06-28` integration that broke when someone added a second data source.
33
-
34
- **Route elsewhere:**
17
+ ## Route elsewhere
35
18
 
36
19
  | Situation | Route to |
37
20
  |---|---|
@@ -50,10 +33,12 @@ a `2022-06-28` integration that broke when someone added a second data source.
50
33
  3. **Share the target database/page with the integration** in the Notion UI
51
34
  (the page `•••` menu → Connections). *Skip this and every call 404s or
52
35
  returns empty* — the integration sees nothing it was not explicitly granted.
53
- 4. **Construct the SDK client with a pinned version.** Official JS SDK is
54
- `@notionhq/client` v5.12.0+ (latest 5.22.0, 2026-05-19); its default
55
- `notionVersion` is `2025-09-03`, and it supports `2026-03-11` if you opt in.
56
- The default and method names below are stable across the whole 5.x line.
36
+ 4. **Construct the SDK client with a pinned `Notion-Version`.** Official JS SDK
37
+ is `@notionhq/client` v5.12.0+ (latest 5.22.0, 2026-05-19); its default
38
+ `notionVersion` is the current major **`2025-09-03`**, and it supports the
39
+ latest **`2026-03-11`** if you opt in. The default and method names below are
40
+ stable across the whole 5.x line. Behavior differs across versions, so pin it
41
+ per client (or per request) — an unpinned client drifts when the default moves.
57
42
 
58
43
  ```ts
59
44
  import { Client } from "@notionhq/client"; // v5.12.0+ (latest 5.22.0)
@@ -66,9 +51,11 @@ const notion = new Client({
66
51
 
67
52
  ## The database → data source model (biggest gotcha)
68
53
 
69
- A database now holds a `data_sources` array; each data source has its **own
70
- schema**. One database can have several. You query and read schema against the
71
- *data source*, not the database.
54
+ Post `2025-09-03` a database is a **container** of one or more data sources, not
55
+ a queryable table: it holds a `data_sources` array and each data source has its
56
+ **own schema**. Resolve the data source *before* you query — query and read
57
+ schema against it, not against the database, or code that worked last year 404s
58
+ silently.
72
59
 
73
60
  | You have… | Do this |
74
61
  |---|---|
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: planetscale
3
- description: "Use when operating PlanetScale serverless MySQL (Vitess) — setting up a database/branch and wiring the pscale CLI, shipping a schema change through branches and deploy requests, debugging a stuck/queued deploy or a cutover that won't finish, designing tables without foreign keys, or picking the serverless HTTP driver for an edge runtime. Triggers: 'schema change without downtime on PlanetScale', 'my migration is stuck in the deploy queue', 'the cutover hung', 'can I still revert 40 minutes after deploy', 'the 30-minute revert window', 'PlanetScale won't let me add a foreign key', 'relationMode prisma', 'MySQL connection times out on Vercel Edge', 'cambio de esquema sin downtime', 'no me deja usar foreign keys'. NOT raw MySQL query tuning, EXPLAIN, or index choice (that is postgresdb's MySQL-engine analogue / the mysql sibling), and NOT generic expand-contract migration theory (that is db-migrations)."
3
+ description: "Use when operating PlanetScale (Vitess serverless MySQL) — branches and the `pscale` CLI, schema changes through deploy requests (Online DDL cutover, ~30-min revert window), tables without foreign keys, or the edge HTTP driver. NOT MySQL engine work like EXPLAIN or index choice (that is `mysql`), NOT generic migration theory (that is `db-migrations`)."
4
4
  tags: [planetscale, vitess, mysql, deploy-requests, serverless-db]
5
5
  recommends: [mysql, db-migrations, prisma-orm, drizzle-orm, github-actions, postgresdb]
6
6
  origin: risco
@@ -17,34 +17,15 @@ then **cuts over** with no table lock. Because Online DDL and sharding are the p
17
17
  gravity, you **design without foreign keys** (FKs are now an opt-in but unsharded-only feature). This
18
18
  skill owns that workflow — not MySQL the engine.
19
19
 
20
- ## When to use / When NOT to use
20
+ Heuristic for the boundary: **would this answer be identical on RDS MySQL? Then it is not this skill.**
21
21
 
22
- **When to use:**
23
-
24
- - Standing up a PlanetScale database, branch, or org and wiring the `pscale` CLI.
25
- - Shipping a schema change safely: dev branch → DDL → deploy request → diff → review → deploy → revert window.
26
- - A deploy request that is stuck/queued, a cutover that won't finish, or a lint error blocking deploy.
27
- - Designing relations when FKs are unavailable/discouraged: emulated relations, app-level integrity, ORM `relationMode`.
28
- - Choosing a connection path for edge/serverless (Cloudflare Workers, Vercel Edge, Lambda) where TCP MySQL is blocked.
29
- - Wiring deploy requests into CI (GitHub Actions) or a branch-per-PR preview database.
30
-
31
- **When NOT to use:**
32
-
33
- - A slow query, an `EXPLAIN` plan, an index decision, JSON columns, locking — that is MySQL-engine
34
- work. The closest built sibling for engine-level relational guidance is `../postgresdb/SKILL.md`
35
- (Postgres, but the engine-thinking transfers); pure MySQL tuning routes to the `mysql` sibling.
36
- Heuristic: **would this answer be identical on RDS MySQL? Then it is not this skill.**
37
- - PostgreSQL engine/design — even PlanetScale's managed Postgres SQL semantics → `../postgresdb/SKILL.md`.
38
- PlanetScale sells Postgres too, but this skill is strictly the **Vitess/MySQL branch+deploy-request** flow.
39
- - Engine-agnostic migration *theory* (expand-contract, backfill ordering) → the `db-migrations` sibling.
40
- Here you get the concrete PlanetScale realization, not the theory.
41
- - Neon's Postgres copy-on-write branches → the `neon` sibling. Supabase's Postgres BaaS → `supabase`.
42
- - ORM client ergonomics (Prisma `updateMany`, Drizzle query builder) → `prisma-orm` / `../drizzle-orm/SKILL.md`.
43
- This skill owns only the PlanetScale-specific knobs those ORMs expose (`relationMode`, serverless driver adapter).
44
-
45
- Deep dives: [deploy-requests](references/deploy-requests.md) (lifecycle states, queue/gating, lint
46
- errors, revert edge cases, declarative vs imperative) · [no-foreign-keys](references/no-foreign-keys.md)
47
- (emulated relations, ORM `relationMode`, the unsharded FK opt-in, Bad→Good schemas).
22
+ | When the question is really about | Route to |
23
+ | --- | --- |
24
+ | A slow query, an `EXPLAIN` plan, an index decision, JSON columns, locking | `../mysql/SKILL.md` for MySQL-engine work; `../postgresdb/SKILL.md` when you want the transferable engine reasoning |
25
+ | PostgreSQL engine/design — even PlanetScale's own managed Postgres SQL semantics | `../postgresdb/SKILL.md`. PlanetScale sells Postgres too; this skill is strictly the **Vitess/MySQL branch+deploy-request** flow |
26
+ | Engine-agnostic migration *theory* (expand-contract, backfill ordering) | `../db-migrations/SKILL.md`. Here you get the concrete PlanetScale realization, not the theory |
27
+ | Postgres copy-on-write branches | `../neon/SKILL.md`. Postgres BaaS → `../supabase/SKILL.md` |
28
+ | ORM client ergonomics (Prisma `updateMany`, Drizzle query builder) | `../prisma-orm/SKILL.md` / `../drizzle-orm/SKILL.md`. This skill owns only the PlanetScale-specific knobs those ORMs expose (`relationMode`, serverless driver adapter) |
48
29
 
49
30
  ## Mental model
50
31
 
@@ -149,9 +130,14 @@ datasource db {
149
130
  ```
150
131
 
151
132
  With Drizzle, model the relation in app code (`relations()`), index the join column, and skip the
152
- `references()`-backed FK constraint at the DB layer. The opt-in unsharded FK is acceptable only when
153
- you are certain the table will never shard and you accept the Online DDL friction — see
154
- [no-foreign-keys](references/no-foreign-keys.md) for emulated cascades, ORM specifics, and the opt-in caveats.
133
+ `references()`-backed FK constraint at the DB layer.
134
+
135
+ | Will this table shard? | FK strategy | Notes |
136
+ | --- | --- | --- |
137
+ | Yes, or unsure | No DB FK — emulate in app/ORM | Default. Index the relation column; cascade in code. |
138
+ | Never (small, bounded, unsharded) | Unsharded FK opt-in *is* acceptable | Accept Online DDL friction; document why it won't shard. |
139
+
140
+ See [no-foreign-keys](references/no-foreign-keys.md) for emulated cascades, ORM specifics, and the opt-in caveats.
155
141
 
156
142
  ## Connecting
157
143
 
@@ -196,7 +182,7 @@ installs `pscale`, authenticates with a service token (`PLANETSCALE_SERVICE_TOKE
196
182
 
197
183
  For a **branch-per-PR preview database**, create a PlanetScale branch named after the PR head, run the
198
184
  app's migrations against it, and tear the branch down when the PR closes. The orchestration (matrix,
199
- secrets, cleanup-on-close) is generic CI — defer the Actions plumbing to the `github-actions` sibling
185
+ secrets, cleanup-on-close) is generic CI — defer the Actions plumbing to `../github-actions/SKILL.md`
200
186
  and keep the PlanetScale verbs here.
201
187
 
202
188
  ## Anti-patterns
@@ -210,14 +196,3 @@ and keep the PlanetScale verbs here.
210
196
  | Treating revert as available forever | The window is **~30 min** and excludes dropped tables/cols and FK changes | Plan rollbacks as new forward deploys |
211
197
  | Deploying without reading `deploy-request diff` | You ship DDL you never saw; surprise table rewrites | Always run the diff before `deploy` |
212
198
  | Inventing a driver name (`@planetscale/serverless`, `planetscale-js`) | There is no such package; install fails | The package is exactly `@planetscale/database` |
213
-
214
- ## Decision: FK strategy by sharding posture
215
-
216
- | Will this table shard? | FK strategy | Notes |
217
- | --- | --- | --- |
218
- | Yes, or unsure | No DB FK — emulate in app/ORM | Default. Index the relation column; cascade in code. |
219
- | Never (small, bounded, unsharded) | Unsharded FK opt-in *is* acceptable | Accept Online DDL friction; document why it won't shard. |
220
-
221
- When the question is really about reading a plan, picking an index, or query latency, that is engine
222
- work — route to the `mysql` sibling (or `../postgresdb/SKILL.md` for the transferable engine
223
- reasoning). This skill stops at the Vitess platform boundary.
@@ -58,13 +58,13 @@ model Order {
58
58
  }
59
59
  ```
60
60
 
61
- ORM-client ergonomics beyond this knob belong to the `prisma-orm` sibling, not here.
61
+ ORM-client ergonomics beyond this knob belong to `../../prisma-orm/SKILL.md`, not here.
62
62
 
63
63
  ## Drizzle: app-level relations
64
64
 
65
65
  Drizzle models relations in app code via `relations()` and queries them with the relational API. On
66
66
  PlanetScale you index the join column and skip the `references()`-backed DB constraint; the relation
67
- lives in code, not in the database. The query-builder side is the `../drizzle-orm/SKILL.md` sibling.
67
+ lives in code, not in the database. The query-builder side is `../../drizzle-orm/SKILL.md`.
68
68
 
69
69
  ## The unsharded FK opt-in
70
70