Install
openclaw skills install blog-embargoCoordinate publishAt embargo timing across blog index visibility and social post scheduling. Ensures blog posts don't appear in the index before social posts are queued, and social posts don't fire before the post is indexed.
openclaw skills install blog-embargoThe blog uses a publishAt frontmatter field to support soft embargoes: post deploys to Vercel immediately on merge but is hidden from the blog index (/blog listing) until publishAt passes. The index revalidates hourly.
Without this field, posts are indexed immediately on deploy. If social posts aren't already in Buffer before the merge, you get a window where the post is live but no social amplification is queued.
This skill coordinates the three moving parts:
publishAt in MDX frontmatter (blog index gate)--publish-at in convert-to-mdx.py (how to set it)publishAt + 15 min (social timing)Blog index visibility and social posts must be coordinated. Always decide
publishAtbefore raising the PR.
| Scenario | publishAt | Buffer timing |
|---|---|---|
| Want coordinated launch (blog + social together) | Set to future slot | Schedule Buffer at publishAt + 15 min |
| Post can go live now, social later same day | Set to now + 30 min | Schedule Buffer at publishAt + 15 min |
| No embargo needed (emergency fix, no social) | Omit or set to now | Post immediately |
Before raising the PR, decide a specific AEST datetime:
publishAt = "2026-04-04T09:00:00+11:00" # AEDT
publishAt = "2026-04-04T09:00:00+10:00" # AEST
Pass --publish-at to convert-to-mdx.py:
python3 scripts/blog/convert-to-mdx.py \
--input projects/blog-pipeline/converted/<slug>.md \
--output projects/blog-pipeline/converted/<slug>.mdx \
--slug <slug> \
--title "<title>" \
--description "<description>" \
--tags "<tags>" \
--author "Nissan Dookeran" \
--publish-at "2026-04-04T09:00:00+11:00"
Check: MDX frontmatter should contain publishAt: "2026-04-04T09:00:00+11:00".
Post deploys immediately. Direct URL (/blog/slug) resolves. Blog index (/blog) hides the post until publishAt.
After merge, schedule social posts with dueAt = publishAt + 15 min:
# X/Twitter — at publishAt + 15 min
node scripts/buffer-post.mjs \
--text "$(cat projects/social-growth/thread-<slug>.md | ...)" \
--channel twitter \
--publish-at "2026-04-04T09:15:00+11:00"
# LinkedIn — same time or later slot
node scripts/buffer-post.mjs \
--text "$(cat projects/social-growth/linkedin-<slug>.md | ...)" \
--channel linkedin \
--publish-at "2026-04-04T09:15:00+11:00"
Or use the batch file approach with scripts/buffer-post.mjs --file posts.json.
If a post was merged without publishAt and is already indexed:
This happened with:
ollama-embeddings (2026-04-03) — live, no social queuedportkey-patterns (2026-04-03) — live, no social queuedpublishAt is an ISO8601 field added to MDX frontmatter/blog) filters posts where publishAt > nowexport const revalidate = 3600 means the index updates hourly/blog/slug) always resolves — no gate at the page levelconvert-to-mdx.py sets both publishAt and aligns the date field when --publish-at is providedplaybooks/blog-publish/PLAYBOOK.md — Step 0b (Embargo Gate) uses this skillscripts/blog/convert-to-mdx.py — --publish-at flagscripts/buffer-post.mjs — --publish-at flag or publishAt in batch JSON69c29939af47dacb694d3d1f, LinkedIn 69c29382af47dacb694d24b42026-04-03: Two posts (ollama-embeddings, portkey-patterns) merged without publishAt. Both immediately indexed. Social copy existed but hadn't been approved yet, so nothing was in Buffer. Result: posts live with no social amplification window. Fix: this skill + mandatory Step 0b in blog-publish playbook.