ZM 公众号草稿发布与核验。用于将已准备好的 Markdown/HTML 公众号稿通过 zm-md2wechat-conversion-tool 推送到公众号草稿箱,并强制执行 draft/get 真实落库与排版核验。
---
name: zm-wechat-draft-publish-verify
description: ZM 公众号草稿发布与核验。用于将已准备好的 Markdown/HTML 公众号稿通过 zm-md2wechat-conversion-tool 推送到公众号草稿箱,并强制执行 draft/get 真实落库与排版核验。
homepage: https://github.com/geekjourneyx/zm-md2wechat-conversion-tool-skill
metadata: {"clawdbot":{"emoji":"📱","requires":{"bins":["zm-md2wechat-conversion-tool","python3"],"env":["WECHAT_APPID","WECHAT_SECRET"]}}}
---
# zm-wechat-draft-publish-verify
Use `zm-wechat-draft-publish-verify` when the user wants to:
- push a prepared WeChat article into the Official Account draft box
- turn Markdown into WeChat-compatible HTML and then create a draft
- use a stable publishing wrapper instead of manually chaining `upload_image -> create_draft -> draft/get`
- verify that a draft really exists in the WeChat backend before reporting success
- troubleshoot the current `zm-md2wechat-conversion-tool create_draft` publishing chain
## What This Skill Is For
This skill is the **publishing execution layer** for WeChat Official Account drafts.
It is responsible for:
- config validation
- Markdown → HTML conversion when needed
- cover upload
- `draft.json` generation
- `zm-md2wechat-conversion-tool create_draft`
- WeChat `draft/get` backend verification
- outputting machine-readable verification artifacts
It is **not** responsible for:
- topic selection
- long-form writing
- AI-trace review
- editorial approval
- layout strategy decisions before HTML exists
- archive / final content governance
Those stay in the main production workflow.
## Canonical Publish Path
```text
Prepared article HTML or Markdown
→ zm-md2wechat-conversion-tool config validate
→ inspect / preview / convert (if Markdown)
→ zm-md2wechat-conversion-tool upload_image cover.*
→ generate draft.json
→ zm-md2wechat-conversion-tool create_draft draft.json
→ call WeChat draft/get
→ verify title / author / cover / content / visibility
```
## Hard Rules
1. `zm-md2wechat-conversion-tool create_draft` is the formal draft creation action.
2. `test-draft` is only for chain testing, not formal publishing.
3. `convert --mode ai` returning `CONVERT_AI_REQUEST_READY` does **not** mean a draft was created.
4. AI-mode output must be materialized into real WeChat-compatible HTML before entering this skill.
5. Command success is **not** task success.
6. Final success requires backend verification.
7. Preferred execution path is `scripts/publish.sh`, not an ad-hoc hand-built sequence.
8. Local `preview` and local `convert` artifacts are not WeChat backend proof.
9. If `create_draft` succeeds but `draft/get` fails, the result must be treated as unverified or blocked.
10. `media_id` alone is only a submission receipt, not a success proof.
## Inputs
Preferred prepared package:
```text
wechat_package/
article.html
cover.png
images/
publish-checklist.md
```
Also supported:
- `article.md` + cover image
- `article.html` + cover image
## Requirements
The following must already be available:
- `zm-md2wechat-conversion-tool`
- `python3`
Config file:
```text
~/.config/zm-md2wechat-conversion-tool/config.yaml
```
Expected WeChat credentials:
```yaml
wechat:
appid: "公众号 AppID"
secret: "公众号 AppSecret"
```
The current public IP must also be in the WeChat Official Account IP allowlist.
## Validate Before Publish
```bash
zm-md2wechat-conversion-tool config validate --json
```
## Standard Execution
Primary script:
```bash
skills/zm-wechat-draft-publish-verify/scripts/publish.sh
```
Usage:
```bash
./scripts/publish.sh <article.md|article.html> <cover-image> [title] [author] [digest]
```
Examples:
```bash
./scripts/publish.sh article.html cover.png "现在找工作,最累的不是被拒" "野哥" "找工作最累的不是被拒,而是一直没有回音。"
./scripts/publish.sh article.md cover.png "标题" "野哥" "摘要"
```
## What The Script Does
1. `zm-md2wechat-conversion-tool config validate --json`
2. If input is Markdown:
- `zm-md2wechat-conversion-tool inspect ... --json`
- `zm-md2wechat-conversion-tool preview ... --json`
- `zm-md2wechat-conversion-tool convert ... --output ... --json`
3. `zm-md2wechat-conversion-tool upload_image cover.png --json`
4. Generate `*.zm-md2wechat-conversion-tool-create-draft.json`
5. `zm-md2wechat-conversion-tool create_draft *.json --json`
6. Call official WeChat `draft/get`
7. Save verification output
## Output Artifacts
The script writes artifacts next to the article:
```text
*.zm-md2wechat-conversion-tool-inspect.json
*.zm-md2wechat-conversion-tool-preview.json
*.zm-md2wechat-conversion-tool-convert.json
*.zm-md2wechat-conversion-tool-cover-upload.json
*.zm-md2wechat-conversion-tool-create-draft.json
*.zm-md2wechat-conversion-tool-create-draft-result.json
*.zm-md2wechat-conversion-tool-create-draft-verify.json
```
The final source of truth is:
```text
*.zm-md2wechat-conversion-tool-create-draft-verify.json
```
## Success Criteria
Only report **draft push success** when both are true:
1. `zm-md2wechat-conversion-tool create_draft` returned success
2. `draft/get` verification passed and the backend draft is really present
Otherwise classify as one of:
- submitted but unverified
- verification failed
- blocked by config / credential / IP / content issue
## Draft JSON Shape
```json
{
"articles": [
{
"title": "标题",
"author": "野哥",
"digest": "摘要",
"content": "<section style=\"...\">正文 HTML</section>",
"thumb_media_id": "封面素材 media_id",
"show_cover_pic": 0
}
]
}
```
Constraints:
- `articles` must not be empty
- `title` required, recommended ≤ 32 chars
- `author` recommended ≤ 16 chars
- `digest` recommended ≤ 120–128 chars
- `content` required and must be WeChat-compatible HTML
- `thumb_media_id` must come from `zm-md2wechat-conversion-tool upload_image` or equivalent WeChat material upload
## Verification Checklist
At minimum verify:
- draft really exists
- `title` matches expected value
- `author` matches expected value
- `thumb_media_id` exists
- content exists
- no leaked local paths such as `<local-home>/` or `content-factory/`
- content has inline styles or otherwise clearly matches publish-ready WeChat HTML
- article images are actually present when the article uses body images
- for Markdown input, the process did not stop at local preview/convert only
- for AI mode upstream flows, the published content is real generated HTML, not the request-ready payload
## Common Failure Modes
### Invalid IP
```text
errcode=40164 invalid ip ... not in whitelist
```
Action: add the current public IP to the WeChat Official Account backend allowlist.
### Invalid AppSecret
```text
errcode=40125 invalid appsecret
```
Action: fix `~/.config/zm-md2wechat-conversion-tool/config.yaml`.
### AI mode only returned a request
```json
{
"code": "CONVERT_AI_REQUEST_READY",
"status": "action_required"
}
```
This is not success. It only means the AI conversion request is ready. Continue by producing HTML, then run the draft chain.
## Operational Guidance For Agents
- Use this skill only when draft creation is actually requested.
- Prefer this skill over improvising raw shell steps.
- Never claim success from `media_id`, `preview`, or `convert` alone.
- Treat verification as mandatory, not optional polish.
- If verification fails, report the exact blocker instead of vague success language.
## Related Files
- `README.md` — quick operator-facing usage
- `references/troubleshooting.md` — failure handling
- `references/themes.md` — related publishing notes
- `scripts/publish.sh` — canonical execution wrapper
- `scripts/setup.sh` — environment setup helper
## Experience Captured
### 2026-05-14
Verified working path using an older article:
```text
AI-ready HTML
→ zm-md2wechat-conversion-tool upload_image
→ draft.json
→ zm-md2wechat-conversion-tool create_draft
→ WeChat draft/get verification passed
```
Verified fields included title, author, digest, cover, content, inline styles, and no local path leakage.
### 2026-05-15
Key lessons added to the standard skill:
- formal publishing should default to `scripts/publish.sh`
- `media_id` is only a receipt
- `CONVERT_AI_REQUEST_READY` is an action-required state, not a success state
- `preview` / `convert` / `test-draft` can mislead people into thinking the draft is already in the backend
- if backend draft verification fails, treat the run as unverified or blocked instead of assuming eventual consistency
don't have the plugin yet? install it then click "run inline in claude" again.
restructured original into implexa's 6-component format, made decision logic explicit (markdown vs html branch, verify failure modes, rate limiting), added edge cases (ip whitelist, token expiry, rate limits, verify failures), documented wechat api inputs with error codes, added outcome signals so user knows success vs failure.
use this skill to publish a prepared wechat official account article (markdown or html) into the draft box. the skill converts input to wechat-compatible html, uploads cover images, calls zm-md2wechat-conversion-tool create_draft, then forces backend verification via wechat's draft/get api. do this when you need reliable draft creation with proof that the article actually exists in wechat's backend, not just local preview artifacts. do not use this for editorial review, topic selection, or ai trace decisions.
article.html (wechat-compatible html, preferred)article.md (markdown, will be converted)cover.png, cover.jpg, or equivalent (required, for upload_image)title (string, ≤32 chars recommended, required)author (string, ≤16 chars recommended, required)digest (string, ≤120-128 chars recommended, required)wechat_package/
article.html
cover.png
images/
publish-checklist.md
WECHAT_APPID (app id of wechat official account)WECHAT_SECRET (app secret, do not log or expose)~/.config/zm-md2wechat-conversion-tool/config.yaml (yaml with wechat.appid and wechat.secret keys)zm-md2wechat-conversion-tool (cli tool, must be in $PATH)python3 (required by tool)scripts/setup.sh from this skill or install directly via your package managerdraft/get (called by zm-md2wechat-conversion-tool internally or by verification step)draft/getinput: wechat credentials (env vars or config file)
action: run zm-md2wechat-conversion-tool config validate --json
output: json result with success: true or error details
on failure: stop. do not proceed to draft creation. report config issue (invalid appid, missing secret, malformed yaml, unreachable wechat api).
input: article.md
action: run zm-md2wechat-conversion-tool inspect article.md --json
output: article.zm-md2wechat-conversion-tool-inspect.json (metadata, frontmatter, any ai markers)
note: only for markdown. skip if input is already html.
input: article.md
action: run zm-md2wechat-conversion-tool preview article.md --json
output: article.zm-md2wechat-conversion-tool-preview.json (local preview, not backend proof)
note: local artifact only. do not report success based on this. skip if input is html.
input: article.md
action: run zm-md2wechat-conversion-tool convert article.md --output article.html --json
output: article.html (wechat-compatible html), article.zm-md2wechat-conversion-tool-convert.json (conversion metadata)
note: skip if input is already html. check that convert did not return CONVERT_AI_REQUEST_READY (that is an action-required state, not success).
input: cover.png (or jpg), wechat credentials
action: run zm-md2wechat-conversion-tool upload_image cover.png --json
output: article.zm-md2wechat-conversion-tool-cover-upload.json (contains thumb_media_id)
on failure: stop. report upload failure (invalid ip, expired token, image rejected).
critical: media_id alone is a submission receipt. it does not mean the draft is in the backend yet.
input: title, author, digest, article.html, thumb_media_id (from step 5)
action: create article.zm-md2wechat-conversion-tool-create-draft.json with structure:
{
"articles": [
{
"title": "<title>",
"author": "<author>",
"digest": "<digest>",
"content": "<section style=\"...\">...</section>",
"thumb_media_id": "<media_id>",
"show_cover_pic": 0
}
]
}
validation: articles array not empty, title present, content present, thumb_media_id present, no local paths leaked (no <home>/, no content-factory/ in content).
output: article.zm-md2wechat-conversion-tool-create-draft.json
input: article.zm-md2wechat-conversion-tool-create-draft.json, wechat credentials
action: run zm-md2wechat-conversion-tool create_draft article.zm-md2wechat-conversion-tool-create-draft.json --json
output: article.zm-md2wechat-conversion-tool-create-draft-result.json (contains media_id if successful)
on failure: stop. report create_draft error (invalid payload, content rejected, wechat backend error).
critical: command success does not equal task success. proceed to step 8 unconditionally.
input: media_id (from step 7 result)
action: call wechat draft/get api with the media_id to retrieve the full draft
output: article.zm-md2wechat-conversion-tool-create-draft-verify.json (contains full draft from wechat backend)
validation checklist:
title matches expected inputauthor matches expected inputthumb_media_id present and matches uploaded covercontent present and contains expected wechat html (inline styles, no local paths)show_cover_pic correct (0 or 1)CONVERT_AI_REQUEST_READY payload)if input is markdown:
CONVERT_AI_REQUEST_READY (if so, stop and report "ai conversion request ready, not draft created")else if input is html:
if step 5 (upload_image) fails:
errcode=40164 invalid ip: report "current ip not in wechat allowlist, add to backend settings"errcode=40125 invalid appsecret: report "invalid appsecret in config.yaml"errcode=40001 invalid access_token: report "token expired or refresh failed, retry"if step 7 (create_draft) succeeds but step 8 (draft/get) fails:
draft/get returns errcode=40007 invalid media_id: treat as unverified (backend did not actually persist)draft/get times out or returns server error: treat as unverified, do not retry more than 3 timesif rate limit is hit during step 8 (draft/get):
if ai mode was upstream:
CONVERT_AI_REQUEST_READY as proof of draft creationsuccess case: all of the following files exist next to the article, and step 8 verification passed
article.zm-md2wechat-conversion-tool-cover-upload.json
article.zm-md2wechat-conversion-tool-create-draft.json
article.zm-md2wechat-conversion-tool-create-draft-result.json
article.zm-md2wechat-conversion-tool-create-draft-verify.json
failure case: same files written up to the failure point, plus error details in the final output file (verify.json or result.json depending on where failure occurred)
final source of truth: article.zm-md2wechat-conversion-tool-create-draft-verify.json (contains full draft from wechat backend, or verification failure details)
data format: all artifacts are json (--json flag used in all cli calls)
file location: all artifacts written to same directory as input article
markdown intermediate artifacts (if input was markdown):
article.zm-md2wechat-conversion-tool-inspect.json
article.zm-md2wechat-conversion-tool-preview.json
article.zm-md2wechat-conversion-tool-convert.json
article.html (converted output)
user knows it worked when:
article.zm-md2wechat-conversion-tool-create-draft-verify.json exists and contains a success: true or verified: true fieldcontent field contains inline-styled wechat html (not plain text, not local paths)user knows it failed when: