@gobing-ai/knowledge-kit 0.0.13 → 0.0.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 (125) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +20 -0
  3. package/package.json +1 -1
  4. package/plugins/generations/content-gen/dist/index.js +20 -0
  5. package/plugins/generations/core-facts-gen/dist/index.js +20 -0
  6. package/plugins/generations/daily-article-gen/dist/index.js +29 -1
  7. package/plugins/generations/daily-article-gen/src/index.ts +13 -1
  8. package/plugins/generations/dailynews-gen/dist/index.js +20 -0
  9. package/plugins/generations/episode-plan-gen/dist/index.js +24 -0
  10. package/plugins/generations/episode-plan-gen/src/index.ts +11 -0
  11. package/plugins/generations/image-gen/dist/index.js +2296 -53
  12. package/plugins/generations/image-gen/src/providers/agnes.ts +110 -0
  13. package/plugins/generations/image-gen/src/providers/azure.ts +153 -0
  14. package/plugins/generations/image-gen/src/providers/codex-cli.ts +170 -0
  15. package/plugins/generations/image-gen/src/providers/dashscope.ts +485 -0
  16. package/plugins/generations/image-gen/src/providers/google.ts +268 -0
  17. package/plugins/generations/image-gen/src/providers/huggingface.ts +59 -0
  18. package/plugins/generations/image-gen/src/providers/jimeng.ts +259 -0
  19. package/plugins/generations/image-gen/src/providers/minimax.ts +171 -0
  20. package/plugins/generations/image-gen/src/providers/openai.ts +319 -0
  21. package/plugins/generations/image-gen/src/providers/openrouter.ts +257 -0
  22. package/plugins/generations/image-gen/src/providers/refs.ts +24 -0
  23. package/plugins/generations/image-gen/src/providers/replicate.ts +279 -0
  24. package/plugins/generations/image-gen/src/providers/seedream.ts +1 -1
  25. package/plugins/generations/image-gen/src/providers/types.ts +52 -51
  26. package/plugins/generations/image-gen/src/providers/zai.ts +237 -0
  27. package/plugins/generations/news-report-gen/dist/index.js +22193 -0
  28. package/plugins/generations/news-report-gen/package.json +17 -0
  29. package/plugins/generations/news-report-gen/plugin.json +7 -0
  30. package/plugins/generations/news-report-gen/src/index.ts +308 -0
  31. package/plugins/generations/news-report-gen/tsconfig.json +4 -0
  32. package/plugins/generations/omni-voice-gen/Makefile +14 -0
  33. package/plugins/generations/omni-voice-gen/README.md +118 -0
  34. package/plugins/generations/omni-voice-gen/bin/omni-voice-gen +2 -0
  35. package/plugins/generations/omni-voice-gen/dist/omni-voice-gen-prr8skpb. +2 -0
  36. package/plugins/generations/omni-voice-gen/dist/omni-voice-gen.js +6 -0
  37. package/plugins/generations/omni-voice-gen/plugin.json +6 -0
  38. package/plugins/generations/omni-voice-gen/profiles.json +16 -0
  39. package/plugins/generations/omni-voice-gen/pyproject.toml +25 -0
  40. package/plugins/generations/omni-voice-gen/scripts/coverage_gate.py +74 -0
  41. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/__init__.py +1 -0
  42. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/__main__.py +39 -0
  43. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/audio.py +190 -0
  44. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/backend.py +161 -0
  45. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/contract.py +76 -0
  46. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/mp3.py +60 -0
  47. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +305 -0
  48. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/profiles.py +108 -0
  49. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/qc.py +234 -0
  50. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +340 -0
  51. package/plugins/generations/omni-voice-gen/uv.lock +3510 -0
  52. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
  53. package/plugins/generations/voice-gen/dist/index.js +30 -2
  54. package/plugins/generations/voice-gen/src/index.ts +16 -1
  55. package/plugins/generations/voice-gen/src/voicebox-client.ts +3 -1
  56. package/plugins/ingestions/aihot-ingest/dist/index.js +20 -0
  57. package/plugins/ingestions/horizon-ingest/dist/index.js +20 -0
  58. package/plugins/ingestions/last30days-ingest/dist/index.js +20 -0
  59. package/plugins/ingestions/web-search/dist/index.js +20 -0
  60. package/plugins/kk/commands/image-extract.md +40 -0
  61. package/plugins/kk/commands/image-generate.md +18 -1
  62. package/plugins/kk/commands/workflow-run.md +54 -22
  63. package/plugins/kk/config.example.yaml +5 -0
  64. package/plugins/kk/plugin.json +1 -1
  65. package/plugins/kk/scripts/kk-workflow-stages.ts +1226 -0
  66. package/plugins/kk/{workflows → scripts}/validate-voicescript.ts +4 -4
  67. package/plugins/kk/{workflows → scripts}/wrap-voicescript-doc.ts +7 -6
  68. package/plugins/kk/skills/audio-authoring/SKILL.md +11 -12
  69. package/plugins/kk/skills/image-authoring/SKILL.md +10 -3
  70. package/plugins/kk/skills/image-authoring/references/format-drafting.md +57 -0
  71. package/plugins/kk/skills/image-authoring/references/style-extraction.md +14 -9
  72. package/plugins/kk/skills/itc-generating/SKILL.md +2 -2
  73. package/plugins/kk/skills/itc-generating/references/generic-craft.md +1 -1
  74. package/plugins/kk/skills/itc-generating/references/platform-english.md +1 -1
  75. package/plugins/kk/skills/itc-generating/references/platform-wechat.md +1 -1
  76. package/plugins/kk/skills/storm-research/SKILL.md +5 -5
  77. package/plugins/kk/skills/topic/SKILL.md +7 -7
  78. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +219 -133
  79. package/plugins/kk/workflows/kk-itc.yaml +12 -29
  80. package/plugins/kk/workflows/kk-solo-podcast.yaml +72 -131
  81. package/plugins/kk/workflows/kk-storm-research.yaml +12 -62
  82. package/plugins/publishings/emdash-pub/dist/index.js +20 -0
  83. package/plugins/publishings/podcast-pub/dist/index.js +57 -3
  84. package/plugins/publishings/podcast-pub/src/index.ts +18 -2
  85. package/plugins/publishings/podcast-pub/src/show-notes.ts +56 -9
  86. package/plugins/publishings/qiita-pub/dist/index.js +20 -0
  87. package/plugins/publishings/surfdash-pub/dist/index.js +96 -6
  88. package/plugins/publishings/surfdash-pub/src/index.ts +109 -9
  89. package/plugins/publishings/zenn-pub/dist/index.js +20 -0
  90. package/plugins/sp/scripts/batch-preflight.mjs +346 -0
  91. package/plugins/sp/scripts/batch-preflight.ts +459 -0
  92. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +615 -0
  93. package/plugins/sp/scripts/daily-summary/daily-summary.ts +846 -0
  94. package/plugins/sp/scripts/daily-summary/logger.ts +28 -0
  95. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +223 -0
  96. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +367 -0
  97. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +132 -0
  98. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +169 -0
  99. package/plugins/sp/scripts/feature-dev-precheck.mjs +171 -0
  100. package/plugins/sp/scripts/feature-dev-precheck.ts +238 -0
  101. package/plugins/sp/scripts/feature-sync-bounded.mjs +285 -0
  102. package/plugins/sp/scripts/feature-sync-bounded.ts +478 -0
  103. package/plugins/sp/scripts/history-anatomy-cache.mjs +902 -0
  104. package/plugins/sp/scripts/history-anatomy-cache.ts +1028 -0
  105. package/plugins/sp/scripts/idea-handoff.mjs +22 -0
  106. package/plugins/sp/scripts/idea-handoff.ts +44 -0
  107. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +185 -0
  108. package/plugins/sp/scripts/inline-run-setup.ts +198 -0
  109. package/plugins/sp/scripts/pr-reviewing.mjs +769 -0
  110. package/plugins/sp/scripts/pr-reviewing.ts +925 -0
  111. package/plugins/sp/scripts/quality-gate.mjs +179 -0
  112. package/plugins/sp/scripts/quality-gate.ts +217 -0
  113. package/plugins/sp/scripts/script-contract-check.ts +319 -0
  114. package/plugins/sp/scripts/stage-registry-adapter.ts +1533 -0
  115. package/plugins/sp/scripts/surface-drift-inventory.ts +929 -0
  116. package/plugins/sp/scripts/task-evidence-precheck.ts +181 -0
  117. package/plugins/sp/scripts/task-size-precheck.ts +175 -0
  118. package/plugins/sp/scripts/transition-shim-check.ts +238 -0
  119. package/plugins/sp/scripts/validate-commands.ts +689 -0
  120. package/plugins/sp/scripts/validate-flag-contracts.ts +878 -0
  121. package/plugins/sp/scripts/verify-answer-lint.ts +530 -0
  122. package/plugins/sp/scripts/workflow-step-profile.mjs +316 -0
  123. package/plugins/sp/scripts/workflow-step-profile.ts +456 -0
  124. package/plugins/sp/scripts/wrapup-steps.mjs +373 -0
  125. package/plugins/sp/scripts/wrapup-steps.ts +466 -0
