@gobing-ai/knowledge-kit 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +132 -25
- package/package.json +1 -1
- package/plugins/generations/content-gen/dist/index.js +4 -0
- package/plugins/generations/core-facts-gen/dist/index.js +4 -0
- package/plugins/generations/daily-article-gen/dist/index.js +4 -0
- package/plugins/generations/dailynews-gen/dist/index.js +4 -0
- package/plugins/generations/episode-plan-gen/dist/index.js +102 -1
- package/plugins/generations/episode-plan-gen/src/index.ts +2 -4
- package/plugins/generations/gemini-tts/dist/index.js +4 -0
- package/plugins/generations/image-gen/dist/index.js +4 -0
- package/plugins/generations/news-report-gen/dist/index.js +4 -0
- package/plugins/generations/voice-gen/dist/index.js +4 -0
- package/plugins/generations/voxcpm-tts/dist/index.js +4 -0
- package/plugins/ingestions/aihot-ingest/dist/index.js +4 -0
- package/plugins/ingestions/horizon-ingest/dist/index.js +4 -0
- package/plugins/ingestions/last30days-ingest/dist/index.js +11 -3
- package/plugins/ingestions/last30days-ingest/src/index.ts +14 -3
- package/plugins/ingestions/web-search/dist/index.js +4 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-scheduled-run.ts +298 -0
- package/plugins/kk/scripts/daily-stages.ts +1083 -49
- package/plugins/kk/scripts/kk-workflow-stages.ts +76 -10
- package/plugins/kk/scripts/safety-artifact-check.ts +235 -0
- package/plugins/kk/scripts/sentences.ts +134 -0
- package/plugins/kk/scripts/series-names.ts +124 -0
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +194 -113
- package/plugins/kk/workflows/kk-daily-safety.yaml +160 -36
- package/plugins/publishings/bilibili-pub/dist/index.js +6 -1
- package/plugins/publishings/bilibili-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/cnblogs-pub/dist/index.js +6 -1
- package/plugins/publishings/cnblogs-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/csdn-pub/dist/index.js +6 -1
- package/plugins/publishings/csdn-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/emdash-pub/dist/index.js +4 -0
- package/plugins/publishings/infoq-pub/dist/index.js +6 -1
- package/plugins/publishings/infoq-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/jike-pub/dist/index.js +6 -1
- package/plugins/publishings/jike-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/juejin-pub/dist/index.js +6 -1
- package/plugins/publishings/juejin-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/medium-pub/dist/index.js +4 -0
- package/plugins/publishings/podcast-pub/dist/index.js +151 -16
- package/plugins/publishings/podcast-pub/src/index.ts +62 -5
- package/plugins/publishings/podcast-pub/src/seo.ts +2 -4
- package/plugins/publishings/podcast-pub/src/show-notes.ts +3 -8
- package/plugins/publishings/qiita-pub/dist/index.js +4 -0
- package/plugins/publishings/shipinhao-pub/dist/index.js +6 -1
- package/plugins/publishings/shipinhao-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/sspai-pub/dist/index.js +6 -1
- package/plugins/publishings/sspai-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/substack-pub/dist/index.js +6 -1
- package/plugins/publishings/substack-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/surfdash-pub/dist/index.js +691 -63
- package/plugins/publishings/surfdash-pub/src/digest-check.ts +284 -0
- package/plugins/publishings/surfdash-pub/src/index.ts +656 -84
- package/plugins/publishings/surfdash-pub/src/patch.ts +44 -7
- package/plugins/publishings/threads-pub/dist/index.js +4 -0
- package/plugins/publishings/wechatmp-pub/dist/index.js +937 -98
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +64 -3
- package/plugins/publishings/wechatmp-pub/src/flow.ts +1125 -85
- package/plugins/publishings/wechatmp-pub/src/index.ts +112 -30
- package/plugins/publishings/wechatmp-pub/src/page.ts +105 -0
- package/plugins/publishings/weibo-pub/dist/index.js +6 -1
- package/plugins/publishings/weibo-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/x-pub/dist/index.js +6 -1
- package/plugins/publishings/x-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/xhs-pub/dist/index.js +6 -1
- package/plugins/publishings/xhs-pub/dist/profile-check.js +6 -1
- package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +6 -1
- package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/zenn-pub/dist/index.js +4 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# Deployment-ready copy. Promote this file to plugins/kk/workflows/kk-daily-ai-voice2.yaml only
|
|
2
|
+
# after approval; all self-validation and nested-workflow references intentionally use active paths.
|
|
1
3
|
name: kk-daily-ai-voice2
|
|
2
4
|
kind: dag
|
|
3
5
|
description: >-
|
|
@@ -8,7 +10,7 @@ description: >-
|
|
|
8
10
|
set at QC so it never re-enters a later pool — whether or not the run publishes. The daily work is
|
|
9
11
|
unchanged after that — plan → content QC → article/cover/script →
|
|
10
12
|
content safety (nested state machine, kk-daily-safety.yaml) → operator review → audio → publish
|
|
11
|
-
(surfdash + podcast) →
|
|
13
|
+
(surfdash + podcast) → soft side channels (xhs, wechat) plus the required local-locale gate → classify → ledger. It
|
|
12
14
|
supersedes kk-daily-ai-voice.yaml, which stays untouched until cut-over §8.
|
|
13
15
|
vars:
|
|
14
16
|
# Composed by the `prepare` node (`kk daily prepare`), then read back into vars through the
|
|
@@ -80,7 +82,12 @@ vars:
|
|
|
80
82
|
# siblings declare `role: reviewer` (capable-1), two rungs above the primary's `scribe`, so the
|
|
81
83
|
# default `inline` resolves the retry above any rung the primary reaches by climbing past an
|
|
82
84
|
# unusable cheap executor (ac5-live-1). The operator may still pin a known-good executor here.
|
|
83
|
-
|
|
85
|
+
# 2026-10-24 pin: `inline` resolved to agy-gemini, whose headless write path is dead (terminal
|
|
86
|
+
# sandbox denies workspace writes), so the retry failed the same way twice — the whole day closed
|
|
87
|
+
# with no plan. `claude` carries `--permission-mode acceptEdits --allowedTools Write Edit` and is
|
|
88
|
+
# the tier that actually delivered both producers on this date. Re-point it at whatever
|
|
89
|
+
# `spur agent doctor <name>` reports usable; an empty retry tier fails the run loudly.
|
|
90
|
+
agent_retry: "claude"
|
|
84
91
|
# QC pins — lockstep with episode-plan-gen's DEFAULT_FILTER_CONFIG (env wins), same as voice1.
|
|
85
92
|
qc_categories: "ai-news,ai-technical,ai-research,ai-industry"
|
|
86
93
|
qc_min_quality: "2"
|
|
@@ -161,7 +168,7 @@ nodes:
|
|
|
161
168
|
command: >-
|
|
162
169
|
kk stage daily-preflight plugins/kk/workflows/kk-daily-ai-voice2.yaml
|
|
163
170
|
plugins/kk/workflows/kk-daily-safety.yaml
|
|
164
|
-
&& kk daily prepare --workflow kk-daily-ai-voice2 --run-date "${vars.run_date}" --ledger-sync
|
|
171
|
+
&& kk daily prepare --workflow kk-daily-ai-voice2 --work-dir "${vars.work_dir}" --run-date "${vars.run_date}" --ledger-sync
|
|
165
172
|
--run "${vars.__runId}" --definition-digest "${vars.__definitionDigest}"
|
|
166
173
|
--setting window="${vars.window}" --setting language="${vars.language}" --setting vdriver="${vars.vdriver}"
|
|
167
174
|
--setting skip_review="${vars.skip_review}" --setting transcode_mp3="${vars.transcode_mp3}"
|
|
@@ -317,6 +324,7 @@ nodes:
|
|
|
317
324
|
options:
|
|
318
325
|
role: scribe
|
|
319
326
|
agent: ${vars.agent}
|
|
327
|
+
timeoutMs: 600000
|
|
320
328
|
# ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3; 0138 R4a copy discipline).
|
|
321
329
|
# The anchor is shared with plan-translate-retry so the two attempts cannot drift apart.
|
|
322
330
|
# 20261015 forensics: headless agy's terminal sandbox denies workspace writes for EVERY
|
|
@@ -354,6 +362,7 @@ nodes:
|
|
|
354
362
|
options:
|
|
355
363
|
role: reviewer
|
|
356
364
|
agent: ${vars.agent_retry}
|
|
365
|
+
timeoutMs: 600000
|
|
357
366
|
input: *plan-translate-input
|
|
358
367
|
expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
|
|
359
368
|
allowAppend: false
|
|
@@ -375,6 +384,7 @@ nodes:
|
|
|
375
384
|
options:
|
|
376
385
|
role: scribe
|
|
377
386
|
agent: ${vars.agent}
|
|
387
|
+
timeoutMs: 600000
|
|
378
388
|
# ADR-115 error tier: keep this prompt <= 1000 chars. No title line: the canonical episode
|
|
379
389
|
# title is computed by daily-article-gen (0189 R4).
|
|
380
390
|
# The anchor is shared with article-write-retry so the two attempts cannot drift apart.
|
|
@@ -406,6 +416,7 @@ nodes:
|
|
|
406
416
|
options:
|
|
407
417
|
role: reviewer
|
|
408
418
|
agent: ${vars.agent_retry}
|
|
419
|
+
timeoutMs: 600000
|
|
409
420
|
input: *article-write-input
|
|
410
421
|
expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
|
|
411
422
|
allowAppend: false
|
|
@@ -450,6 +461,7 @@ nodes:
|
|
|
450
461
|
options:
|
|
451
462
|
role: scribe
|
|
452
463
|
agent: ${vars.agent}
|
|
464
|
+
timeoutMs: 600000
|
|
453
465
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
454
466
|
input: |
|
|
455
467
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of plan docs; don't edit it.
|
|
@@ -512,6 +524,7 @@ nodes:
|
|
|
512
524
|
options:
|
|
513
525
|
role: scribe
|
|
514
526
|
agent: ${vars.agent}
|
|
527
|
+
timeoutMs: 600000
|
|
515
528
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
516
529
|
input: |
|
|
517
530
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of docs; don't edit it. Also read the style contract ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_style.md and obey its char caps, duration budget and banned phrases.
|
|
@@ -547,6 +560,8 @@ nodes:
|
|
|
547
560
|
are stripped at the nested-spur boundary (the same two `NESTED_SPUR_UNSET` vars the stage
|
|
548
561
|
runner strips, kk-workflow-stages.ts): without it `spur workflow run` refuses to start inside
|
|
549
562
|
an active run. Non-publishing runs skip the gate entirely, exactly like voice1.
|
|
563
|
+
`skip_review` is forwarded as the nested `unattended` flag, so a real safety escalation cannot
|
|
564
|
+
silently reach publication when the caller has no pause node.
|
|
550
565
|
dependsOn: [script]
|
|
551
566
|
condition:
|
|
552
567
|
kind: shell
|
|
@@ -558,7 +573,8 @@ nodes:
|
|
|
558
573
|
command: >-
|
|
559
574
|
env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST
|
|
560
575
|
spur workflow run plugins/kk/workflows/kk-daily-safety.yaml
|
|
561
|
-
--
|
|
576
|
+
--run-id "${vars.__runId}-safety"
|
|
577
|
+
--vars "{\"work_dir\":\"${vars.work_dir}\",\"run_date\":\"${vars.run_date}\",\"safety_max_revise\":\"${vars.safety_max_revise}\",\"safety_platforms\":\"${vars.safety_platforms}\",\"agent\":\"${vars.agent}\",\"agent_retry\":\"${vars.agent_retry}\",\"unattended\":\"${vars.skip_review}\"}"
|
|
562
578
|
--quiet --no-plan
|
|
563
579
|
|
|
564
580
|
- id: review
|
|
@@ -705,21 +721,10 @@ nodes:
|
|
|
705
721
|
command: >-
|
|
706
722
|
kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
|
|
707
723
|
|
|
708
|
-
#
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
tail, wrap-docs and publish-prep (the seam's merge is part of the audited bytes); publish-prep
|
|
713
|
-
being skipped on a non-publishing run does not skip the audit.
|
|
714
|
-
dependsOn: [duration, wrap-docs, publish-prep]
|
|
715
|
-
action:
|
|
716
|
-
kind: shell
|
|
717
|
-
options:
|
|
718
|
-
command: >-
|
|
719
|
-
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
720
|
-
|
|
721
|
-
- id: publish-surfdash
|
|
722
|
-
description: "Publish the merged Content to surfdash (single target); its result.json is what show-notes links and what patch-surfdash (folded into publish-classify) patches"
|
|
724
|
+
# Surfdash's CMS sync now requires all locale files. Materialize zh first so the locale branch
|
|
725
|
+
# can scaffold and translate en/ja before the final publish invokes refine/sync.
|
|
726
|
+
- id: publish-surfdash-prepare
|
|
727
|
+
description: "Materialize the local zh Surfdash post without invoking CMS sync; locale translation runs from this path"
|
|
723
728
|
dependsOn: [publish-prep]
|
|
724
729
|
condition:
|
|
725
730
|
kind: shell
|
|
@@ -729,80 +734,22 @@ nodes:
|
|
|
729
734
|
kind: shell
|
|
730
735
|
options:
|
|
731
736
|
command: >-
|
|
732
|
-
kk stage daily-
|
|
733
|
-
|
|
734
|
-
- id: show-notes
|
|
735
|
-
description: "Render timeline show notes from the episode plan (podcast-pub show-notes op; article URL from the surfdash result postPath — a surfdash failure degrades to the legacy full-body layout)"
|
|
736
|
-
dependsOn: [quality, publish-surfdash]
|
|
737
|
-
# Pool-gated (0266): its only skip path was dependency cascade, which a done+skipped mix
|
|
738
|
-
# defeats — a duration-refused run would still render notes for an unpublished episode.
|
|
739
|
-
condition:
|
|
740
|
-
kind: shell
|
|
741
|
-
options:
|
|
742
|
-
command: >-
|
|
743
|
-
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
744
|
-
action:
|
|
745
|
-
kind: shell
|
|
746
|
-
options:
|
|
747
|
-
command: >-
|
|
748
|
-
PODCAST_PUB_SEO="${vars.podcast_seo}"
|
|
749
|
-
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
750
|
-
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
751
|
-
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
752
|
-
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
753
|
-
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
754
|
-
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
755
|
-
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
756
|
-
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
757
|
-
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
758
|
-
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
759
|
-
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
760
|
-
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
761
|
-
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
762
|
-
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
763
|
-
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
764
|
-
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
765
|
-
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
766
|
-
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
767
|
-
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
768
|
-
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
769
|
-
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
737
|
+
kk stage daily-prepare-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
770
738
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
739
|
+
# ---- audio QC: fans in the audio branch and the publish seam ----
|
|
740
|
+
- id: quality
|
|
741
|
+
description: >-
|
|
742
|
+
Verify audio quality, speech rate, and silence/repetition QC metrics. Depends on the audio
|
|
743
|
+
tail, wrap-docs and publish-prep (the seam's merge is part of the audited bytes); publish-prep
|
|
744
|
+
being skipped on a non-publishing run does not skip the audit.
|
|
745
|
+
dependsOn: [duration, wrap-docs, publish-prep]
|
|
778
746
|
action:
|
|
779
747
|
kind: shell
|
|
780
748
|
options:
|
|
781
749
|
command: >-
|
|
782
|
-
|
|
783
|
-
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
784
|
-
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
785
|
-
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
786
|
-
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
787
|
-
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
788
|
-
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
789
|
-
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
790
|
-
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
791
|
-
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
792
|
-
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
793
|
-
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
794
|
-
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
795
|
-
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
796
|
-
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
797
|
-
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
798
|
-
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
799
|
-
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
800
|
-
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
801
|
-
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
802
|
-
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
803
|
-
kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
750
|
+
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
804
751
|
|
|
805
|
-
# ----
|
|
752
|
+
# ---- soft side channels + local-locale gate (exit 0 on channel failure; final Surfdash remains fail-closed) ----
|
|
806
753
|
- id: xhs-write
|
|
807
754
|
description: "Plan the XHS card series across the episode's plan items via agent.run; onError: continue — a failed plan skips the XHS target, never the run"
|
|
808
755
|
dependsOn: [script, review]
|
|
@@ -818,6 +765,7 @@ nodes:
|
|
|
818
765
|
options:
|
|
819
766
|
role: scribe
|
|
820
767
|
agent: ${vars.agent}
|
|
768
|
+
timeoutMs: 600000
|
|
821
769
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
822
770
|
input: |
|
|
823
771
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
|
|
@@ -866,22 +814,9 @@ nodes:
|
|
|
866
814
|
command: >-
|
|
867
815
|
kk stage daily-publish-xhs "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.xhs_live}" "true"
|
|
868
816
|
|
|
869
|
-
- id: wechat-publish
|
|
870
|
-
description: "Publish the daily article to the WeChat MP draft box via wechatmp-pub — draft, or live only when wechat_live=true; independent and non-blocking (a sessionless profile refuses fast, exit 0)"
|
|
871
|
-
dependsOn: [publish-prep]
|
|
872
|
-
condition:
|
|
873
|
-
kind: shell
|
|
874
|
-
options:
|
|
875
|
-
command: 'test "${vars.publish_enabled}" = "true" && test "${vars.wechat_enabled}" = "true"'
|
|
876
|
-
action:
|
|
877
|
-
kind: shell
|
|
878
|
-
options:
|
|
879
|
-
command: >-
|
|
880
|
-
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}" "${vars.wechat_collection}"
|
|
881
|
-
|
|
882
817
|
- id: translate-localize
|
|
883
818
|
description: "Locales — surfdash-pub scaffolds the en/ja drafts (deterministic, plugin-owned) and writes one marker file per locale naming the draft to translate"
|
|
884
|
-
dependsOn: [publish-surfdash]
|
|
819
|
+
dependsOn: [publish-surfdash-prepare]
|
|
885
820
|
condition:
|
|
886
821
|
kind: shell
|
|
887
822
|
options:
|
|
@@ -896,14 +831,14 @@ nodes:
|
|
|
896
831
|
description: >-
|
|
897
832
|
Agent translates both scaffolded locale drafts in place. The two draft paths are read from the
|
|
898
833
|
marker files at their fixed paths (no var projection after the fan-out: concurrent setVars is
|
|
899
|
-
last-writer-wins, design §6), and an empty marker means that locale is absent.
|
|
900
|
-
|
|
834
|
+
last-writer-wins, design §6), and an empty marker means that locale is absent. The local
|
|
835
|
+
validation node gates final Surfdash publish so its strict all-locale CMS policy is met.
|
|
901
836
|
dependsOn: [translate-localize]
|
|
902
837
|
action:
|
|
903
838
|
kind: agent.run
|
|
904
839
|
options:
|
|
905
|
-
# 20261012 hardening: this is the LAST agent step and
|
|
906
|
-
#
|
|
840
|
+
# 20261012 hardening: this is the LAST agent step and a translation failure must not abort
|
|
841
|
+
# the overall report/podcast close, yet the step carried no tolerance and no retry sibling — a stochastic
|
|
907
842
|
# executor failure here aborted the DAG *after* surfdash and podcast had published, so
|
|
908
843
|
# publish-classify, mark-published, run-report and ledger never ran for an otherwise complete
|
|
909
844
|
# day. Tolerating the failure is safe because a scaffold is an unpublished draft: the prompt
|
|
@@ -913,32 +848,177 @@ nodes:
|
|
|
913
848
|
onError: continue
|
|
914
849
|
role: scribe
|
|
915
850
|
agent: ${vars.agent}
|
|
916
|
-
|
|
851
|
+
timeoutMs: 600000
|
|
852
|
+
# 2026-10-24: `onError: continue` keeps a failed primary eligible for the capable retry
|
|
853
|
+
# sibling below. The local validation node refuses final publish while either locale remains
|
|
854
|
+
# an untranslated scaffold.
|
|
855
|
+
input: &translate-input |
|
|
917
856
|
Translate the en/ja draft posts of the zh article. Read the two marker files at these fixed paths — each holds one draft path, or is empty when that locale was not scaffolded:
|
|
918
857
|
English: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_en-post-path.txt
|
|
919
858
|
Japanese: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_ja-post-path.txt
|
|
920
859
|
For each: if the marker is empty or unreadable, skip that locale. Otherwise edit the named file in place — translate the frontmatter title and the whole markdown body to natural English/Japanese respectively. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Set draft: false (the translated post publishes to the site) and remove any canonical: frontmatter line pointing at a different locale's URL.
|
|
860
|
+
Also translate these frontmatter values into the target locale, keeping all other keys untouched: description, seo_title, seo_description, and each key_points item. Each stays one coherent standalone sentence that summarizes the same story as the zh source — never splice fragments from different items or stories into one, and key_points must keep exactly the same number of items as the zh post. The 2026-10-23 run failed this: spliced description/key_points had to be hand-repaired before sync.
|
|
861
|
+
Series names are fixed product identity, never invented or freely translated: the en series name is "Daily AI Stew" (show "Salty Talk"); the ja series name is "デイリーAIシチュー" (show "咸話咸説"). Never emit 毎日AIブレンド, ごった煮, "Daily AI Medley", "hodgepodge", "Daily AI Briefing", or the zh name 每日AI乱炖 in en/ja prose or link text (a URL keeps whatever it contains). Openers: en "Your Daily AI Stew from Robin, on Salty Talk."; ja "咸話咸説のデイリーAIシチュー、Robin がお届けします。" Listen links: en "[listen to Daily AI Stew · <date>](<url>)", ja "[デイリーAIシチュー · <date> を聴く](<url>)". In each key_points item keep the "Label — sentence" shape: the label before " — " stays a short phrase of at most 14 characters (ja) / 28 (en), never truncated with "…". The local translate-validate stage rewrites known retired names deterministically and fails the publish gate on leftovers.
|
|
921
862
|
Keep frontmatter valid YAML: double-quote any value containing ': '. Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
|
|
922
863
|
Do not wait for operator confirmation. Each edited file must end fully in its target language.
|
|
923
864
|
|
|
924
|
-
-
|
|
925
|
-
|
|
865
|
+
# 2026-10-24: the locale translation carried `onError: continue` with NO retry sibling, so a
|
|
866
|
+
# failed executor left the en/ja drafts untranslated and the old sync stage could not distinguish
|
|
867
|
+
# a scaffold from a publishable locale. The primary tolerates, this sibling is loud-tolerant and
|
|
868
|
+
# runs only when a scaffolded draft is still untranslated; translate-validate is the final gate.
|
|
869
|
+
- id: translate-retry
|
|
870
|
+
description: "Retry the en/ja locale translation on the capable tier when the primary left a scaffolded draft untranslated (2026-10-24)"
|
|
926
871
|
dependsOn: [translate]
|
|
872
|
+
condition:
|
|
873
|
+
kind: shell
|
|
874
|
+
options:
|
|
875
|
+
command: >-
|
|
876
|
+
test "$(cat "${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt" 2>/dev/null)" != ""
|
|
877
|
+
&& { for L in en ja; do M="${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_$L-post-path.txt"; P="$(cat "$M" 2>/dev/null)"; [ -z "$P" ] && continue; grep -q '^draft: *false' "$P" || { echo "untranslated: $L ($P)"; exit 0; }; done; exit 1; }
|
|
878
|
+
action:
|
|
879
|
+
kind: agent.run
|
|
880
|
+
options:
|
|
881
|
+
onError: continue
|
|
882
|
+
role: reviewer
|
|
883
|
+
agent: ${vars.agent_retry}
|
|
884
|
+
timeoutMs: 600000
|
|
885
|
+
input: *translate-input
|
|
886
|
+
|
|
887
|
+
- id: translate-refine
|
|
888
|
+
description: "Run the Surfdash-owned refine pass over zh/en/ja locally before the locale fingerprint is admitted; no CMS, git, or remote side effect"
|
|
889
|
+
dependsOn: [translate, translate-retry]
|
|
890
|
+
condition:
|
|
891
|
+
kind: shell
|
|
892
|
+
options:
|
|
893
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
894
|
+
action:
|
|
895
|
+
kind: shell
|
|
896
|
+
onError: continue
|
|
897
|
+
options:
|
|
898
|
+
command: >-
|
|
899
|
+
kk stage daily-surfdash-refine "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
900
|
+
|
|
901
|
+
- id: translate-validate
|
|
902
|
+
description: "Validate and fingerprint the refined translated locale files locally before the final Surfdash publish; no CMS side effect"
|
|
903
|
+
dependsOn: [translate-refine]
|
|
904
|
+
condition:
|
|
905
|
+
kind: shell
|
|
906
|
+
options:
|
|
907
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
908
|
+
action:
|
|
909
|
+
kind: shell
|
|
910
|
+
onError: continue
|
|
911
|
+
options:
|
|
912
|
+
command: >-
|
|
913
|
+
kk stage daily-translate-validate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt"
|
|
914
|
+
|
|
915
|
+
- id: publish-surfdash
|
|
916
|
+
description: "Publish the validated merged Content to surfdash (single target); its result.json is what show-notes links and what patch-surfdash (folded into publish-classify) patches"
|
|
917
|
+
dependsOn: [publish-prep, translate-validate]
|
|
918
|
+
condition:
|
|
919
|
+
kind: shell
|
|
920
|
+
options:
|
|
921
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
922
|
+
action:
|
|
923
|
+
kind: shell
|
|
924
|
+
options:
|
|
925
|
+
command: >-
|
|
926
|
+
kk stage daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "true"
|
|
927
|
+
|
|
928
|
+
- id: wechat-publish
|
|
929
|
+
description: "Publish the daily article via wechatmp-pub after Surfdash settles so its observed public URL fills 原文链接; draft by default, live only when wechat_live=true; failures remain non-blocking"
|
|
930
|
+
dependsOn: [publish-prep, publish-surfdash]
|
|
931
|
+
dependencyPolicy: all
|
|
932
|
+
condition:
|
|
933
|
+
kind: shell
|
|
934
|
+
options:
|
|
935
|
+
command: 'test "${vars.publish_enabled}" = "true" && test "${vars.wechat_enabled}" = "true"'
|
|
936
|
+
action:
|
|
937
|
+
kind: shell
|
|
938
|
+
options:
|
|
939
|
+
command: >-
|
|
940
|
+
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}" "${vars.wechat_collection}"
|
|
941
|
+
|
|
942
|
+
- id: show-notes
|
|
943
|
+
description: "Render timeline show notes from the episode plan (podcast-pub show-notes op; article URL from the surfdash result postPath — a surfdash failure degrades to the legacy full-body layout)"
|
|
944
|
+
dependsOn: [quality, publish-surfdash]
|
|
945
|
+
# Pool-gated (0266): its only skip path was dependency cascade, which a done+skipped mix
|
|
946
|
+
# defeats — a duration-refused run would still render notes for an unpublished episode.
|
|
947
|
+
condition:
|
|
948
|
+
kind: shell
|
|
949
|
+
options:
|
|
950
|
+
command: >-
|
|
951
|
+
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
952
|
+
action:
|
|
953
|
+
kind: shell
|
|
954
|
+
options:
|
|
955
|
+
command: >-
|
|
956
|
+
PODCAST_PUB_SEO="${vars.podcast_seo}"
|
|
957
|
+
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
958
|
+
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
959
|
+
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
960
|
+
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
961
|
+
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
962
|
+
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
963
|
+
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
964
|
+
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
965
|
+
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
966
|
+
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
967
|
+
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
968
|
+
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
969
|
+
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
970
|
+
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
971
|
+
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
972
|
+
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
973
|
+
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
974
|
+
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
975
|
+
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
976
|
+
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
977
|
+
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
978
|
+
|
|
979
|
+
- id: publish-podcast
|
|
980
|
+
description: "Publish the episode through podcast-pub with the rendered show notes (metadata.showNotes)"
|
|
981
|
+
dependsOn: [show-notes]
|
|
982
|
+
condition:
|
|
983
|
+
kind: shell
|
|
984
|
+
options:
|
|
985
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
927
986
|
action:
|
|
928
987
|
kind: shell
|
|
929
988
|
options:
|
|
930
989
|
command: >-
|
|
931
|
-
|
|
990
|
+
PODCAST_PUB_SEO="${vars.podcast_seo}"
|
|
991
|
+
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
992
|
+
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
993
|
+
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
994
|
+
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
995
|
+
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
996
|
+
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
997
|
+
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
998
|
+
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
999
|
+
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
1000
|
+
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
1001
|
+
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
1002
|
+
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
1003
|
+
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
1004
|
+
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
1005
|
+
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
1006
|
+
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
1007
|
+
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
1008
|
+
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
1009
|
+
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
1010
|
+
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
1011
|
+
kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
932
1012
|
|
|
933
1013
|
# ---- fan-in: classify the publish outcome, then close the run ----
|
|
934
1014
|
- id: publish-classify
|
|
935
1015
|
description: >-
|
|
936
|
-
Fan-in of every publish leaf (surfdash, podcast, xhs, wechat,
|
|
1016
|
+
Fan-in of every publish leaf (surfdash, podcast, xhs, wechat, locale validation) with policy `all`:
|
|
937
1017
|
classifies the sequenced surfdash → podcast outcomes into publish-status.txt full|partial|none
|
|
938
|
-
(exit 0 in all three), records
|
|
1018
|
+
(exit 0 in all three), records side-channel/locale completeness, and folds in the
|
|
939
1019
|
surfdash post-patching + the partial-publish marker that voice1 ran as separate states (a
|
|
940
1020
|
classify is the only node that knows the status). Non-publishing runs skip the whole subtree.
|
|
941
|
-
dependsOn: [publish-podcast, publish-surfdash, xhs-publish, wechat-publish, translate-
|
|
1021
|
+
dependsOn: [publish-podcast, publish-surfdash, xhs-publish, wechat-publish, translate-validate]
|
|
942
1022
|
dependencyPolicy: all
|
|
943
1023
|
condition:
|
|
944
1024
|
kind: shell
|
|
@@ -963,7 +1043,8 @@ nodes:
|
|
|
963
1043
|
kind: shell
|
|
964
1044
|
options:
|
|
965
1045
|
command: >-
|
|
966
|
-
|
|
1046
|
+
test "$(cat "${vars.work_dir}/31-publish/${vars.run_date}_17_publish_publish-status.txt" 2>/dev/null)" = "full"
|
|
1047
|
+
&& kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
967
1048
|
&& test "${vars.publish_enabled}" = "true"
|
|
968
1049
|
action:
|
|
969
1050
|
kind: shell
|