@gobing-ai/knowledge-kit 0.1.14 → 0.1.15

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 +796 -176
  3. package/package.json +1 -1
  4. package/plugins/generations/content-gen/dist/index.js +2 -2
  5. package/plugins/generations/core-facts-gen/dist/index.js +2 -2
  6. package/plugins/generations/daily-article-gen/dist/index.js +2 -2
  7. package/plugins/generations/dailynews-gen/dist/index.js +72 -9
  8. package/plugins/generations/dailynews-gen/src/index.ts +23 -1
  9. package/plugins/generations/dailynews-gen/src/script-plan.ts +103 -5
  10. package/plugins/generations/episode-plan-gen/dist/index.js +2 -2
  11. package/plugins/generations/gemini-tts/dist/index.js +2 -2
  12. package/plugins/generations/image-gen/dist/index.js +2 -2
  13. package/plugins/generations/news-report-gen/dist/index.js +2 -2
  14. package/plugins/generations/voice-gen/dist/index.js +2 -2
  15. package/plugins/generations/voxcpm-tts/dist/index.js +2 -2
  16. package/plugins/ingestions/aihot-ingest/dist/index.js +2 -2
  17. package/plugins/ingestions/horizon-ingest/dist/index.js +2 -2
  18. package/plugins/ingestions/last30days-ingest/dist/index.js +2 -2
  19. package/plugins/ingestions/web-search/dist/index.js +2 -2
  20. package/plugins/kk/plugin.json +1 -1
  21. package/plugins/kk/scripts/daily-scheduled-run.ts +4 -0
  22. package/plugins/kk/scripts/daily-stages.ts +476 -12
  23. package/plugins/kk/scripts/kk-workflow-stages.ts +20 -4
  24. package/plugins/kk/skills/content-judge/SKILL.md +5 -4
  25. package/plugins/kk/skills/content-judge/references/rubrics.md +14 -0
  26. package/plugins/kk/skills/news-editorial/SKILL.md +122 -0
  27. package/plugins/kk/skills/news-editorial/references/selection.md +47 -0
  28. package/plugins/kk/skills/news-editorial/references/style-contract.zh.md +99 -0
  29. package/plugins/kk/skills/publish/SKILL.md +3 -3
  30. package/plugins/kk/skills/web-automation/SKILL.md +229 -77
  31. package/plugins/kk/skills/web-automation/references/browser-launch.md +127 -36
  32. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +4 -6
  33. package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +183 -222
  34. package/plugins/kk/workflows/kk-daily-publish3.yaml +16 -8
  35. package/plugins/publishings/bilibili-pub/dist/index.js +5 -3
  36. package/plugins/publishings/bilibili-pub/dist/profile-check.js +3 -1
  37. package/plugins/publishings/cnblogs-pub/dist/index.js +5 -3
  38. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +3 -1
  39. package/plugins/publishings/csdn-pub/dist/index.js +5 -3
  40. package/plugins/publishings/csdn-pub/dist/profile-check.js +5 -3
  41. package/plugins/publishings/emdash-pub/dist/index.js +2 -2
  42. package/plugins/publishings/infoq-pub/dist/index.js +5 -3
  43. package/plugins/publishings/infoq-pub/dist/profile-check.js +5 -3
  44. package/plugins/publishings/jike-pub/dist/index.js +5 -3
  45. package/plugins/publishings/jike-pub/dist/profile-check.js +5 -3
  46. package/plugins/publishings/juejin-pub/dist/index.js +5 -3
  47. package/plugins/publishings/juejin-pub/dist/profile-check.js +5 -3
  48. package/plugins/publishings/linkedin-pub/dist/index.js +5 -3
  49. package/plugins/publishings/linkedin-pub/dist/profile-check.js +5 -3
  50. package/plugins/publishings/medium-pub/dist/index.js +2 -2
  51. package/plugins/publishings/podcast-pub/dist/index.js +147 -4
  52. package/plugins/publishings/podcast-pub/src/index.ts +71 -2
  53. package/plugins/publishings/podcast-pub/src/map.ts +3 -0
  54. package/plugins/publishings/podcast-pub/src/transcript.ts +195 -0
  55. package/plugins/publishings/qiita-pub/dist/index.js +2 -2
  56. package/plugins/publishings/shipinhao-pub/dist/index.js +5 -3
  57. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +3 -1
  58. package/plugins/publishings/sspai-pub/dist/index.js +5 -3
  59. package/plugins/publishings/sspai-pub/dist/profile-check.js +3 -1
  60. package/plugins/publishings/substack-pub/dist/index.js +5 -3
  61. package/plugins/publishings/substack-pub/dist/profile-check.js +5 -3
  62. package/plugins/publishings/surfdash-pub/dist/index.js +2 -2
  63. package/plugins/publishings/threads-pub/dist/index.js +2 -2
  64. package/plugins/publishings/wechatmp-pub/dist/index.js +52 -33
  65. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +5 -3
  66. package/plugins/publishings/wechatmp-pub/src/flow.ts +70 -33
  67. package/plugins/publishings/weibo-pub/dist/index.js +5 -3
  68. package/plugins/publishings/weibo-pub/dist/profile-check.js +5 -3
  69. package/plugins/publishings/x-pub/dist/index.js +5 -3
  70. package/plugins/publishings/x-pub/dist/profile-check.js +5 -3
  71. package/plugins/publishings/xhs-pub/dist/index.js +5 -3
  72. package/plugins/publishings/xhs-pub/dist/profile-check.js +5 -3
  73. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +5 -3
  74. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +3 -1
  75. package/plugins/publishings/zenn-pub/dist/index.js +2 -2
  76. package/plugins/kk/scripts/daily-voice-style.zh.md +0 -70
