@gobing-ai/knowledge-kit 0.0.22 → 0.0.24

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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +137 -59
  3. package/package.json +4 -4
  4. package/plugins/generations/content-gen/dist/index.js +97 -45
  5. package/plugins/generations/core-facts-gen/dist/index.js +106 -57
  6. package/plugins/generations/daily-article-gen/dist/index.js +105 -56
  7. package/plugins/generations/dailynews-gen/dist/index.js +235 -57
  8. package/plugins/generations/dailynews-gen/src/index.ts +67 -2
  9. package/plugins/generations/dailynews-gen/src/script-builder.ts +36 -15
  10. package/plugins/generations/dailynews-gen/src/script-plan.ts +142 -0
  11. package/plugins/generations/episode-plan-gen/dist/index.js +115 -66
  12. package/plugins/generations/gemini-tts/README.md +125 -0
  13. package/plugins/generations/gemini-tts/dist/index.js +23348 -0
  14. package/plugins/generations/gemini-tts/package.json +16 -0
  15. package/plugins/generations/gemini-tts/plugin.json +6 -0
  16. package/plugins/generations/gemini-tts/profiles.json +3 -0
  17. package/plugins/generations/gemini-tts/src/gemini-client.ts +220 -0
  18. package/plugins/generations/gemini-tts/src/index.ts +33 -0
  19. package/plugins/generations/gemini-tts/src/voice-create.ts +180 -0
  20. package/plugins/generations/gemini-tts/tsconfig.json +8 -0
  21. package/plugins/generations/image-gen/dist/index.js +338 -117
  22. package/plugins/generations/image-gen/presets/formats/daily-news-cover.yaml +19 -0
  23. package/plugins/generations/image-gen/presets/formats/xhs-card.yaml +55 -0
  24. package/plugins/generations/image-gen/presets/styles/daily-news.yaml +9 -0
  25. package/plugins/generations/image-gen/presets/styles/xhs-knowledge.yaml +7 -0
  26. package/plugins/generations/image-gen/src/index.ts +148 -14
  27. package/plugins/generations/image-gen/src/job.ts +30 -0
  28. package/plugins/generations/image-gen/src/providers/errors.ts +91 -0
  29. package/plugins/generations/image-gen/src/providers/types.ts +79 -0
  30. package/plugins/generations/news-report-gen/dist/index.js +136 -55
  31. package/plugins/generations/news-report-gen/src/index.ts +57 -1
  32. package/plugins/generations/voice-gen/dist/index.js +121 -69
  33. package/plugins/generations/voice-gen/package.json +3 -0
  34. package/plugins/generations/voice-gen/src/index.ts +2 -1
  35. package/plugins/generations/voice-gen/src/voicebox-client.ts +2 -1
  36. package/plugins/ingestions/aihot-ingest/dist/index.js +103 -53
  37. package/plugins/ingestions/horizon-ingest/dist/index.js +102 -53
  38. package/plugins/ingestions/karakeep-local/dist/index.js +273 -224
  39. package/plugins/ingestions/last30days-ingest/dist/index.js +97 -48
  40. package/plugins/ingestions/web-search/dist/index.js +121 -72
  41. package/plugins/kk/config.example.yaml +13 -0
  42. package/plugins/kk/plugin.json +1 -1
  43. package/plugins/kk/scripts/daily-voice-style.zh.md +52 -0
  44. package/plugins/kk/scripts/kk-workflow-stages.ts +438 -17
  45. package/plugins/kk/skills/publish/SKILL.md +24 -1
  46. package/plugins/kk/skills/xhs-images/SKILL.md +89 -0
  47. package/plugins/kk/skills/xhs-images/references/card-layouts.md +35 -0
  48. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +164 -13
  49. package/plugins/publishings/emdash-pub/dist/index.js +94 -45
  50. package/plugins/publishings/infoq-pub/dist/index.js +94 -45
  51. package/plugins/publishings/juejin-pub/dist/index.js +94 -45
  52. package/plugins/publishings/medium-pub/dist/index.js +94 -45
  53. package/plugins/publishings/podcast-pub/dist/index.js +115 -55
  54. package/plugins/publishings/podcast-pub/src/map.ts +30 -10
  55. package/plugins/publishings/podcast-pub/src/show-notes.ts +6 -1
  56. package/plugins/publishings/qiita-pub/dist/index.js +94 -45
  57. package/plugins/publishings/substack-pub/dist/index.js +94 -45
  58. package/plugins/publishings/surfdash-pub/dist/index.js +99 -63
  59. package/plugins/publishings/surfdash-pub/src/index.ts +10 -1
  60. package/plugins/publishings/wechatmp-pub/dist/index.js +94 -45
  61. package/plugins/publishings/x-pub/dist/index.js +94 -45
  62. package/plugins/publishings/xhs-pub/dist/index.js +8908 -229
  63. package/plugins/publishings/xhs-pub/package.json +1 -0
  64. package/plugins/publishings/xhs-pub/plugin.json +1 -1
  65. package/plugins/publishings/xhs-pub/src/automation-guard.ts +128 -0
  66. package/plugins/publishings/xhs-pub/src/index.ts +753 -24
  67. package/plugins/publishings/xhs-pub/src/profile-check.ts +133 -0
  68. package/plugins/publishings/zenn-pub/dist/index.js +94 -45
  69. package/plugins/sp/lib/env.ts +46 -0
  70. package/plugins/sp/lib/inline-run.generated.mjs +1489 -0
  71. package/plugins/sp/scripts/feature-verification-steps.ts +275 -0
  72. package/plugins/sp/scripts/inline-run-setup.ts +571 -47
  73. package/plugins/sp/scripts/wrapup-drift-probe.mjs +190 -0
  74. package/plugins/sp/scripts/wrapup-drift-probe.ts +258 -0
  75. package/plugins/sp/scripts/wrapup-steps.mjs +77 -6
  76. package/plugins/sp/scripts/wrapup-steps.ts +91 -5
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: xhs-images
3
+ description: >-
4
+ Use the xhs-images skill to turn source content into an ordered Xiaohongshu
5
+ card series — "make XHS cards from this article", "generate a card series",
6
+ "turn these notes into 小红书图文卡片". Plans N ordered cards (cover, content,
7
+ closing) grounded in the source, renders them through the image-gen executor
8
+ with the xhs-card/xhs-knowledge presets, packages a publishable Content, and
9
+ hands off to kk:publish channel xhs. Never publishes directly.
10
+ ---
11
+
12
+ # xhs-images — source-grounded XHS card series (plan → render → package → handoff)
13
+
14
+ Adapted from `baoyu-xhs-images` (MIT © Jim Liu) — layout, typography, and
15
+ consistency guidance only. Not imported: backend selection, batch/parallel
16
+ generation, `EXTEND.md` chain. Layout catalog:
17
+ `references/card-layouts.md`. Presets: `xhs-card` (format) +
18
+ `xhs-knowledge` (style). Rendering always goes through
19
+ `kk executor run image-gen` — this skill holds no transport.
20
+
21
+ ## Inputs
22
+
23
+ | Input | Required | Notes |
24
+ | ----- | -------- | ----- |
25
+ | source file (md/txt) | yes | read-only; never written or modified |
26
+ | `--out <dir>` | yes | fresh dir = new plan; existing dir = resume |
27
+ | `--count <n>` | no | 1–10; default: cover + one card per major point, capped at 10 |
28
+ | `--style <name>` | no | default `xhs-knowledge` |
29
+ | `--live` | no | passed through to kk:publish as the operator's live request |
30
+
31
+ `--count` above `min(10, image.max_images)` is refused, naming
32
+ `KK_IMAGE_MAX_IMAGES` / `image.max_images`.
33
+
34
+ ## Procedure
35
+
36
+ 1. **Plan** — read the source; produce ordered cards: card 01 `role: cover`,
37
+ then `content` cards in source order, then an optional `closing` card. Each
38
+ card records `index`, `role`, `sourcePoint` (verbatim heading or sentence),
39
+ `copy` (short Chinese on-card text), `layout` (from the reference catalog),
40
+ `subject` (visual scene). Write `<out>/cards.json` with
41
+ `{ source, cards: [{ index, role, sourcePoint, copy, layout, promptPath,
42
+ imagePath, status: 'pending' }] }`.
43
+ 2. **Envelopes** — for each card write `<out>/cards/NN.in.json`: the `image-gen`
44
+ envelope with `job: free`, `format: xhs-card`, `style`, `vars` (role,
45
+ layout, headline, points, subject, language 简体中文, index), `provider`,
46
+ `model`, `maxImages: 1`, `outputStem: card-NN`. For NN ≥ 02 add
47
+ `referenceImages: [<cover path>]` **only when** `kk:image-authoring`
48
+ `references/providers.md` marks the provider/model reference-capable.
49
+ 3. **Dry-run + one confirmation** — run each envelope with `KK_IMAGE_DRY_RUN=1`;
50
+ save the emitted `Content.body` as `<out>/cards/NN.prompt.txt` (the exact
51
+ composed prompt; it must equal `ImageArtifact.prompt` after render). Resolve
52
+ provider/model via `kk:image-authoring`'s preference chain (arguments >
53
+ env > `$KK_CONFIG` > defaults). Show **one** spend preview for the series
54
+ (provider, model, style, N, cover prompt, per-card copy list, plus the
55
+ consistency path: cover-reference chain when the provider is
56
+ reference-capable per `kk:image-authoring` `references/providers.md`, or an
57
+ explicit weaker-consistency disclosure when it is not) and follow the
58
+ `kk:image-authoring` Confirmation policy once before any paid render.
59
+ 4. **Render sequentially** — card 01, then 02..N, each via
60
+ `kk executor run image-gen --in cards/NN.in.json --out cards/NN.json`.
61
+ On the first failure: stop spending, mark the card `failed`, later cards
62
+ stay `pending`, report the retry.
63
+ 5. **Package** — write `<out>/content.json`: `title` (XHS title, ≤ 20 chars),
64
+ `body` (Chinese note + hashtags, ≤ 1000 chars), `format: 'text'`,
65
+ `metadata.images` = each card's `cards/NN.json` `metadata.images[0]` in
66
+ index order, `metadata.publishStatus: 'draft'`. Packaging refuses while any
67
+ card is not `rendered` or its prompt/image file is missing.
68
+ 6. **Handoff** — never spawn `xhs-pub` directly. Hand `content.json` to
69
+ `kk:publish` channel `xhs`; pass `--live` through as the operator's live
70
+ request.
71
+
72
+ ## Resume
73
+
74
+ Re-running with the same `--out` reuses the existing `cards.json` and
75
+ `cards/NN.in.json` (no replanning) and renders only cards without an existing
76
+ image file — check existence via the artifact path recorded in the prior run's
77
+ `cards/NN.json` (or a `cards/card-NN-*` glob), since the provider decides the
78
+ extension. Replanning requires a new `--out`.
79
+
80
+ ## Checks and limits
81
+
82
+ - N ≤ 10 and ≤ `image.max_images`; refusal names the cap env/key.
83
+ - Source file is never written.
84
+ - One spend confirmation per series; renders strictly sequential.
85
+ - Card text stays minimal (headline ≤ 12 chars, 2–4 short points) — the model
86
+ renders text, so fewer words means fewer rendering defects.
87
+ - Consistency: cover prompt fixes palette/typography wording; per-card prompts
88
+ reuse it verbatim and vary only layout/points/subject. When the provider is
89
+ reference-capable, cards ≥ 02 reference the rendered cover.
@@ -0,0 +1,35 @@
1
+ # XHS card layouts — adapted from `baoyu-xhs-images`
2
+
3
+ Source: `vendors/baoyu-skills/skills/baoyu-xhs-images/SKILL.md` (MIT © Jim Liu).
4
+ Adapted: layout names, typography rules, consistency guidance. Not imported:
5
+ backend selection, batch/parallel generation, `EXTEND.md` chain.
6
+
7
+ ## Layout catalog
8
+
9
+ | Layout | Use when | On-card structure |
10
+ | ------ | -------- | ----------------- |
11
+ | `cover` | Card 01 | Big headline (≤ 12 chars), one-line hook, small series label |
12
+ | `definition` | One concept to explain | Term + one-sentence definition + 2 short points |
13
+ | `list` | 3–4 parallel items | Numbered short lines, uniform phrasing |
14
+ | `comparison` | Two options | Two columns, 2–3 contrast points each |
15
+ | `steps` | Ordered process | 3–4 numbered steps, verb-first |
16
+ | `quote` | Key sentence from source | Large quote text + attribution line |
17
+ | `summary` | Closing card | 3 takeaways + one CTA line |
18
+
19
+ ## Typography rules
20
+
21
+ - Headline: extra-large bold; ≤ 12 Chinese characters.
22
+ - Points: short lines, 2–4 per card, generous line spacing.
23
+ - Keep total on-card text minimal — the model renders text, so fewer words
24
+ means fewer rendering defects.
25
+ - Language default: 简体中文 (`xhs-card` format `language` var).
26
+
27
+ ## Consistency rules
28
+
29
+ - Compose the cover prompt first; fix palette/typography wording there.
30
+ - Per-card prompts reuse that wording verbatim; vary only
31
+ layout/points/subject (via the `xhs-card` template variables).
32
+ - When the provider/model is reference-capable per `kk:image-authoring`
33
+ `references/providers.md`, cards ≥ 02 set `referenceImages: [<cover path>]`.
34
+ - Renders are strictly sequential (card 01 → N) so the cover reference exists
35
+ before dependents render.
@@ -5,7 +5,8 @@ kind: state-machine
5
5
  description: >-
6
6
  Daily AI news end-to-end pipeline (F61 destination): collect (fan-in --source: aihot + last30days
7
7
  + Horizon) → core facts → plan → content QC (filter, done-no-content terminal) → article + cover
8
- + script + review + voice → publish (surfdash + podcast, full|partial|none routing) → locales
8
+ + script + review + voice → publish (surfdash + podcast, full|partial|none routing) → xhs draft
9
+ (independent, non-blocking target) → locales
9
10
  initialState: prepare
10
11
  terminalStates:
11
12
  - done
@@ -19,7 +20,7 @@ vars:
19
20
  limit: "10"
20
21
  language: "zh"
21
22
  voice_profile: "robin-news"
22
- # 20260917: voice driver switch — voicebox (default, voice-gen) | ominivoice (omni-voice-gen).
23
+ # 20260917: voice driver switch — voicebox (default, voice-gen) | ominivoice (omni-voice-gen) | gemini (gemini-tts).
23
24
  # 20260920: default flipped to voicebox — operator A/B at matched speech rate (~5.2 chars/s)
24
25
  # judged voicebox clearly better (ominivoice carries ~1min dead air over 16min + ASR-garble class).
25
26
  vdriver: "voicebox"
@@ -31,11 +32,45 @@ vars:
31
32
  cursor: ""
32
33
  state_file: ""
33
34
  skip_review: "false"
34
- transcode_mp3: "false"
35
+ # 0171 R1: podcast-pub publishes metadata.mp3Path, so "false" fell back to the WAV render — an
36
+ # enclosure Apple Podcasts rejects and no ID3 tag can ride.
37
+ transcode_mp3: "true"
38
+ # 0171 R3/R4: ID3 artist/album stamped onto the published MP3 (title comes from the article,
39
+ # date from run_date); an empty var omits that tag.
40
+ podcast_artist: "Robin Min"
41
+ podcast_album: "咸话咸说"
35
42
  cover_enabled: "true"
43
+ # F614: the daily cover pins provider+model in vars — each provider has its own look, so the
44
+ # pin is part of the style contract; it stays a var because the right value depends on which
45
+ # credentials the machine has (2026-09-24 run: minimax/image-01).
46
+ cover_provider: "minimax"
47
+ cover_model: "image-01"
48
+ # D6 0166: runtime-fallback opt-in for the cover render — "" (default) keeps strict fail-fast,
49
+ # "true" retries down AUTO_ORDER, anything else is a comma-separated provider list. The empty
50
+ # default keeps F614's pinned look the default; availability trades against it only on opt-in.
51
+ cover_fallback: ""
52
+ # E8: XHS image-card target. Cards are planned from the lead plan item (the same source the
53
+ # cover uses) and rendered through the pinned xhs-card/xhs-knowledge presets. `xhs_live` stays a
54
+ # separate launch-time switch (ADR-022), so an unattended run drafts and never posts live.
55
+ # Default OFF (2026-09-28): XHS warned the operator about automated login/publish activity.
56
+ # `xhs-pub` always launches a headed browser and drives the creator console, and an unattended
57
+ # daily job repeating that is precisely the pattern the platform flags — so the target is
58
+ # operator-opt-in until a reviewed automation policy exists. Do NOT flip this on for a scheduled
59
+ # run; publish XHS deliberately, by hand, and rarely.
60
+ xhs_enabled: "false"
61
+ xhs_cards: "4"
62
+ xhs_style: "xhs-knowledge"
63
+ # Same credential surface as the cover pin: the provider/model must be one this machine can
64
+ # actually reach, and each provider renders its own look.
65
+ xhs_provider: "minimax"
66
+ xhs_model: "image-01"
67
+ xhs_live: "false"
36
68
  plan_max_items: "20"
37
69
  plan_translate_enabled: "true"
38
70
  article_write_enabled: "true"
71
+ # F613 R3: script-write authors the per-item plan the zh builder consumes; "false" routes
72
+ # article/cover straight to script (template build, no plan file is written).
73
+ script_write_enabled: "true"
39
74
  plugins_path: ""
40
75
  # Projected by the publish state from 3-publish/<run_date>_17_publish_publish-status.txt;
41
76
  # guards test this var instead of cat-ing the file (state at decision time).
@@ -160,24 +195,60 @@ states:
160
195
  kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
161
196
 
162
197
  - id: cover
163
- description: "Generate the article cover image via the kk:image-authoring skill (agent.run)"
198
+ description: "Render the daily cover from the lead plan item through the pinned daily-news presets (brief agent.run -> pinned stage render -> normalize, F614)"
164
199
  onEnter:
165
200
  - kind: agent.run
201
+ # A bad or failed brief must never halt the run: the stage falls back to the format's
202
+ # default subject (F614 R4) and keeps the same pinned format/style/provider/model.
203
+ onError: continue
166
204
  options:
167
205
  role: scribe
168
206
  agent: ${vars.agent}
207
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
169
208
  input: |
170
- Invoke the kk:image-authoring skill (generate operation).
171
- Non-interactive step: run every image command with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 — never prompt (cover_enabled is the spend approval).
172
- Command: --format cover --content ${vars.work_dir}/2-article/${vars.run_date}_06_article_article.md --out ${vars.work_dir}/2-cover
173
- Template variables (title, subtitle, topics) come from the article as the skill owns; do not pass --style.
174
- The plugin writes ${vars.work_dir}/2-cover/content.json plus ${vars.work_dir}/2-cover/content-cover-01.<ext> (ext follows the elected provider: minimax → .jpg, google/ark → .png). On failure report the error verbatim and stop — no procedural fallback.
175
- expectFile: ${vars.work_dir}/2-cover/content-cover-01.jpg
209
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
210
+ Take item 1 only (its "title" and "metadata.brief") — the episode's lead story.
211
+ Write one concrete visual scene in English, ≤240 characters, that a viewer would connect to that story: objects, settings, symbols, metaphor.
212
+ No text, letters, numbers, logos, brand marks, flags, or recognizable real people. No style, color, lighting, or composition words — the preset owns those.
213
+ Output strictly valid JSON {"subject": "..."} to ${vars.work_dir}/2-cover/${vars.run_date}_07_cover-brief.json.
214
+ expectFile: ${vars.work_dir}/2-cover/${vars.run_date}_07_cover-brief.json
215
+ allowAppend: false
216
+ - kind: shell
217
+ options:
218
+ command: >-
219
+ kk stage daily-cover "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.cover_provider}" "${vars.cover_model}" "${vars.cover_fallback}"
176
220
  - kind: shell