@@ -19,6 +19,7 @@ vars:
19
19
  limit: "10"
20
20
  language: "zh"
21
21
  voice_profile: "robin-news"
22
+ voice_generator: "omni-voice-gen"
22
23
  title: ""
23
24
  cursor: ""
24
25
  state_file: ""
@@ -33,7 +34,7 @@ vars:
33
34
  publish_enabled: "false"
34
35
  last30days_enabled: "false"
35
36
  run_date: ""
36
- agent: "auto"
37
+ agent: "inline"
37
38
  surfdash_post_path: ""
38
39
  surfdash_en_post_path: ""
39
40
  surfdash_ja_post_path: ""
@@ -54,13 +55,12 @@ states:
54
55
  - kind: shell
55
56
  options:
56
57
  command: >-
57
- test -n "${vars.work_dir}" || { echo "missing required variable: work_dir" >&2 && exit 1; };
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}"
58
+ kbin="$(command -v kk 2>/dev/null)";
59
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
60
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
61
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
62
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
63
+ bun "$st" daily-prepare "${vars.work_dir}" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" "${vars.last30days_enabled}" "${vars.last30days_topic}" "${vars.horizon_hours}"
64
64
  - kind: file.read.into-var
65
65
  options:
66
66
  path: ${vars.work_dir}/run-date.txt
