
Post Writer
Draft platform-tuned full social posts AND long-form Articles — LinkedIn posts + Articles, X posts + threads + Articles, Instagram caption + carousel, Threads, Facebook, YouTube (title + description) and Reddit (advise-only). Voice-aware (reads voice.md), hook-aware (composes hook-generator output). Each post scored against 2026 platform algorithm patterns, structure rules, an AI-slop blacklist, and 74 metric-bearing real exemplars + 5 enforcement scripts. GUIDANCE-ONLY: no backend and no methods — the agent's own LLM composes posts following SKILL.md, read via skill_read from the nested skills/post-writer/ layout; state persists via the upstream OPVS Coordination skills (agentdocs / agentmemory / agentboard) declared as peers. Knows the SpiderIQ publishing stack: social → vibe-publish; owned site → spiderpublish; assets → spidermedia.
I built post-writer because cross-posting one formula to five platforms and grading every copy by likes measures the wrong number. X ranks replies and bookmarks. Threads ranks your reply-to-like ratio and lifts creator self-replies by 42%. Instagram ranks saves. LinkedIn ranks dwell and comments. So the structure and the call-to-action change per platform, because the KPI changes per platform. It is guidance-only: your agent reads SKILL.md, loads the recipe for the platform you named, installs matching exemplars from 74 metric-bearing real posts, and writes the post with its own model. Then five scripts grade it and exit 1 with printed numbers if it fails. The rule I did not expect to write is no-mechanics-mode — the all-time most-liked posts carry no hook, no CTA and no framework, so for emotional or reactive posts the skill switches its own machinery off. Composes with voice-builder, hook-generator and copy-engine.
Features6
Eleven recipes across LinkedIn posts and Articles, X posts, threads and Articles, Instagram caption and carousel, Threads, Facebook, YouTube (title + description) and Reddit. Length band, structure and CTA follow that platform's ranked action.
no-mechanics-mode drops the hook framework and CTA for emotional, reactive or announcement posts, where the machinery makes the post measurably worse.
Per-platform length and hook truncation, a 55-entry zero-tolerance AI-slop blacklist, tone drift measured against your own voice.md, an article CTA gate, and a coordination check. Numbers printed, not vibes.
Every exemplar carries its source URL and its metric. 38 are high-confidence; 9 are flagged needs_native_validation rather than quietly trusted.
Long-form on LinkedIn and X is a separate surface, not a longer post — its own editor, length regime, media spec and publish route. The post verifier is explicitly excluded, because it would fail an article on length for the wrong reason.
The slop blacklist is English-only, so non-English posts return WARN not FAIL. Private Instagram and Threads reach figures are never presented as fact. There is no X corpus at all, so every number in the X Article recipe is stamped as an untested hypothesis instead of borrowed from LinkedIn.
Videos1 video
DemoPost Writer Skill Demo
Demo · SpiderMediaArticles about Post Writer2 articles
I thought every post needed a hook. My best one had none.
opvs.ai21 Jul 2026I thought every post needed a hook. My best one had none.
I built a skill that enforces hook, structure and CTA on every social post. The rules that turned out to matter are the ones that switch it off: when to drop the machinery entirely, why my buyers read everything and comment on nothing, and why a long-form Article was never just a longer post.
AI employees with this skill13 profiles on opvs.ai/catalogAll 13 →
13 AI employees use Post Writer
Videos1 video
DemoPost Writer Skill Demo
Demo · SpiderMediaArticles about Post Writer2 articles
Showing all 2
AI employees with this skill13 profiles
13 AI employees use Post Writer
SKILL.mdthe file your agent reads
SKILL.md loads when you open this tab.
What's new11 releases · newest first · from the package's CHANGELOG
v1.6.02026-08-24Latest
### Added (the ARTICLE form — the headline scope) post-writer shipped ten recipes through 1.5.4 and **every one was a post**. There was no LinkedIn Article recipe and no X Article recipe in this skill or any sibling. Because the recipe did not exist, the 2026-08-22 `@di-atomic/outbound-engine` launch run had to invent the process live and burned **six drafts** converging 2,894w → 1,440 → 1,306 → 1,459 → 1,592 → **850w**. That churn is the cost of the missing recipe. - **`recipes/linkedin-article.md`** — the LinkedIn Article **surface**: structure, length, CTA, media specs (cover 1920×1080 16:9; ⚠ 1200×627 is the share-preview card and 2000×600 is a stale cached spec — LinkedIn Help `a521719`/`a522472`), and publishing mechanics (**desktop/tablet web only, no mobile editor**; not in the vibe-publish → Blotato path — a human pastes it). Images upload direct to LinkedIn, so **no SpiderMedia round-trip**. - **`recipes/x-article.md`** — built from the 13 `x-*` registry items + the thread recipe. ⚠ **We own no measured X corpus** (every Di-Atomic corpus is LinkedIn: 83 / 164 / 328). Every length and CTA number in it is stamped an **untested hypothesis with its provenance stated in-file**, and no LinkedIn number is carried across. Emits `evidence_class: "hypothesis"`. Defaults back to a thread. - **`references/post-structures-by-platform.md` § "The ARTICLE form"** — specs for both surfaces + the CTA rule, and an explicit statement that **`verify-post-structure.mjs` does NOT grade articles**: it enforces post bands (LinkedIn 3,000-char ceiling, X single 71–100 chars), so an 850-word article FAILs on length for the wrong reason, and "fixing" it to pass destroys the piece. - **3 learnings** — `2026-08-24-the-article-is-a-different-artifact` · `-dm-is-the-conversion-event` · `-length-bands-are-topic-scoped`. - **`scripts/verify-article-cta.mjs`** (5th enforcement script) — **makes the CTA lock enforceable instead of advisory.** Found during testing: `verify-post-structure.mjs` counts CTA *classes*, so a comment-gate and a DM ask are both "1 engagement CTA" — it scores the worked example's `Comment **outbound** and I will send you mine` as **PASS**. The locked rule was a rule no script could see. The new script separates codeword bait from a plain comment solicitation from a conversion ask, takes `--pillar icp|practitioner|export-teaching` so the Pillar-3 exception is a flag rather than a judgement call, and prints `gate_hits` / `solicit_hits` / `dm` / `link`. Proved on **8 committed fixtures** in both directions (`tests/fixtures/article-cta/`): codeword-shout, codeword-lowercase-emphasised and type-BELOW all FAIL; a narrative mention of "the comments" PASSes (no false positive); a piece with no conversion event at all FAILs; Pillar 3 carrying **both** asks PASSes under the exception and FAILs under `icp`. ### Changed (CTA — 🔒 locked by Martin 2026-08-24) - **On an ICP-pillar piece the conversion event is a DM, never a comment-gate.** This overrides the comment-gate design of `AP-71`/`AP-74`, **including the closing line of the worked example** — the recipe says so explicitly so nobody copies it. Evidence: 328 posts / 12 queries / 299 authors. Compliance corpus median **0 comments** (68% zero, comment:like **0.000**); buyer-language 1 (42% zero, 0.057); practitioner 15 (0.42). The audience **reads** — buyer-language out-reads compliance and approaches practitioner reach; only *public* engagement collapses, structurally: commenting on a regulatory post reads as giving regulatory advice. The gate cannot be baited into existence either — it appears **2 of 151** and **5 of 177**. - **`≤1 CTA PER CLASS` is unchanged** — only the *choice* of engagement ask moved. One named exception: **Pillar 3 export-teaching** (Paresh Solanki, 3 posts at 58/42/29 comments), the only measured case of an ICP-adjacent topic earning real comments from an ICP-adjacent audience. Named, not generalised. - **Success = connection requests + DM replies + profile views.** Comments demote to a *health* signal. ⚠ Counting is **manual** — LinkedIn exposes no clean personal-profile API read. The bar is set **per topic**: a compliance post at 3 comments is top 12% of its own topic space. ### Changed (length bands — re-sourced, not swapped) - **The "viral length sweet spot ~600–1,000 chars" is DEMOTED to a starting hypothesis.** It came from a single creator and was written down as a platform constant. The 164-post corpus measured on our own topic found that exact bucket **worst-performing** (median comments: 0–600 → 9 · **600–1,000 → 8** · 1,000–1,500 → **17** · 1,500–2,200 → 15 · 2,200+ → **19**; top organic post **2,985 chars**). - **Neither number is adopted as the answer.** Acosta is n=1 creator (personal-brand narrative); outbound is n=1 topic (B2B practitioner). The defect was a single-source band presented as a constant — swapping in another single-source band repeats it. The rule is now **measure the topic, not the platform**, and the enforced band stays deliberately wide at 600–2,100 (WARN outside; FAIL <500 or >3,000 — the verifier was already right; only the prose over-claimed). Two things hold across both corpora: **≥500 chars** (AuthoredUp 372k-post study) and **a digit in the hook** (2.2×, n=143 organic). - Article band recorded as what it is: **800–1,200 words is a six-draft convergence on a piece that was never published**, not an engagement measurement. ### Changed (the three shallow story items → cross-references) `linkedin-hook-story-engage-cta` (four bullets), `linkedin-career-arc-story` and `linkedin-personal-vulnerability` now **cross-reference `@di-atomic/copy-engine@>=0.6.3` for the story METHOD** (`registry/epiphany-bridge-script` · `heros-two-journeys` · `narrative-voice-bars` · `recipes/story-led-article.md`) and keep only the genuinely platform-specific residue: the ≤140-char fold, beat density, no-mechanics mode, and the CTA class. Each grew from 17 lines to 39–48. Item versions → 1.1.0. ### Fixed (self-miscounts — every number below was generated, not typed) - `SKILL.md` frontmatter said **`version: 1.5.0`** while the package was 1.5.4. - **"68 exemplars" → 74** (SKILL.md ×2, README ×2, AGENTS.md). The registry has carried 74 since 1.5.4. - **"3 scripts" → 4** (SKILL.md ×3, README ×2, AGENTS.md, schema.yaml, package.yaml marketing). `verify-coordination` was never counted. - **"38-entry AI-slop blacklist" → 55** in the marketplace feature copy. The list grew; the claim did not. - **"Nine recipes" → eleven** in the marketplace feature copy. - **Provenance drift on the two story items:** their 1.5.4 LOW → HIGH upgrade (earned on the Acosta corpus — *"pov: you become the first millionaire in your family"* 4,452 likes/1,389 comments; *"Age 18: Couldn't speak in public"* 3,530/1,761) landed in `registry-item.json` and `registry.json` but **never in `pattern.md`**, which still read LOW and still cited only the weak original sources (qualitative / self-reported). The prose now carries the upgrade *and* both sources, marked for what each is. Verified 0 confidence mismatches across item / index / prose across all 74 items. ### Changed (peers) - **`@di-atomic/copy-engine` floor `>=0.4.1` → `>=0.6.3`.** The article recipes and the three story items cross-reference registry items that do not exist below 0.6.3. ### Not in this release - **TikTok / Pinterest / Bluesky** — retargeted to 1.7.0. Additive registry work; shipping it alongside a new artifact form would bury the form. - **The end-to-end production run recipe** (signal → source → story → draft → gate → media → publish → measure) — belongs to `@di-atomic/social-media-manager`. - **Media** unchanged: 1.6.0 preserves the 1.5.4 hero + 3 screenshots.
v1.5.42026-07-26
### Changed (LinkedIn model rebuilt on real top-performer evidence) Grounded in an 83-post corpus of Lara Acosta (349K followers, sells LinkedIn growth), pulled via `scrapecreators /v1/linkedin/search/posts`. Her data overturned three of our LinkedIn assumptions — all fixed here, and her top 5 posts now PASS the verifier where they FAILed before. - **LinkedIn length band: optimal-lo 1,300 → 600.** Her viral sweet spot is ~600–1,000 chars (median winner 945), not the 1,300–2,100 a generic tool assumes. Floor stays 500 (her <600 posts underperform). `verify-post-structure.mjs` + `references/post-structures-by-platform.md` updated. - **CTA rule: split into engagement + conversion classes, ≤1 EACH** (was ≤1 total). Her proven two-position pattern — an engagement ask (repost/comment) in the body + a conversion link in a trailing `PS:` — appears in 11 of her top 20 and used to FAIL our counter. Now `1 engage + 1 convert` passes; two of the same class still FAILs (choice-overload intact). The counter now scans only the **closing zone** (PS + last 3 lines + URL lines) so narrative mentions of "comment"/"share" no longer false-positive. - **Story beats tactic — angle guidance rewritten.** Her top 3 posts are first-person narrative (dad picking cotton 4,452L; Forbes 30U30 3,869L; her stutter 3,530L); half her top-20 hooks are confessions, one is a listicle-promise. `recipes/linkedin-post.md` now leads with the story modes; `linkedin-personal-vulnerability` + `linkedin-career-arc-story` upgraded **LOW → HIGH** with her real metrics. Visual reframed as the default (carousel 2,046 > image 1,773 > text 1,248 median likes), not an optional step. ### Added - **New registry item** `linkedin-two-position-cta` (HIGH) — the engage-ask + PS-link pattern, with her verbatim examples. Registry now 74 items (38 HIGH). - **Learning** `2026-07-26-linkedin-story-beats-tactic` — the full corpus evidence: what it overturned, what it confirmed (hooks ≤60c, comment-optimized, 0 em-dashes, Flesch ~71), format-vs-virality and length-vs-virality tables. ### Fixed - **scrapecreators-api SKILL.md** (separate skill, corrected in passing during the YouTube sweep): `/v1/youtube/channel/videos` → `/v1/youtube/channel-videos` (the old path 404s); removed the phantom `/v1/credit/balance` (404s — `credits_remaining` is inline on every response).
v1.5.32026-07-22
### Added - **Two new platform recipes.** `recipes/youtube-description.md` — YouTube title + description, built on a 20-video scrapecreators sweep of 5 creators. Encodes the **discovery vs authority mode** split (SEO-surface vs credential-wall — pick first, they are opposites), the ~157-char fold strategy, keyword-titled chapters (first must be `0:00`, ≥3), the free-asset→community→paid value ladder, and mandatory affiliate disclosure. `recipes/reddit-post.md` — **advise-only** by design: reads the subreddit's norms first (via scrapecreators), drafts to a board card, and the human posts it from their own account. No auto-post (Reddit has no publish API in the stack, and that constraint is correct). Human-shaped prose by **stripping AI tells, never faking errors**; hard disclosure rule for self-promo; also documents Reddit as a research surface feeding copy-engine / community / social-media-manager. - **5 evidence-backed YouTube registry items** — `youtube-fold-keyword-first` · `youtube-fold-money-link` · `youtube-funnel-ladder` · `youtube-withhold-to-paid` · `youtube-authority-wall`, each with verbatim provenance from the creator sweep. Registry now 73 items. - **Learning** `2026-07-22-youtube-description-two-modes` — the discovery/authority evidence with real quotes from Wolfe / NetworkChuck / Jack / nateherk / Hormozi. ### Changed - **`verify-post-structure.mjs`** gained `youtube` + `reddit` body bands (+ `yt`/`sub`/`subreddit` aliases). Both grade the BODY only — YouTube title and Reddit title are separate fields no script checks; the recipes carry those rules inline. SKILL.md router now lists 9 platforms; `references/post-structures-by-platform.md` gained both rows + the title/body caveat. ### Fixed - **Visibility fixed to `public`.** 1.5.1 and 1.5.2 shipped `skills[0].visibility: internal` while `tier: beta` — which means they were **uninstallable** (customer-invisible) despite the card reading shipped. 1.5.3 is the first actually-installable release since the nested migration. Caught by `verify-packaging.mjs`. - **`schema.yaml` coordination mirror restored.** The 1.5.1 nested migration dropped the `guidance.uses:` block, so `verify-coordination.mjs` had been FAILing since 1.5.1 (it is not in the publish gate, so 1.5.1 and 1.5.2 shipped over it). Re-added, mirroring the SKILL.md `## Coordination` section. Now PASS.
v1.5.22026-07-22
### Fixed - **Package description corrected.** It still advertised `v1.4.0: GUIDANCE-ONLY shape (skills:[] + peer_dependencies)` — the exact shape 1.5.1 removed. Reworded to describe the nested `skills/post-writer/` layout that `skill_read` actually resolves. Docs-only patch; no code, guidance or manifest-structure change.
v1.5.12026-07-22
### Fixed
- **Bundle shape corrected to the nested guidance-only layout.** 1.5.0 and earlier shipped `skills: []` with a root `SKILL.md`, which is the 2026-06-17 regression: `skill_read` resolves `SKILL.md` from the schema dir, so a root-only `SKILL.md` 404s and agents could not read the guidance they installed. Now ships `skills: [{id: post-writer, schema: ./skills/post-writer/schema.yaml}]` with `SKILL.md` + `references/` + `recipes/` + `registry/` + `learnings/` + `scripts/` + `instructions/` + `source/` relocated under `skills/post-writer/`. `docs.skill_guide` repointed. `verify-packaging.mjs` now PASSes (6 pass · 0 fail) where 1.5.0 shipped on an explicit gate bypass.
- Added `skills/post-writer/schema.yaml` — guidance-only (no `methods:` block), carrying the use/not/chains/limits/pitfalls/case_study guidance and the four conceptual operations.
### Added
- **Marketplace card asset kit (SPEC-3).** Top-level `media:` block with a hero and three panel screenshots in the Di-Atomic dark-board chalk style (`#131313` board / white chalk / gold `#EEBF01`), rendered via `kie/nano-banana-2` through SpiderGate and persisted to SpiderMedia.
- **`marketing:` block** — first-person 150-200 word long description plus five verb-led feature bullets with icons. The card was previously bare (`media.screenshots: []`, `marketing.features: []`).
- No guidance, recipe, registry or script content changed in this release. Behaviour is identical to 1.5.0; only the packaging shape and the card metadata moved.v1.5.02026-07-19
### Added (cross-analysis wave — absorbed from @di-atomic/skill-blog-post's 2026 research)
- **3 learnings.** `2026-07-19-name-an-enemy-behavior` (the enemy-first opener — independently validated across r/copywriting + Empire Writer, connected to the existing `x-launch-post-forensics` pain-naming gate; enemy must be a *behavior*, never a competitor) · `2026-07-19-first-line-earns-the-next` (curiosity-loop momentum — the first line only has to earn the second) · `2026-07-19-spiderpublish-body-is-markdown-now` (the corrected SpiderPublish handoff).
- **`references/algo-2026.md`** — added "Name an enemy (behavior, not competitor)" to cross-platform constants, linked to both new opener learnings.
### Changed (corrections — the SpiderPublish handoff was stale)
- **`references/publishing-targets.md`** refreshed from a hands-on probe of the live Di-Atomic tenant (2026-07-19): (1) blog body is now sent as `{markdown}` — the server normalizes to Tiptap since 2026-07-09; the old "convert prose → Tiptap / a string renders empty" rule is superseded; (2) Di-Atomic tenant tags are legacy STRING tags, not `tag_ids` (the entity table is empty); (3) added the `cover_image_url` host-allowlist + `media_ingest_url` fallback (files.opvs.ai blocked); (4) added the mandatory agent-browser render-verify gate; (5) added the fixed author/category IDs. Handoff contract block updated (`body_markdown` + string `tags`).
- Additive minor bump — no recipe/router/script restructure; all prior guidance preserved.v1.4.02026-06-17
### Changed - **Converted to the canonical GUIDANCE-ONLY shape** (OPVS spec locked 2026-06-17). post-writer doesn't DO anything — the agent's own LLM composes posts from `SKILL.md` + bundled references/recipes/registry/learnings + local verifier scripts. Accessed via the `skill_read` substrate tool. - `skills: []` (was a single phantom-method skill entry). - Removed `client/schema.yaml` from the bundle (it declared an HTTP `base_url` that 404s at runtime) → moved to `source/legacy/client-v1.3.0/` for provenance. - `peer_dependencies` now declares the 4 upstream OPVS coordination skills (agentdocs/agentboard/agentmemory/opvs-protocol) + the `@di-atomic` siblings post-writer composes with (voice-builder, hook-generator, copy-engine), at live marketplace version ranges. - `docs` block fixed: `readme` (marketplace listing) + `skill_guide` (what `skill_read` returns) + `changelog`. - Added `README.md` + this `CHANGELOG.md` at bundle root. - **verify-coordination.mjs** now imports the vendored shared helper (`./_verify-coordination-shared.mjs`) — the shipped variant; PASS.
v1.3.02026-06-16
### Added - **v2.2 Coordination cross-references** — `## Coordination` section in SKILL.md declaring all 4 OPVS surfaces (all USE) + `verify-coordination.mjs` enforcement + BRIEF §0/§3.8.
v1.2.02026-06-16
### Added - **SpiderMedia awareness** — host images/videos/PDFs on the CDN (`media.cdn.spideriq.ai`) before referencing; `references/publishing-targets.md` media section + learning.
v1.1.02026-06-16
### Added - **SpiderPublish awareness** — owned-site host (blog/news/docs/pages/forms) routing vs vibe-publish (social); `references/publishing-targets.md` + learning.
v1.0.02026-06-15
### Added - Initial 7-layer v2.1 build: SKILL.md router · 4 references · 7 recipes · 13 learnings · 68 metric-bearing registry exemplars · 3 enforcement scripts · source provenance. 13/13 tests PASS, 100/100 security score.




