@@ -7,17 +7,16 @@ description: >-
7
7
  generate-side channel payloads (`social-write` + `daily-bundle-variants`) and the frozen
8
8
  `bundle.json` that `kk-daily-publish3` consumes. It publishes nothing: every channel decision and
9
9
  every send lives in the publish preset and the publish workflow, which is why this graph carries
10
- no `xhs_*` / `wechat_*` / `publish_enabled` switch. voice2 stays the production path until the
11
- operator swaps the 04:30 schedule entry (§8).
10
+ no `xhs_*` / `wechat_*` / `publish_enabled` switch. Production since the 2026-11-05 cutover (§8);
11
+ it still runs in voice2's namespace (work dir, store selections, prepare ledger) by design (§11).
12
12
 
13
13
  vars:
14
14
 
15
15
  # ---- voice3 additions (task 0324 R4) ----
16
16
  # `auto_publish` launches publish3 nested when true; off by default exactly like voice2's
17
17
  # publish_enabled, and the operator turns it on in the schedule entry after the shadow period.
18
- # Card-series authoring/rendering (billed image generation). The cards ROW in the publish preset
19
- # is `off` by default, so this stays false until an operator turns both on.
20
- cards_enabled: "false"
18
+ # Card-series authoring/rendering (billed image generation). Turned on by default for XHS 上传图文.
19
+ cards_enabled: "true"
21
20
  # Card count (voice2's `xhs_cards`, minus the channel name): the card plan targets this many.
22
21
  cards: "6"
23
22
  auto_publish: "false"
@@ -67,19 +66,13 @@ vars:
67
66
  cover_fallback: ""
68
67
  # 0219 R3: image-gen vendor fallback for the XHS render; "" restores strict fail-fast.
69
68
  xhs_fallback: "true"
70
- # 2026-10-21 operator decision: OFF for now (was on since 2026-09-29). The plugin reported today's
71
- # draft as saved (`草稿箱` 12 -> 13 plus a scoped `保存成功` toast) while the console showed the
72
- # operator no draft, so the channel's success evidence is not trustworthy until that is resolved.
73
- # Card-image fallback to the aspect-only request when the exact canvas is refused.
74
- # launch-time switch and nothing else in the run depends on this target.
75
69
  xhs_style: "xhs-knowledge"
76
70
  xhs_provider: "minimax"
77
71
  xhs_model: "image-01"
78
72
  # The 合集 tag a channel payload carries is that channel's preset `options` (the shipped preset
79
73
  # pins 「每日AI资讯」), not a generate-side var: voice3 does not know which channels exist.
80
74
  plan_max_items: "20"
81
- plan_translate_enabled: "true"
82
- article_write_enabled: "true"
75
+ curate_enabled: "true"
83
76
  script_write_enabled: "true"
84
77
  plugins_path: ""
85
78
  # Content-safety budget and platforms, forwarded to the nested kk-daily-safety state machine.
@@ -144,15 +137,14 @@ nodes:
144
137
  kind: shell
145
138
  options:
146
139
  command: >-
147
- kk stage daily-preflight plugins/kk/workflows/kk-daily-ai-voice2.yaml
140
+ kk stage daily-preflight plugins/kk/workflows/kk-daily-ai-voice3.yaml
148
141
  plugins/kk/workflows/kk-daily-safety.yaml
149
142
  plugins/kk/workflows/kk-daily-publish3.yaml
150
143
  && kk daily prepare --workflow kk-daily-ai-voice2 --run-date "${vars.run_date}" --ledger-sync
151
144
  --run "${vars.__runId}" --definition-digest "${vars.__definitionDigest}"
152
145
  --setting window="${vars.window}" --setting language="${vars.language}" --setting vdriver="${vars.vdriver}"
153
146
  --setting skip_review="${vars.skip_review}" --setting transcode_mp3="${vars.transcode_mp3}"
154
- --setting cover_enabled="${vars.cover_enabled}" --setting plan_translate_enabled="${vars.plan_translate_enabled}"
155
- --setting article_write_enabled="${vars.article_write_enabled}"
147
+ --setting cover_enabled="${vars.cover_enabled}" --setting curate_enabled="${vars.curate_enabled}"
156
148
  --setting script_write_enabled="${vars.script_write_enabled}" --setting plan_max_items="${vars.plan_max_items}"
157
149
  --setting safety_max_revise="${vars.safety_max_revise}" --setting qc_min_quality="${vars.qc_min_quality}"
158
150
  --setting qc_min_importance="${vars.qc_min_importance}" --setting qc_min_urgency="${vars.qc_min_urgency}"
@@ -232,7 +224,7 @@ nodes:
232
224
  kind: shell
