claude-plugin-wordpress-manager 2.12.2 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -3
- package/CHANGELOG.md +55 -0
- package/docs/GUIDE.md +240 -1
- package/docs/VALIDATION.md +341 -0
- package/docs/plans/2026-03-02-content-framework-architecture.md +612 -0
- package/docs/plans/2026-03-02-content-framework-strategic-reflections.md +228 -0
- package/docs/plans/2026-03-02-content-intelligence-phase2.md +560 -0
- package/docs/plans/2026-03-02-content-pipeline-phase1.md +456 -0
- package/docs/plans/2026-03-02-editorial-calendar-phase3.md +490 -0
- package/docs/validation/.gitkeep +0 -0
- package/docs/validation/dashboard.html +286 -0
- package/docs/validation/results.json +1705 -0
- package/package.json +12 -3
- package/scripts/run-validation.mjs +1132 -0
- package/servers/wp-rest-bridge/build/server.js +16 -5
- package/servers/wp-rest-bridge/build/tools/index.js +0 -9
- package/servers/wp-rest-bridge/build/tools/plugin-repository.js +23 -31
- package/servers/wp-rest-bridge/build/tools/schema.js +10 -2
- package/servers/wp-rest-bridge/build/tools/unified-content.js +10 -2
- package/servers/wp-rest-bridge/build/wordpress.d.ts +0 -3
- package/servers/wp-rest-bridge/build/wordpress.js +16 -98
- package/servers/wp-rest-bridge/package.json +1 -0
- package/skills/wp-analytics/SKILL.md +153 -0
- package/skills/wp-analytics/references/signals-feed-schema.md +417 -0
- package/skills/wp-content-pipeline/SKILL.md +461 -0
- package/skills/wp-content-pipeline/references/content-brief-schema.md +377 -0
- package/skills/wp-content-pipeline/references/site-config-schema.md +431 -0
- package/skills/wp-editorial-planner/SKILL.md +262 -0
- package/skills/wp-editorial-planner/references/editorial-schema.md +268 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wp-content-pipeline
|
|
3
|
+
description: This skill should be used when the user asks to "publish a brief",
|
|
4
|
+
"process content briefs", "publish from GenCorpComm", "run the content pipeline",
|
|
5
|
+
"create a brief", "list pending briefs", or mentions publishing Gen* output
|
|
6
|
+
to WordPress. Orchestrates the flow from structured brief files through
|
|
7
|
+
WordPress publishing and multi-channel distribution.
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# WordPress Content Pipeline Skill
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The content pipeline orchestrates a structured workflow from content brief files (`.brief.md`) through WordPress publishing and multi-channel distribution. Briefs are authored by Gen* skills (GenCorpComm, GenMarketing, GenSignal, GenBrand) or created manually, then placed in `.content-state/pipeline-active/` for processing. The pipeline reads each brief's frontmatter, merges it with site configuration defaults, validates quality gates, publishes to WordPress via MCP tools, distributes to social and email channels, and archives the completed brief. All state is tracked in the `.content-state/` directory.
|
|
16
|
+
|
|
17
|
+
## When to Use
|
|
18
|
+
|
|
19
|
+
- User has a content brief to publish to WordPress
|
|
20
|
+
- User ran GenCorpComm (or another Gen* skill) and wants to push output to WordPress
|
|
21
|
+
- User asks to check, list, or review pending briefs
|
|
22
|
+
- User wants to create a new brief manually
|
|
23
|
+
- User asks to distribute a published post to social channels
|
|
24
|
+
- User mentions "content pipeline", "brief pipeline", or "publish from brief"
|
|
25
|
+
|
|
26
|
+
## Pipeline Workflow
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
SCAN → CONFIG → VALIDATE → PUBLISH → DISTRIBUTE → UPDATE → ARCHIVE
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each step reads specific files, calls specific tools, and has defined decision points. If any step fails or requires user input, the pipeline stops and reports status -- it never silently skips steps.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Step 1: SCAN
|
|
37
|
+
|
|
38
|
+
**What it does:** Discovers brief files that are ready for processing.
|
|
39
|
+
|
|
40
|
+
**Files read:**
|
|
41
|
+
- `.content-state/pipeline-active/*.brief.md` -- all brief files in the active directory
|
|
42
|
+
|
|
43
|
+
**Procedure:**
|
|
44
|
+
1. List all `.brief.md` files in `.content-state/pipeline-active/`
|
|
45
|
+
2. Read the YAML frontmatter of each file
|
|
46
|
+
3. Filter for briefs with `status: ready`
|
|
47
|
+
4. Present the list to the user with key details for each brief:
|
|
48
|
+
- `brief_id`
|
|
49
|
+
- `content.title`
|
|
50
|
+
- `target.site_id`
|
|
51
|
+
- `target.content_type`
|
|
52
|
+
- `distribution.channels`
|
|
53
|
+
- `created` date
|
|
54
|
+
|
|
55
|
+
**Decision points:**
|
|
56
|
+
- If no `.brief.md` files exist in `pipeline-active/` → report "No briefs found" and stop
|
|
57
|
+
- If files exist but none have `status: ready` → list all briefs with their current status (`draft`, `published`, etc.) and stop
|
|
58
|
+
- If multiple briefs are `ready` → list all and ask user which to process (or process all sequentially if user confirms)
|
|
59
|
+
|
|
60
|
+
**Output format:**
|
|
61
|
+
```
|
|
62
|
+
Pipeline Scan Results:
|
|
63
|
+
1. BRF-2026-014 — "Acqua di Cactus: La Rivoluzione" → opencactus (post) [linkedin, twitter]
|
|
64
|
+
2. BRF-2026-015 — "Summer Campaign Launch" → opencactus (page) [mailchimp]
|
|
65
|
+
|
|
66
|
+
Which brief to process? (enter number, or "all")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Step 2: CONFIG
|
|
72
|
+
|
|
73
|
+
**What it does:** Loads site configuration and merges it with brief values.
|
|
74
|
+
|
|
75
|
+
**Files read:**
|
|
76
|
+
- `.content-state/{site_id}.config.md` -- site configuration for the brief's target site
|
|
77
|
+
|
|
78
|
+
**Procedure:**
|
|
79
|
+
1. Read `target.site_id` from the selected brief (e.g., `opencactus`)
|
|
80
|
+
2. Load `.content-state/{site_id}.config.md` (e.g., `.content-state/opencactus.config.md`)
|
|
81
|
+
3. Parse the YAML frontmatter for site defaults, brand context, channel config, and SEO settings
|
|
82
|
+
4. Apply the override hierarchy -- brief values take precedence over site config defaults:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Brief value > Site config default > System default
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Specific merge rules:**
|
|
89
|
+
| Field | Brief source | Config fallback |
|
|
90
|
+
|-------|-------------|-----------------|
|
|
91
|
+
| `content_type` | `brief.target.content_type` | `config.defaults.content_type` |
|
|
92
|
+
| `status` | `brief.target.status` | `config.defaults.status` |
|
|
93
|
+
| `categories` | `brief.target.categories` | `config.defaults.categories` |
|
|
94
|
+
| `author` | `brief.content.author` | `config.defaults.author` |
|
|
95
|
+
| `schema_type` | `brief.seo.schema_type` | `config.seo.default_schema` |
|
|
96
|
+
| `seo_score_min` | `brief.gates.seo_score_min` | `config.seo.min_score` |
|
|
97
|
+
|
|
98
|
+
5. Read the config body section (Markdown after frontmatter) for brand context -- this informs content adaptation in the DISTRIBUTE step
|
|
99
|
+
|
|
100
|
+
**Decision points:**
|
|
101
|
+
- If config file does not exist for the `site_id` → stop and report: "No site config found for '{site_id}'. Create `.content-state/{site_id}.config.md` first. See `references/site-config-schema.md` for the format."
|
|
102
|
+
- If config is missing required fields (`site_id`, `site_url`, `brand.tone`, `brand.language`) → stop and report which fields are missing
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Step 3: VALIDATE
|
|
107
|
+
|
|
108
|
+
**What it does:** Checks quality gates before publishing.
|
|
109
|
+
|
|
110
|
+
Note: Even though the brief has `status: ready`, re-evaluate gates at publish time as a safety check -- briefs may be manually promoted without automated gate validation.
|
|
111
|
+
|
|
112
|
+
**Procedure:**
|
|
113
|
+
1. Verify all required brief fields are present:
|
|
114
|
+
- `brief_id`, `status`, `source.skill`, `target.site_id`, `target.content_type`, `content.title`
|
|
115
|
+
2. Verify the Markdown body (after frontmatter) is non-empty
|
|
116
|
+
3. Check quality gates from the merged configuration:
|
|
117
|
+
|
|
118
|
+
**Gate: `require_review`**
|
|
119
|
+
- If `gates.require_review: true` → present the full brief summary to the user and wait for explicit approval before proceeding
|
|
120
|
+
- Display: title, excerpt, target site, content type, categories, tags, distribution channels, and a preview of the first 500 characters of the body
|
|
121
|
+
- User must confirm with "approve" or "yes" to continue
|
|
122
|
+
|
|
123
|
+
**Gate: SEO and Readability**
|
|
124
|
+
- If `gates.seo_score_min` or `gates.readability_min` are set → evaluate the content quality based on:
|
|
125
|
+
- Keyword usage and placement (title, first paragraph, headings, body density)
|
|
126
|
+
- Meta description length and keyword inclusion
|
|
127
|
+
- Heading structure (single H1, logical H2/H3 hierarchy)
|
|
128
|
+
- Internal link presence (from `seo.internal_links`)
|
|
129
|
+
- Content length and paragraph structure for readability
|
|
130
|
+
- These are evaluated by Claude based on content quality analysis, not automated scoring tools
|
|
131
|
+
- Report an estimated score with reasoning
|
|
132
|
+
|
|
133
|
+
4. Validate distribution channels against site config:
|
|
134
|
+
- For each channel in `brief.distribution.channels`, check that `config.channels.{channel}.enabled: true`
|
|
135
|
+
- If a brief requests a disabled channel → warn user and remove that channel from the processing list
|
|
136
|
+
|
|
137
|
+
**Decision points:**
|
|
138
|
+
- If required fields are missing → stop, report which fields are missing, keep brief as `status: ready`
|
|
139
|
+
- If body is empty → stop, report "Brief body is empty -- add content before publishing"
|
|
140
|
+
- If `require_review: true` and user does not approve → stop, keep brief as `status: ready`
|
|
141
|
+
- If SEO/readability gates fail → report scores and reasoning, ask user whether to proceed anyway or edit first
|
|
142
|
+
- If all gates pass → proceed to PUBLISH
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Step 4: PUBLISH
|
|
147
|
+
|
|
148
|
+
**What it does:** Creates the content on WordPress using MCP tools.
|
|
149
|
+
|
|
150
|
+
**MCP tools used:**
|
|
151
|
+
- `create_content` -- create the WordPress post/page
|
|
152
|
+
- `assign_terms_to_content` -- set categories and tags
|
|
153
|
+
- `sd_inject` -- inject Schema.org structured data (if applicable)
|
|
154
|
+
- `switch_site` -- switch to the target site (if multi-site)
|
|
155
|
+
|
|
156
|
+
**Procedure:**
|
|
157
|
+
|
|
158
|
+
1. **Switch site** (if needed):
|
|
159
|
+
```
|
|
160
|
+
switch_site:
|
|
161
|
+
site_id: {target.site_id}
|
|
162
|
+
```
|
|
163
|
+
If `switch_site` fails (site_id not found in WP_SITES_CONFIG) → stop, report: "Site `{site_id}` not configured. Add credentials to WP_SITES_CONFIG before publishing."
|
|
164
|
+
|
|
165
|
+
2. **Check for slug duplicates**: Before creating content, check for existing content with the same slug using `list_content` with a search parameter matching the intended slug. If a match is found, report to the user and ask before creating a duplicate.
|
|
166
|
+
|
|
167
|
+
3. **Create content as draft first** (safety rule -- always draft first):
|
|
168
|
+
```
|
|
169
|
+
create_content:
|
|
170
|
+
content_type: {target.content_type} # e.g., "post"
|
|
171
|
+
title: {content.title}
|
|
172
|
+
content: {body from brief markdown}
|
|
173
|
+
excerpt: {content.excerpt}
|
|
174
|
+
status: "draft" # always draft initially
|
|
175
|
+
slug: {auto-generated from title}
|
|
176
|
+
```
|
|
177
|
+
Record the returned `post_id` and `post_url`.
|
|
178
|
+
|
|
179
|
+
4. **Assign taxonomy terms**:
|
|
180
|
+
```
|
|
181
|
+
assign_terms_to_content:
|
|
182
|
+
content_type: {target.content_type}
|
|
183
|
+
id: {post_id}
|
|
184
|
+
categories: {target.categories} # e.g., ["sustainability", "innovation"]
|
|
185
|
+
tags: {target.tags} # e.g., ["cactus-water", "zero-calorie"]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
5. **Inject structured data** (if `seo.schema_type` is defined):
|
|
189
|
+
```
|
|
190
|
+
sd_inject:
|
|
191
|
+
post_id: {post_id}
|
|
192
|
+
schema_type: {seo.schema_type} # e.g., "Article"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
6. **Present draft to user for final confirmation**:
|
|
196
|
+
- Show: title, post URL (draft), categories, tags, schema type
|
|
197
|
+
- Ask: "Content created as draft. Publish now?"
|
|
198
|
+
|
|
199
|
+
7. **Update to target status** (after user confirms, or if `target.status` is explicitly `publish` in the brief):
|
|
200
|
+
```
|
|
201
|
+
update_content:
|
|
202
|
+
content_type: {target.content_type}
|
|
203
|
+
id: {post_id}
|
|
204
|
+
status: {target.status} # "publish", "future", etc.
|
|
205
|
+
scheduled_date: {target.scheduled_date} # only if status is "future"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Decision points:**
|
|
209
|
+
- If `create_content` fails → stop, report the error, keep brief as `status: ready`
|
|
210
|
+
- If user declines to publish after draft → keep as draft on WordPress, keep brief as `status: ready`, report draft URL
|
|
211
|
+
- If `target.status` is explicitly `publish` in the brief → still create as draft first, then update to publish after showing user the draft URL (unless the brief also has `gates.require_review: false`, in which case proceed automatically)
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Step 5: DISTRIBUTE
|
|
216
|
+
|
|
217
|
+
**What it does:** Distributes the published content to external channels.
|
|
218
|
+
|
|
219
|
+
**Prerequisite:** Step 4 must have completed successfully with the content in `publish` status on WordPress.
|
|
220
|
+
|
|
221
|
+
**Procedure:**
|
|
222
|
+
|
|
223
|
+
1. Check if `distribution.channels` is non-empty. If empty, skip to Step 6.
|
|
224
|
+
|
|
225
|
+
2. For each channel in `distribution.channels`:
|
|
226
|
+
|
|
227
|
+
a. **Verify channel is enabled** in site config (`config.channels.{channel}.enabled: true`). Skip disabled channels with a warning.
|
|
228
|
+
|
|
229
|
+
b. **Adapt content** if `distribution.adapt_format: true`:
|
|
230
|
+
- Use the `wp-content-repurposing` skill patterns to transform the content for the target channel
|
|
231
|
+
- Read `config.channels.{channel}.format` for the adaptation style
|
|
232
|
+
- Read `config.brand` block for tone and language guidance
|
|
233
|
+
|
|
234
|
+
c. **Respect scheduling**: if `distribution.schedule_offset_hours > 0`, note the delayed publish time for each channel post.
|
|
235
|
+
|
|
236
|
+
3. **Channel-specific MCP tool calls:**
|
|
237
|
+
|
|
238
|
+
**LinkedIn** (`config.channels.linkedin`):
|
|
239
|
+
```
|
|
240
|
+
li_create_post:
|
|
241
|
+
profile_id: {config.channels.linkedin.profile_id}
|
|
242
|
+
text: {adapted content for LinkedIn}
|
|
243
|
+
link_url: {published WordPress URL}
|
|
244
|
+
visibility: "PUBLIC"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Twitter** (`config.channels.twitter`):
|
|
248
|
+
- For short content or `format: concise`:
|
|
249
|
+
```
|
|
250
|
+
tw_create_tweet:
|
|
251
|
+
text: {adapted content for Twitter}
|
|
252
|
+
url: {published WordPress URL}
|
|
253
|
+
```
|
|
254
|
+
- For long content or `format: thread`:
|
|
255
|
+
```
|
|
256
|
+
tw_create_thread:
|
|
257
|
+
tweets: [{thread segments adapted from content}]
|
|
258
|
+
url: {published WordPress URL}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Buffer** (`config.channels.buffer`):
|
|
262
|
+
Compute `scheduled_at` as: current date at `config.cadence.publish_time` plus `distribution.schedule_offset_hours` hours, expressed as UTC ISO 8601 timestamp (e.g., `2026-03-15T14:00:00Z`).
|
|
263
|
+
```
|
|
264
|
+
buf_create_update:
|
|
265
|
+
profile_ids: [{config.channels.buffer.profile_id}]
|
|
266
|
+
text: {adapted content}
|
|
267
|
+
media:
|
|
268
|
+
photo: {featured image URL if available}
|
|
269
|
+
scheduled_at: {ISO 8601 UTC timestamp}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Mailchimp** (`config.channels.mailchimp`):
|
|
273
|
+
```
|
|
274
|
+
mc_create_campaign:
|
|
275
|
+
type: "regular"
|
|
276
|
+
audience_id: {config.channels.mailchimp.audience_id}
|
|
277
|
+
subject: {content.title}
|
|
278
|
+
from_name: {from site brand}
|
|
279
|
+
```
|
|
280
|
+
Convert the brief's Markdown body to HTML before passing to `mc_set_campaign_content`. Use standard Markdown-to-HTML rendering.
|
|
281
|
+
```
|
|
282
|
+
mc_set_campaign_content:
|
|
283
|
+
campaign_id: {returned campaign_id}
|
|
284
|
+
html: {email-formatted content from brief}
|
|
285
|
+
|
|
286
|
+
# STOP — require explicit user confirmation before sending
|
|
287
|
+
mc_send_campaign:
|
|
288
|
+
campaign_id: {campaign_id}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
4. Record the result of each channel distribution (success/failure, post ID or URL, timestamp).
|
|
292
|
+
|
|
293
|
+
**Decision points:**
|
|
294
|
+
- If a channel tool call fails → log the failure, continue with remaining channels, report all failures at the end
|
|
295
|
+
- If Mailchimp is in the channel list → ALWAYS stop and confirm with user before calling `mc_send_campaign`
|
|
296
|
+
- If `schedule_offset_hours > 0` → use scheduling parameters in tool calls rather than immediate posting
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Step 6: UPDATE
|
|
301
|
+
|
|
302
|
+
**What it does:** Updates the brief file with publishing results.
|
|
303
|
+
|
|
304
|
+
**Procedure:**
|
|
305
|
+
|
|
306
|
+
1. Update the brief's YAML frontmatter with:
|
|
307
|
+
```yaml
|
|
308
|
+
status: published
|
|
309
|
+
published_at: {ISO 8601 timestamp of publication}
|
|
310
|
+
post_id: {WordPress post ID}
|
|
311
|
+
post_url: {published WordPress URL}
|
|
312
|
+
distribution_log:
|
|
313
|
+
- channel: linkedin
|
|
314
|
+
status: success
|
|
315
|
+
url: "https://linkedin.com/feed/update/..."
|
|
316
|
+
timestamp: "2026-03-02T12:00:00Z"
|
|
317
|
+
- channel: twitter
|
|
318
|
+
status: success
|
|
319
|
+
tweet_id: "1234567890"
|
|
320
|
+
timestamp: "2026-03-02T12:00:00Z"
|
|
321
|
+
- channel: mailchimp
|
|
322
|
+
status: sent
|
|
323
|
+
campaign_id: "mc_camp_abc123"
|
|
324
|
+
timestamp: "2026-03-02T14:00:00Z"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
2. Write the updated frontmatter back to the brief file in `pipeline-active/` (before archiving).
|
|
328
|
+
|
|
329
|
+
**Decision points:**
|
|
330
|
+
- If some distribution channels failed → still mark brief as `published` (the WordPress publish succeeded), but log failures in `distribution_log` with `status: failed` and error details
|
|
331
|
+
- If WordPress publish itself failed → do not update status, leave as `ready`
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Step 7: ARCHIVE
|
|
336
|
+
|
|
337
|
+
**What it does:** Moves the completed brief from active to archive.
|
|
338
|
+
|
|
339
|
+
**Procedure:**
|
|
340
|
+
|
|
341
|
+
1. Move the brief file from `.content-state/pipeline-active/` to `.content-state/pipeline-archive/`:
|
|
342
|
+
```
|
|
343
|
+
.content-state/pipeline-active/BRF-2026-014.brief.md
|
|
344
|
+
→ .content-state/pipeline-archive/BRF-2026-014.brief.md
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
2. Report the final summary to the user:
|
|
348
|
+
```
|
|
349
|
+
Pipeline Complete:
|
|
350
|
+
Brief: BRF-2026-014
|
|
351
|
+
Title: "Acqua di Cactus: La Rivoluzione Zero-Calorie dalla Sicilia"
|
|
352
|
+
WordPress: https://opencactus.com/acqua-di-cactus-rivoluzione/ (published)
|
|
353
|
+
Distribution:
|
|
354
|
+
- LinkedIn: posted ✓
|
|
355
|
+
- Twitter: posted ✓
|
|
356
|
+
- Mailchimp: sent ✓
|
|
357
|
+
Archived: .content-state/pipeline-archive/BRF-2026-014.brief.md
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Decision points:**
|
|
361
|
+
- If user wants to keep the brief in `pipeline-active/` for further editing → skip archive, report that brief remains active
|
|
362
|
+
- If brief was only partially processed (e.g., published but distribution pending) → keep in `pipeline-active/` until distribution is complete
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Creating a Brief Manually
|
|
367
|
+
|
|
368
|
+
When the user wants to create a brief from scratch (not from a Gen* skill):
|
|
369
|
+
|
|
370
|
+
**Procedure:**
|
|
371
|
+
|
|
372
|
+
1. **Read site config** for defaults:
|
|
373
|
+
- Load `.content-state/{site_id}.config.md`
|
|
374
|
+
- Extract `defaults` block and `brand` block
|
|
375
|
+
|
|
376
|
+
2. **Generate `brief_id`**:
|
|
377
|
+
- Format: `BRF-YYYY-NNN` where `YYYY` is the current year and `NNN` is zero-padded sequential
|
|
378
|
+
- Scan existing briefs in both `pipeline-active/` and `pipeline-archive/` to determine the next sequential number
|
|
379
|
+
- Example: if `BRF-2026-014` is the highest existing ID, the next brief is `BRF-2026-015`
|
|
380
|
+
|
|
381
|
+
3. **Write brief file** to `pipeline-active/`:
|
|
382
|
+
```
|
|
383
|
+
.content-state/pipeline-active/BRF-2026-015.brief.md
|
|
384
|
+
```
|
|
385
|
+
With frontmatter populated from site defaults:
|
|
386
|
+
```yaml
|
|
387
|
+
---
|
|
388
|
+
brief_id: BRF-2026-015
|
|
389
|
+
created: {current ISO 8601 timestamp}
|
|
390
|
+
status: draft
|
|
391
|
+
|
|
392
|
+
source:
|
|
393
|
+
skill: manual
|
|
394
|
+
domain: general
|
|
395
|
+
|
|
396
|
+
target:
|
|
397
|
+
site_id: {user-specified site}
|
|
398
|
+
content_type: {config.defaults.content_type}
|
|
399
|
+
status: {config.defaults.status}
|
|
400
|
+
categories: {config.defaults.categories}
|
|
401
|
+
tags: []
|
|
402
|
+
|
|
403
|
+
content:
|
|
404
|
+
title: "{user-provided title}"
|
|
405
|
+
excerpt: ""
|
|
406
|
+
author: {config.defaults.author}
|
|
407
|
+
|
|
408
|
+
distribution:
|
|
409
|
+
channels: []
|
|
410
|
+
adapt_format: true
|
|
411
|
+
schedule_offset_hours: 0
|
|
412
|
+
|
|
413
|
+
seo:
|
|
414
|
+
focus_keyword: ""
|
|
415
|
+
schema_type: {config.seo.default_schema}
|
|
416
|
+
|
|
417
|
+
gates:
|
|
418
|
+
seo_score_min: {config.seo.min_score}
|
|
419
|
+
readability_min: 60
|
|
420
|
+
require_review: true
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
# {Title}
|
|
424
|
+
|
|
425
|
+
{User fills in content here}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
4. **Inform user**:
|
|
429
|
+
- Brief created at `pipeline-active/BRF-2026-015.brief.md` with `status: draft`
|
|
430
|
+
- User should fill in the content body and any missing fields
|
|
431
|
+
- When ready, set `status: ready` to enable pipeline processing
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Safety Rules
|
|
436
|
+
|
|
437
|
+
- **ALWAYS** create WordPress content as `draft` first, then update to the target status only after user confirmation -- unless the brief explicitly sets `target.status: publish` AND `gates.require_review: false`
|
|
438
|
+
- **NEVER** publish content without showing the user the brief summary first
|
|
439
|
+
- **ALWAYS** confirm with the user before sending Mailchimp email campaigns (`mc_send_campaign`)
|
|
440
|
+
- **ALWAYS** log all distribution actions in the brief's `distribution_log` field
|
|
441
|
+
- **NEVER** activate a distribution channel that is `enabled: false` in the site config, even if the brief requests it
|
|
442
|
+
- **NEVER** modify the brief body content during publishing -- the pipeline publishes what is in the brief, it does not rewrite
|
|
443
|
+
- **ALWAYS** preserve the original brief file (with updated frontmatter) in the archive for audit trail
|
|
444
|
+
- **CHECK** for existing WordPress content with the same slug before creating, to avoid duplicates
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Reference Files
|
|
449
|
+
|
|
450
|
+
- **`references/content-brief-schema.md`** -- complete schema for `.brief.md` files including all frontmatter fields, status lifecycle, and validation rules
|
|
451
|
+
- **`references/site-config-schema.md`** -- complete schema for `.config.md` files including brand, defaults, channels, SEO, and cadence configuration
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Related Skills
|
|
456
|
+
|
|
457
|
+
- **`wp-content`** -- content creation and management (creating posts, pages, taxonomy management)
|
|
458
|
+
- **`wp-content-optimization`** -- SEO and readability enhancement (optimizing content before publishing)
|
|
459
|
+
- **`wp-content-repurposing`** -- multi-format adaptation for distribution (transforming content for social/email channels)
|
|
460
|
+
- **`wp-social-email`** -- distribution channel tools (Mailchimp, Buffer, SendGrid MCP tool reference)
|
|
461
|
+
- **`wp-structured-data`** -- Schema.org markup injection (structured data for rich search results)
|