@@ -75,16 +75,18 @@ states:
75
75
  # fan-in 30m per-source default and making empty episodes stochastic — horizon gets a
76
76
  # 45m leash; aihot/last30days keep the default.
77
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"
78
+ __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"collect\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
79
+ if [ -f "${vars.work_dir}/1-ingest/${vars.run_date}_02_collect_blended.json" ]; then echo "collect cached: ${vars.work_dir}/1-ingest/${vars.run_date}_02_collect_blended.json"; exit 0; fi;
80
+ 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/${vars.run_date}_02_collect_blended.json"
80
81
  - kind: shell
81
82
  options:
82
83
  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"
84
+ kbin="$(command -v kk 2>/dev/null)";
85
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
86
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
87
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
88
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
89
+ bun "$st" daily-collect-facts "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
88
90
 
89
91
  - id: plan
90
92
  description: "Score/dedup/select + annotate the blended Doc[] into the episode plan (episode-plan-gen; annotate rides inside planEpisode)"
@@ -92,13 +94,12 @@ states:
92
94
  - kind: shell
93
95
  options:
94
96
  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; }
97
+ kbin="$(command -v kk 2>/dev/null)";
98
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
99
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
100
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
101
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
102
+ bun "$st" daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
102
103
 
103
104
  - id: quality-control-content
104
105
  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"
@@ -106,11 +107,12 @@ states:
106
107
  - kind: shell
107
108
  options:
108
109
  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" "$@"
110
+ kbin="$(command -v kk 2>/dev/null)";
111
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
112
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
113
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
114
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
115
+ bun "$st" 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}"
114
116
 
115
117
  - id: plan-translate
116
118
  description: "Translate needsTranslation plan items into the output plan.json via agent.run"
@@ -120,14 +122,12 @@ states:
120
122
  role: scribe
121
123
  agent: ${vars.agent}
122
124
  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
125
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_04_quality-control-content_candidates.json — its metadata.docs (Doc[]) is the episode plan to translate. Do not edit candidates.json.
126
+ For every item with needsTranslation, translate "title" and "body" into natural, concise simplified Chinese.
127
+ Titles must read as a news event (subject + action + object), derived from the body — e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型". Keep product/model/company names verbatim, preserve URLs inside the body, and use 「」 instead of raw ASCII double quotes inside Chinese text.
128
+ Keep every other field untouched (id, sourceUri, mediaType, metadata), keep technical terms in their original form where that is clearer, and produce no trailing commas or comments.
129
+ Output strictly valid JSON — a same-length, same-order array carrying the same ids — to ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json.
130
+ expectFile: ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json
131
131
  allowAppend: false
132
132
 
133
133
  - id: article
@@ -136,23 +136,36 @@ states:
136
136
  - kind: shell
137
137
  options:
138
138
  command: >-