233
225
  options:
234
226
  command: >-
235
- kk stage daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}" "${vars.plan_translate_enabled}"
227
+ kk stage daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}" "${vars.curate_enabled}"
236
228
 
237
229
  - id: qc-content
238
230
  description: >-
@@ -295,249 +287,212 @@ nodes:
295
287
  kk store select --topic ai-news --workflow kk-daily-ai-voice2 --run "${vars.__runId}"
296
288
  --in "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json"
297
289
 
298
- # ---- content branches: translate / prose article / cover / script author (concurrent) ----
299
- #
300
- # 0238 R2: `plan-translate` and `article-write` are the two HARD-FAIL agent.run producers — a
301
- # missing expectFile fails the run (R4 property, kept). The resolved executor is stochastic: a run
302
- # answered a greeting with zero tool calls and exited 0 (0238 evidence). The engine has no node
303
- # retry, so each producer carries a `-retry` sibling on a DIFFERENT tier (`agent_retry`, role
304
- # reviewer) with the SAME input/expectFile; the primary becomes `onError: continue` and the sibling
305
- # (no onError) is the loud gate. Downstream readers depend on BOTH, so the done+skipped mix of a
306
- # successful primary still proceeds. Delete the siblings when spur grows an agent.run retry.
307
-
308
- - id: plan-translate
309
- description: "Translate the needsTranslation plan items into the output plan.json via agent.run; onError: continue — plan-translate-retry below is the loud gate (0238 R2)"
290
+ # ---- editorial pipeline: curate -> script-write (master) -> script -> editorial-render (0345) ----
291
+
292
+ - id: script-style
293
+ description: "Snapshot the shipped kk:news-editorial skill and zh style contract into the work dir (20-editorial/news-editorial/ + 24-script/<date>_08_script-write_style.md)"
310
294
  dependsOn: [qc-content, pool-gate]
311
295
  condition:
312
296
  kind: shell
313
297
  options:
314
298
  # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
315
- command: >-
316
- test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
317
- && test "${vars.plan_translate_enabled}" = "true"
318
- && ! jq -e --argjson n "$(jq '.metadata.docs|length' "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json")" 'type=="array" and length==$n and all(.[];type=="object")' "${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json"
299
+ command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
319
300
  action:
320
- kind: agent.run
321
- # 0238 R2: a stochastic executor no-op must not end the run here — the retry sibling decides.
322
- onError: continue
301
+ kind: shell
323
302
  options:
324
- role: scribe
325
- agent: ${vars.agent}
326
- # ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3; 0138 R4a copy discipline).
327
- # The anchor is shared with plan-translate-retry so the two attempts cannot drift apart.
328
- # 20261015 forensics: headless agy's terminal sandbox denies workspace writes for EVERY
329
- # shell command (command() rules are promptless-but-sandboxed; unsandboxed() is dead), so
330
- # the producer must deliver via the file-write tool; and the condition gate checks JSON
331
- # validity (array, length == candidates docs, all objects) because a pi token-cap staged
332
- # write once flowed 4/12 items downstream. Keep both when editing prompt or condition.
333
- input: &plan-translate-input |
334
- Read ${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json; metadata.docs (Doc[]) is the plan.
335
- For needsTranslation items adapt "title", "body" and "metadata.brief" into spoken simplified Chinese — keep every fact, number, name, quote, hedge (口播全长改编,不摘要不省略), body ≈0.6–0.7× source.
336
- Titles read as news; keep product/model names and body URLs verbatim, use 「」 not ASCII quotes.
337
- Copy discipline: reader-facing news copy — no scaffold labels (「可做角度」「为什么现在值得注意」「社区讨论」「背景」「潜在影响」「影响」), no hedging labels (「读者宜另行核实」「仍待验证」) — merge or drop.
338
- Keep id/sourceUri/mediaType/metadata as-is; no comments/trailing commas.
339
- Deliver with your file-write tool (headless agy sandbox blocks shell writes); the file must end as ONE valid JSON array — no split markers/placeholders.
340
- Output the JSON array — same length and order — to ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json.
341
- expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
342
- allowAppend: false
303
+ command: >-
304
+ kk stage daily-script-style "${vars.work_dir}" "${vars.run_date}"
343
305
 
344
- - id: plan-translate-retry
345
- description: >-
346
- Bounded recovery for plan-translate (0238 R2): runs only when the primary settled without a
347
- VALID expectFile (missing or contract-violating JSON), retries the same input on a different
348
- tier, and carries NO onError — a second miss fails the run loudly instead of letting an
349
- untranslated or truncated plan flow downstream.
350
- dependsOn: [plan-translate, pool-gate]
306
+ - id: curate
307
+ description: "0345 R7: curate QC candidates into kept translated plan.json + _03_curate_excluded.json via kk:news-editorial mode `curate`; onError: continue — curate-check/curate-fix/curate-final below validate and recover"
308
+ dependsOn: [qc-content, script-style, pool-gate]
351
309
  condition:
352
310
  kind: shell
353
311
  options:
354
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
355
312
  command: >-
356
313
  test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
357
- && test "${vars.plan_translate_enabled}" = "true"
358
- && ! jq -e --argjson n "$(jq '.metadata.docs|length' "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json")" 'type=="array" and length==$n and all(.[];type=="object")' "${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json"
314
+ && test "${vars.curate_enabled}" = "true"
315
+ && ! (test -s "${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json" && test -s "${vars.work_dir}/21-plan/${vars.run_date}_03_curate_excluded.json")
359
316
  action:
360
317
  kind: agent.run
318
+ onError: continue
361
319
  options:
362
- role: reviewer
363
- agent: ${vars.agent_retry}
364
- input: *plan-translate-input
320
+ role: scribe
321
+ agent: ${vars.agent}
322
+ timeoutMs: 600000
323
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
324
+ input: |
325
+ Follow mode `curate` in ${vars.work_dir}/20-editorial/news-editorial/SKILL.md (read references/selection.md and references/style-contract.zh.md first).
326
+ --in ${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json (metadata.docs is the candidate Doc[]; don't edit it)
327
+ --out-plan ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
328
+ --out-excluded ${vars.work_dir}/21-plan/${vars.run_date}_03_curate_excluded.json
329
+ Deliver both JSON files with your file-write tool (headless agy sandbox blocks shell writes).
365
330
  expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
366
331
  allowAppend: false
367
332
 
368
- - id: article-write
369
- description: "Write the tech-sharing prose article from the translated plan via agent.run (3-format split: surfdash gets prose, not a news list); onError: continue — article-write-retry below is the loud gate (0238 R2)"
370
- dependsOn: [qc-content, plan-translate, plan-translate-retry, pool-gate]
333
+ - id: curate-check
334
+ description: "0345 R8: validate the curate split, unwrap any Content envelope, and dedup near-duplicate titles; writes _03_curate_errors.json on failure"
335
+ dependsOn: [qc-content, script-style, curate, pool-gate]
371
336
  condition:
372
337
  kind: shell
373
338
  options:
374
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
375
- command: >-
376
- test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
377
- && test "${vars.article_write_enabled}" = "true"
378
- && ! test -s "${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md"
339
+ command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
379
340
  action:
380
- kind: agent.run
381
- # 0238 R2: a stochastic executor no-op must not end the run here — the retry sibling decides.
341
+ kind: shell
382
342
  onError: continue
383
343
  options:
384
- role: scribe
385
- agent: ${vars.agent}
386
- # ADR-115 error tier: keep this prompt <= 1000 chars. No title line: the canonical episode
387
- # title is computed by daily-article-gen (0189 R4).
388
- # The anchor is shared with article-write-retry so the two attempts cannot drift apart.
389
- input: &article-write-input |
390
- Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — metadata.docs (Doc[]) is the translated plan; don't edit it.
391
- Write vivid simplified-Chinese tech news for 咸话咸说之每日AI乱炖 (column 每日AI乱炖, host Robin). No title line. Open with exactly「这里是由Robin为你带来的咸话咸说之每日AI乱炖」。Never「我是咸话咸说」or an AI-production footer. Each ## section: what happened, why it matters, one concrete detail — a human reporter, not a telegram. Inline [来源:<label>](<sourceUri>). Close by tying the items together.
392
- 选题边界:只写技术内容;某条主线若属政治/外交或贸易管制/军事/公共政策争议/社会公共事件,跳过该条并在文末加一行「> 注:<N> 条条目因选题边界未写入:<id>(<原因>)、<id>(<原因>)」。
393
- 事实纪律:每个事实取自该条计划内容,不虚构、不引他处;数字与主体不得放大;引语只能归属其原句主语;保留「据报道/称/承诺」等限定词;导语/收束不加计划外程度词;材料不足就少写或不写,不凑数。
394
- Narrative register — smooth transitions, no list-like bulleting, no scaffold labels (「可做角度」「背景」「潜在影响」), no hedging (「读者宜另行核实」「仍待验证」). 「」 quotes.
395
- Output the markdown to ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md.
396
- expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
397
- allowAppend: false
344
+ command: >-
345
+ kk stage daily-curate-check "${vars.work_dir}" "${vars.run_date}"
398
346
 
399
- - id: article-write-retry
400
- description: >-
401
- Bounded recovery for article-write (0238 R2): runs only when the primary settled without its
402
- expectFile, retries the same input on a different tier, and carries NO onError — a second miss
403
- fails the run loudly instead of publishing an article nobody wrote.
404
- dependsOn: [article-write, pool-gate]
347
+ - id: curate-fix
348
+ description: "0345 R8: one targeted kk:news-editorial `curate --fix` pass on curate-check errors"
349
+ dependsOn: [curate-check, pool-gate]
405
350
  condition:
406
351
  kind: shell
407
352
  options:
408
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
409
353
  command: >-
410
354
  test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
411
- && test "${vars.article_write_enabled}" = "true"
412
- && ! test -s "${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md"
355
+ && test -s "${vars.work_dir}/21-plan/${vars.run_date}_03_curate_errors.json"
413
356
  action:
414
357
  kind: agent.run
358
+ onError: continue
415
359
  options:
416
360
  role: reviewer
417
361
  agent: ${vars.agent_retry}
418
- input: *article-write-input
419
- expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
362
+ timeoutMs: 600000
363
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
364
+ input: |
365
+ Follow mode `curate --fix` in ${vars.work_dir}/20-editorial/news-editorial/SKILL.md (read references/selection.md and references/style-contract.zh.md first).
366
+ --in ${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json (metadata.docs is the candidate Doc[]; don't edit it)
367
+ --fix ${vars.work_dir}/21-plan/${vars.run_date}_03_curate_errors.json
368
+ --out-plan ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
369
+ --out-excluded ${vars.work_dir}/21-plan/${vars.run_date}_03_curate_excluded.json
370
+ Deliver both corrected JSON files with your file-write tool.
371
+ expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
420
372
  allowAppend: false
421
373
 
422
- - id: article
423
- description: >-
424
- Generate the dated daily article Content from the episode plan (daily-article-gen template).
425
- Depends on the QC candidates (so the prose/translate nodes can be disabled without skipping
426
- this node: a done dependency in the mix keeps it runnable) as well as on both authoring nodes
427
- AND their retry siblings, and it carries the same candidates guard — a pool that filtered to
428
- zero items must not build an empty article.
429
- dependsOn: [qc-content, plan-translate, plan-translate-retry, article-write, article-write-retry, pool-gate]
374
+ - id: curate-final
375
+ description: "0345 R8: final curate validation; falls back to untranslated candidates with [] exclusions if still invalid"
376
+ dependsOn: [curate-check, curate-fix, pool-gate]
430
377
  condition:
431
378
  kind: shell
432
379
  options:
433
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
434
380
  command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
435
381
  action:
436
382
  kind: shell
437
383
  options:
438
384
  command: >-
439
- kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
385
+ kk stage daily-curate-check "${vars.work_dir}" "${vars.run_date}" --final
440
386
 
441
- - id: cover-brief
442
- description: "Author the cover subject from the lead plan item via agent.run (F614; onError: continue — a bad brief falls back to the format default subject in the render stage)"
443
- # 0238 R3: reads 21-plan/<date>_03_plan_plan.json, so it must not start before the plan
444
- # producers settle. `qc-content` stays in the list: a done dependency in the mix keeps this node
445
- # runnable when the plan-authoring nodes are disabled (all-skipped deps skip a node).
446
- dependsOn: [qc-content, plan-translate, plan-translate-retry, pool-gate]
387
+ - id: script-write
388
+ description: "Author the master script plan (intro/outro, lead/insight/card per doc id, cover, cards, social, excluded) from the curated plan via agent.run; onError: continue — a failed plan falls back to templates (F613, 0345 R9/R10)"
389
+ dependsOn: [script-style, curate-final, pool-gate]
447
390
  condition:
448
391
  kind: shell
449
392
  options:
450
393
  # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
451
394
  command: >-
452
395
  test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
453
- && test "${vars.cover_enabled}" = "true"
454
- && test ! -f "${vars.work_dir}/23-cover/${vars.run_date}_07_cover_cover.png"
396
+ && test "${vars.script_write_enabled}" = "true"
397
+ && test "${vars.language}" = "zh"
398
+ && ! test -s "${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json"
455
399
  action:
456
400
  kind: agent.run
457
- # A bad or failed brief must never halt the run: the stage falls back to the format's default
458
- # subject and keeps the same pinned format/style/provider/model.
459
401
  onError: continue
460
402
  options:
461
403
  role: scribe
462
404
  agent: ${vars.agent}
405
+ timeoutMs: 600000
463
406
  # ADR-115 error tier: keep this prompt <= 1000 chars.
464
407
  input: |
465
- Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of plan docs; don't edit it.
466
- Take item 1 only (its "title" and "metadata.brief") — the episode's lead story. If item 1's mainline is public affairs (politics/diplomacy, trade controls, military, policy disputes, social events), take the first in-scope item instead.
467
- Write one concrete visual scene in English, ≤240 characters, that a viewer would connect to that story: objects, settings, symbols, metaphor.
468
- No text, letters, numbers, logos, brand marks, flags, or recognizable real people. No style, color, lighting, or composition words — the preset owns those.
469
- Output strictly valid JSON {"subject": "..."} to ${vars.work_dir}/23-cover/${vars.run_date}_07_cover-brief.json.
470
- expectFile: ${vars.work_dir}/23-cover/${vars.run_date}_07_cover-brief.json
408
+ Follow mode `write` in ${vars.work_dir}/20-editorial/news-editorial/SKILL.md (ADR-045: the kk:news-editorial skill owns selection, balance, style contract and the master output shape; read its references first).
409
+ --in ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json (don't edit it)
410
+ --excluded ${vars.work_dir}/21-plan/${vars.run_date}_03_curate_excluded.json
411
+ --out ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
412
+ Output only the JSON file.
413
+ expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
471
414
  allowAppend: false
472
415
 
473
- - id: cover
474
- description: "Render the daily cover through the pinned daily-news presets (F614)"
475
- dependsOn: [cover-brief]
476
- action:
416
+ - id: script-check
417
+ description: "0345 R4: dry-run the dailynews-gen validator on the script plan; writes _08_script-write_errors.json when it is missing or rejected"
418
+ dependsOn: [script-style, script-write, pool-gate]
419
+ condition:
477
420
  kind: shell
478
421
  options:
479
422
  command: >-
480
- kk stage daily-cover "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.cover_provider}" "${vars.cover_model}" "${vars.cover_fallback}"
481
-
482
- - id: cover-normalize
483
- description: "Normalize the rendered cover (dimensions/format pins) so every consumer sees the same asset"
484
- dependsOn: [cover]
423
+ test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
424
+ && test "${vars.script_write_enabled}" = "true"
425
+ && test "${vars.language}" = "zh"
485
426
  action:
486
427
  kind: shell
428
+ onError: continue
487
429
  options:
488
430
  command: >-
489
- kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
431
+ kk stage daily-script-check "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
490
432
 
491
- - id: script-style
492
- description: "Snapshot the shipped zh style contract into the work dir (separate node: it is also an audit input for the revision loop)"
493
- dependsOn: [qc-content, pool-gate]
433
+ - id: script-write-fix
434
+ description: "0345 R4: one targeted kk:news-editorial `write --fix` pass on validator errors; onError: continue — still-rejected plans fall back to templates"
435
+ dependsOn: [script-check, curate-final, pool-gate]
494
436
  condition:
495
437
  kind: shell
496
438
  options:
497
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
498
- command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
439
+ command: >-
440
+ test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
441
+ && test -s "${vars.work_dir}/24-script/${vars.run_date}_08_script-write_errors.json"
499
442
  action:
500
- kind: shell
443
+ kind: agent.run
444
+ onError: continue
501
445
  options:
502
- command: >-
503
- kk stage daily-script-style "${vars.work_dir}" "${vars.run_date}"
446
+ role: scribe
447
+ agent: ${vars.agent_retry}
448
+ timeoutMs: 600000
449
+ # ADR-115 error tier: keep this prompt <= 1000 chars.
450
+ input: |
451
+ Follow mode `write --fix` in ${vars.work_dir}/20-editorial/news-editorial/SKILL.md (read its references first).
452
+ --in ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json (don't edit it)
453
+ --excluded ${vars.work_dir}/21-plan/${vars.run_date}_03_curate_excluded.json
454
+ Previous plan (may be absent): ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
455
+ --fix ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_errors.json
456
+ --out ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json (overwrite it with the full corrected plan)
457
+ Output only the JSON file.
458
+ expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
459
+ allowAppend: false
504
460
 
505
- - id: script-write
506
- description: "Author the per-item script plan (intro/outro + lead/insight per doc id) from the plan via agent.run; onError: continue — a failed plan falls back to templates (F613)"
507
- # 0238 R3: reads 21-plan/<date>_03_plan_plan.json, so it must not start before the plan
508
- # producers settle. `script-style` stays first in the list: a done dependency in the mix keeps
509
- # this node runnable when the plan-authoring nodes are disabled (all-skipped deps skip a node).
510
- dependsOn: [script-style, plan-translate, plan-translate-retry, article, pool-gate]
461
+ - id: editorial-judge
462
+ description: "0345 R11: advisory kk:content-judge evaluation of the script plan under the news-balance rubric; nothing depends on it for control flow"
463
+ dependsOn: [script-check, script-write-fix, curate-final, pool-gate]
511
464
  condition:
512
465
  kind: shell
513
466
  options:
514
- # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
515
467
  command: >-
516
468
  test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
517
- && test "${vars.script_write_enabled}" = "true"
518
- && test "${vars.language}" = "zh"
519
- && ! test -s "${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json"
469
+ && test -s "${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json"
520
470
  action:
521
471
  kind: agent.run
522
472
  onError: continue
523
473
  options:
524
- role: scribe
474
+ role: reviewer
525
475
  agent: ${vars.agent}
476
+ timeoutMs: 600000
526
477
  # ADR-115 error tier: keep this prompt <= 1000 chars.
527
478
  input: |
528
- Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of docs; don't edit it. Also read the style contract ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_style.md and obey its char caps, duration budget and banned phrases.
529
- For each doc write a `lead` (a spoken bridge, not containing the title) and an `insight` (a take, not a recap: the narration just read that doc's facts — never restate its facts, numbers or wording; give only your angle, judgment or what-to-watch, no new facts, keep hedges(据报道), no new degree words).
530
- intro/outro hook only in-scope items; no greeting or sign-off — the builder adds both. 选题边界:政治/外交、贸易管制、军事、公共政策争议、社会公共事件条目绝不复述;读文章 22-article/*_06_article-write_body.md,其文末注记点名的 id 一并进 excluded,各配中文原因。
531
- Output only JSON {"version":1,"intro":"…","outro":"…","items":[{"id":"…","lead":"…","insight":"…"}],"excluded":[{"id":"…","reason":"…"}]} to ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json. Exactly one `items` entry per doc not in `excluded`.
532
- expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
479
+ You are an LLM-as-judge evaluating a daily news script plan for the knowledge-kit pipeline.
480
+ Content to evaluate: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
481
+ Plan docs reference: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
482
+ Rubric: news-balance
483
+ Output verdict to: ${vars.work_dir}/24-script/${vars.run_date}_08_editorial-judge_verdict.json
484
+ Load the rubric definition from plugins/kk/skills/content-judge/references/rubrics.md (section: news-balance) and the contract from plugins/kk/skills/content-judge/SKILL.md.
485
+ Evaluate the script plan against every criterion in news-balance (news-first, commentary-share, no-restatement, tech-depth, ordering).
486
+ Write the verdict JSON (with confidence HIGH | MEDIUM | LOW) to ${vars.work_dir}/24-script/${vars.run_date}_08_editorial-judge_verdict.json.
487
+ expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_editorial-judge_verdict.json
533
488
  allowAppend: false
534
489
 
535
490
  - id: script
536
491
  description: >-
537
492
  Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content. Depends on
538
- `article` so the node still runs when the plan-authoring nodes were disabled or skipped (the
493
+ `curate-final` so the node still runs when the plan-authoring nodes were disabled or skipped (the
539
494
  builder falls back to templates) — but not when the whole content branch is skipped.
540
- dependsOn: [article, script-style, script-write, pool-gate]
495
+ dependsOn: [curate-final, script-style, script-write, script-check, script-write-fix, pool-gate]
541
496
  condition:
542
497
  kind: shell
543
498
  options:
@@ -549,6 +504,59 @@ nodes:
549
504
  command: >-
550
505
  kk stage daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}" "${vars.episode_max_minutes}" "${vars.vdriver}"
551
506
 
507
+ - id: editorial-render
508
+ description: "0345 R10: deterministically render the prose article body, cover brief, XHS card plan and social posts from the master script plan and curated plan"
509
+ dependsOn: [script, curate-final, pool-gate]
510
+ condition:
511
+ kind: shell
512
+ options:
513
+ command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
514
+ action:
515
+ kind: shell
516
+ options:
517
+ command: >-
518
+ kk stage daily-editorial-render "${vars.work_dir}" "${vars.run_date}" "${vars.cards_enabled}" "${vars.cards}"
519
+
520
+ - id: article
521
+ description: >-
522
+ Generate the dated daily article Content from the episode plan and rendered article body (daily-article-gen).
523
+ dependsOn: [qc-content, curate-final, editorial-render, pool-gate]
524
+ condition:
525
+ kind: shell
526
+ options:
527
+ # 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
528
+ command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
529
+ action:
530
+ kind: shell
531
+ options:
532
+ command: >-
533
+ kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
534
+
535
+ - id: cover
536
+ description: "Render the daily cover through the pinned daily-news presets (F614)"
537
+ dependsOn: [editorial-render, pool-gate]
538
+ condition:
539
+ kind: shell
540
+ options:
541
+ command: >-
542
+ test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS
543
+ && test "${vars.cover_enabled}" = "true"
544
+ && test ! -f "${vars.work_dir}/23-cover/${vars.run_date}_07_cover_cover.png"
545
+ action:
546
+ kind: shell
547
+ options:
548
+ command: >-
549
+ kk stage daily-cover "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.cover_provider}" "${vars.cover_model}" "${vars.cover_fallback}"
550
+
551
+ - id: cover-normalize
552
+ description: "Normalize the rendered cover (dimensions/format pins) so every consumer sees the same asset"
553
+ dependsOn: [cover]
554
+ action:
555
+ kind: shell
556
+ options:
557
+ command: >-
558
+ kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
559
+
552
560
  # ---- content safety: a nested state machine (no sub-workflow action exists; ADR-031) ----
553
561
 
554
562
  - id: safety
@@ -560,7 +568,7 @@ nodes:
560
568
  runner strips, kk-workflow-stages.ts): without it `spur workflow run` refuses to start inside
561
569
  an active run. voice3 always runs the gate: it is the only check between an LLM draft and a
562
570
  published post.
563
- dependsOn: [script]
571
+ dependsOn: [article, script]
564
572
  action:
565
573
  kind: shell
566
574
  options:
@@ -729,7 +737,7 @@ nodes:
729
737
  description: >-
730
738
  Render timeline show notes from the episode plan — content, not publishing: the publish half
731
739
  ships them as the episode variant's `metadata.showNotes`, and the link placeholders stay
732
- unresolved until a send resolves them."
740
+ unresolved until a send resolves them.
733
741
  dependsOn: [quality, pool-gate]
734
742
  condition:
735
743
  kind: shell
@@ -764,36 +772,11 @@ nodes:
764
772
  options:
765
773
  command: >-
766
774
  kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
767
- - id: cards-write
768
- description: "Plan the card series across the episode's plan items via agent.run; onError: continue — a failed plan skips the card series, never the run"
769
- dependsOn: [script, review, pool-gate]
770
- condition:
771
- kind: shell
772
- options:
773
- command: 'test "${vars.cards_enabled}" = "true" && test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
774
- action:
775
- kind: agent.run
776
- onError: continue
777
- options:
778
- role: scribe
779
- agent: ${vars.agent}
780
- # ADR-115 error tier: keep this prompt <= 1000 chars.
781
- input: |
782
- Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
783
- Plan the whole episode, not the lead item alone. Card 1 is the episode hook.
784
- Write a card note: "title" (≤20 chars) and "body" (Chinese lead paragraph + 3-5 hashtags) — the stage appends one numbered line per image below it. 选题边界:公共事务主线不写入,只写技术内容。
785
- Write exactly ${vars.cards} cards in plan order: card 1 "cover"; one "content" card per plan item, up to ${vars.cards} - 2 of them; the last "closing". Each card carries "role", "copy" (Chinese headline ≤12 chars), "points" (2-4 short Chinese bullets), "layout", "subject" (one concrete English visual scene), "sourcePoint" (that item's fact) and "itemId" (its "id").
786
- Ground every field in that item — no invented facts. No writing, letters, numbers, logos or people; screens stay blank.
787
- Output strictly valid JSON {"title": "...", "body": "...", "cards": [...]} to ${vars.work_dir}/25-xhs/${vars.run_date}_18_xhs_plan.json.
788
- expectFile: ${vars.work_dir}/25-xhs/${vars.run_date}_18_xhs_plan.json
789
- allowAppend: false
790
-
791
775
  - id: cards-render
792
776
  description: "Render the card series through the pinned xhs-card/xhs-knowledge presets, attach the episode cover last and pair every image with its numbered line (0235 note_scope=episode); onError: continue — the publish node names the missing series and stays soft"
793
- dependsOn: [cards-write, pool-gate]
794
- # A skipped cards-write still admits its dependents, so each card node keeps its own switch:
795
- # without it a run with cards_enabled=false would re-package a stale same-day 25-xhs/ series.
796
- # The pool guard is `pool-gate`'s alone in voice3 (0324 R3).
777
+ dependsOn: [editorial-render, review, pool-gate]
778
+ # Each card node keeps its own switch: without it a run with cards_enabled=false would
779
+ # re-package a stale same-day 25-xhs/ series. The pool guard is `pool-gate`'s alone in voice3 (0324 R3).
797
780
  condition:
798
781
  kind: shell
799
782
  options:
@@ -807,31 +790,6 @@ nodes:
807
790
 
808
791
  # ---- generate-side payloads and the frozen bundle (0324 R2/R3) ----
809
792
 
810
- - id: social-write
811
- description: >-
812
- Author one short post per social platform (x, weibo, threads, linkedin) from the approved
813
- plan, in zh, inside each platform's character cap. `daily-bundle-variants` truncates as a
814
- backstop, so a missed cap degrades rather than fails; a missing file simply skips that
815
- platform's variant.
816
- dependsOn: [quality]
817
- action:
818
- kind: agent.run
819
- onError: continue
820
- options:
821
- role: scribe
822
- agent: ${vars.agent}
823
- input: |
824
- Read the approved episode plan at ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json.
825
- Write four short zh posts, one per file:
826
- - ${vars.work_dir}/26-social/x.md — at most 280 weighted characters (this feed counts CJK as 2, so ~140 汉字 or 280 Latin letters)
827
- - ${vars.work_dir}/26-social/weibo.md — at most 2000 characters
828
- - ${vars.work_dir}/26-social/threads.md — at most 500 characters
829
- - ${vars.work_dir}/26-social/linkedin.md — at most 3000 characters
830
- Each post: the day's single most interesting item, one concrete number or name from the
831
- plan, no hashtag spam, no emoji, no "点击链接", no invented facts. Plain text, no markdown
832
- headings. Deliver with your file-write tool; each file must be non-empty.
833
- expectFile: ${vars.work_dir}/26-social/x.md
834
-
835
793
  - id: bundle-variants
836
794
  description: >-
837
795
  Build the channel payloads from the run's own artifacts (0327 builders): `30-variants/<key>`
@@ -840,7 +798,7 @@ nodes:
840
798
  F16: depends on every producer it reads (the 0324 file order alone was not a guarantee),
841
799
  wipes stale variants first, and follows the cards/cover switches so a disabled producer's
842
800
  variant is a skip, not a stale re-read.
843
- dependsOn: [quality, social-write, publish-prep, cover-normalize, cards-render, pool-gate]
801
+ dependsOn: [quality, editorial-render, publish-prep, cover-normalize, cards-render, pool-gate]
844
802
  condition:
845
803
  kind: shell
846
804
  options:
@@ -934,9 +892,12 @@ nodes:
934
892
  --vars "{\"work_dir\":\"${vars.work_dir}\",\"run_date\":\"${vars.run_date}\",\"window\":\"${vars.publish_window}\",\"preset\":\"${vars.publish_preset}\",\"dummy\":\"${vars.publish_dummy}\",\"live_channels\":\"${vars.publish_live_channels}\"}"
935
893
 
936
894
  - id: ledger
937
- description: "Append this run to the workflow ledger; bookkeeping never fails a generation."
895
+ description: >-
896
+ Backfill the voice3 ledger with every finished run it does not record yet (an in-run
897
+ `kk ledger append` of this run always refuses with `kk.ledger.run.running`; the scheduled
898
+ wrapper's post-run sync records this one). `|| true` — bookkeeping never fails a generation.
938
899
  dependsOn: [generation-report, publish]
939
900
  action:
940
901
  kind: shell
941
902
  options:
942
- command: 'kk ledger append --workflow kk-daily-ai-voice3 --work-dir "${vars.work_dir}" --run "${vars.__runId}" || true'
903
+ command: 'kk ledger sync --workflow kk-daily-ai-voice3 || true'