@gobing-ai/knowledge-kit 0.0.31 → 0.0.33
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/marketplace.json +1 -1
- package/dist/index.js +32320 -15021
- package/package.json +4 -4
- package/plugins/generations/content-gen/dist/index.js +10 -9
- package/plugins/generations/core-facts-gen/dist/index.js +13 -12
- package/plugins/generations/daily-article-gen/dist/index.js +14 -13
- package/plugins/generations/daily-article-gen/src/index.ts +1 -1
- package/plugins/generations/dailynews-gen/dist/index.js +10 -9
- package/plugins/generations/episode-plan-gen/dist/index.js +58 -15
- package/plugins/generations/episode-plan-gen/src/index.ts +76 -6
- package/plugins/generations/gemini-tts/README.md +11 -0
- package/plugins/generations/gemini-tts/dist/index.js +10 -9
- package/plugins/generations/image-gen/dist/index.js +154 -151
- package/plugins/generations/image-gen/src/compose/chromium.ts +60 -42
- package/plugins/generations/news-report-gen/dist/index.js +13 -12
- package/plugins/generations/voice-gen/dist/index.js +10 -9
- package/plugins/ingestions/aihot-ingest/dist/index.js +13 -12
- package/plugins/ingestions/horizon-ingest/dist/index.js +13 -12
- package/plugins/ingestions/karakeep-local/dist/index.js +13 -13
- package/plugins/ingestions/last30days-ingest/dist/index.js +13 -12
- package/plugins/ingestions/web-search/dist/index.js +11 -10
- package/plugins/kk/README.md +38 -0
- package/plugins/kk/decisions/extract/ai-news.yaml +21 -0
- package/plugins/kk/decisions/kk-items.yaml +113 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/schedules/cron.tmpl +15 -0
- package/plugins/kk/schedules/launchd.plist.tmpl +77 -0
- package/plugins/kk/scripts/daily-stages.ts +1533 -0
- package/plugins/kk/scripts/kk-workflow-stages.ts +268 -1228
- package/plugins/kk/skills/content-judge/SKILL.md +12 -2
- package/plugins/kk/skills/content-judge/references/rubrics.md +8 -2
- package/plugins/kk/skills/content-safety/SKILL.md +60 -18
- package/plugins/kk/skills/web-automation/SKILL.md +100 -0
- package/plugins/kk/skills/web-automation/references/browser-launch.md +86 -0
- package/plugins/kk/workflows/kk-collect.yaml +258 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +74 -11
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +655 -0
- package/plugins/kk/workflows/kk-daily-safety.yaml +170 -0
- package/plugins/publishings/emdash-pub/dist/index.js +10 -9
- package/plugins/publishings/infoq-pub/dist/index.js +8695 -211
- package/plugins/publishings/infoq-pub/package.json +3 -1
- package/plugins/publishings/infoq-pub/plugin.json +2 -1
- package/plugins/publishings/infoq-pub/src/index.ts +358 -29
- package/plugins/publishings/infoq-pub/src/profile-check.ts +21 -0
- package/plugins/publishings/juejin-pub/dist/index.js +8746 -211
- package/plugins/publishings/juejin-pub/package.json +3 -1
- package/plugins/publishings/juejin-pub/plugin.json +2 -1
- package/plugins/publishings/juejin-pub/src/index.ts +345 -29
- package/plugins/publishings/juejin-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/medium-pub/dist/index.js +10 -9
- package/plugins/publishings/podcast-pub/dist/index.js +10 -9
- package/plugins/publishings/qiita-pub/dist/index.js +10 -9
- package/plugins/publishings/substack-pub/dist/index.js +8687 -211
- package/plugins/publishings/substack-pub/package.json +3 -1
- package/plugins/publishings/substack-pub/plugin.json +2 -1
- package/plugins/publishings/substack-pub/src/index.ts +302 -29
- package/plugins/publishings/substack-pub/src/profile-check.ts +76 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +10 -9
- package/plugins/publishings/wechatmp-pub/dist/index.js +1315 -352
- package/plugins/publishings/wechatmp-pub/plugin.json +1 -0
- package/plugins/publishings/wechatmp-pub/src/enrich.ts +229 -0
- package/plugins/publishings/wechatmp-pub/src/flow.ts +896 -0
- package/plugins/publishings/wechatmp-pub/src/index.ts +223 -520
- package/plugins/publishings/wechatmp-pub/src/modes.ts +403 -0
- package/plugins/publishings/wechatmp-pub/src/page.ts +260 -0
- package/plugins/publishings/wechatmp-pub/src/profile-check.ts +84 -38
- package/plugins/publishings/x-pub/dist/index.js +8707 -223
- package/plugins/publishings/x-pub/package.json +3 -1
- package/plugins/publishings/x-pub/plugin.json +2 -1
- package/plugins/publishings/x-pub/src/index.ts +315 -23
- package/plugins/publishings/x-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/xhs-pub/dist/index.js +897 -354
- package/plugins/publishings/xhs-pub/plugin.json +2 -1
- package/plugins/publishings/xhs-pub/src/flow.ts +572 -0
- package/plugins/publishings/xhs-pub/src/index.ts +108 -596
- package/plugins/publishings/xhs-pub/src/modes.ts +300 -0
- package/plugins/publishings/xhs-pub/src/page.ts +254 -0
- package/plugins/publishings/xhs-pub/src/profile-check.ts +29 -17
- package/plugins/publishings/zenn-pub/dist/index.js +10 -9
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
name: kk-daily-ai-voice2
|
|
2
|
+
kind: dag
|
|
3
|
+
description: >-
|
|
4
|
+
Daily AI tech-sharing pipeline for the 「AI 乱炖」 column of show 「咸话咸说」, rebuilt as a static
|
|
5
|
+
dependency DAG over the kk store (task 0211, ADR-031, design docs/design/kk-collect-and-daily-voice2.md §6).
|
|
6
|
+
It starts from the store, not from ingestion: `kk store extract` selects today's pool from the
|
|
7
|
+
labels `kk-collect` (0208/0209) wrote, and the store's `episode_usage` ledger is the cross-day
|
|
8
|
+
dedup. The daily work is unchanged after that — plan → content QC → article/cover/script →
|
|
9
|
+
content safety (nested state machine, kk-daily-safety.yaml) → operator review → audio → publish
|
|
10
|
+
(surfdash + podcast) → independent soft channels (xhs, wechat, locales) → classify → ledger. It
|
|
11
|
+
supersedes kk-daily-ai-voice.yaml, which stays untouched until cut-over §8.
|
|
12
|
+
vars:
|
|
13
|
+
# Composed by the `prepare` node (`kk daily prepare`), then read back into vars through the
|
|
14
|
+
# single-node read chain below — shell actions cannot set vars, and a concurrent `setVars` merge
|
|
15
|
+
# is last-writer-wins, so the DAG exchanges data by fixed paths under work_dir, never by vars.
|
|
16
|
+
work_dir: ""
|
|
17
|
+
# Digits-only run-date stamp the artifact names carry; projected from <work_dir>/run-date.txt.
|
|
18
|
+
run_date: ""
|
|
19
|
+
# Store extract window (kk store extract --window). The old workflow's ingestion window was
|
|
20
|
+
# intrinsic to the fan-in; here it is the pool's freshness bound (kk-collect refreshes every 6h).
|
|
21
|
+
window: "24h"
|
|
22
|
+
language: "zh"
|
|
23
|
+
voice_profile: "robin-news"
|
|
24
|
+
# Voice driver switch — voicebox (voice-gen) | ominivoice (omni-voice-gen) | gemini (gemini-tts).
|
|
25
|
+
# Same default as kk-daily-ai-voice: gemini-tts (20260929 operator override, verified live).
|
|
26
|
+
vdriver: "gemini"
|
|
27
|
+
intro_music: ".works/kk-daily-ai-voice/AI每日资讯_8秒播客片头.mp3"
|
|
28
|
+
surfdash_root: "/Users/robin/xprojects/surfdash"
|
|
29
|
+
title: ""
|
|
30
|
+
skip_review: "false"
|
|
31
|
+
transcode_mp3: "true"
|
|
32
|
+
podcast_artist: "Robin Min"
|
|
33
|
+
podcast_album: "咸话咸说"
|
|
34
|
+
cover_enabled: "true"
|
|
35
|
+
cover_provider: "minimax"
|
|
36
|
+
cover_model: "image-01"
|
|
37
|
+
cover_fallback: ""
|
|
38
|
+
# 0219 R3: image-gen vendor fallback for the XHS render; "" restores strict fail-fast.
|
|
39
|
+
xhs_fallback: "true"
|
|
40
|
+
xhs_enabled: "true"
|
|
41
|
+
xhs_cards: "4"
|
|
42
|
+
xhs_style: "xhs-knowledge"
|
|
43
|
+
xhs_provider: "minimax"
|
|
44
|
+
xhs_model: "image-01"
|
|
45
|
+
# ADR-022 rail: draft-box only. "true" posts live and stays a launch-time opt-in.
|
|
46
|
+
xhs_live: "false"
|
|
47
|
+
wechat_enabled: "true"
|
|
48
|
+
wechat_live: "false"
|
|
49
|
+
wechat_collection: "AI 乱炖"
|
|
50
|
+
plan_max_items: "20"
|
|
51
|
+
plan_translate_enabled: "true"
|
|
52
|
+
article_write_enabled: "true"
|
|
53
|
+
script_write_enabled: "true"
|
|
54
|
+
plugins_path: ""
|
|
55
|
+
# Content-safety budget and platforms, forwarded to the nested kk-daily-safety state machine.
|
|
56
|
+
safety_max_revise: "2"
|
|
57
|
+
safety_platforms: "xiaoyuzhou,apple-podcasts,wechatmp"
|
|
58
|
+
publish_enabled: "false"
|
|
59
|
+
agent: "inline"
|
|
60
|
+
# QC pins — lockstep with episode-plan-gen's DEFAULT_FILTER_CONFIG (env wins), same as voice1.
|
|
61
|
+
qc_categories: "ai-news,ai-technical,ai-research,ai-industry"
|
|
62
|
+
qc_min_quality: "2"
|
|
63
|
+
qc_min_importance: "3"
|
|
64
|
+
qc_min_urgency: "2"
|
|
65
|
+
qc_min_impact: "1"
|
|
66
|
+
qc_min_episode_items: "8"
|
|
67
|
+
episode_min_minutes: "15"
|
|
68
|
+
episode_max_minutes: "20"
|
|
69
|
+
# Run id, injected by spur at launch (0674 R5/R8) — the episode_usage / ledger / mark-used
|
|
70
|
+
# identity. A shell command may only reference declared vars.
|
|
71
|
+
__runId: ""
|
|
72
|
+
|
|
73
|
+
# One action per node (engine: DagNodeDef.action), no back-edges (ADR-031). A node whose deps are
|
|
74
|
+
# ALL skipped is skipped; a done+skipped mix proceeds — which is how the empty-pool path still
|
|
75
|
+
# reaches run-report and ledger (they list `extract` in dependsOn, G11 R3).
|
|
76
|
+
#
|
|
77
|
+
# NODES ARE DECLARED IN TOPOLOGICAL ORDER (every node after all of its `dependsOn`). The engine
|
|
78
|
+
# settles skips in one sweeping pass over this list, in declaration order, and stops as soon as a
|
|
79
|
+
# pass finds no ready node — a node declared before a dependency that becomes `skipped` later in
|
|
80
|
+
# that same pass stays `pending` and the run ends `dag-unreachable-nodes` instead of `done`.
|
|
81
|
+
#
|
|
82
|
+
# Fail-fast is the other half: any failed node fails the run, so every non-blocking channel (xhs,
|
|
83
|
+
# wechat, locales) keeps its exit-0-on-soft-failure kk stage, and `onError: continue` stays where
|
|
84
|
+
# voice1 had it. Wave-level waiting is the accepted ceiling (design §6): a node starts when its own
|
|
85
|
+
# deps settle, but its whole wave is awaited before the next wave is dispatched.
|
|
86
|
+
nodes:
|
|
87
|
+
- id: prepare
|
|
88
|
+
description: >-
|
|
89
|
+
Sync the workflow ledger and compose today's workspace: `kk daily prepare` writes the effective
|
|
90
|
+
work_dir to .spur/runs/kk-daily-ai-voice2/work-dir.txt, the run date to <work_dir>/run-date.txt,
|
|
91
|
+
the artifact directories and the safety revise budget. `kk ledger sync` records runs that
|
|
92
|
+
already finished without a ledger row (failed ones included) — a fail-fast DAG never reaches
|
|
93
|
+
its own `ledger` node, so the next run's prepare is the backfill (design §7).
|
|
94
|
+
action:
|
|
95
|
+
kind: shell
|
|
96
|
+
options:
|
|
97
|
+
command: >-
|
|
98
|
+
kk daily prepare --workflow kk-daily-ai-voice2 --run-date "${vars.run_date}" --ledger-sync
|
|
99
|
+
|
|
100
|
+
- id: read-work-dir
|
|
101
|
+
description: "Project the effective work_dir into vars (single-node wave: nothing has fanned out yet)"
|
|
102
|
+
dependsOn: [prepare]
|
|
103
|
+
action:
|
|
104
|
+
kind: file.read.into-var
|
|
105
|
+
options:
|
|
106
|
+
path: .spur/runs/kk-daily-ai-voice2/work-dir.txt
|
|
107
|
+
var: work_dir
|
|
108
|
+
|
|
109
|
+
- id: read-run-date
|
|
110
|
+
description: "Project the digits-only run-date stamp into vars (single-node wave, after work_dir is known)"
|
|
111
|
+
dependsOn: [read-work-dir]
|
|
112
|
+
action:
|
|
113
|
+
kind: file.read.into-var
|
|
114
|
+
options:
|
|
115
|
+
path: ${vars.work_dir}/run-date.txt
|
|
116
|
+
var: run_date
|
|
117
|
+
|
|
118
|
+
- id: extract
|
|
119
|
+
description: >-
|
|
120
|
+
Select today's episode pool from the kk store: the ai-news topic profile over the labels
|
|
121
|
+
kk-collect wrote, minus everything this workflow already used (episode_usage). Writes
|
|
122
|
+
<work_dir>/10-collect/pool.json (the fan-in blended.json shape) and prints {count}. An empty
|
|
123
|
+
pool writes [] and exits 0 — the run's no-content path, not a failure.
|
|
124
|
+
dependsOn: [read-run-date]
|
|
125
|
+
action:
|
|
126
|
+
kind: shell
|
|
127
|
+
options:
|
|
128
|
+
command: >-
|
|
129
|
+
kk store extract --topic ai-news --window "${vars.window}" --exclude-used kk-daily-ai-voice2
|
|
130
|
+
--out "${vars.work_dir}/10-collect/pool.json"
|
|
131
|
+
|
|
132
|
+
- id: plan
|
|
133
|
+
description: >-
|
|
134
|
+
Score/dedup/select + annotate the pool into the episode plan (episode-plan-gen; annotate rides
|
|
135
|
+
inside planEpisode). The labels extract inlined into metadata replace its keyword re-scoring.
|
|
136
|
+
Guarded on the pool: an empty pool skips the whole content subtree, and run-report/ledger
|
|
137
|
+
still run.
|
|
138
|
+
dependsOn: [extract]
|
|
139
|
+
condition:
|
|
140
|
+
kind: shell
|
|
141
|
+
options:
|
|
142
|
+
command: 'kk store pool-count "${vars.work_dir}/10-collect/pool.json" --min 1'
|
|
143
|
+
action:
|
|
144
|
+
kind: shell
|
|
145
|
+
options:
|
|
146
|
+
command: >-
|
|
147
|
+
kk stage daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
|
|
148
|
+
|
|
149
|
+
- id: qc-content
|
|
150
|
+
description: >-
|
|
151
|
+
Content QC: filter the plan's annotated candidates by category allowlist + score thresholds
|
|
152
|
+
(episode-plan-gen --mode filter). Writes 21-plan/<date>_04_quality-control-content_candidates.json,
|
|
153
|
+
whose length guards the whole content fan-out below — a pool that filtered to zero items ends
|
|
154
|
+
the run as no-content instead of building an empty episode.
|
|
155
|
+
dependsOn: [plan]
|
|
156
|
+
action:
|
|
157
|
+
kind: shell
|
|
158
|
+
options:
|
|
159
|
+
command: >-
|
|
160
|
+
kk stage daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}" "${vars.qc_min_episode_items}"
|
|
161
|
+
|
|
162
|
+
# ---- content branches: translate / prose article / cover / script author (concurrent) ----
|
|
163
|
+
- id: plan-translate
|
|
164
|
+
description: "Translate the needsTranslation plan items into the output plan.json via agent.run"
|
|
165
|
+
dependsOn: [qc-content]
|
|
166
|
+
condition:
|
|
167
|
+
kind: shell
|
|
168
|
+
options:
|
|
169
|
+
command: >-
|
|
170
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
171
|
+
&& test "${vars.plan_translate_enabled}" = "true"
|
|
172
|
+
action:
|
|
173
|
+
kind: agent.run
|
|
174
|
+
options:
|
|
175
|
+
role: scribe
|
|
176
|
+
agent: ${vars.agent}
|
|
177
|
+
# ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3; 0138 R4a copy discipline).
|
|
178
|
+
input: |
|
|
179
|
+
Read ${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json — metadata.docs (Doc[]) is the plan; don't edit it.
|
|
180
|
+
For needsTranslation items, adapt "title", "body" and "metadata.brief" into spoken simplified Chinese — full coverage, not a summary: keep every fact, number, name, quote and hedge (口播全长改编,不摘要不省略), body ≈0.6–0.7× the source.
|
|
181
|
+
Titles read as a news event; keep product/model names verbatim, preserve body URLs, use 「」 not ASCII double quotes.
|
|
182
|
+
Copy discipline: reader-facing news copy — no scaffold labels (「可做角度」「为什么现在值得注意」「社区讨论」「背景」「潜在影响」「影响」), no duplicated labels or hedging (「读者宜另行核实」「仍待验证」) — merge their substance into prose or drop.
|
|
183
|
+
Keep other fields (id, sourceUri, mediaType, metadata) as-is; no comments or trailing commas.
|
|
184
|
+
Output strictly valid JSON — a same-length, same-order array — to ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json.
|
|
185
|
+
expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
|
|
186
|
+
allowAppend: false
|
|
187
|
+
|
|
188
|
+
- id: article-write
|
|
189
|
+
description: "Write the tech-sharing prose article from the translated plan via agent.run (3-format split: surfdash gets prose, not a news list)"
|
|
190
|
+
dependsOn: [qc-content]
|
|
191
|
+
condition:
|
|
192
|
+
kind: shell
|
|
193
|
+
options:
|
|
194
|
+
command: >-
|
|
195
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
196
|
+
&& test "${vars.article_write_enabled}" = "true"
|
|
197
|
+
action:
|
|
198
|
+
kind: agent.run
|
|
199
|
+
options:
|
|
200
|
+
role: scribe
|
|
201
|
+
agent: ${vars.agent}
|
|
202
|
+
# ADR-115 error tier: keep this prompt <= 1000 chars. No title line: the canonical episode
|
|
203
|
+
# title is computed by daily-article-gen (0189 R4).
|
|
204
|
+
input: |
|
|
205
|
+
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — metadata.docs (Doc[]) is the translated plan; don't edit it.
|
|
206
|
+
Write a tech-sharing prose article in natural simplified Chinese for the 「AI 乱炖」 column (show 「咸话咸说」, tools/papers/hands-on practice 随聊): no title line (the pipeline computes it), an intro paragraph (2–3 sentences), then one section per item (## numbered title, 2–4 sentences of flowing prose, source link [来源:<label>](<sourceUri>) inline), a closing paragraph tying items together.
|
|
207
|
+
选题边界:只写技术内容;某条主线若属政治/外交或贸易管制/军事/公共政策争议/社会公共事件,跳过该条并在文末加一行「> 注:<N> 条条目因选题边界未写入:<id>(<原因>)、<id>(<原因>)」。
|
|
208
|
+
事实纪律:每个事实取自该条计划内容,不虚构、不引他处;数字与主体不得放大;引语只能归属其原句主语;保留「据报道/称/承诺」等限定词;导语/收束不加计划外程度词;材料不足就少写或不写,不凑数。
|
|
209
|
+
Narrative register — smooth transitions, no list-like bulleting, no scaffold labels (「可做角度」「背景」「潜在影响」), no hedging (「读者宜另行核实」「仍待验证」). 「」 quotes.
|
|
210
|
+
Output the markdown to ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md.
|
|
211
|
+
expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
|
|
212
|
+
allowAppend: false
|
|
213
|
+
|
|
214
|
+
- id: article
|
|
215
|
+
description: >-
|
|
216
|
+
Generate the dated daily article Content from the episode plan (daily-article-gen template).
|
|
217
|
+
Depends on the QC candidates (so the prose/translate nodes can be disabled without skipping
|
|
218
|
+
this node: a done dependency in the mix keeps it runnable) as well as on both authoring nodes,
|
|
219
|
+
and it carries the same candidates guard — a pool that filtered to zero items must not build
|
|
220
|
+
an empty article.
|
|
221
|
+
dependsOn: [qc-content, plan-translate, article-write]
|
|
222
|
+
condition:
|
|
223
|
+
kind: shell
|
|
224
|
+
options:
|
|
225
|
+
command: >-
|
|
226
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
227
|
+
action:
|
|
228
|
+
kind: shell
|
|
229
|
+
options:
|
|
230
|
+
command: >-
|
|
231
|
+
kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
232
|
+
|
|
233
|
+
- id: cover-brief
|
|
234
|
+
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)"
|
|
235
|
+
dependsOn: [qc-content]
|
|
236
|
+
condition:
|
|
237
|
+
kind: shell
|
|
238
|
+
options:
|
|
239
|
+
command: >-
|
|
240
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
241
|
+
&& test "${vars.cover_enabled}" = "true"
|
|
242
|
+
&& test ! -f "${vars.work_dir}/23-cover/${vars.run_date}_07_cover_cover.png"
|
|
243
|
+
action:
|
|
244
|
+
kind: agent.run
|
|
245
|
+
# A bad or failed brief must never halt the run: the stage falls back to the format's default
|
|
246
|
+
# subject and keeps the same pinned format/style/provider/model.
|
|
247
|
+
onError: continue
|
|
248
|
+
options:
|
|
249
|
+
role: scribe
|
|
250
|
+
agent: ${vars.agent}
|
|
251
|
+
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
252
|
+
input: |
|
|
253
|
+
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of plan docs; don't edit it.
|
|
254
|
+
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.
|
|
255
|
+
Write one concrete visual scene in English, ≤240 characters, that a viewer would connect to that story: objects, settings, symbols, metaphor.
|
|
256
|
+
No text, letters, numbers, logos, brand marks, flags, or recognizable real people. No style, color, lighting, or composition words — the preset owns those.
|
|
257
|
+
Output strictly valid JSON {"subject": "..."} to ${vars.work_dir}/23-cover/${vars.run_date}_07_cover-brief.json.
|
|
258
|
+
expectFile: ${vars.work_dir}/23-cover/${vars.run_date}_07_cover-brief.json
|
|
259
|
+
allowAppend: false
|
|
260
|
+
|
|
261
|
+
- id: cover
|
|
262
|
+
description: "Render the daily cover through the pinned daily-news presets (F614)"
|
|
263
|
+
dependsOn: [cover-brief]
|
|
264
|
+
action:
|
|
265
|
+
kind: shell
|
|
266
|
+
options:
|
|
267
|
+
command: >-
|
|
268
|
+
kk stage daily-cover "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.cover_provider}" "${vars.cover_model}" "${vars.cover_fallback}"
|
|
269
|
+
|
|
270
|
+
- id: cover-normalize
|
|
271
|
+
description: "Normalize the rendered cover (dimensions/format pins) so every consumer sees the same asset"
|
|
272
|
+
dependsOn: [cover]
|
|
273
|
+
action:
|
|
274
|
+
kind: shell
|
|
275
|
+
options:
|
|
276
|
+
command: >-
|
|
277
|
+
kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
|
|
278
|
+
|
|
279
|
+
- id: script-style
|
|
280
|
+
description: "Snapshot the shipped zh style contract into the work dir (separate node: it is also an audit input for the revision loop)"
|
|
281
|
+
dependsOn: [qc-content]
|
|
282
|
+
condition:
|
|
283
|
+
kind: shell
|
|
284
|
+
options:
|
|
285
|
+
command: >-
|
|
286
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
287
|
+
action:
|
|
288
|
+
kind: shell
|
|
289
|
+
options:
|
|
290
|
+
command: >-
|
|
291
|
+
kk stage daily-script-style "${vars.work_dir}" "${vars.run_date}"
|
|
292
|
+
|
|
293
|
+
- id: script-write
|
|
294
|
+
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)"
|
|
295
|
+
dependsOn: [script-style]
|
|
296
|
+
condition:
|
|
297
|
+
kind: shell
|
|
298
|
+
options:
|
|
299
|
+
command: >-
|
|
300
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
301
|
+
&& test "${vars.script_write_enabled}" = "true"
|
|
302
|
+
&& test "${vars.language}" = "zh"
|
|
303
|
+
action:
|
|
304
|
+
kind: agent.run
|
|
305
|
+
onError: continue
|
|
306
|
+
options:
|
|
307
|
+
role: scribe
|
|
308
|
+
agent: ${vars.agent}
|
|
309
|
+
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
310
|
+
input: |
|
|
311
|
+
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.
|
|
312
|
+
For each doc write a `lead` (a spoken bridge, not containing the title) and an `insight` (grounded only in that doc's facts: keep its numbers and hedges(据报道), add no new degree words).
|
|
313
|
+
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,各配中文原因。
|
|
314
|
+
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`.
|
|
315
|
+
expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
|
|
316
|
+
allowAppend: false
|
|
317
|
+
|
|
318
|
+
- id: script
|
|
319
|
+
description: >-
|
|
320
|
+
Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content. Depends on
|
|
321
|
+
`article` so the node still runs when the plan-authoring nodes were disabled or skipped (the
|
|
322
|
+
builder falls back to templates) — but not when the whole content branch is skipped.
|
|
323
|
+
dependsOn: [article, script-style, script-write]
|
|
324
|
+
condition:
|
|
325
|
+
kind: shell
|
|
326
|
+
options:
|
|
327
|
+
command: >-
|
|
328
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
329
|
+
action:
|
|
330
|
+
kind: shell
|
|
331
|
+
options:
|
|
332
|
+
command: >-
|
|
333
|
+
kk stage daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
|
|
334
|
+
|
|
335
|
+
# ---- content safety: a nested state machine (no sub-workflow action exists; ADR-031) ----
|
|
336
|
+
- id: safety
|
|
337
|
+
description: >-
|
|
338
|
+
Content safety as a nested state machine (kk-daily-safety.yaml): review → judge → bounded
|
|
339
|
+
revise loop → clean|block. It blocks here (and only here), so a non-zero exit means the drafts
|
|
340
|
+
must not publish — `block` or an integrity failure. The launcher vars the parent run exports
|
|
341
|
+
are stripped at the nested-spur boundary (the same two `NESTED_SPUR_UNSET` vars the stage
|
|
342
|
+
runner strips, kk-workflow-stages.ts): without it `spur workflow run` refuses to start inside
|
|
343
|
+
an active run. Non-publishing runs skip the gate entirely, exactly like voice1.
|
|
344
|
+
dependsOn: [script]
|
|
345
|
+
condition:
|
|
346
|
+
kind: shell
|
|
347
|
+
options:
|
|
348
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
349
|
+
action:
|
|
350
|
+
kind: shell
|
|
351
|
+
options:
|
|
352
|
+
command: >-
|
|
353
|
+
env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST
|
|
354
|
+
spur workflow run plugins/kk/workflows/kk-daily-safety.yaml
|
|
355
|
+
--vars '{"work_dir":"${vars.work_dir}","run_date":"${vars.run_date}","safety_max_revise":"${vars.safety_max_revise}","safety_platforms":"${vars.safety_platforms}","agent":"${vars.agent}"}'
|
|
356
|
+
--quiet --no-plan
|
|
357
|
+
|
|
358
|
+
- id: review
|
|
359
|
+
description: >-
|
|
360
|
+
Operator review of the safety-gated drafts: `pause: true` stops the run here and RESUME is the
|
|
361
|
+
approval (the engine marks a paused node done on resume, so this node carries no action);
|
|
362
|
+
cancelling the run is the rejection. Same contract as voice1's HITL confirmation, now a node
|
|
363
|
+
instead of a state. Skipped when skip_review=true or when there is nothing to publish.
|
|
364
|
+
dependsOn: [safety]
|
|
365
|
+
pause: true
|
|
366
|
+
condition:
|
|
367
|
+
kind: shell
|
|
368
|
+
options:
|
|
369
|
+
command: 'test "${vars.skip_review}" != "true"'
|
|
370
|
+
|
|
371
|
+
# ---- audio branch ----
|
|
372
|
+
# wrap-docs must be DECLARED BEFORE generate: the DAG requires nodes in topological order, and
|
|
373
|
+
# generate consumes wrap-docs's _10_wrap-docs_docs.json as its TTS input (0212 dogfood finding —
|
|
374
|
+
# an edge to a later-declared node wedges the dispatcher with generate never dispatched).
|
|
375
|
+
- id: wrap-docs
|
|
376
|
+
description: "Wrap VoiceScript into Doc[] input for the voice generator plugin (also the publish-prep input)"
|
|
377
|
+
dependsOn: [script, review]
|
|
378
|
+
condition:
|
|
379
|
+
kind: shell
|
|
380
|
+
options:
|
|
381
|
+
command: >-
|
|
382
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
383
|
+
action:
|
|
384
|
+
kind: shell
|
|
385
|
+
options:
|
|
386
|
+
command: >-
|
|
387
|
+
kk stage daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
|
|
388
|
+
|
|
389
|
+
- id: generate
|
|
390
|
+
description: "Invoke the voice driver plugin (vdriver: gemini -> gemini-tts) to generate the audio"
|
|
391
|
+
# wrap-docs is a data dependency, not a sibling: daily-generate reads its _10_wrap-docs_docs.json
|
|
392
|
+
# as the TTS input. Without this edge the two nodes race in the same wave (0212 dogfood finding).
|
|
393
|
+
dependsOn: [script, safety, review, wrap-docs]
|
|
394
|
+
condition:
|
|
395
|
+
kind: shell
|
|
396
|
+
options:
|
|
397
|
+
command: >-
|
|
398
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
399
|
+
action:
|
|
400
|
+
kind: shell
|
|
401
|
+
options:
|
|
402
|
+
command: >-
|
|
403
|
+
OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}"
|
|
404
|
+
|
|
405
|
+
- id: intro
|
|
406
|
+
description: "Prepend the 8s intro sting (intro_music var; empty = skip), update duration/audioPath"
|
|
407
|
+
dependsOn: [generate]
|
|
408
|
+
action:
|
|
409
|
+
kind: shell
|
|
410
|
+
options:
|
|
411
|
+
command: >-
|
|
412
|
+
kk stage daily-intro "${vars.work_dir}" "${vars.run_date}" "${vars.intro_music}"
|
|
413
|
+
|
|
414
|
+
- id: duration
|
|
415
|
+
description: "Fail loud unless final episode duration sits in the 15–20 min window (duration authoritative)"
|
|
416
|
+
dependsOn: [intro]
|
|
417
|
+
action:
|
|
418
|
+
kind: shell
|
|
419
|
+
options:
|
|
420
|
+
command: >-
|
|
421
|
+
kk stage daily-episode-duration "${vars.work_dir}" "${vars.run_date}" "${vars.episode_min_minutes}" "${vars.episode_max_minutes}"
|
|
422
|
+
|
|
423
|
+
# ---- publish-prep branch ----
|
|
424
|
+
- id: publish-prep
|
|
425
|
+
description: "Merge article Content with the MP3 (rejects a non-.mp3 enclosure) and stamp its ID3 tags — the publish seam's input"
|
|
426
|
+
dependsOn: [wrap-docs]
|
|
427
|
+
condition:
|
|
428
|
+
kind: shell
|
|
429
|
+
options:
|
|
430
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
431
|
+
action:
|
|
432
|
+
kind: shell
|
|
433
|
+
options:
|
|
434
|
+
command: >-
|
|
435
|
+
kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
|
|
436
|
+
|
|
437
|
+
# ---- audio QC: fans in the audio branch and the publish seam ----
|
|
438
|
+
- id: quality
|
|
439
|
+
description: >-
|
|
440
|
+
Verify audio quality, speech rate, and silence/repetition QC metrics. Depends on the audio
|
|
441
|
+
tail, wrap-docs and publish-prep (the seam's merge is part of the audited bytes); publish-prep
|
|
442
|
+
being skipped on a non-publishing run does not skip the audit.
|
|
443
|
+
dependsOn: [duration, wrap-docs, publish-prep]
|
|
444
|
+
action:
|
|
445
|
+
kind: shell
|
|
446
|
+
options:
|
|
447
|
+
command: >-
|
|
448
|
+
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
449
|
+
|
|
450
|
+
- id: publish-surfdash
|
|
451
|
+
description: "Publish the merged Content to surfdash (single target); its result.json is what show-notes links and what patch-surfdash (folded into publish-classify) patches"
|
|
452
|
+
dependsOn: [publish-prep]
|
|
453
|
+
condition:
|
|
454
|
+
kind: shell
|
|
455
|
+
options:
|
|
456
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
457
|
+
action:
|
|
458
|
+
kind: shell
|
|
459
|
+
options:
|
|
460
|
+
command: >-
|
|
461
|
+
kk stage daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
462
|
+
|
|
463
|
+
- id: show-notes
|
|
464
|
+
description: "Render timeline show notes from the episode plan (podcast-pub show-notes op; article URL from the surfdash result postPath — a surfdash failure degrades to the legacy full-body layout)"
|
|
465
|
+
dependsOn: [quality, publish-surfdash]
|
|
466
|
+
action:
|
|
467
|
+
kind: shell
|
|
468
|
+
options:
|
|
469
|
+
command: >-
|
|
470
|
+
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
471
|
+
|
|
472
|
+
- id: publish-podcast
|
|
473
|
+
description: "Publish the episode through podcast-pub with the rendered show notes (metadata.showNotes)"
|
|
474
|
+
dependsOn: [show-notes]
|
|
475
|
+
condition:
|
|
476
|
+
kind: shell
|
|
477
|
+
options:
|
|
478
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
479
|
+
action:
|
|
480
|
+
kind: shell
|
|
481
|
+
options:
|
|
482
|
+
command: >-
|
|
483
|
+
kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
484
|
+
|
|
485
|
+
# ---- independent soft channels: xhs, wechat, locales (exit 0 on soft failure; fail-fast DAG) ----
|
|
486
|
+
- id: xhs-write
|
|
487
|
+
description: "Plan the XHS card series from the lead plan item via agent.run; onError: continue — a failed plan skips the XHS target, never the run"
|
|
488
|
+
dependsOn: [script, review]
|
|
489
|
+
condition:
|
|
490
|
+
kind: shell
|
|
491
|
+
options:
|
|
492
|
+
command: >-
|
|
493
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
494
|
+
&& test "${vars.xhs_enabled}" = "true"
|
|
495
|
+
action:
|
|
496
|
+
kind: agent.run
|
|
497
|
+
onError: continue
|
|
498
|
+
options:
|
|
499
|
+
role: scribe
|
|
500
|
+
agent: ${vars.agent}
|
|
501
|
+
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
502
|
+
input: |
|
|
503
|
+
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
|
|
504
|
+
Take item 1 only (its "title" and "metadata.brief") — the lead story.
|
|
505
|
+
Write an XHS note: "title" (≤20 chars) and "body" (Chinese note ≤1000 chars, ending with 3-5 hashtags). 选题边界:公共事务主线不写入,只写技术内容。
|
|
506
|
+
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).
|
|
507
|
+
Ground every "copy", "points" and "sourcePoint" in that story — no invented facts. No text, letters, numbers, logos or real people in "subject".
|
|
508
|
+
Output strictly valid JSON {"title": "...", "body": "...", "cards": [...]} to ${vars.work_dir}/25-xhs/${vars.run_date}_18_xhs_plan.json.
|
|
509
|
+
expectFile: ${vars.work_dir}/25-xhs/${vars.run_date}_18_xhs_plan.json
|
|
510
|
+
allowAppend: false
|
|
511
|
+
|
|
512
|
+
- id: xhs-render
|
|
513
|
+
description: "Render the card series through the pinned xhs-card/xhs-knowledge presets; onError: continue — the publish node names the missing series and stays soft"
|
|
514
|
+
dependsOn: [xhs-write]
|
|
515
|
+
action:
|
|
516
|
+
kind: shell
|
|
517
|
+
onError: continue
|
|
518
|
+
options:
|
|
519
|
+
command: >-
|
|
520
|
+
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}" "${vars.xhs_fallback}"
|
|
521
|
+
|
|
522
|
+
- id: xhs-publish
|
|
523
|
+
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, and xhs-pub's own headless preflight + publish cap make a sessionless run fail soft (exit 0)"
|
|
524
|
+
dependsOn: [xhs-render]
|
|
525
|
+
action:
|
|
526
|
+
kind: shell
|
|
527
|
+
options:
|
|
528
|
+
command: >-
|
|
529
|
+
kk stage daily-publish-xhs "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.xhs_live}"
|
|
530
|
+
|
|
531
|
+
- id: wechat-publish
|
|
532
|
+
description: "Publish the daily article to the WeChat MP draft box via wechatmp-pub — draft, or live only when wechat_live=true; independent and non-blocking (a sessionless profile refuses fast, exit 0)"
|
|
533
|
+
dependsOn: [publish-prep]
|
|
534
|
+
condition:
|
|
535
|
+
kind: shell
|
|
536
|
+
options:
|
|
537
|
+
command: 'test "${vars.publish_enabled}" = "true" && test "${vars.wechat_enabled}" = "true"'
|
|
538
|
+
action:
|
|
539
|
+
kind: shell
|
|
540
|
+
options:
|
|
541
|
+
command: >-
|
|
542
|
+
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}" "${vars.wechat_collection}"
|
|
543
|
+
|
|
544
|
+
- id: translate-localize
|
|
545
|
+
description: "Locales — surfdash-pub scaffolds the en/ja drafts (deterministic, plugin-owned) and writes one marker file per locale naming the draft to translate"
|
|
546
|
+
dependsOn: [publish-surfdash]
|
|
547
|
+
condition:
|
|
548
|
+
kind: shell
|
|
549
|
+
options:
|
|
550
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
551
|
+
action:
|
|
552
|
+
kind: shell
|
|
553
|
+
options:
|
|
554
|
+
command: >-
|
|
555
|
+
kk stage daily-translate-localize "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
556
|
+
|
|
557
|
+
- id: translate
|
|
558
|
+
description: >-
|
|
559
|
+
Agent translates both scaffolded locale drafts in place. The two draft paths are read from the
|
|
560
|
+
marker files at their fixed paths (no var projection after the fan-out: concurrent setVars is
|
|
561
|
+
last-writer-wins, design §6), and an empty marker means that locale is absent. Non-blocking:
|
|
562
|
+
the sync node and publish-classify treat the locales as an independent channel.
|
|
563
|
+
dependsOn: [translate-localize]
|
|
564
|
+
action:
|
|
565
|
+
kind: agent.run
|
|
566
|
+
options:
|
|
567
|
+
role: scribe
|
|
568
|
+
agent: ${vars.agent}
|
|
569
|
+
input: |
|
|
570
|
+
Translate the en/ja draft posts of the zh article. Read the two marker files at these fixed paths — each holds one draft path, or is empty when that locale was not scaffolded:
|
|
571
|
+
English: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_en-post-path.txt
|
|
572
|
+
Japanese: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_ja-post-path.txt
|
|
573
|
+
For each: if the marker is empty or unreadable, skip that locale. Otherwise edit the named file in place — translate the frontmatter title and the whole markdown body to natural English/Japanese respectively. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Set draft: false (the translated post publishes to the site) and remove any canonical: frontmatter line pointing at a different locale's URL.
|
|
574
|
+
Keep frontmatter valid YAML: double-quote any value containing ': '. Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
|
|
575
|
+
Do not wait for operator confirmation. Each edited file must end fully in its target language.
|
|
576
|
+
|
|
577
|
+
- id: translate-sync
|
|
578
|
+
description: "Sync the translated en/ja drafts into the surfdash CMS (the zh post path rides the marker file)"
|
|
579
|
+
dependsOn: [translate]
|
|
580
|
+
action:
|
|
581
|
+
kind: shell
|
|
582
|
+
options:
|
|
583
|
+
command: >-
|
|
584
|
+
kk stage daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt"
|
|
585
|
+
|
|
586
|
+
# ---- fan-in: classify the publish outcome, then close the run ----
|
|
587
|
+
- id: publish-classify
|
|
588
|
+
description: >-
|
|
589
|
+
Fan-in of every publish leaf (surfdash, podcast, xhs, wechat, locales) with policy `all`:
|
|
590
|
+
classifies the sequenced surfdash → podcast outcomes into publish-status.txt full|partial|none
|
|
591
|
+
(exit 0 in all three), records the independent channels' completeness, and folds in the
|
|
592
|
+
surfdash post-patching + the partial-publish marker that voice1 ran as separate states (a
|
|
593
|
+
classify is the only node that knows the status). Non-publishing runs skip the whole subtree.
|
|
594
|
+
dependsOn: [publish-podcast, publish-surfdash, xhs-publish, wechat-publish, translate-sync]
|
|
595
|
+
dependencyPolicy: all
|
|
596
|
+
condition:
|
|
597
|
+
kind: shell
|
|
598
|
+
options:
|
|
599
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
600
|
+
action:
|
|
601
|
+
kind: shell
|
|
602
|
+
options:
|
|
603
|
+
command: >-
|
|
604
|
+
kk daily publish-classify --work-dir "${vars.work_dir}" --run-date "${vars.run_date}"
|
|
605
|
+
--surfdash-root "${vars.surfdash_root}" --plugins-path "${vars.plugins_path}" --fold-tail
|
|
606
|
+
|
|
607
|
+
- id: mark-used
|
|
608
|
+
description: >-
|
|
609
|
+
Record the items this run used in the store's episode_usage (the cross-day dedup): runs after
|
|
610
|
+
publish-classify, so a run that failed to classify records nothing. Skipped on an empty pool
|
|
611
|
+
(there is no plan to mark).
|
|
612
|
+
dependsOn: [publish-classify, extract]
|
|
613
|
+
condition:
|
|
614
|
+
kind: shell
|
|
615
|
+
options:
|
|
616
|
+
command: >-
|
|
617
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
618
|
+
action:
|
|
619
|
+
kind: shell
|
|
620
|
+
options:
|
|
621
|
+
command: >-
|
|
622
|
+
kk store mark-used --workflow kk-daily-ai-voice2 --run "${vars.__runId}"
|
|
623
|
+
--in "${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json"
|
|
624
|
+
|
|
625
|
+
- id: run-report
|
|
626
|
+
description: >-
|
|
627
|
+
Assemble the per-run report from the existing artifacts (news-report-gen; output-only, never
|
|
628
|
+
throws) and record the pool outcome next to it: `no-content` when the store pool was empty or
|
|
629
|
+
filtered to zero items. It lists `extract` in dependsOn, so an empty pool still runs it (the
|
|
630
|
+
engine's skip rule: all-deps-skipped skips a node, a done+skipped mix proceeds).
|
|
631
|
+
dependsOn: [mark-used, publish-classify, extract]
|
|
632
|
+
action:
|
|
633
|
+
kind: shell
|
|
634
|
+
options:
|
|
635
|
+
command: >-
|
|
636
|
+
kk daily run-report --work-dir "${vars.work_dir}" --run-date "${vars.run_date}"
|
|
637
|
+
--plugins-path "${vars.plugins_path}" --qc-min-quality "${vars.qc_min_quality}"
|
|
638
|
+
--qc-min-importance "${vars.qc_min_importance}" --qc-min-urgency "${vars.qc_min_urgency}"
|
|
639
|
+
--qc-min-impact "${vars.qc_min_impact}" --pool "${vars.work_dir}/10-collect/pool.json"
|
|
640
|
+
--candidates "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json"
|
|
641
|
+
|
|
642
|
+
- id: ledger
|
|
643
|
+
description: >-
|
|
644
|
+
Final node: append this run's section to .works/kk-daily-ai-voice2/ledger.md. It lists
|
|
645
|
+
`extract` in dependsOn so an empty-pool run still records (with the run-report's `no-content`
|
|
646
|
+
line as its Result). onError: continue — a run's own trace is still `running` while a node
|
|
647
|
+
executes, so `kk ledger append` refuses it (exit 1); the next run's `prepare` backfills
|
|
648
|
+
through `kk ledger sync`.
|
|
649
|
+
dependsOn: [run-report, extract]
|
|
650
|
+
action:
|
|
651
|
+
kind: shell
|
|
652
|
+
onError: continue
|
|
653
|
+
options:
|
|
654
|
+
command: >-
|
|
655
|
+
kk ledger append --workflow kk-daily-ai-voice2 --run "${vars.__runId}" --work-dir "${vars.work_dir}"
|