139
- set --;
140
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
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
139
+ kbin="$(command -v kk 2>/dev/null)";
140
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
141
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
142
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
143
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
144
+ bun "$st" daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
143
145
 
144
146
  - id: cover
145
- description: "Generate the article cover image via the wt-image-cover skill (agent.run)"
147
+ description: "Generate the article cover image via the kk:image-authoring skill (agent.run)"
146
148
  onEnter:
147
149
  - kind: agent.run
148
150
  options:
149
151
  role: scribe
150
152
  agent: ${vars.agent}
151
153
  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
154
+ Invoke the kk:image-authoring skill (generate operation).
155
+ Non-interactive step: run every image command with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 — never prompt (cover_enabled is the spend approval).
156
+ Command: --format cover --content ${vars.work_dir}/2-article/${vars.run_date}_06_article_article.md --out ${vars.work_dir}/2-cover
157
+ Template variables (title, subtitle, topics) come from the article as the skill owns; do not pass --style.
158
+ The plugin writes ${vars.work_dir}/2-cover/content.json plus ${vars.work_dir}/2-cover/content-cover-01.<ext> (ext follows the elected provider: minimax → .jpg, google/ark → .png). On failure report the error verbatim and stop — no procedural fallback.
159
+ expectFile: ${vars.work_dir}/2-cover/content-cover-01.jpg
160
+ - kind: shell
161
+ options:
162
+ command: >-
163
+ kbin="$(command -v kk 2>/dev/null)";
164
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
165
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
166
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
167
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
168
+ bun "$st" daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
156
169
 
157
170
  - id: script
158
171
  description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
@@ -160,10 +173,12 @@ states:
160
173
  - kind: shell
161
174
  options:
162
175
  command: >-
163
- set --;
164
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
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
176
+ kbin="$(command -v kk 2>/dev/null)";
177
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
178
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
179
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
180
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
181
+ bun "$st" daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
167
182
 
168
183
  - id: review
169
184
  description: "Operator reviews the generated news script before speech synthesis"
@@ -171,27 +186,34 @@ states:
171
186
  onEnter:
172
187
  - kind: hitl.confirm
173
188
  options:
174
- prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml. Proceed to voice generation?"
189
+ prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/${vars.run_date}_08_script_voicescript.yaml. Proceed to voice generation?"
175
190
  var: __hitlAnswer
176
191
 
177
192
  - id: wrap-docs
178
- description: "Wrap VoiceScript into Doc[] input for voice-gen"
193
+ description: "Wrap VoiceScript into Doc[] input for the voice generator plugin"
179
194
  onEnter:
180
195
  - kind: shell
181
196
  options:
182
197
  command: >-
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}"
198
+ kbin="$(command -v kk 2>/dev/null)";
199
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
200
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
201
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
202
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
203
+ bun "$st" daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
184
204
 
185
205
  - id: generate
186
- description: "Invoke voice-gen generator plugin to generate audio"
206
+ description: "Invoke voice generator plugin (voice_generator var) to generate audio"
187
207
  onEnter:
188
208
  - kind: shell
189
209
  options:
190
210
  command: >-
191
- set --;
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;
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" "$@"
211
+ kbin="$(command -v kk 2>/dev/null)";
212
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
213
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
214
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
215
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
216
+ bun "$st" daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.voice_generator}"
195
217
 
196
218
  - id: quality-control
197
219
  description: "Verify audio quality, speech rate, and silence/repetition QC metrics"
@@ -199,56 +221,103 @@ states:
199
221
  - kind: shell
200
222
  options:
201
223
  command: >-
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"
224
+ kbin="$(command -v kk 2>/dev/null)";
225
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
226
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
227
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
228
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
229
+ bun "$st" daily-quality-report "${vars.work_dir}" "${vars.run_date}"
203
230
 
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)"
231
+ - id: publish-prep
232
+ 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)"
206
233
  onEnter:
207
234
  - kind: shell
208
235
  options:
209
236
  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" "$@"
