@gobing-ai/knowledge-kit 0.0.11 → 0.0.13
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/dist/index.js +451 -152
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +22167 -0
- package/plugins/generations/content-gen/plugin.json +1 -1
- package/plugins/generations/content-gen/src/storm.ts +99 -50
- package/plugins/generations/core-facts-gen/dist/index.js +22048 -0
- package/plugins/generations/core-facts-gen/package.json +17 -0
- package/plugins/generations/core-facts-gen/plugin.json +7 -0
- package/plugins/generations/core-facts-gen/src/index.ts +116 -0
- package/plugins/generations/core-facts-gen/tsconfig.json +4 -0
- package/plugins/generations/daily-article-gen/dist/index.js +22022 -0
- package/plugins/generations/daily-article-gen/package.json +17 -0
- package/plugins/generations/daily-article-gen/plugin.json +7 -0
- package/plugins/generations/daily-article-gen/src/index.ts +91 -0
- package/plugins/generations/daily-article-gen/tsconfig.json +4 -0
- package/plugins/generations/dailynews-gen/dist/index.js +22324 -0
- package/plugins/generations/dailynews-gen/plugin.json +1 -1
- package/plugins/generations/dailynews-gen/src/index.ts +11 -0
- package/plugins/generations/dailynews-gen/src/script-builder.ts +1 -1
- package/plugins/generations/episode-plan-gen/dist/index.js +22479 -0
- package/plugins/generations/episode-plan-gen/package.json +17 -0
- package/plugins/generations/episode-plan-gen/plugin.json +7 -0
- package/plugins/generations/episode-plan-gen/src/index.ts +726 -0
- package/plugins/generations/episode-plan-gen/tsconfig.json +4 -0
- package/plugins/generations/image-gen/config.example.yaml +75 -0
- package/plugins/generations/image-gen/dist/index.js +22619 -0
- package/plugins/generations/image-gen/package.json +17 -0
- package/plugins/generations/image-gen/plugin.json +7 -0
- package/plugins/generations/image-gen/presets/formats/cover.yaml +57 -0
- package/plugins/generations/image-gen/presets/formats/free.yaml +46 -0
- package/plugins/generations/image-gen/presets/formats/illustration.yaml +48 -0
- package/plugins/generations/image-gen/presets/styles/clean-webapp-ui.yaml +28 -0
- package/plugins/generations/image-gen/presets/styles/cute.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/editorial.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/fresh.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/minimalist.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/photorealistic.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/sketch.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/technical-diagram.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/vibrant.yaml +3 -0
- package/plugins/generations/image-gen/presets/styles/warm.yaml +3 -0
- package/plugins/generations/image-gen/src/bytes.ts +19 -0
- package/plugins/generations/image-gen/src/index.ts +319 -0
- package/plugins/generations/image-gen/src/job.ts +143 -0
- package/plugins/generations/image-gen/src/paths.ts +31 -0
- package/plugins/generations/image-gen/src/presets.ts +344 -0
- package/plugins/generations/image-gen/src/providers/seedream.ts +128 -0
- package/plugins/generations/image-gen/src/providers/types.ts +285 -0
- package/plugins/generations/image-gen/tsconfig.json +8 -0
- package/plugins/generations/voice-gen/dist/index.js +23027 -0
- package/plugins/generations/voice-gen/plugin.json +1 -1
- package/plugins/generations/voice-gen/src/index.ts +102 -11
- package/plugins/generations/voice-gen/src/qc.ts +154 -9
- package/plugins/ingestions/aihot-ingest/dist/index.js +22358 -0
- package/plugins/ingestions/aihot-ingest/plugin.json +2 -2
- package/plugins/ingestions/aihot-ingest/src/index.ts +72 -13
- package/plugins/ingestions/aihot-ingest/src/mapper.ts +1 -0
- package/plugins/ingestions/aihot-ingest/src/rss.ts +151 -0
- package/plugins/ingestions/horizon-ingest/dist/index.js +22105 -0
- package/plugins/ingestions/horizon-ingest/package.json +17 -0
- package/plugins/ingestions/horizon-ingest/plugin.json +7 -0
- package/plugins/ingestions/horizon-ingest/src/index.ts +205 -0
- package/plugins/ingestions/horizon-ingest/tsconfig.json +4 -0
- package/plugins/ingestions/karakeep-local/dist/index.js +24204 -0
- package/plugins/ingestions/karakeep-local/plugin.json +1 -1
- package/plugins/ingestions/last30days-ingest/dist/index.js +22050 -0
- package/plugins/ingestions/last30days-ingest/package.json +17 -0
- package/plugins/ingestions/last30days-ingest/plugin.json +7 -0
- package/plugins/ingestions/last30days-ingest/src/index.ts +148 -0
- package/plugins/ingestions/last30days-ingest/tsconfig.json +4 -0
- package/plugins/ingestions/web-search/dist/index.js +24379 -0
- package/plugins/ingestions/web-search/plugin.json +1 -1
- package/plugins/kk/commands/image-generate.md +15 -0
- package/plugins/kk/config.example.yaml +80 -0
- package/plugins/kk/skills/image-authoring/SKILL.md +250 -0
- package/plugins/kk/skills/image-authoring/references/illustration-positions.md +87 -0
- package/plugins/kk/skills/image-authoring/references/migrating-from-wt.md +31 -0
- package/plugins/kk/skills/image-authoring/references/providers.md +52 -0
- package/plugins/kk/skills/image-authoring/references/style-extraction.md +134 -0
- package/plugins/kk/skills/taste-unslop/SKILL.md +12 -6
- package/plugins/kk/skills/taste-unslop/references/pattern-guide.md +128 -48
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +432 -19
- package/plugins/publishings/emdash-pub/dist/index.js +22243 -0
- package/plugins/publishings/emdash-pub/plugin.json +1 -1
- package/plugins/publishings/podcast-pub/dist/index.js +22596 -0
- package/plugins/publishings/podcast-pub/package.json +17 -0
- package/plugins/publishings/podcast-pub/plugin.json +13 -0
- package/plugins/publishings/podcast-pub/src/index.ts +538 -0
- package/plugins/publishings/podcast-pub/src/map.ts +165 -0
- package/plugins/publishings/podcast-pub/src/microfeed-client.ts +196 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +132 -0
- package/plugins/publishings/podcast-pub/tsconfig.json +4 -0
- package/plugins/publishings/qiita-pub/dist/index.js +22081 -0
- package/plugins/publishings/qiita-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/dist/index.js +22233 -0
- package/plugins/publishings/surfdash-pub/plugin.json +1 -1
- package/plugins/publishings/surfdash-pub/src/index.ts +328 -62
- package/plugins/publishings/zenn-pub/dist/index.js +22122 -0
- package/plugins/publishings/zenn-pub/plugin.json +1 -1
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
name: kk-daily-ai-voice
|
|
4
4
|
kind: state-machine
|
|
5
5
|
description: >-
|
|
6
|
-
Daily AI news
|
|
6
|
+
Daily AI news end-to-end pipeline (F61 destination): collect (fan-in --source: aihot + last30days
|
|
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
|
|
7
9
|
initialState: prepare
|
|
8
10
|
terminalStates:
|
|
9
11
|
- done
|
|
12
|
+
- done-no-content
|
|
10
13
|
- failed
|
|
11
14
|
failureStates:
|
|
12
15
|
- failed
|
|
@@ -15,46 +18,152 @@ vars:
|
|
|
15
18
|
work_dir: ""
|
|
16
19
|
limit: "10"
|
|
17
20
|
language: "zh"
|
|
18
|
-
voice_profile: "
|
|
21
|
+
voice_profile: "robin-news"
|
|
19
22
|
title: ""
|
|
20
23
|
cursor: ""
|
|
21
24
|
state_file: ""
|
|
22
25
|
skip_review: "false"
|
|
23
26
|
transcode_mp3: "false"
|
|
27
|
+
cover_enabled: "true"
|
|
28
|
+
plan_max_items: "20"
|
|
29
|
+
plan_translate_enabled: "true"
|
|
24
30
|
plugins_path: ""
|
|
31
|
+
last30days_topic: "AI news"
|
|
32
|
+
horizon_hours: "24"
|
|
33
|
+
publish_enabled: "false"
|
|
34
|
+
last30days_enabled: "false"
|
|
35
|
+
run_date: ""
|
|
36
|
+
agent: "auto"
|
|
37
|
+
surfdash_post_path: ""
|
|
38
|
+
surfdash_en_post_path: ""
|
|
39
|
+
surfdash_ja_post_path: ""
|
|
40
|
+
qc_categories: "ai-news,ai-industry"
|
|
41
|
+
# 0098 dogfood calibration (r7, owner-approved 0099): quality 3→2, impact 2→1 — the shipped
|
|
42
|
+
# defaults rejected 20/20 live candidates (strongest ai-news items died solely on impact:1<2).
|
|
43
|
+
# These pins must move in lockstep with episode-plan-gen's DEFAULT_FILTER_CONFIG (env wins).
|
|
44
|
+
qc_min_quality: "2"
|
|
45
|
+
qc_min_importance: "3"
|
|
46
|
+
qc_min_urgency: "2"
|
|
47
|
+
qc_min_impact: "1"
|
|
25
48
|
__hitlAnswer: ""
|
|
26
49
|
|
|
27
50
|
states:
|
|
28
51
|
- id: prepare
|
|
29
|
-
description: "Create workspace subdirectories and validate parameters"
|
|
52
|
+
description: "Create workspace subdirectories and validate parameters; write source configs"
|
|
30
53
|
onEnter:
|
|
31
54
|
- kind: shell
|
|
32
55
|
options:
|
|
33
56
|
command: >-
|
|
34
57
|
test -n "${vars.work_dir}" || { echo "missing required variable: work_dir" >&2 && exit 1; };
|
|
35
|
-
mkdir -p "${vars.work_dir}" "${vars.work_dir}/
|
|
36
|
-
|
|
58
|
+
mkdir -p "${vars.work_dir}/1-ingest" "${vars.work_dir}/2-facts" "${vars.work_dir}/2-plan" "${vars.work_dir}/2-article" "${vars.work_dir}/2-cover" "${vars.work_dir}/2-script" "${vars.work_dir}/3-audio" "${vars.work_dir}/3-publish";
|
|
59
|
+
date +%Y%m%d > "${vars.work_dir}/run-date.txt" &&
|
|
60
|
+
bun -e 'await Bun.write(process.argv[1], JSON.stringify({ limit: Number(process.argv[2]), cursor: process.argv[3] || undefined, stateFile: process.argv[4] || undefined }, null, 2))' "${vars.work_dir}/1-ingest/source.json" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" &&
|
|
61
|
+
if [ "${vars.last30days_enabled}" != "true" ]; then printf '[]' > "${vars.work_dir}/1-ingest/last30days-docs.json"; fi &&
|
|
62
|
+
bun -e 'await Bun.write(process.argv[1], JSON.stringify({ topic: process.argv[2] }, null, 2))' "${vars.work_dir}/1-ingest/last30days.json" "${vars.last30days_topic}" &&
|
|
63
|
+
bun -e 'await Bun.write(process.argv[1], JSON.stringify({ hours: Number(process.argv[2]) }, null, 2))' "${vars.work_dir}/1-ingest/horizon.json" "${vars.horizon_hours}"
|
|
64
|
+
- kind: file.read.into-var
|
|
65
|
+
options:
|
|
66
|
+
path: ${vars.work_dir}/run-date.txt
|
|
67
|
+
var: run_date
|
|
68
|
+
|
|
69
|
+
- id: collect
|
|
70
|
+
description: "Concurrently ingest all sources via kk executor fan-in --source, then render the dated core-facts markdown"
|
|
71
|
+
onEnter:
|
|
72
|
+
- kind: shell
|
|
73
|
+
options:
|
|
74
|
+
# 0098 dogfood: horizon-ingest latency grew 20m40s (r4) → 28m39s (r7), straddling the
|
|
75
|
+
# fan-in 30m per-source default and making empty episodes stochastic — horizon gets a
|
|
76
|
+
# 45m leash; aihot/last30days keep the default.
|
|
77
|
+
command: >-
|
|
78
|
+
if [ -f "${vars.work_dir}/1-ingest/blended.json" ]; then echo "collect cached: ${vars.work_dir}/1-ingest/blended.json"; exit 0; fi;
|
|
79
|
+
kk executor fan-in --source "aihot-ingest:${vars.work_dir}/1-ingest/source.json" --source "last30days-ingest:${vars.work_dir}/1-ingest/last30days.json" --source "horizon-ingest:${vars.work_dir}/1-ingest/horizon.json" --source-timeout-override "horizon-ingest:2700000" --out "${vars.work_dir}/1-ingest/blended.json"
|
|
80
|
+
- kind: shell
|
|
81
|
+
options:
|
|
82
|
+
command: >-
|
|
83
|
+
set --;
|
|
84
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
85
|
+
if [ -f "${vars.work_dir}/2-facts/content.json" ] && [ -f "${vars.work_dir}/2-facts/core-facts.md" ]; then echo "core facts cached: ${vars.work_dir}/2-facts/core-facts.md"; exit 0; fi;
|
|
86
|
+
kk executor run core-facts-gen --in "${vars.work_dir}/1-ingest/blended.json" --out "${vars.work_dir}/2-facts/content.json" "$@" &&
|
|
87
|
+
bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "${vars.work_dir}/2-facts/content.json" "${vars.work_dir}/2-facts/core-facts.md"
|
|
88
|
+
|
|
89
|
+
- id: plan
|
|
90
|
+
description: "Score/dedup/select + annotate the blended Doc[] into the episode plan (episode-plan-gen; annotate rides inside planEpisode)"
|
|
91
|
+
onEnter:
|
|
92
|
+
- kind: shell
|
|
93
|
+
options:
|
|
94
|
+
command: >-
|
|
95
|
+
set --;
|
|
96
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
97
|
+
PLAN_CONTENT="${vars.work_dir}/2-plan/content.json"; PLAN_DOCS="${vars.work_dir}/2-plan/plan.json";
|
|
98
|
+
if [ -f "$PLAN_DOCS" ]; then echo "plan cached: $PLAN_DOCS"; exit 0; fi;
|
|
99
|
+
EPISODE_PLAN_MAX_ITEMS="${vars.plan_max_items}" EPISODE_PLAN_DATE="${vars.run_date}" kk executor run episode-plan-gen --in "${vars.work_dir}/1-ingest/blended.json" --out "$PLAN_CONTENT" "$@" &&
|
|
100
|
+
bun -e 'const content = JSON.parse(await Bun.file(process.argv[1]).text()); const docs = content.metadata?.docs; if (!Array.isArray(docs) || docs.length === 0) { console.error("plan: content.metadata.docs missing or empty"); process.exit(1); } await Bun.write(process.argv[2], JSON.stringify(docs, null, 2))' "$PLAN_CONTENT" "$PLAN_DOCS" &&
|
|
101
|
+
test -f "$PLAN_DOCS" || { echo "plan: $PLAN_DOCS not written after episode-plan-gen"; exit 1; }
|
|
102
|
+
|
|
103
|
+
- id: quality-control-content
|
|
104
|
+
description: "Content QC: filter the plan's annotated candidates by category allowlist + score thresholds (episode-plan-gen --mode filter); zero candidates route to done-no-content"
|
|
105
|
+
onEnter:
|
|
106
|
+
- kind: shell
|
|
107
|
+
options:
|
|
108
|
+
command: >-
|
|
109
|
+
set --;
|
|
110
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
111
|
+
if [ -f "${vars.work_dir}/2-plan/candidates.json" ]; then echo "candidates cached: ${vars.work_dir}/2-plan/candidates.json"; exit 0; fi;
|
|
112
|
+
jq '.metadata.docs' "${vars.work_dir}/2-plan/content.json" > "${vars.work_dir}/2-plan/annotated.json" &&
|
|
113
|
+
EPISODE_PLAN_MODE=filter QC_CATEGORIES="${vars.qc_categories}" QC_MIN_QUALITY="${vars.qc_min_quality}" QC_MIN_IMPORTANCE="${vars.qc_min_importance}" QC_MIN_URGENCY="${vars.qc_min_urgency}" QC_MIN_IMPACT="${vars.qc_min_impact}" kk executor run episode-plan-gen --in "${vars.work_dir}/2-plan/annotated.json" --out "${vars.work_dir}/2-plan/candidates.json" "$@"
|
|
114
|
+
|
|
115
|
+
- id: plan-translate
|
|
116
|
+
description: "Translate needsTranslation plan items into the output plan.json via agent.run"
|
|
117
|
+
onEnter:
|
|
118
|
+
- kind: agent.run
|
|
119
|
+
options:
|
|
120
|
+
role: scribe
|
|
121
|
+
agent: ${vars.agent}
|
|
122
|
+
input: |
|
|
123
|
+
Read the JSON file at ${vars.work_dir}/2-plan/candidates.json. Its metadata.docs is a JSON array of news items (Doc[]) — this is the episode plan to translate. Do not edit candidates.json.
|
|
124
|
+
For every item with needsTranslation: translate its "title" and "body" fields into natural, concise Chinese (simplified).
|
|
125
|
+
Titles must read as a news event, not a bare label: derive the title from the item's body (first sentence carries the event) so "Sora 2" becomes e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型" — subject + action + object. Keep product/model/company names verbatim.
|
|
126
|
+
Keep everything else untouched: metadata (including needsTranslation, planScore, mergedFrom, urls), sourceUri, id, mediaType.
|
|
127
|
+
Preserve any URLs inside the body text. Keep technical terms (product/model/company names) in their original form where that is clearer.
|
|
128
|
+
The output must be strictly valid JSON: escape every double quote inside string values as \", never use raw ASCII double quotes inside Chinese text (use 「」 or \"), and produce no trailing commas or comments.
|
|
129
|
+
Write the translated array — a valid JSON array of the same length, same item order, same ids — to ${vars.work_dir}/2-plan/plan.json (create the file).
|
|
130
|
+
expectFile: ${vars.work_dir}/2-plan/plan.json
|
|
131
|
+
allowAppend: false
|
|
37
132
|
|
|
38
|
-
- id:
|
|
39
|
-
description: "
|
|
133
|
+
- id: article
|
|
134
|
+
description: "Generate the dated daily article from the episode plan (daily-article-gen template)"
|
|
40
135
|
onEnter:
|
|
41
136
|
- kind: shell
|
|
42
137
|
options:
|
|
43
138
|
command: >-
|
|
44
139
|
set --;
|
|
45
140
|
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
46
|
-
|
|
141
|
+
CONTENT_OUT="${vars.work_dir}/2-article/content-${vars.run_date}.json"; ARTICLE_MD="${vars.work_dir}/2-article/article-${vars.run_date}.md";
|
|
142
|
+
if [ -f "$ARTICLE_MD" ]; then echo "article cached: $ARTICLE_MD"; else PLAN_IN="${vars.work_dir}/2-plan/plan.json"; PLAN_OK=1; bun -e 'try { JSON.parse(await Bun.file(process.argv[1]).text()); process.exit(0); } catch (e) { console.warn("article: plan.json invalid JSON — replacing with untranslated plan (downstream states read this file): " + e.message); const docs = JSON.parse(await Bun.file(process.argv[2]).text()).metadata?.docs; if (!Array.isArray(docs) || docs.length === 0) { console.error("article: candidates.json metadata.docs missing"); process.exit(2); } await Bun.write(process.argv[1], JSON.stringify(docs, null, 2)); process.exit(1); }' "$PLAN_IN" "${vars.work_dir}/2-plan/candidates.json"; case $? in 0|1) PLAN_OK=0;; esac; test "$PLAN_OK" -eq 0 || { echo "article: no usable plan input"; exit 1; }; ARTICLE_DATE="${vars.run_date}" kk executor run daily-article-gen --in "$PLAN_IN" --out "$CONTENT_OUT" "$@" && bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "$CONTENT_OUT" "$ARTICLE_MD"; fi
|
|
143
|
+
|
|
144
|
+
- id: cover
|
|
145
|
+
description: "Generate the article cover image via the wt-image-cover skill (agent.run)"
|
|
146
|
+
onEnter:
|
|
147
|
+
- kind: agent.run
|
|
148
|
+
options:
|
|
149
|
+
role: scribe
|
|
150
|
+
agent: ${vars.agent}
|
|
151
|
+
input: |
|
|
152
|
+
Invoke the wt-image-cover skill.
|
|
153
|
+
Generate a 21:9 cover image for the article at ${vars.work_dir}/2-article/article-${vars.run_date}.md and save it to ${vars.work_dir}/2-cover/cover-${vars.run_date}.png.
|
|
154
|
+
Use style "news". If no image-generation backend is available, a procedurally rendered cover (e.g. PIL: news palette, headline typography) is an acceptable, expected deliverable — not a fake; generate it and write the file. Do not wait for operator confirmation.
|
|
155
|
+
expectFile: ${vars.work_dir}/2-cover/cover-${vars.run_date}.png
|
|
47
156
|
|
|
48
157
|
- id: script
|
|
49
|
-
description: "Compile
|
|
158
|
+
description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
|
|
50
159
|
onEnter:
|
|
51
160
|
- kind: shell
|
|
52
161
|
options:
|
|
53
162
|
command: >-
|
|
54
163
|
set --;
|
|
55
164
|
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
56
|
-
|
|
57
|
-
bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "$
|
|
165
|
+
SCRIPT_CONTENT_OUT="${vars.work_dir}/2-script/content-${vars.run_date}.json"; VOICESCRIPT_YAML="${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml";
|
|
166
|
+
if [ -f "$VOICESCRIPT_YAML" ]; then echo "voicescript capped by plan: $VOICESCRIPT_YAML"; else DAILYNEWS_LANGUAGE="${vars.language}" DAILYNEWS_TITLE="${vars.title}" VOICEBOX_DEFAULT_PROFILE="${vars.voice_profile}" kk executor run dailynews-gen --in "${vars.work_dir}/2-plan/plan.json" --out "$SCRIPT_CONTENT_OUT" "$@" && bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "$SCRIPT_CONTENT_OUT" "$VOICESCRIPT_YAML"; fi
|
|
58
167
|
|
|
59
168
|
- id: review
|
|
60
169
|
description: "Operator reviews the generated news script before speech synthesis"
|
|
@@ -62,7 +171,7 @@ states:
|
|
|
62
171
|
onEnter:
|
|
63
172
|
- kind: hitl.confirm
|
|
64
173
|
options:
|
|
65
|
-
prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/voicescript.yaml. Proceed to voice generation?"
|
|
174
|
+
prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml. Proceed to voice generation?"
|
|
66
175
|
var: __hitlAnswer
|
|
67
176
|
|
|
68
177
|
- id: wrap-docs
|
|
@@ -71,7 +180,7 @@ states:
|
|
|
71
180
|
- kind: shell
|
|
72
181
|
options:
|
|
73
182
|
command: >-
|
|
74
|
-
bun -e 'const scriptUrl = new URL(process.argv[1], "file://" + process.cwd() + "/"); const scriptPath = decodeURIComponent(scriptUrl.pathname); const doc = { id: new Bun.CryptoHasher("sha256").update(scriptUrl.href).digest("hex").slice(0, 16), title: "Daily AI News VoiceScript", sourceUri: scriptUrl.href, body: await Bun.file(scriptPath).text(), mediaType: "application/yaml", metadata: { voiceProfile: process.argv[3] || process.env.VOICEBOX_DEFAULT_PROFILE || "
|
|
183
|
+
bun -e 'const scriptUrl = new URL(process.argv[1], "file://" + process.cwd() + "/"); const scriptPath = decodeURIComponent(scriptUrl.pathname); const doc = { id: new Bun.CryptoHasher("sha256").update(scriptUrl.href).digest("hex").slice(0, 16), title: "Daily AI News VoiceScript", sourceUri: scriptUrl.href, body: await Bun.file(scriptPath).text(), mediaType: "application/yaml", metadata: { voiceProfile: process.argv[3] || process.env.VOICEBOX_DEFAULT_PROFILE || "robin-news" } }; await Bun.write(process.argv[2], JSON.stringify([doc], null, 2))' "${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml" "${vars.work_dir}/3-audio/docs.json" "${vars.voice_profile}"
|
|
75
184
|
|
|
76
185
|
- id: generate
|
|
77
186
|
description: "Invoke voice-gen generator plugin to generate audio"
|
|
@@ -81,6 +190,7 @@ states:
|
|
|
81
190
|
command: >-
|
|
82
191
|
set --;
|
|
83
192
|
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
193
|
+
if [ -f "${vars.work_dir}/3-audio/content.json" ] && jq -e '.metadata.qc.passed == true' "${vars.work_dir}/3-audio/content.json" > /dev/null 2>&1; then echo "audio cached: ${vars.work_dir}/3-audio/content.json (QC passed)"; exit 0; fi;
|
|
84
194
|
VOICE_GEN_MP3="${vars.transcode_mp3}" kk executor run voice-gen --in "${vars.work_dir}/3-audio/docs.json" --out "${vars.work_dir}/3-audio/content.json" "$@"
|
|
85
195
|
|
|
86
196
|
- id: quality-control
|
|
@@ -91,21 +201,220 @@ states:
|
|
|
91
201
|
command: >-
|
|
92
202
|
bun -e 'const content = JSON.parse(await Bun.file(process.argv[1]).text()); const qc = content.metadata?.qc; console.log("=== Voice Quality Control Report ==="); console.log("Overall Score: " + (qc ? qc.overallScore : 100) + "/100 | Passed: " + (qc ? qc.passed : true)); if (qc && qc.criticalIssues && qc.criticalIssues.length) { console.warn("QC Issues:\n" + qc.criticalIssues.map(function(i) { return " - " + i; }).join("\n")); } else { console.log("All audio segments passed quality control checks cleanly."); }' "${vars.work_dir}/3-audio/content.json"
|
|
93
203
|
|
|
204
|
+
- id: show-notes
|
|
205
|
+
description: "Render uniform show notes from the episode plan (podcast-pub show-notes operation; PODCAST_PUB_TITLE carries the effective title vars.title || article.title)"
|
|
206
|
+
onEnter:
|
|
207
|
+
- kind: shell
|
|
208
|
+
options:
|
|
209
|
+
command: >-
|
|
210
|
+
set --;
|
|
211
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
212
|
+
TITLE_ARG="${vars.title}";
|
|
213
|
+
EFFECTIVE_TITLE="$(jq -r '.title // ""' "${vars.work_dir}/2-article/content-${vars.run_date}.json")";
|
|
214
|
+
if [ -z "$TITLE_ARG" ]; then TITLE_ARG="$EFFECTIVE_TITLE"; fi;
|
|
215
|
+
PODCAST_PUB_TITLE="$TITLE_ARG" PODCAST_PUB_RUN_DATE="${vars.run_date}" kk executor run podcast-pub --in "${vars.work_dir}/2-plan/plan.json" --out "${vars.work_dir}/3-publish/show-notes.md" "$@"
|
|
216
|
+
|
|
217
|
+
- id: publish-prep
|
|
218
|
+
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)"
|
|
219
|
+
onEnter:
|
|
220
|
+
- kind: shell
|
|
221
|
+
options:
|
|
222
|
+
command: >-
|
|
223
|
+
bun -e 'const article = JSON.parse(await Bun.file(process.argv[1]).text()); const audio = JSON.parse(await Bun.file(process.argv[2]).text()); const audioMeta = audio.metadata ?? {}; const rawFile = audioMeta.mp3Path ?? audioMeta.audioPath; if (!rawFile) { console.error("publish-prep: voice content carries no mp3Path/audioPath"); process.exit(1); } const { resolve } = await import("node:path"); const { existsSync } = await import("node:fs"); const audioFile = resolve(rawFile); const coverPath = resolve(process.argv[4]); const merged = { ...article, metadata: { ...(article.metadata ?? {}), audioFile, runDate: process.argv[5] }, options: { audioFile, durationSec: audioMeta.duration, coverPath: existsSync(coverPath) ? coverPath : undefined } }; await Bun.write(process.argv[3], JSON.stringify(merged, null, 2))' "${vars.work_dir}/2-article/content-${vars.run_date}.json" "${vars.work_dir}/3-audio/content.json" "${vars.work_dir}/3-publish/content.json" "${vars.work_dir}/2-cover/cover-${vars.run_date}.png" "${vars.run_date}"
|
|
224
|
+
|
|
225
|
+
- id: publish
|
|
226
|
+
description: "Fan out the merged Content to surfdash-pub and podcast-pub; classify the outcome full|partial|none into publish-status.txt (exit 0 in all three — routing is transition-owned)"
|
|
227
|
+
onEnter:
|
|
228
|
+
- kind: shell
|
|
229
|
+
options:
|
|
230
|
+
command: >-
|
|
231
|
+
set --;
|
|
232
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
233
|
+
RESULT="${vars.work_dir}/3-publish/result.json"; STATUS_FILE="${vars.work_dir}/3-publish/publish-status.txt";
|
|
234
|
+
if jq -e '.ok == true and ([.targets[]? | select(.ok == true and .status == "published")] | length) == 2' "$RESULT" > /dev/null 2>&1; then echo "publish cached: $RESULT (2 targets ok)"; printf 'full' > "$STATUS_FILE"; exit 0; fi;
|
|
235
|
+
OK_COUNT=0; TOTAL=0; STATUS="none";
|
|
236
|
+
env -u SPUR_WORKFLOW_RUN_ACTIVE kk executor fan-out --content "${vars.work_dir}/3-publish/content.json" --target surfdash-pub --target podcast-pub --out "$RESULT" "$@" || true;
|
|
237
|
+
if [ -f "$RESULT" ]; then OK_COUNT="$(jq '[.targets[]? | select(.ok == true and .status == "published")] | length' "$RESULT")"; TOTAL="$(jq '[.targets[]?] | length' "$RESULT")"; if [ "$OK_COUNT" -gt 0 ] && [ "$OK_COUNT" -eq "$TOTAL" ]; then STATUS="full"; elif [ "$OK_COUNT" -gt 0 ]; then STATUS="partial"; fi; else echo "publish: fan-out wrote no result.json — routing to none" >&2; fi;
|
|
238
|
+
printf '%s' "$STATUS" > "$STATUS_FILE";
|
|
239
|
+
echo "publish: status=$STATUS ($OK_COUNT/$TOTAL targets published); routing is transition-owned"
|
|
240
|
+
|
|
241
|
+
- id: publish-partial
|
|
242
|
+
description: "Partial publish — at least one target published, at least one failed; echo warning, write a .spur/run marker, preserve result.json for --retry-from, continue to done"
|
|
243
|
+
onEnter:
|
|
244
|
+
- kind: shell
|
|
245
|
+
options:
|
|
246
|
+
command: >-
|
|
247
|
+
FAILED="$(jq -r '[.targets[]? | select(.ok != true or .status != "published") | .target] | join(",")' "${vars.work_dir}/3-publish/result.json")";
|
|
248
|
+
if [ -z "$FAILED" ]; then FAILED="(unparsed)"; fi;
|
|
249
|
+
echo "WARNING: partial publish — failed targets: $FAILED" >&2;
|
|
250
|
+
mkdir -p .spur/run &&
|
|
251
|
+
printf '%s partial publish: failed targets: %s (result.json preserved for kk executor fan-out --retry-from)\n' "${vars.run_date}" "$FAILED" > ".spur/run/publish-partial-${vars.run_date}.txt"
|
|
252
|
+
|
|
253
|
+
- id: translate-en
|
|
254
|
+
description: "Scaffold en draft from the zh article via surfdash-pub scaffold-locale (postPath from publish result; done-no-content/none-publish runs carry no zh path and skip)"
|
|
255
|
+
onEnter:
|
|
256
|
+
- kind: shell
|
|
257
|
+
options:
|
|
258
|
+
command: >-
|
|
259
|
+
RESULT="${vars.work_dir}/3-publish/result.json";
|
|
260
|
+
ZH="$(jq -r '[(.targets // [])[] | select(.target == "surfdash-pub" and .ok == true) | .metadata.postPath // empty] | first // ""' "$RESULT")";
|
|
261
|
+
case "$ZH" in *index.md) ;; *) ZH="";; esac;
|
|
262
|
+
printf '%s' "$ZH" > "${vars.work_dir}/3-publish/zh-post-path.txt";
|
|
263
|
+
EN="$(printf '%s' "$ZH" | sed 's|/zh/|/en/|')";
|
|
264
|
+
printf '%s' "$EN" > "${vars.work_dir}/3-publish/en-post-path.txt";
|
|
265
|
+
test -n "$ZH" || { echo "no zh post path — skip translate-en"; exit 0; };
|
|
266
|
+
if [ -f "$EN" ]; then echo "en draft cached: $EN"; exit 0; fi;
|
|
267
|
+
set --;
|
|
268
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
269
|
+
jq -n --arg postPath "$ZH" --arg target en '{body: "", options: {operation: "scaffold-locale", postPath: $postPath, target: $target}}' > "${vars.work_dir}/3-publish/scaffold-en.json" &&
|
|
270
|
+
kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/scaffold-en.json" --out "${vars.work_dir}/3-publish/scaffold-en-result.json" "$@" &&
|
|
271
|
+
jq -r '.metadata.draftPath // ""' "${vars.work_dir}/3-publish/scaffold-en-result.json" > "${vars.work_dir}/3-publish/en-post-path.txt"
|
|
272
|
+
- kind: file.read.into-var
|
|
273
|
+
options:
|
|
274
|
+
path: ${vars.work_dir}/3-publish/zh-post-path.txt
|
|
275
|
+
var: surfdash_post_path
|
|
276
|
+
- kind: file.read.into-var
|
|
277
|
+
options:
|
|
278
|
+
path: ${vars.work_dir}/3-publish/en-post-path.txt
|
|
279
|
+
var: surfdash_en_post_path
|
|
280
|
+
|
|
281
|
+
- id: translate-en-agent
|
|
282
|
+
description: "Translate the en draft in place via agent.run"
|
|
283
|
+
onEnter:
|
|
284
|
+
- kind: agent.run
|
|
285
|
+
options:
|
|
286
|
+
role: scribe
|
|
287
|
+
agent: ${vars.agent}
|
|
288
|
+
input: |
|
|
289
|
+
Translate the English draft post at ${vars.surfdash_en_post_path} from Chinese to English.
|
|
290
|
+
Edit the file in place: translate the title in frontmatter and the whole markdown body to natural English. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Keep the draft: true flag.
|
|
291
|
+
Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
|
|
292
|
+
Do not wait for operator confirmation. The file must end fully in English.
|
|
293
|
+
expectFile: ${vars.surfdash_en_post_path}
|
|
294
|
+
|
|
295
|
+
- id: translate-ja
|
|
296
|
+
description: "Scaffold ja draft from the zh article via surfdash-pub scaffold-locale (sed-derived path as cache pre-check; plugin's draftPath is authoritative once scaffolded)"
|
|
297
|
+
onEnter:
|
|
298
|
+
- kind: shell
|
|
299
|
+
options:
|
|
300
|
+
command: >-
|
|
301
|
+
ZH="$(cat "${vars.work_dir}/3-publish/zh-post-path.txt" 2>/dev/null || printf '')";
|
|
302
|
+
JA="$(printf '%s' "$ZH" | sed 's|/zh/|/ja/|')";
|
|
303
|
+
printf '%s' "$JA" > "${vars.work_dir}/3-publish/ja-post-path.txt";
|
|
304
|
+
test -n "$ZH" || { echo "no zh post path — skip translate-ja"; exit 0; };
|
|
305
|
+
if [ -f "$JA" ]; then echo "ja draft cached: $JA"; exit 0; fi;
|
|
306
|
+
set --;
|
|
307
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
308
|
+
jq -n --arg postPath "$ZH" --arg target ja '{body: "", options: {operation: "scaffold-locale", postPath: $postPath, target: $target}}' > "${vars.work_dir}/3-publish/scaffold-ja.json" &&
|
|
309
|
+
kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/scaffold-ja.json" --out "${vars.work_dir}/3-publish/scaffold-ja-result.json" "$@" &&
|
|
310
|
+
JA="$(jq -r '.metadata.draftPath // ""' "${vars.work_dir}/3-publish/scaffold-ja-result.json")" &&
|
|
311
|
+
printf '%s' "$JA" > "${vars.work_dir}/3-publish/ja-post-path.txt"
|
|
312
|
+
- kind: file.read.into-var
|
|
313
|
+
options:
|
|
314
|
+
path: ${vars.work_dir}/3-publish/ja-post-path.txt
|
|
315
|
+
var: surfdash_ja_post_path
|
|
316
|
+
|
|
317
|
+
- id: translate-ja-agent
|
|
318
|
+
description: "Translate the ja draft in place via agent.run"
|
|
319
|
+
onEnter:
|
|
320
|
+
- kind: agent.run
|
|
321
|
+
options:
|
|
322
|
+
role: scribe
|
|
323
|
+
agent: ${vars.agent}
|
|
324
|
+
input: |
|
|
325
|
+
Translate the Japanese draft post at ${vars.surfdash_ja_post_path} from Chinese to Japanese.
|
|
326
|
+
Edit the file in place: translate the title in frontmatter and the whole markdown body to natural Japanese. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Keep the draft: true flag.
|
|
327
|
+
Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
|
|
328
|
+
Do not wait for operator confirmation. The file must end fully in Japanese.
|
|
329
|
+
expectFile: ${vars.surfdash_ja_post_path}
|
|
330
|
+
|
|
331
|
+
- id: translate-sync
|
|
332
|
+
description: "Sync the zh post and its en/ja locale variants into the surfdash CMS (surfdash-pub sync operation)"
|
|
333
|
+
onEnter:
|
|
334
|
+
- kind: shell
|
|
335
|
+
options:
|
|
336
|
+
command: >-
|
|
337
|
+
test -n "${vars.surfdash_post_path}" || { echo "no zh post path — skip translate-sync"; exit 0; };
|
|
338
|
+
set --;
|
|
339
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
340
|
+
jq -n --arg postPath "${vars.surfdash_post_path}" '{body: "", options: {operation: "sync", postPath: $postPath}}' > "${vars.work_dir}/3-publish/sync.json" &&
|
|
341
|
+
kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/sync.json" --out "${vars.work_dir}/3-publish/sync-result.json" "$@"
|
|
342
|
+
|
|
94
343
|
- id: done
|
|
95
|
-
description: "Terminal — Daily AI news voice
|
|
344
|
+
description: "Terminal — Daily AI news pipeline completed (collect → QC'd plan → article/cover/script/voice → publish/locales)"
|
|
345
|
+
|
|
346
|
+
- id: done-no-content
|
|
347
|
+
description: "Terminal — quality-control-content admitted zero candidates under the QC thresholds; no article/script/audio/publish artifacts were produced"
|
|
348
|
+
|
|
96
349
|
- id: failed
|
|
97
350
|
description: "Terminal — Pipeline failed"
|
|
98
351
|
|
|
99
352
|
transitions:
|
|
100
353
|
- from: prepare
|
|
101
|
-
to:
|
|
102
|
-
description: "Workspace prepared ->
|
|
354
|
+
to: collect
|
|
355
|
+
description: "Workspace prepared -> concurrent source collection"
|
|
103
356
|
guard:
|
|
104
357
|
kind: always
|
|
105
358
|
|
|
106
|
-
- from:
|
|
359
|
+
- from: collect
|
|
360
|
+
to: plan
|
|
361
|
+
description: "Sources merged into blended.json -> plan the episode (score/dedup/select/annotate)"
|
|
362
|
+
guard:
|
|
363
|
+
kind: always
|
|
364
|
+
|
|
365
|
+
- from: plan
|
|
366
|
+
to: quality-control-content
|
|
367
|
+
description: "Plan annotated -> content QC filter over the plan's candidates"
|
|
368
|
+
guard:
|
|
369
|
+
kind: always
|
|
370
|
+
|
|
371
|
+
- from: quality-control-content
|
|
372
|
+
to: done-no-content
|
|
373
|
+
description: "QC filter admitted 0 candidates -> done-no-content (an empty episode is not a failure)"
|
|
374
|
+
guard:
|
|
375
|
+
kind: shell
|
|
376
|
+
options:
|
|
377
|
+
command: 'test "$(jq ''.metadata.docs | length == 0'' "${vars.work_dir}/2-plan/candidates.json")" = "true"'
|
|
378
|
+
|
|
379
|
+
- from: quality-control-content
|
|
380
|
+
to: plan-translate
|
|
381
|
+
description: "Candidates passed QC and plan_translate_enabled -> translate flagged items in place"
|
|
382
|
+
guard:
|
|
383
|
+
kind: shell
|
|
384
|
+
options:
|
|
385
|
+
command: 'test "${vars.plan_translate_enabled}" = "true"'
|
|
386
|
+
|
|
387
|
+
- from: quality-control-content
|
|
388
|
+
to: article
|
|
389
|
+
description: "Candidates passed QC, plan-translate disabled -> straight to article"
|
|
390
|
+
guard:
|
|
391
|
+
kind: shell
|
|
392
|
+
options:
|
|
393
|
+
command: 'test "${vars.plan_translate_enabled}" != "true"'
|
|
394
|
+
|
|
395
|
+
- from: plan-translate
|
|
396
|
+
to: article
|
|
397
|
+
description: "Plan translated -> generate article from plan"
|
|
398
|
+
guard:
|
|
399
|
+
kind: always
|
|
400
|
+
|
|
401
|
+
- from: article
|
|
402
|
+
to: cover
|
|
403
|
+
description: "cover_enabled and today's cover missing -> generate cover image"
|
|
404
|
+
guard:
|
|
405
|
+
kind: shell
|
|
406
|
+
options:
|
|
407
|
+
command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/cover-${vars.run_date}.png"'
|
|
408
|
+
|
|
409
|
+
- from: article
|
|
107
410
|
to: script
|
|
108
|
-
description: "
|
|
411
|
+
description: "cover disabled or today's cover cached -> skip to script"
|
|
412
|
+
guard:
|
|
413
|
+
kind: always
|
|
414
|
+
|
|
415
|
+
- from: cover
|
|
416
|
+
to: script
|
|
417
|
+
description: "Cover generated -> build broadcast script"
|
|
109
418
|
guard:
|
|
110
419
|
kind: always
|
|
111
420
|
|
|
@@ -152,7 +461,111 @@ transitions:
|
|
|
152
461
|
kind: always
|
|
153
462
|
|
|
154
463
|
- from: quality-control
|
|
464
|
+
to: show-notes
|
|
465
|
+
description: "publish enabled AND QC passed -> render show notes, then publish"
|
|
466
|
+
guard:
|
|
467
|
+
kind: shell
|
|
468
|
+
options:
|
|
469
|
+
command: 'test "${vars.publish_enabled}" = "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
|
|
470
|
+
|
|
471
|
+
- from: quality-control
|
|
472
|
+
to: done
|
|
473
|
+
description: "publish disabled AND QC passed -> audio episode complete (no publish)"
|
|
474
|
+
guard:
|
|
475
|
+
kind: shell
|
|
476
|
+
options:
|
|
477
|
+
command: 'test "${vars.publish_enabled}" != "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
|
|
478
|
+
|
|
479
|
+
- from: quality-control
|
|
480
|
+
to: failed
|
|
481
|
+
description: "Audio QC failed -> pipeline failed"
|
|
482
|
+
guard:
|
|
483
|
+
kind: always
|
|
484
|
+
|
|
485
|
+
- from: show-notes
|
|
486
|
+
to: publish-prep
|
|
487
|
+
description: "Show notes rendered -> merge article + audio for the publish seam"
|
|
488
|
+
guard:
|
|
489
|
+
kind: always
|
|
490
|
+
|
|
491
|
+
- from: publish-prep
|
|
492
|
+
to: publish
|
|
493
|
+
description: "Merged content ready -> fan out to surfdash-pub + podcast-pub"
|
|
494
|
+
guard:
|
|
495
|
+
kind: always
|
|
496
|
+
|
|
497
|
+
- from: publish
|
|
498
|
+
to: translate-en
|
|
499
|
+
description: "Publish full (all targets published) -> scaffold + translate en locale"
|
|
500
|
+
guard:
|
|
501
|
+
kind: shell
|
|
502
|
+
options:
|
|
503
|
+
command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "full"'
|
|
504
|
+
|
|
505
|
+
- from: publish
|
|
506
|
+
to: publish-partial
|
|
507
|
+
description: "Publish partial (>=1 target published, >=1 failed) -> record warning + marker, preserve result.json, continue to done"
|
|
508
|
+
guard:
|
|
509
|
+
kind: shell
|
|
510
|
+
options:
|
|
511
|
+
command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "partial"'
|
|
512
|
+
|
|
513
|
+
- from: publish
|
|
514
|
+
to: failed
|
|
515
|
+
description: "Publish none (all targets failed or fan-out crash) -> pipeline failed"
|
|
516
|
+
guard:
|
|
517
|
+
kind: shell
|
|
518
|
+
options:
|
|
519
|
+
command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "none"'
|
|
520
|
+
|
|
521
|
+
- from: publish-partial
|
|
522
|
+
to: done
|
|
523
|
+
description: "Partial publish recorded (warning + .spur/run marker) -> done; retry failed targets later via kk executor fan-out --retry-from"
|
|
524
|
+
guard:
|
|
525
|
+
kind: always
|
|
526
|
+
|
|
527
|
+
- from: translate-en
|
|
528
|
+
to: translate-en-agent
|
|
529
|
+
description: "zh post published and en draft scaffolded -> agent translates en draft in place"
|
|
530
|
+
guard:
|
|
531
|
+
kind: shell
|
|
532
|
+
options:
|
|
533
|
+
command: 'test -n "${vars.surfdash_post_path}" && test -f "${vars.surfdash_en_post_path}"'
|
|
534
|
+
|
|
535
|
+
- from: translate-en
|
|
536
|
+
to: translate-ja
|
|
537
|
+
description: "no zh post path -> skip en locale"
|
|
538
|
+
guard:
|
|
539
|
+
kind: always
|
|
540
|
+
|
|
541
|
+
- from: translate-en-agent
|
|
542
|
+
to: translate-ja
|
|
543
|
+
description: "en draft translated -> scaffold ja locale"
|
|
544
|
+
guard:
|
|
545
|
+
kind: always
|
|
546
|
+
|
|
547
|
+
- from: translate-ja
|
|
548
|
+
to: translate-ja-agent
|
|
549
|
+
description: "zh post published and ja draft scaffolded -> agent translates ja draft in place"
|
|
550
|
+
guard:
|
|
551
|
+
kind: shell
|
|
552
|
+
options:
|
|
553
|
+
command: 'test -n "${vars.surfdash_post_path}" && test -f "${vars.surfdash_ja_post_path}"'
|
|
554
|
+
|
|
555
|
+
- from: translate-ja
|
|
556
|
+
to: translate-sync
|
|
557
|
+
description: "no zh post path -> skip ja locale"
|
|
558
|
+
guard:
|
|
559
|
+
kind: always
|
|
560
|
+
|
|
561
|
+
- from: translate-ja-agent
|
|
562
|
+
to: translate-sync
|
|
563
|
+
description: "ja draft translated -> sync all locales into the CMS"
|
|
564
|
+
guard:
|
|
565
|
+
kind: always
|
|
566
|
+
|
|
567
|
+
- from: translate-sync
|
|
155
568
|
to: done
|
|
156
|
-
description: "
|
|
569
|
+
description: "zh post + locales synced into surfdash CMS -> episode complete"
|
|
157
570
|
guard:
|
|
158
571
|
kind: always
|