177
221
  options:
178
222
  command: >-
179
223
  kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
180
224
 
225
+ - id: script-write
226
+ description: "Author the per-item script plan (intro/outro + lead/insight per doc id) from the show-notes plan source via agent.run; onError: continue — a failed plan falls back to templates (F613)"
227
+ onEnter:
228
+ - kind: shell
229
+ options:
230
+ # Spur has no workflow-dir var, so the prompt cannot name the shipped sidecar: this
231
+ # stage snapshots it into the work dir (idempotent; a missing sidecar is a broken
232
+ # install and fails loud). The snapshot also freezes the contract the plan was
233
+ # written against, per run, for audit.
234
+ command: >-
235
+ kk stage daily-script-style "${vars.work_dir}" "${vars.run_date}"
236
+ - kind: agent.run
237
+ # A rejected or failed plan must never halt the unattended daily run: the builder
238
+ # decides plan-vs-template from the file itself and falls back to templates.
239
+ onError: continue
240
+ options:
241
+ role: scribe
242
+ agent: ${vars.agent}
243
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
244
+ input: |
245
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json — a JSON array of docs; use each doc's `id`; don't edit it. Also read the style contract ${vars.work_dir}/2-script/${vars.run_date}_08_script-write_style.md and obey its char caps and banned phrases.
246
+ For each doc write a `lead` (a spoken bridge into the title, not containing it) and an `insight` (why it matters, grounded only in that doc's facts).
247
+ Write an `intro` hooking today's actual themes and an `outro`; no greeting or sign-off — the builder adds both.
248
+ Output only the JSON object {"version":1,"intro":"…","outro":"…","items":[{"id":"…","lead":"…","insight":"…"}]} with one item per doc id to ${vars.work_dir}/2-script/${vars.run_date}_08_script-write_plan.json.
249
+ expectFile: ${vars.work_dir}/2-script/${vars.run_date}_08_script-write_plan.json
250
+ allowAppend: false
251
+
181
252
  - id: script
182
253
  description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
183
254
  onEnter:
@@ -204,7 +275,7 @@ states:
204
275
  kk stage daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
205
276
 
206
277
  - id: generate
207
- description: "Invoke voice driver plugin (vdriver var: ominivoice -> omni-voice-gen, voicebox -> voice-gen) to generate audio"
278
+ description: "Invoke voice driver plugin (vdriver var: ominivoice -> omni-voice-gen, voicebox -> voice-gen, gemini -> gemini-tts) to generate audio"
208
279
  onEnter:
209
280
  - kind: shell
210
281
  options:
@@ -228,12 +299,12 @@ states:
228
299
  kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
229
300
 
230
301
  - id: publish-prep
231
- description: "Merge article Content with audio (podcast assembly facts ride the content options channel for podcast-pub's absorbed assembly; runDate kept for surfdash slug)"
302
+ description: "Merge article Content with the MP3 (rejects a non-.mp3 enclosure) and stamp its ID3 tags (podcast assembly facts ride the content options channel for podcast-pub's absorbed assembly; runDate kept for surfdash slug)"
232
303
  onEnter:
233
304
  - kind: shell
234
305
  options:
235
306
  command: >-
236
- kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
307
+ kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
237
308
 
238
309
  - id: publish-surfdash
239
310
  description: "Publish the merged Content to surfdash (single target); result lands in result.json — surfdash publishes first so show-notes can link the article (task 0118 seam reorder)"
@@ -259,6 +330,41 @@ states:
259
330
  command: >-
260
331
  kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
261
332
 
333
+ - id: xhs-write
334
+ description: "Plan the XHS card series from the lead plan item via agent.run (title + note + N ordered cards), then render it through the pinned xhs-card/xhs-knowledge presets (E8); onError: continue — a failed plan or render skips the XHS target, never the run"
335
+ onEnter:
336
+ - kind: agent.run
337
+ # XHS is an independent, non-blocking target: a bad or failed plan must never halt the run.
338
+ # The render stage still fails loud; the publish state then skips naming the missing series.
339
+ onError: continue
340
+ options:
341
+ role: scribe
342
+ agent: ${vars.agent}
343
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
344
+ input: |
345
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
346
+ Take item 1 only (its "title" and "metadata.brief") — the lead story.
347
+ Write a Xiaohongshu note: "title" (≤20 chars) and "body" (Chinese note ≤1000 chars, ending with 3-5 hashtags).
348
+ Write exactly ${vars.xhs_cards} cards: card 1 role "cover", the rest "content", except a final "closing". Each card carries "role", "copy" (Chinese on-card headline ≤12 chars), "points" (2-4 short Chinese bullets), "layout", "subject" (one concrete English visual scene), "sourcePoint" (the lead-story fact that card uses).
349
+ Ground every "copy", "points" and "sourcePoint" in that story alone — no invented facts. No text, letters, numbers, logos or real people in "subject".
350
+ Output strictly valid JSON {"title": "...", "body": "...", "cards": [...]} to ${vars.work_dir}/2-xhs/${vars.run_date}_18_xhs_plan.json.
351
+ expectFile: ${vars.work_dir}/2-xhs/${vars.run_date}_18_xhs_plan.json
352
+ allowAppend: false
353
+ - kind: shell
354
+ onError: continue
355
+ options:
356
+ command: >-
357
+ kk stage daily-xhs-render "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.xhs_cards}" "${vars.xhs_style}" "${vars.xhs_provider}" "${vars.xhs_model}"
358
+
359
+ - id: xhs-publish
360
+ description: "Publish the packaged card series to XHS — draft, or live only when xhs_live=true (ADR-022); independent and non-blocking: its outcome never enters publish_status. xhs-pub itself enforces a headless session preflight and a 24h publish/check cap (ADR-025), so a sessionless or capped run fails soft with a named reason and the run continues"
361
+ onEnter:
362
+ - kind: shell
363
+ onError: continue
364
+ options:
365
+ command: >-
366
+ kk stage daily-publish-xhs "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.xhs_live}"
367
+
262
368
  - id: publish
263
369
  description: "Classify the sequenced surfdash → podcast outcomes full|partial|none into publish-status.txt (exit 0 in all three — routing is transition-owned; task 0118: per-target classification over the merged result.json)"
264
370
  onEnter:
@@ -416,18 +522,43 @@ transitions:
416
522
  options:
417
523
  command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/${vars.run_date}_07_cover_cover.png"'
418
524
 
525
+ # F613 R3: first match wins — the cover edge stays above script-write so the cover still
526
+ # generates before the plan is authored; script-write sits above the always `-> script` edges
527
+ # and skips itself when disabled, non-zh, or the plan/YAML already exists (resume cost).
528
+ - from: article
529
+ to: script-write
530
+ description: "script_write_enabled, zh, and neither today's plan nor VoiceScript exists -> author the script plan"
531
+ guard:
532
+ kind: shell
533
+ options:
534
+ command: 'test "${vars.script_write_enabled}" = "true" && test "${vars.language}" = "zh" && test ! -f "${vars.work_dir}/2-script/${vars.run_date}_08_script-write_plan.json" && test ! -f "${vars.work_dir}/2-script/${vars.run_date}_08_script_voicescript.yaml"'
535
+
419
536
  - from: article
420
537
  to: script
421
538
  description: "cover disabled or today's cover cached -> skip to script"
422
539
  guard:
423
540
  kind: always
424
541
 
542
+ - from: cover
543
+ to: script-write
544
+ description: "script_write_enabled, zh, and neither today's plan nor VoiceScript exists -> author the script plan"
545
+ guard:
546
+ kind: shell
547
+ options:
548
+ command: 'test "${vars.script_write_enabled}" = "true" && test "${vars.language}" = "zh" && test ! -f "${vars.work_dir}/2-script/${vars.run_date}_08_script-write_plan.json" && test ! -f "${vars.work_dir}/2-script/${vars.run_date}_08_script_voicescript.yaml"'
549
+
425
550
  - from: cover
426
551
  to: script
427
552
  description: "Cover generated -> build broadcast script"
428
553
  guard:
429
554
  kind: always
430
555
 
556
+ - from: script-write
557
+ to: script
558
+ description: "Script plan authored (ok or failed: onError continue) -> build the broadcast script"
559
+ guard:
560
+ kind: always
561
+
431
562
  - from: script
432
563
  to: wrap-docs
433
564
  description: "skip_review enabled -> bypass HITL review"
@@ -519,6 +650,26 @@ transitions:
519
650
  guard:
520
651
  kind: always
521
652
 
653
+ - from: publish-podcast
654
+ to: xhs-write
655
+ description: "xhs_enabled -> plan and render the XHS card series from the lead plan item; first-match-wins, so this sits above the classification edge (XHS is independent and never gates publish_status)"
656
+ guard:
657
+ kind: shell
658
+ options:
659
+ command: 'test "${vars.xhs_enabled}" = "true"'
660
+
661
+ - from: xhs-write
662
+ to: xhs-publish
663
+ description: "Card series planned and rendered (or failed: onError continue) -> publish the XHS draft"
664
+ guard:
665
+ kind: always
666
+
667
+ - from: xhs-publish
668
+ to: publish
669
+ description: "XHS dispatched (or skipped: onError continue) -> classify the surfdash/podcast outcomes; XHS contributes no target to the classification"
670
+ guard:
671
+ kind: always
672
+
522
673
  - from: publish-podcast
523
674
  to: publish
524
675
  description: "Podcast dispatched -> classify the per-target outcomes full|partial|none"