237
+ kbin="$(command -v kk 2>/dev/null)";
238
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
239
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
240
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
241
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
242
+ bun "$st" daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
243
+
244
+ - id: publish-surfdash
245
+ description: "Publish the merged Content to surfdash (single target); result lands in result.json — surfdash publishes first so show-notes can link the article (task 0118 seam reorder)"
246
+ onEnter:
247
+ - kind: shell
248
+ options:
249
+ command: >-
250
+ kbin="$(command -v kk 2>/dev/null)";
251
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
252
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
253
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
254
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
255
+ bun "$st" daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
216
256
 
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)"
257
+ - id: show-notes
258
+ description: "Render timeline show notes from the episode plan (podcast-pub show-notes op, briefMode; article URL from the surfdash result postPath — surfdash failure degrades to legacy full-body layout)"
259
+ onEnter:
260
+ - kind: shell
261
+ options:
262
+ command: >-
263
+ kbin="$(command -v kk 2>/dev/null)";
264
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
265
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
266
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
267
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
268
+ bun "$st" daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
269
+
270
+ - id: publish-podcast
271
+ description: "Publish the episode to podcast-pub with the rendered show notes (metadata.showNotes carries the brief/timeline markdown; map.ts falls back to content.body when absent — task 0118 R3)"
219
272
  onEnter:
220
273
  - kind: shell
221
274
  options:
222
275
  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}"
276
+ kbin="$(command -v kk 2>/dev/null)";
277
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
278
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
279
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
280
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
281
+ bun "$st" daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
224
282
 
225
283
  - 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)"
284
+ description: "Classify the sequenced surfdash → podcast outcomes full|partial|none into publish-status.txt (exit 0 in all three — routing is transition-owned; task 0118: per-target classification over the merged result.json)"
227
285
  onEnter:
228
286
  - kind: shell
229
287
  options:
230
288
  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"
289
+ kbin="$(command -v kk 2>/dev/null)";
290
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
291
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
292
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
293
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
294
+ bun "$st" daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
240
295
 
241
296
  - 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"
297
+ 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 (task 0118: surfdash failure = no-URL fallback notes; podcast failure = its own partial)"
243
298
  onEnter:
244
299
  - kind: shell
245
300
  options:
246
301
  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"
302
+ kbin="$(command -v kk 2>/dev/null)";
303
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
304
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
305
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
306
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
307
+ bun "$st" daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
308
+
309
+ - id: run-report
310
+ description: "Assemble the per-run report (scores+aggregates+rejected counts+step timing) from existing artifacts — news-report-gen; output-only, never throws; 0117"
311
+ onEnter:
312
+ - kind: shell
313
+ options:
314
+ command: >-
315
+ kbin="$(command -v kk 2>/dev/null)";
316
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
317
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
318
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
319
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
320
+ bun "$st" daily-run-report "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
252
321
 
253
322
  - id: translate-en
254
323
  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)"
@@ -256,26 +325,19 @@ states:
256
325
  - kind: shell
257
326
  options:
258
327
  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"
328
+ kbin="$(command -v kk 2>/dev/null)";
329
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
330
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
331
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
332
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
333
+ bun "$st" daily-translate-en "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
272
334
  - kind: file.read.into-var
273
335
  options:
274
- path: ${vars.work_dir}/3-publish/zh-post-path.txt
336
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_zh-post-path.txt
275
337
  var: surfdash_post_path
276
338
  - kind: file.read.into-var
277
339
  options:
278
- path: ${vars.work_dir}/3-publish/en-post-path.txt
340
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_en-post-path.txt
279
341
  var: surfdash_en_post_path
280
342
 
281
343
  - id: translate-en-agent
@@ -298,20 +360,15 @@ states:
298
360
  - kind: shell
299
361
  options:
300
362
  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"
363
+ kbin="$(command -v kk 2>/dev/null)";
364
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
365
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
366
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
367
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
368
+ bun "$st" daily-translate-ja "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
312
369
  - kind: file.read.into-var
313
370
  options:
314
- path: ${vars.work_dir}/3-publish/ja-post-path.txt
371
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_22_translate-ja_ja-post-path.txt
315
372
  var: surfdash_ja_post_path
316
373
 
317
374
  - id: translate-ja-agent
@@ -334,11 +391,12 @@ states:
334
391
  - kind: shell
335
392
  options:
336
393
  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" "$@"
394
+ kbin="$(command -v kk 2>/dev/null)";
395
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
396
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
397
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
398
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
399
+ bun "$st" daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.surfdash_post_path}"
342
400
 
343
401
  - id: done
344
402
  description: "Terminal — Daily AI news pipeline completed (collect → QC'd plan → article/cover/script/voice → publish/locales)"
@@ -374,7 +432,7 @@ transitions:
374
432
  guard:
375
433
  kind: shell
376
434
  options:
377
- command: 'test "$(jq ''.metadata.docs | length == 0'' "${vars.work_dir}/2-plan/candidates.json")" = "true"'
435
+ command: 'test "$(jq ''.metadata.docs | length == 0'' "${vars.work_dir}/2-plan/${vars.run_date}_04_quality-control-content_candidates.json")" = "true"'
378
436
 
