LinkedIn post writer (Chan Meng / Caldera brand)
Write English LinkedIn posts that build Chan Meng's personal brand and expand her network, and hand
them back as one markdown file she can copy straight into LinkedIn.
Hard boundary: you never drive a browser
This skill has no browser step. Do not call claude-in-chrome, Playwright-as-automation,
chrome-devtools, or any other page-driving tool. Do not open LinkedIn. Do not open a composer, type
into one, resolve @mention chips, attach files, or click Post — not even "just to stage a draft for
review."
This is a deliberate removal, not an oversight. LinkedIn's editor cannot be driven reliably: it
swallows the space after a resolved mention chip, lands backspaces at the wrong caret position and
corrupts text that was already correct, and exposes no usable file input for photos. Every attempt
costs more time than it saves and risks mangling copy Chan has already approved.
Chan does the LinkedIn half entirely by hand — she pastes the copy, converts each name into a
tag from the live dropdown, attaches her own photos, and publishes. Your entire deliverable is a
markdown file that makes those thirty seconds frictionless.
Still allowed, because they aren't browser automation:
- WebSearch / WebFetch for research and verification — this is how you get names, titles and
facts right, and it matters more now that Chan tags from your table rather than from a URL.
- The render scripts (
render-card.mjs, render-html.mjs), which screenshot local HTML with
headless Chromium. That's image generation, not page automation — no live site is involved.
When to use
- "Write a LinkedIn post about " / "here's my framework for X" → idea mode.
- "I read <article/news>, here's my take" / a pasted URL + a reaction → take mode.
- "I went to , write a recap" / "post-event reflection" → recap mode.
Reference docs (read the ones you need)
references/brand.md — colors, fonts, logo, voice do/don'ts, imagery direction.
references/voice-and-structure.md — post anatomy per mode + mechanics (read every run).
references/hashtag-bank.md — curated hashtag sets + rules.
references/recap-playbook.md — recap research, verification, photos, @mention mechanics.
references/visuals.md — render recipes. Read only if Step 3's gate says a visual is warranted.
examples/post.txt + examples/diagram.mmd — a real reference idea post + its diagram.
Workflow
Step 0 — Pick mode & gather inputs
- Mode from the
/linkedin-post <idea|take|recap> arg, else infer, else ask once.
- Idea: the thought/angle, target audience, any proof points (repos, numbers, links).
- Take: the article/news URL, what the source actually claims (1–2 sentences), Chan's own take,
and whether she's agreeing, extending, or pushing back. Fetch the URL and read it — don't react to
a headline. Note the author/publication so she can credit them.
- Recap: event name/date/city/link, the people and orgs to credit, Chan's 1–2 genuine takeaways,
and — ask this up front — does she have event photos? (paths, or "no"). That answer decides
Step 3 entirely, so get it before drafting.
- Don't block on completeness — gather what she has.
Step 1 — (Recap only) Research & verify who to credit
Follow references/recap-playbook.md:
- WebSearch + WebFetch the event page and each person Chan named: confirm exact name spelling,
current title, and employer. Fetch the official event/film/company page for the hard facts.
- Never invent a profile or a handle. Chan resolves every tag herself, by hand, in her own LinkedIn
composer — so what she needs from you is the right name plus a way to recognise the right person
in the dropdown (a distinguishing headline), not a URL you guessed.
- Keep the findings for the mention table in Step 4.
Step 2 — Draft the copy
Use the anatomy for the mode in references/voice-and-structure.md plus the voice rules in
references/brand.md. Each mode has its own shape and length band — don't force one into another:
| Mode |
Shape |
Length |
Hashtags |
| idea |
8-beat framework: hook → numbered points → mental shift → CTA |
1,000–1,800 chars |
8–12 |
| take |
4–6 beats: what I read → what landed → my take → so what → CTA |
600–1,200 chars |
5–8 |
| recap |
6 beats: opening → what stood out (@mentions) → takeaway → thanks → CTA |
1,000–1,600 chars |
6–10 |
Tags from references/hashtag-bank.md. Architecture-grade vocabulary, evidence-first, no marketing
fluff. English only.
Plain text (LinkedIn renders no markdown); blank line between beats; emoji as bullets.
Show Chan the draft and iterate before spending effort on a visual.
Step 3 — Decide IF a visual helps, then which
Text-only is a valid, common, and often better outcome. Plenty of strong LinkedIn posts carry no
image at all, and a diagram that merely restates the copy reads as AI filler — it costs Chan
credibility rather than adding reach. "No visual" is a decision to state, not a step you failed.
If Chan explicitly asks for a diagram or an image, make it — the gate governs the default, not her
requests. Otherwise work through these in order and stop at the first one that answers:
1. Does Chan have her own photos? (recap, or any post about something she was physically at)
→ Her photos ARE the visual. Generate nothing. Real faces and a real room beat any render for
authenticity, which is the whole point of a recap. Follow recap-playbook.md → "Photos (the real
visual)": order them, work one visible detail into the copy, and record the order in post.md.
2. Mode default:
- take → no visual. The article link's own preview card is the visual (see Step 4). Adding a
branded graphic on top competes with it and buries the link. (Exception: if Chan wants to show a
specific figure from the source — a chart, a benchmark table — use that image, credited. Don't
redraw it as a Mermaid diagram.)
- recap without photos → offer one branded card (a quote or field-note). Never a diagram —
never draw a box-and-arrow chart of an evening with humans in it.
- idea → continue to the gate.
3. The gate — a diagram ships only if ALL THREE are yes:
- Is there structure a reader can't absorb from the text in one pass — ≥3 parts and named
relationships between them (a flow, a before/after, a layered stack, a decision branch)?
- Would you actually sketch this on a whiteboard to explain it out loud?
- Does the diagram carry information the copy doesn't — rather than restating the numbered list?
Any "no" → no diagram. If the post is structure-free but has one killer line, you may offer
Chan a quote card — offer it, don't assume it. If the gate is genuinely borderline, ask Chan one
question; don't hand her a menu of four options.
4. Route → recipe (commands live in references/visuals.md, read it only now):
| Verdict |
Asset |
| Chan's photos |
none generated — just the ## Photos section of post.md |
| Gate passed |
Mermaid diagram → diagram.mmd + diagram.png |
| One killer line, offered & accepted |
branded card → card.png |
| Card too plain for accepted content |
bespoke HTML → visual.html + visual.png |
| Needs a hero image, nothing else fits |
OpenAI image → image.png (last resort) |
| Everything else |
nothing — ship text-only |
If you generate something, Read the rendered PNG and show Chan the asset. If it looks like filler
on screen, say so and drop it. If you generate nothing, tell Chan in one line
why — that's the deliverable for this step.
Step 4 — Write the handoff: post.md
The deliverable is one file: output/<YYYY-MM-DD>-<slug>/post.md. Chan opens it, copies the
copy, tags the people, attaches her photos, publishes. Everything she needs is in that file — she
should never have to come back and ask you a question to finish posting.
Required sections, in this order:
- Header — mode, date drafted, event/source name, date + venue or article URL, which visual
route Step 3 took (or "none — Chan's photos"), character count, hashtag count.
## The copy — the post inside a ```text fence so blank lines and emoji survive the
copy-paste exactly. One line above it: "Copy everything inside the block, exactly as-is."
## Who to @mention (recap, or any post crediting people) — a table:
# | Name in the copy | Type this | Pick the entry that says | Confidence.
The "Pick the entry that says" column is the important one: LinkedIn's dropdown shows several
people with the same name, so give Chan the headline that identifies the right one. Flag every
near-miss you found ("the first Jane Doe is a different person — she's the one headlined X").
Mark anything unverified as plain-text-only. Never supply a profile URL you aren't certain of.
## Photos — the suggested order, lead shot first, and the rule that nobody visible gets
named or tagged unless they're in the mention table. Skip if there are no photos.
## Link (take mode) — the article URL, and whether to put it in the body (LinkedIn renders
a preview card, which is the visual) or the first comment (no preview, better reach). Recommend
one. Never both a link preview and an attached image.
## Verified facts behind the copy — the sourced facts any claim in the post rests on, so
Chan can defend or correct them.
## Judgement calls made in the copy — anywhere you softened, sharpened, omitted, or chose
between two true framings, with the alternative spelled out. This is where you're honest about
what you decided on her behalf.
Also in the folder, only if Step 3 called for one: the visual (diagram.mmd+diagram.png,
card.png, visual.html+visual.png, or image.png). An empty visual slot is a correct result.
Then, in chat, give Chan the file path and one line on which visual route you took and why —
including "text-only, because …". That keeps the decision auditable and stops the next run from
defaulting to a diagram.
Setup / dependencies
- Only needed if a visual is being rendered — text-only posts need nothing installed.
node_modules/ is gitignored, so it may be absent. If a render script fails with
ERR_MODULE_NOT_FOUND (playwright / openai), run npm install then
npx playwright install chromium from the repo root and retry.
render-mermaid.mjs falls back to a global mmdc when the local one is missing, so a Mermaid
render can succeed even with no node_modules — don't read that as "deps are installed".
- OpenAI image needs a key: copy
.env.example → .env and set OPENAI_API_KEY. Without it,
gen-image.mjs exits 2 — everything else works, and that path is a last resort anyway.
mmdc reuses the existing puppeteer-config.json; the brand theme lives in
templates/mermaid-brand-config.json + templates/mermaid-brand.css.
Guardrails
- English only; on-brand voice; one orange accent per visual.
- Never generate a visual to fill a slot. An unearned diagram reads as AI filler and costs more
credibility than a bare post ever would. No visual is a normal outcome — say so and move on.
- Real photos outrank generated art. If Chan has event photos, nothing gets rendered.
- Never diagram a recap; never stack a branded image on top of an article link preview.
- Never open LinkedIn. No browser automation, no composer, no posting — the deliverable is
post.md and nothing else. Chan publishes.
- Never invent a person's identity/handle. You are telling Chan who to tag, so a wrong name sends
her to tag the wrong human — worse than telling her you couldn't verify it (
recap-playbook.md).
- Keep generated work in
output/ (gitignored); never write drafts to the repo root.
1---2name: linkedin-post3description: Use when Chan Meng wants to write an English LinkedIn post for personal-brand marketing — an "idea" post (her own framework/opinion), a "take" post (a short reaction to a tech article or news she read), or a post-event "recap" (reflecting on an offline tech meetup, crediting speakers/organizers/companies to grow her network). Researches and verifies the people to credit, writes on-brand copy with emoji + hashtags, adds a Caldera-branded visual only when one genuinely earns its place (recaps use Chan's own event photos; many posts ship text-only), and hands back ONE markdown file — copy, who to @mention, and photo notes. Chan posts it herself; the skill never opens LinkedIn.4---56# LinkedIn post writer (Chan Meng / Caldera brand)78Write English LinkedIn posts that build Chan Meng's personal brand and expand her network, and hand9them back as **one markdown file** she can copy straight into LinkedIn.1011## Hard boundary: you never drive a browser1213**This skill has no browser step.** Do not call `claude-in-chrome`, Playwright-as-automation,14chrome-devtools, or any other page-driving tool. Do not open LinkedIn. Do not open a composer, type15into one, resolve @mention chips, attach files, or click Post — not even "just to stage a draft for16review."1718This is a deliberate removal, not an oversight. LinkedIn's editor cannot be driven reliably: it19swallows the space after a resolved mention chip, lands backspaces at the wrong caret position and20corrupts text that was already correct, and exposes no usable file input for photos. Every attempt21costs more time than it saves and risks mangling copy Chan has already approved.2223**Chan does the LinkedIn half entirely by hand** — she pastes the copy, converts each name into a24tag from the live dropdown, attaches her own photos, and publishes. Your entire deliverable is a25markdown file that makes those thirty seconds frictionless.2627Still allowed, because they aren't browser automation:28- **WebSearch / WebFetch** for research and verification — this is how you get names, titles and29 facts right, and it matters more now that Chan tags from your table rather than from a URL.30- **The render scripts** (`render-card.mjs`, `render-html.mjs`), which screenshot local HTML with31 headless Chromium. That's image generation, not page automation — no live site is involved.3233## When to use34- "Write a LinkedIn post about <idea>" / "here's my framework for X" → **idea mode**.35- "I read <article/news>, here's my take" / a pasted URL + a reaction → **take mode**.36- "I went to <event>, write a recap" / "post-event reflection" → **recap mode**.3738## Reference docs (read the ones you need)39- `references/brand.md` — colors, fonts, logo, voice do/don'ts, imagery direction.40- `references/voice-and-structure.md` — post anatomy per mode + mechanics (read every run).41- `references/hashtag-bank.md` — curated hashtag sets + rules.42- `references/recap-playbook.md` — recap research, verification, photos, @mention mechanics.43- `references/visuals.md` — render recipes. **Read only if Step 3's gate says a visual is warranted.**44- `examples/post.txt` + `examples/diagram.mmd` — a real reference idea post + its diagram.4546## Workflow4748### Step 0 — Pick mode & gather inputs49- Mode from the `/linkedin-post <idea|take|recap>` arg, else infer, else ask once.50- **Idea:** the thought/angle, target audience, any proof points (repos, numbers, links).51- **Take:** the article/news URL, what the source actually claims (1–2 sentences), Chan's own take,52 and whether she's agreeing, extending, or pushing back. Fetch the URL and read it — don't react to53 a headline. Note the author/publication so she can credit them.54- **Recap:** event name/date/city/link, the people and orgs to credit, Chan's 1–2 genuine takeaways,55 **and — ask this up front — does she have event photos? (paths, or "no")**. That answer decides56 Step 3 entirely, so get it before drafting.57- Don't block on completeness — gather what she has.5859### Step 1 — (Recap only) Research & verify who to credit60Follow `references/recap-playbook.md`:61- **WebSearch + WebFetch** the event page and each person Chan named: confirm exact name spelling,62 current title, and employer. Fetch the official event/film/company page for the hard facts.63- Never invent a profile or a handle. Chan resolves every tag herself, by hand, in her own LinkedIn64 composer — so what she needs from you is **the right name plus a way to recognise the right person65 in the dropdown** (a distinguishing headline), not a URL you guessed.66- Keep the findings for the mention table in Step 4.6768### Step 2 — Draft the copy69- Use the anatomy **for the mode** in `references/voice-and-structure.md` plus the voice rules in70 `references/brand.md`. Each mode has its own shape and length band — don't force one into another:7172 | Mode | Shape | Length | Hashtags |73 |------|-------|--------|----------|74 | idea | 8-beat framework: hook → numbered points → mental shift → CTA | 1,000–1,800 chars | 8–12 |75 | take | 4–6 beats: what I read → what landed → my take → so what → CTA | 600–1,200 chars | 5–8 |76 | recap | 6 beats: opening → what stood out (@mentions) → takeaway → thanks → CTA | 1,000–1,600 chars | 6–10 |7778- Tags from `references/hashtag-bank.md`. Architecture-grade vocabulary, evidence-first, no marketing79 fluff. English only.80- Plain text (LinkedIn renders no markdown); blank line between beats; emoji as bullets.81- Show Chan the draft and iterate **before** spending effort on a visual.8283### Step 3 — Decide IF a visual helps, then which8485**Text-only is a valid, common, and often better outcome.** Plenty of strong LinkedIn posts carry no86image at all, and a diagram that merely restates the copy reads as AI filler — it costs Chan87credibility rather than adding reach. "No visual" is a decision to state, not a step you failed.8889If Chan explicitly asks for a diagram or an image, make it — the gate governs the *default*, not her90requests. Otherwise work through these in order and stop at the first one that answers:9192**1. Does Chan have her own photos?** (recap, or any post about something she was physically at)93→ **Her photos ARE the visual. Generate nothing.** Real faces and a real room beat any render for94authenticity, which is the whole point of a recap. Follow `recap-playbook.md` → "Photos (the real95visual)": order them, work one visible detail into the copy, and record the order in `post.md`.9697**2. Mode default:**98- **take** → **no visual.** The article link's own preview card is the visual (see Step 4). Adding a99 branded graphic on top competes with it and buries the link. (Exception: if Chan wants to show a100 specific figure *from the source* — a chart, a benchmark table — use that image, credited. Don't101 redraw it as a Mermaid diagram.)102- **recap without photos** → *offer* one branded card (a quote or field-note). **Never a diagram** —103 never draw a box-and-arrow chart of an evening with humans in it.104- **idea** → continue to the gate.105106**3. The gate — a diagram ships only if ALL THREE are yes:**1071. Is there structure a reader can't absorb from the text in one pass — **≥3 parts *and* named108 relationships between them** (a flow, a before/after, a layered stack, a decision branch)?1092. Would you actually sketch this on a whiteboard to explain it out loud?1103. Does the diagram carry information the copy doesn't — rather than restating the numbered list?111112Any "no" → **no diagram.** If the post is structure-free but has one killer line, you may *offer*113Chan a quote card — offer it, don't assume it. If the gate is genuinely borderline, ask Chan **one**114question; don't hand her a menu of four options.115116**4. Route → recipe** (commands live in `references/visuals.md`, read it only now):117118| Verdict | Asset |119|---------|-------|120| Chan's photos | none generated — just the `## Photos` section of `post.md` |121| Gate passed | Mermaid diagram → `diagram.mmd` + `diagram.png` |122| One killer line, offered & accepted | branded card → `card.png` |123| Card too plain for accepted content | bespoke HTML → `visual.html` + `visual.png` |124| Needs a hero image, nothing else fits | OpenAI image → `image.png` (last resort) |125| Everything else | **nothing — ship text-only** |126127If you generate something, **Read the rendered PNG** and show Chan the asset. If it looks like filler128on screen, say so and drop it. If you generate nothing, tell Chan in one line129why — that's the deliverable for this step.130131### Step 4 — Write the handoff: `post.md`132133The deliverable is **one file**: `output/<YYYY-MM-DD>-<slug>/post.md`. Chan opens it, copies the134copy, tags the people, attaches her photos, publishes. Everything she needs is in that file — she135should never have to come back and ask you a question to finish posting.136137Required sections, in this order:1381391. **Header** — mode, date drafted, event/source name, date + venue or article URL, which visual140 route Step 3 took (or "none — Chan's photos"), character count, hashtag count.1412. **`## The copy`** — the post inside a ```` ```text ```` fence so blank lines and emoji survive the142 copy-paste exactly. One line above it: "Copy everything inside the block, exactly as-is."1433. **`## Who to @mention`** (recap, or any post crediting people) — a table:144 `# | Name in the copy | Type this | Pick the entry that says | Confidence`.145 The **"Pick the entry that says"** column is the important one: LinkedIn's dropdown shows several146 people with the same name, so give Chan the headline that identifies the right one. Flag every147 near-miss you found ("the *first* Jane Doe is a different person — she's the one headlined X").148 Mark anything unverified as plain-text-only. Never supply a profile URL you aren't certain of.1494. **`## Photos`** — the suggested order, lead shot first, and the rule that nobody visible gets150 named or tagged unless they're in the mention table. Skip if there are no photos.1515. **`## Link`** (take mode) — the article URL, and whether to put it in the body (LinkedIn renders152 a preview card, which is the visual) or the first comment (no preview, better reach). Recommend153 one. Never both a link preview and an attached image.1546. **`## Verified facts behind the copy`** — the sourced facts any claim in the post rests on, so155 Chan can defend or correct them.1567. **`## Judgement calls made in the copy`** — anywhere you softened, sharpened, omitted, or chose157 between two true framings, with the alternative spelled out. This is where you're honest about158 what you decided on her behalf.159160Also in the folder, **only if Step 3 called for one**: the visual (`diagram.mmd`+`diagram.png`,161`card.png`, `visual.html`+`visual.png`, or `image.png`). An empty visual slot is a correct result.162163Then, in chat, give Chan the file path and one line on which visual route you took and why —164including "text-only, because …". That keeps the decision auditable and stops the next run from165defaulting to a diagram.166167## Setup / dependencies168- **Only needed if a visual is being rendered** — text-only posts need nothing installed.169- `node_modules/` is gitignored, so it may be absent. If a render script fails with170 `ERR_MODULE_NOT_FOUND` (playwright / openai), run `npm install` then171 `npx playwright install chromium` from the repo root and retry.172- `render-mermaid.mjs` falls back to a global `mmdc` when the local one is missing, so a Mermaid173 render can succeed even with no `node_modules` — don't read that as "deps are installed".174- **OpenAI image** needs a key: copy `.env.example` → `.env` and set `OPENAI_API_KEY`. Without it,175 `gen-image.mjs` exits 2 — everything else works, and that path is a last resort anyway.176- `mmdc` reuses the existing `puppeteer-config.json`; the brand theme lives in177 `templates/mermaid-brand-config.json` + `templates/mermaid-brand.css`.178179## Guardrails180- English only; on-brand voice; one orange accent per visual.181- **Never generate a visual to fill a slot.** An unearned diagram reads as AI filler and costs more182 credibility than a bare post ever would. No visual is a normal outcome — say so and move on.183- **Real photos outrank generated art.** If Chan has event photos, nothing gets rendered.184- Never diagram a recap; never stack a branded image on top of an article link preview.185- **Never open LinkedIn.** No browser automation, no composer, no posting — the deliverable is186 `post.md` and nothing else. Chan publishes.187- Never invent a person's identity/handle. You are telling Chan who to tag, so a wrong name sends188 her to tag the wrong human — worse than telling her you couldn't verify it (`recap-playbook.md`).189- Keep generated work in `output/` (gitignored); never write drafts to the repo root.