Turn a fact-checked and compliance-approved final Markdown draft into multi-channel publishing assets — WeChat article, LinkedIn post, standalone responsive HTML, and an archive ledger. It only orchestrates publishing and format conversion; it does not repeat fact-checking and does not auto-publish. Trigger keywords: publish to multiple channels, WeChat article, LinkedIn post, archive ledger, 发布套件, 多渠道发布, 微信排版, 内容归档.
---
name: content-publishing-suite
slug: content-publishing-suite-skill
displayName: Content Publishing Suite
description: >
Turn a fact-checked and compliance-approved final Markdown draft into multi-channel publishing assets — WeChat article, LinkedIn post, standalone responsive HTML, and an archive ledger. It only orchestrates publishing and format conversion; it does not repeat fact-checking and does not auto-publish. Trigger keywords: publish to multiple channels, WeChat article, LinkedIn post, archive ledger, 发布套件, 多渠道发布, 微信排版, 内容归档.
description_zh: 内容发布套件
description_en: Content publishing suite
version: "1.1.2"
agent_created: true
read_when:
- "publish to multiple channels"
- "WeChat article"
- "LinkedIn post"
- "archive ledger"
- "发布套件"
- "多渠道发布"
- "微信排版"
- "内容归档"
---
name: content-publishing-suite
description: Turn a fact-checked and compliance-approved final Markdown draft into multi-channel publishing assets — WeChat article, LinkedIn post, standalone responsive HTML, and an archive ledger. It only orchestrates publishing and format conversion; it does not repeat fact-checking and does not auto-publish.
description_zh: 内容发布套件
description_en: Content publishing suite
version: 1.1.1
disable: false
agent_created: true
---
# Content Publishing Suite
Convert a **reviewed, approved final draft** into multi-channel publishing assets and generate an archive ledger. This skill is the publishing-orchestration layer — it does not repeat fact-checking, originality review, or cross-material auditing (those are upstream skills), and it never performs any external publish action automatically.
> In this document `{SKILL_DIR}` is replaced automatically with the skill's actual install path.
## When to use
- The user has a final draft that passed final check (`07-final.md` from `industry-deep-dive-pipeline`, or an equivalent Markdown explicitly marked "已审核 / reviewed"), and wants WeChat / LinkedIn / standalone HTML / archive assets.
- The user asks to "publish this to multiple platforms", "generate a publish package", or "lay out and archive".
## Do not use
- Content that has not completed fact-checking or compliance review → run `industry-deep-dive-pipeline` / `claim-to-source-auditor` / `cross-material-consistency-auditor` first.
- Pure WeChat layout with a dedicated WeChat-layout skill already installed, or an existing platform drafting flow → use that tool directly; this suite is unnecessary.
- Writing new body text, rewriting, or adding facts → that belongs to the writing / verification stage, not publishing orchestration.
## Input
```yaml
draft:
path: # reviewed final Markdown
title:
author:
date:
approval_gate: # one of these must be satisfied
final_check_json: # product of industry-deep-dive-pipeline; needs Gate B approved, red-line hits 0, credential/privacy P0 0
reviewed_marker: false # or draft header carries `状态: 已审核` / `reviewed: true` AND user confirms
channels: [wechat, linkedin, html, archive]
output_dir:
external_action: dry-run # default: generate only, do not send
read_only_upstream: true # do not introduce new facts not covered upstream
```
One of the two gates must be satisfied, otherwise refuse to enter:
1. Provide `final-check.json` (product of `industry-deep-dive-pipeline`) with `Gate B: approved`, `red-line hits: 0`, `credential/privacy P0: 0`; or
2. The final draft header carries an explicit `状态: 已审核` or `reviewed: true` marker, AND the user confirms "已审核" in the request.
If the downstream introduces any **new fact** not covered upstream, pause and prompt to roll back to upstream for re-verification — never publish silently.
## Output channels
| Channel | Artifact | Notes |
|---------|----------|-------|
| WeChat | `wechat_snippet.html` (paste-ready fragment) + `wechat_preview.html` (mobile-frame preview) | 135-editor-compatible inline styles; does not auto-create draft |
| LinkedIn | `linkedin-post.md` | hook + body + 3–5 hashtags; Chinese/English by target audience |
| Standalone HTML | `standalone.html` | responsive single page with TOC and reference links |
| Archive | `archive-ledger.json` (local) + optional Notion entry | title/channel/time/status; optional page-ID write with readback |
## Workflow
```
ingest final draft → input gate → generate per-channel assets → output gate (script validation) → package-manifest → archive ledger → external-action gate (confirm before send)
```
### Step 1: [Deterministic + LLM] Ingest and input gate
Read the final draft Markdown and confirm the input gate is satisfied. Record title, author, date, and core judgment. Stop immediately if the gate fails.
### Step 2: [LLM] Generate per-channel assets
- **WeChat**: per `references/wechat-style.md`, convert Markdown to fully-inline `<section>` HTML. Title 22px centered, subhead 17px bold `#1a3a5c`, body 16px / line-height 1.8 / `#333`, key sentences `<strong style="color:#1a3a5c;">`, table with dark-blue header + white text + zebra rows. Must end with `<v2></v2>`. Never use `<style>`, `<script>`, external class, or an outer `<div>` container. Template: `templates/wechat-snippet.html`.
- **LinkedIn**: per `templates/linkedin-post.md`, take the core judgment as the hook, compress body to 200–300 words, end with 3–5 hashtags. No Markdown, no internal notes, no unpublished data, no process meta.
- **Standalone HTML**: per `templates/standalone.html`, generate a responsive single page with title/author/date/TOC/body/reference links. Flat-fill style, generous whitespace, **absolutely no tech-circuit motifs, glow, or digital-grid patterns**.
- **Archive ledger**: append one record to `archive-ledger.json` (create if missing); fields per `templates/archive-record.json`.
### Step 3: [Deterministic] Output gate (script validation)
Run the deterministic script to verify each channel file meets its format contract:
```bash
python3 {SKILL_DIR}/scripts/validate_publish_output.py --package <output_dir> --enforce
```
P0 blocks (e.g. WeChat missing `<section>`/`<v2>`, contains `<script>`, HTML without structure, ledger missing fields); P1 warnings (e.g. LinkedIn too long). Exit code 2 means a P0 exists. The author's internal pen-name list is passed via the `PUBLISH_PEN_NAMES` environment variable (comma-separated), never hardcoded.
### Step 4: [Deterministic] Package and ledger
```bash
python3 {SKILL_DIR}/scripts/build_publish_package.py \
--draft <final.md> \
--approved-gate <final-check.json or --approved flag> \
--channels wechat,linkedin,html,archive \
--output <output_dir>
```
The script produces each channel file, updates `archive-ledger.json`, and outputs `package-manifest.json`.
### Step 5: [Human] External-action gate
Before any actual push, article-platform draft creation, or Notion write, **list the targets and obtain user confirmation**. Default is generate-only (`--dry-run`). Execute only after confirmation, and always read back to verify after writing (Notion uses page-ID writes, idempotency key prevents duplicates).
## Hard Rules
1. Refuse to enter if the input gate is not satisfied (no `final-check.json` approved, no explicit "已审核" marker) — never substitute for upstream verification.
2. Zero conversation traces, zero process meta, zero internal notes / pen-names / unauthorized data in any publish asset.
3. WeChat assets must be fully inline-styled, wrapped in `<section>`, and end with `<v2></v2>`; forbid `<style>`/`<script>`/external class/outer `<div>`.
4. Use the real name or neutral phrasing for any person in working docs and publish assets; the author's internal pen-name is read from `PUBLISH_PEN_NAMES` for blocking only, never hardcoded into this skill.
5. Any external action (push, draft creation, Notion write) defaults to `--dry-run`; list targets and get user confirmation first; read back after writing.
6. Credentials come only from environment variables (Notion uses `NOTION_TOKEN`, database ID uses `NOTION_DB_ID`; article platforms via their existing MCP/connectors) — never hardcoded.
7. Scripts only read the user-specified final draft and write to the user-specified output directory; no network egress.
## Failure Handling
| Scenario | Action |
|---|---|
| Input gate not satisfied | Stop; list missing items, prompt to finish upstream verification |
| Output gate hits P0 (exit code 2) | Do not generate manifest; list each P0 and its file, fix and re-run |
| Downstream finds a new fact not covered upstream | Pause publishing, roll back upstream for re-verification, never publish silently |
| External write fails | Retry once and cross-verify; if still failing, keep the local ledger, report the reason, leave no half-written state |
| Final draft has unparseable embedded content (charts/image-with-text) | Mark as unparseable, request text extraction or manual confirmation |
| Target channel unspecified | Default to generating all four channels and prompt |
## Output Format
```text
<output_dir>/
├── wechat_snippet.html
├── wechat_preview.html
├── linkedin-post.md
├── standalone.html
├── archive-ledger.json # append-style archive ledger
└── package-manifest.json # this publish package manifest (file paths + gate results)
```
Gate report: P0/P1 list; when a P0 exists, explicitly mark "未通过、禁止发布 / failed, publishing forbidden".
## References
| Resource | Purpose |
|----------|---------|
| `references/wechat-style.md` | WeChat 135 inline-style spec and component styles |
| `references/channel-contracts.md` | Output-format contracts per channel (validation basis) |
| `templates/wechat-snippet.html` | WeChat fragment template |
| `templates/linkedin-post.md` | LinkedIn post template |
| `templates/standalone.html` | Standalone HTML single-page template |
| `templates/archive-record.json` | Archive ledger entry template |
| `templates/notion-mapping.example.json` | Notion mapping example (credentials via `NOTION_TOKEN` env var; database ID placeholder, never real value) |
| `scripts/build_publish_package.py` | Organize artifacts, update ledger, output manifest |
| `scripts/validate_publish_output.py` | Per-channel format-contract validation, outputs P0/P1 gate |
## Verification
- [ ] `validate_publish_output.py --enforce` exits 0 (no P0).
- [ ] WeChat fragment contains `<section>`, ends with `<v2></v2>`, no `<script>`/`<style>`/outer class.
- [ ] LinkedIn has no Markdown markers, no internal notes / pen-names.
- [ ] Standalone HTML starts with `<!DOCTYPE html>`, no tech-circuit / glow / digital-grid motifs.
- [ ] `package-manifest.json` generated for all channels, `archive-ledger.json` appended one entry.
- [ ] User confirmation obtained before any external action; default dry-run.
## Pitfalls
- Treating an unreviewed draft as reviewed and publishing directly — must pass the input gate first.
- WeChat asset accidentally gets `<style>`/outer `<div>`, then the 135 editor strips the styles on paste.
- LinkedIn retains Markdown bold/list symbols or an internal pen-name — the platform won't render it or it leaks internal info.
- Writing a real Notion database ID or credential in an example/template — always use placeholders and environment variables.
- External write without readback verification, causing duplicate writes or half-written state.
- Downstream silently adds a "new fact" that bypasses upstream verification — any new fact must roll back upstream.
---
## 中文摘要(Chinese Summary)
本 Skill 是**发布编排层**:把一篇已通过终检的定稿(如 `industry-deep-dive-pipeline` 的 `07-final.md`,或显式标记「已审核」的等价 Markdown)稳定转换为微信 / LinkedIn / 独立 HTML / 入库四类物料,并生成入库台账。
**关键约束(双语要点 / Bilingual key points):**
- **输入门禁 Input gate**:必须提供 `final-check.json`(Gate B approved、红线 0、凭据/隐私 P0 0)或定稿头部显式「已审核」标记且用户确认;否则拒绝进入。
- **零泄漏 Zero leakage**:发布物料中零对话痕迹、零流程元信息、零内部备注/笔名;作者笔名仅经 `PUBLISH_PEN_NAMES` 环境变量传入用于拦截,绝不硬编码。
- **微信硬规则 WeChat hard rule**:全内联样式、`<section>` 包裹、文末 `<v2></v2>`;禁止 `<style>`/`<script>`/外联 class/外层 `<div>`。
- **外部动作门禁 External-action gate**:任何推送、建草稿、写 Notion 默认 `--dry-run`,须列目标并取得用户确认,写后必须回读核验。
- **凭据 Credentials**:只走环境变量(`NOTION_TOKEN` / `NOTION_DB_ID`),绝不硬编码。
它不重复事实核验、原创性复核或跨材料审计,也不自动执行任何发布动作。
don't have the plugin yet? install it then click "run inline in claude" again.
Convert a reviewed, approved final draft into multi-channel publishing assets and generate an archive ledger. this skill is the publishing-orchestration layer , it does not repeat fact-checking, originality review, or cross-material auditing (those are upstream skills), and it never performs any external publish action automatically.
in this document
{SKILL_DIR}is replaced automatically with the skill's actual install path.
use this skill when you have a final draft that passed fact-check and compliance review and you want to generate WeChat, LinkedIn, standalone HTML, and archive ledger assets in one orchestrated run. this skill converts format and layouts content for multiple channels without touching verification again. do not use it on unreviewed drafts or to auto-publish to external platforms.
draft file
07-final.md from industry-deep-dive-pipeline, or explicitly marked reviewed: true or 状态: 已审核)approval gate (one of these must be satisfied)
final_check_json: output artifact from industry-deep-dive-pipeline with Gate B: approved, red-line hits: 0, credential/privacy P0: 0; orreviewed_marker: draft header carries explicit 状态: 已审核 or reviewed: true, AND user confirms "已审核" in the requestchannel selection
wechat, linkedin, html, archive (default: all four)output directory
external action mode
dry-run (default): generate files locally only, do not push to any platformpublish: perform external writes (Notion, article platform drafts) after user confirmationenvironment variables (required for external writes)
NOTION_TOKEN: OAuth token for Notion write operations (if channels includes Notion and external_action: publish)NOTION_DB_ID: database ID for archive ledger sync (if applicable)PUBLISH_PEN_NAMES: comma-separated list of internal author pen-names (used for blocking/filtering only, never hardcoded into assets)upstream context (read-only)
read_only_upstream: true enforces that no new facts are introduced beyond what upstream verification coveredfinal_check_json provided: parse it, verify Gate B: approved, red-line hits: 0, credential/privacy P0: 0; if any condition fails, stop and list missing itemsreviewed_marker approach: scan draft header for 状态: 已审核 or reviewed: true; if found, ask user "确认已审核? / confirm reviewed?" and record responseinputs to step 1: draft file path, approval gate parameters, user response (if manual confirmation required)
outputs from step 1: validated draft object (title, author, date, body, gate status), gate timestamp
generate each requested channel artifact according to format contract below.
2a. WeChat asset
<style> tag, no <script>)<h2 style="font-size:22px;text-align:center;margin:20px 0;color:#000;"> {title} </h2><p style="font-size:17px;font-weight:bold;color:#1a3a5c;margin:15px 0 10px 0;"> {subhead} </p><p style="font-size:16px;line-height:1.8;color:#333;margin:12px 0;"> {text} </p><strong style="color:#1a3a5c;"> {phrase} </strong>background:#1a3a5c;color:white;), white text, zebra-stripe body rows (background:#f5f5f5 on even rows)<section> tag (no outer <div>, no class attribute)</section><v2></v2> at the end (required for 135-editor compatibility)templates/wechat-snippet.html{output_dir}/wechat_snippet.htmlwechat_preview.html: same content wrapped in a mobile-frame preview (viewport meta tags, responsive padding)inputs to 2a: validated draft, wechat-style.md reference, wechat-snippet.html template
outputs from 2a: wechat_snippet.html, wechat_preview.html
2b. LinkedIn asset
**bold**, no - list, no [link](url))templates/linkedin-post.md{output_dir}/linkedin-post.mdinputs to 2b: validated draft, linkedin-post.md template, target audience language setting (inferred from content or requested)
outputs from 2b: linkedin-post.md
2c. Standalone HTML asset
<!DOCTYPE html> opening, single file, no external resources)<article>, <nav>, <footer>, etc.)templates/standalone.html{output_dir}/standalone.htmlinputs to 2c: validated draft, standalone.html template, target audience (inferred or requested)
outputs from 2c: standalone.html
2d. Archive ledger entry
{output_dir}/archive-ledger.json (initialize as [] if missing)templates/archive-record.json:id: uuid (auto-generated)title: from draft frontmatterauthor: from draft frontmatterchannels: array of generated channels (e.g., ["wechat", "linkedin", "html"])publish_timestamp: ISO 8601 current timestatus: "generated" (or "published" if external action completed)draft_path: relative path to original final draftartifacts: object mapping channel name to artifact file path (e.g., {"wechat":"wechat_snippet.html","linkedin":"linkedin-post.md"})gate_result: "approved" or "manual_reviewed" (value from step 1)notion_page_id: null initially (populated if Notion write succeeds in step 5)archive-ledger.jsoninputs to 2d: validated draft, channel list, gate result from step 1
outputs from 2d: updated {output_dir}/archive-ledger.json with one new entry
python3 {SKILL_DIR}/scripts/validate_publish_output.py --package {output_dir} --enforce
<section> tag, contains <script> or <style>, missing </v2>, malformed inline styles<!DOCTYPE html>, uses forbidden design motifs, broken reference linksPUBLISH_PEN_NAMES environment variable (comma-separated); never hardcodedinputs to step 3: all artifacts from step 2, PUBLISH_PEN_NAMES env var, channel-contracts.md reference
outputs from step 3: validation report (pass/fail + P0/P1 list), proceed/halt signal
python3 {SKILL_DIR}/scripts/build_publish_package.py \
--draft {final_draft_path} \
--approved-gate {final-check.json or --approved flag} \
--channels {comma-separated list} \
--output {output_dir}
{output_dir}/source-draft.md for audit trailpackage-manifest.json containing:archive-ledger.json if a new entry is needed (idempotent on record id){output_dir}/package-manifest.jsoninputs to step 4: source draft path, approval gate flag/file, artifact directory, channel list
outputs from step 4: package-manifest.json, finalized archive-ledger.json
external_action: dry-run (default):external_action: publish:NOTION_TOKEN and NOTION_DB_ID env vars; write archive record to Notion database with idempotency key (prevents duplicate entries on retry); read back the page_id and update archive-ledger.json notion_page_id fieldtitle, author, status)archive-ledger.json status field to "published" and publish_timestamp to actual external timestamp (not local)inputs to step 5: external_action mode, NOTION_TOKEN and NOTION_DB_ID (if publish mode), user confirmation response
outputs from step 5: (dry-run) confirmation message + local artifacts; (publish) external-action report with readback verification results, updated archive-ledger.json
approval gate (binary branch after step 1 input check)
final_check_json with all required fields passing: proceed with automated gate result "approved"状态: 已审核 or reviewed: true AND user explicitly confirms "已审核": proceed with gate result "manual_reviewed"output gate (blocking branch after step 3 validation)
external-action branch (step 5)
external_action: dry-run (default): skip all platform operations, output local artifacts onlyexternal_action: publish and user confirms: execute Notion write (if NOTION_TOKEN set) and article-platform drafts (via MCP); perform readback verification; if any write fails, rollback and reportexternal_action: publish and user declines: output "publish cancelled", keep local artifacts, exit 0new-fact detection (inline with step 2 generation)
channel unspecified
channels array: default to [wechat, linkedin, html, archive] and prompt user "generating all four channels; override with --channels {list} if needed"missing environment variables (for external action)
external_action: publish and NOTION_TOKEN is empty: skip Notion write, proceed with article-platform drafts only, output warning "NOTION_TOKEN not set, skipping Notion sync"PUBLISH_PEN_NAMES not set and step 3 validation cannot run pen-name filters: output warning "PUBLISH_PEN_NAMES empty , pen-name blocking disabled, manual review recommended"draft parse failures
file locations and formats
all artifacts written to {output_dir} unless otherwise specified.
| artifact | path | format | size limit | character encoding |
|---|---|---|---|---|
| WeChat snippet | wechat_snippet.html |
HTML fragment (single <section>, no outer div, ends with </v2>) |
50 KB | UTF-8 |
| WeChat preview | wechat_preview.html |
complete HTML5 page (mobile viewport meta, responsive padding) | 100 KB | UTF-8 |
| LinkedIn post | linkedin-post.md |
plain Markdown (200-300 words, 3-5 hashtags, no internal notes) | 10 KB | UTF-8 |
| Standalone HTML | standalone.html |
complete HTML5 page (semantic tags, responsive, no external resources) | 200 KB | UTF-8 |
| Archive ledger | archive-ledger.json |
JSON array of record objects, append-style (idempotent on record id) | unbounded | UTF-8 |
| Package manifest | package-manifest.json |
JSON object (metadata, artifact paths, gate results, checksums) | 50 KB | UTF-8 |
| Source draft (audit trail) | source-draft.md |
copy of input final draft | original size | UTF-8 |
gate report format (output_dir/validation-report.txt or within manifest)
Gate Input: [PASS / FAIL]
- approval_gate_status: {approved | manual_reviewed | rejected}
- reason: {reason if rejected}
Gate Output: [PASS / FAIL]
- p0_blocks: [{file, line, error_message}, ...] (if any)
- p1_warnings: [{file, line, warning_message}, ...] (if any)
- status: {通过 / failed, publishing forbidden}
Channels Generated: {list}
- wechat_snippet.html ✓
- wechat_preview.html ✓
- linkedin-post