379
437
  - from: quality-control-content
380
438
  to: plan-translate
@@ -404,7 +462,7 @@ transitions:
404
462
  guard:
405
463
  kind: shell
406
464
  options:
407
- command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/cover-${vars.run_date}.png"'
465
+ command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/${vars.run_date}_07_cover_cover.png"'
408
466
 
409
467
  - from: article
410
468
  to: script
@@ -461,12 +519,12 @@ transitions:
461
519
  kind: always
462
520
 
463
521
  - from: quality-control
464
- to: show-notes
465
- description: "publish enabled AND QC passed -> render show notes, then publish"
522
+ to: publish-prep
523
+ description: "publish enabled AND QC passed -> merge article + audio for the publish seam"
466
524
  guard:
467
525
  kind: shell
468
526
  options:
469
- command: 'test "${vars.publish_enabled}" = "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
527
+ command: 'test "${vars.publish_enabled}" = "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/${vars.run_date}_11_generate_content.json")" = "true"'
470
528
 
471
529
  - from: quality-control
472
530
  to: done
@@ -474,7 +532,7 @@ transitions:
474
532
  guard:
475
533
  kind: shell
476
534
  options:
477
- command: 'test "${vars.publish_enabled}" != "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
535
+ command: 'test "${vars.publish_enabled}" != "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/${vars.run_date}_11_generate_content.json")" = "true"'
478
536
 
479
537
  - from: quality-control
480
538
  to: failed
@@ -482,25 +540,37 @@ transitions:
482
540
  guard:
483
541
  kind: always
484
542
 
543
+ - from: publish-prep
544
+ to: publish-surfdash
545
+ description: "Merged content ready -> surfdash publishes first (task 0118 seam reorder: article URLs must exist before show-notes render)"
546
+ guard:
547
+ kind: always
548
+
549
+ - from: publish-surfdash
550
+ to: show-notes
551
+ description: "Surfdash dispatched (ok or failed — failure degrades to no-URL notes) -> render timeline show notes"
552
+ guard:
553
+ kind: always
554
+
485
555
  - from: show-notes
486
- to: publish-prep
487
- description: "Show notes rendered -> merge article + audio for the publish seam"
556
+ to: publish-podcast
557
+ description: "Show notes rendered (briefMode timeline or legacy fallback) -> podcast publish carries them via metadata.showNotes"
488
558
  guard:
489
559
  kind: always
490
560
 
491
- - from: publish-prep
561
+ - from: publish-podcast
492
562
  to: publish
493
- description: "Merged content ready -> fan out to surfdash-pub + podcast-pub"
563
+ description: "Podcast dispatched -> classify the per-target outcomes full|partial|none"
494
564
  guard:
495
565
  kind: always
496
566
 
497
567
  - from: publish
498
- to: translate-en
499
- description: "Publish full (all targets published) -> scaffold + translate en locale"
568
+ to: run-report
569
+ description: "Publish full (all targets published) -> assemble run report first"
500
570
  guard:
501
571
  kind: shell
502
572
  options:
503
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "full"'
573
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
504
574
 
505
575
  - from: publish
506
576
  to: publish-partial
@@ -508,7 +578,7 @@ transitions:
508
578
  guard:
509
579
  kind: shell
510
580
  options:
511
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "partial"'
581
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
512
582
 
513
583
  - from: publish
514
584
  to: failed
@@ -516,14 +586,30 @@ transitions:
516
586
  guard:
517
587
  kind: shell
518
588
  options:
519
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "none"'
589
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "none"'
520
590
 
521
591
  - 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"
592
+ to: run-report
593
+ description: "Partial publish recorded (warning + .spur/run marker) -> still assemble the run report"
524
594
  guard:
525
595
  kind: always
526
596
 
597
+ - from: run-report
598
+ to: translate-en
599
+ description: "Run report assembled after full publish -> scaffold + translate en locale"
600
+ guard:
601
+ kind: shell
602
+ options:
603
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
604
+
605
+ - from: run-report
606
+ to: done
607
+ description: "Run report assembled after partial publish -> done; retry failed targets later via kk executor fan-out --retry-from"
608
+ guard:
609
+ kind: shell
610
+ options:
611
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
612
+
527
613
  - from: translate-en
528
614
  to: translate-en-agent
529
615
  description: "zh post published and en draft scaffolded -> agent translates en draft in place"