@gobing-ai/knowledge-kit 0.1.10 → 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 +128 -25
- package/package.json +1 -1
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-scheduled-run.ts +298 -0
- package/plugins/kk/scripts/daily-stages.ts +1057 -108
- package/plugins/kk/scripts/kk-workflow-stages.ts +76 -10
- package/plugins/kk/scripts/safety-artifact-check.ts +235 -0
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +168 -121
- package/plugins/kk/workflows/kk-daily-safety.yaml +129 -50
- package/plugins/publishings/bilibili-pub/dist/index.js +2 -1
- package/plugins/publishings/bilibili-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/cnblogs-pub/dist/index.js +2 -1
- package/plugins/publishings/cnblogs-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/csdn-pub/dist/index.js +2 -1
- package/plugins/publishings/csdn-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/infoq-pub/dist/index.js +2 -1
- package/plugins/publishings/infoq-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/jike-pub/dist/index.js +2 -1
- package/plugins/publishings/jike-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/juejin-pub/dist/index.js +2 -1
- package/plugins/publishings/juejin-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/podcast-pub/dist/index.js +46 -8
- package/plugins/publishings/podcast-pub/src/index.ts +62 -5
- package/plugins/publishings/shipinhao-pub/dist/index.js +2 -1
- package/plugins/publishings/shipinhao-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/sspai-pub/dist/index.js +2 -1
- package/plugins/publishings/sspai-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/substack-pub/dist/index.js +2 -1
- package/plugins/publishings/substack-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/surfdash-pub/dist/index.js +460 -52
- package/plugins/publishings/surfdash-pub/src/digest-check.ts +85 -19
- package/plugins/publishings/surfdash-pub/src/index.ts +589 -64
- package/plugins/publishings/wechatmp-pub/dist/index.js +933 -98
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +60 -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 +2 -1
- package/plugins/publishings/weibo-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/x-pub/dist/index.js +2 -1
- package/plugins/publishings/x-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/xhs-pub/dist/index.js +2 -1
- package/plugins/publishings/xhs-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +2 -1
- package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +2 -1
|
@@ -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
|
|
@@ -166,7 +168,7 @@ nodes:
|
|
|
166
168
|
command: >-
|
|
167
169
|
kk stage daily-preflight plugins/kk/workflows/kk-daily-ai-voice2.yaml
|
|
168
170
|
plugins/kk/workflows/kk-daily-safety.yaml
|
|
169
|
-
&& 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
|
|
170
172
|
--run "${vars.__runId}" --definition-digest "${vars.__definitionDigest}"
|
|
171
173
|
--setting window="${vars.window}" --setting language="${vars.language}" --setting vdriver="${vars.vdriver}"
|
|
172
174
|
--setting skip_review="${vars.skip_review}" --setting transcode_mp3="${vars.transcode_mp3}"
|
|
@@ -322,6 +324,7 @@ nodes:
|
|
|
322
324
|
options:
|
|
323
325
|
role: scribe
|
|
324
326
|
agent: ${vars.agent}
|
|
327
|
+
timeoutMs: 600000
|
|
325
328
|
# ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3; 0138 R4a copy discipline).
|
|
326
329
|
# The anchor is shared with plan-translate-retry so the two attempts cannot drift apart.
|
|
327
330
|
# 20261015 forensics: headless agy's terminal sandbox denies workspace writes for EVERY
|
|
@@ -359,6 +362,7 @@ nodes:
|
|
|
359
362
|
options:
|
|
360
363
|
role: reviewer
|
|
361
364
|
agent: ${vars.agent_retry}
|
|
365
|
+
timeoutMs: 600000
|
|
362
366
|
input: *plan-translate-input
|
|
363
367
|
expectFile: ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json
|
|
364
368
|
allowAppend: false
|
|
@@ -380,6 +384,7 @@ nodes:
|
|
|
380
384
|
options:
|
|
381
385
|
role: scribe
|
|
382
386
|
agent: ${vars.agent}
|
|
387
|
+
timeoutMs: 600000
|
|
383
388
|
# ADR-115 error tier: keep this prompt <= 1000 chars. No title line: the canonical episode
|
|
384
389
|
# title is computed by daily-article-gen (0189 R4).
|
|
385
390
|
# The anchor is shared with article-write-retry so the two attempts cannot drift apart.
|
|
@@ -411,6 +416,7 @@ nodes:
|
|
|
411
416
|
options:
|
|
412
417
|
role: reviewer
|
|
413
418
|
agent: ${vars.agent_retry}
|
|
419
|
+
timeoutMs: 600000
|
|
414
420
|
input: *article-write-input
|
|
415
421
|
expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
|
|
416
422
|
allowAppend: false
|
|
@@ -455,6 +461,7 @@ nodes:
|
|
|
455
461
|
options:
|
|
456
462
|
role: scribe
|
|
457
463
|
agent: ${vars.agent}
|
|
464
|
+
timeoutMs: 600000
|
|
458
465
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
459
466
|
input: |
|
|
460
467
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of plan docs; don't edit it.
|
|
@@ -517,6 +524,7 @@ nodes:
|
|
|
517
524
|
options:
|
|
518
525
|
role: scribe
|
|
519
526
|
agent: ${vars.agent}
|
|
527
|
+
timeoutMs: 600000
|
|
520
528
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
521
529
|
input: |
|
|
522
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.
|
|
@@ -552,6 +560,8 @@ nodes:
|
|
|
552
560
|
are stripped at the nested-spur boundary (the same two `NESTED_SPUR_UNSET` vars the stage
|
|
553
561
|
runner strips, kk-workflow-stages.ts): without it `spur workflow run` refuses to start inside
|
|
554
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.
|
|
555
565
|
dependsOn: [script]
|
|
556
566
|
condition:
|
|
557
567
|
kind: shell
|
|
@@ -563,7 +573,8 @@ nodes:
|
|
|
563
573
|
command: >-
|
|
564
574
|
env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST
|
|
565
575
|
spur workflow run plugins/kk/workflows/kk-daily-safety.yaml
|
|
566
|
-
--
|
|
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}\"}"
|
|
567
578
|
--quiet --no-plan
|
|
568
579
|
|
|
569
580
|
- id: review
|
|
@@ -710,21 +721,10 @@ nodes:
|
|
|
710
721
|
command: >-
|
|
711
722
|
kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
|
|
712
723
|
|
|
713
|
-
#
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
tail, wrap-docs and publish-prep (the seam's merge is part of the audited bytes); publish-prep
|
|
718
|
-
being skipped on a non-publishing run does not skip the audit.
|
|
719
|
-
dependsOn: [duration, wrap-docs, publish-prep]
|
|
720
|
-
action:
|
|
721
|
-
kind: shell
|
|
722
|
-
options:
|
|
723
|
-
command: >-
|
|
724
|
-
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
725
|
-
|
|
726
|
-
- id: publish-surfdash
|
|
727
|
-
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"
|
|
728
728
|
dependsOn: [publish-prep]
|
|
729
729
|
condition:
|
|
730
730
|
kind: shell
|
|
@@ -734,80 +734,22 @@ nodes:
|
|
|
734
734
|
kind: shell
|
|
735
735
|
options:
|
|
736
736
|
command: >-
|
|
737
|
-
kk stage daily-
|
|
737
|
+
kk stage daily-prepare-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
738
738
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
options:
|
|
747
|
-
command: >-
|
|
748
|
-
kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1
|
|
749
|
-
action:
|
|
750
|
-
kind: shell
|
|
751
|
-
options:
|
|
752
|
-
command: >-
|
|
753
|
-
PODCAST_PUB_SEO="${vars.podcast_seo}"
|
|
754
|
-
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
755
|
-
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
756
|
-
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
757
|
-
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
758
|
-
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
759
|
-
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
760
|
-
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
761
|
-
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
762
|
-
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
763
|
-
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
764
|
-
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
765
|
-
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
766
|
-
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
767
|
-
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
768
|
-
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
769
|
-
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
770
|
-
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
771
|
-
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
772
|
-
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
773
|
-
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
774
|
-
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
775
|
-
|
|
776
|
-
- id: publish-podcast
|
|
777
|
-
description: "Publish the episode through podcast-pub with the rendered show notes (metadata.showNotes)"
|
|
778
|
-
dependsOn: [show-notes]
|
|
779
|
-
condition:
|
|
780
|
-
kind: shell
|
|
781
|
-
options:
|
|
782
|
-
command: 'test "${vars.publish_enabled}" = "true"'
|
|
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]
|
|
783
746
|
action:
|
|
784
747
|
kind: shell
|
|
785
748
|
options:
|
|
786
749
|
command: >-
|
|
787
|
-
|
|
788
|
-
PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
|
|
789
|
-
PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
|
|
790
|
-
PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
|
|
791
|
-
PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
|
|
792
|
-
PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
|
|
793
|
-
PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
|
|
794
|
-
PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
|
|
795
|
-
PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
|
|
796
|
-
PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
|
|
797
|
-
PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
|
|
798
|
-
PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
|
|
799
|
-
PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
|
|
800
|
-
PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
|
|
801
|
-
PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
|
|
802
|
-
PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
|
|
803
|
-
PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
|
|
804
|
-
PODCAST_PUB_SLUG="${vars.podcast_slug}"
|
|
805
|
-
PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
|
|
806
|
-
PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
|
|
807
|
-
PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
|
|
808
|
-
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}"
|
|
809
751
|
|
|
810
|
-
# ----
|
|
752
|
+
# ---- soft side channels + local-locale gate (exit 0 on channel failure; final Surfdash remains fail-closed) ----
|
|
811
753
|
- id: xhs-write
|
|
812
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"
|
|
813
755
|
dependsOn: [script, review]
|
|
@@ -823,6 +765,7 @@ nodes:
|
|
|
823
765
|
options:
|
|
824
766
|
role: scribe
|
|
825
767
|
agent: ${vars.agent}
|
|
768
|
+
timeoutMs: 600000
|
|
826
769
|
# ADR-115 error tier: keep this prompt <= 1000 chars.
|
|
827
770
|
input: |
|
|
828
771
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
|
|
@@ -871,22 +814,9 @@ nodes:
|
|
|
871
814
|
command: >-
|
|
872
815
|
kk stage daily-publish-xhs "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.xhs_live}" "true"
|
|
873
816
|
|
|
874
|
-
- id: wechat-publish
|
|
875
|
-
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)"
|
|
876
|
-
dependsOn: [publish-prep]
|
|
877
|
-
condition:
|
|
878
|
-
kind: shell
|
|
879
|
-
options:
|
|
880
|
-
command: 'test "${vars.publish_enabled}" = "true" && test "${vars.wechat_enabled}" = "true"'
|
|
881
|
-
action:
|
|
882
|
-
kind: shell
|
|
883
|
-
options:
|
|
884
|
-
command: >-
|
|
885
|
-
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}" "${vars.wechat_collection}"
|
|
886
|
-
|
|
887
817
|
- id: translate-localize
|
|
888
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"
|
|
889
|
-
dependsOn: [publish-surfdash]
|
|
819
|
+
dependsOn: [publish-surfdash-prepare]
|
|
890
820
|
condition:
|
|
891
821
|
kind: shell
|
|
892
822
|
options:
|
|
@@ -901,14 +831,14 @@ nodes:
|
|
|
901
831
|
description: >-
|
|
902
832
|
Agent translates both scaffolded locale drafts in place. The two draft paths are read from the
|
|
903
833
|
marker files at their fixed paths (no var projection after the fan-out: concurrent setVars is
|
|
904
|
-
last-writer-wins, design §6), and an empty marker means that locale is absent.
|
|
905
|
-
|
|
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.
|
|
906
836
|
dependsOn: [translate-localize]
|
|
907
837
|
action:
|
|
908
838
|
kind: agent.run
|
|
909
839
|
options:
|
|
910
|
-
# 20261012 hardening: this is the LAST agent step and
|
|
911
|
-
#
|
|
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
|
|
912
842
|
# executor failure here aborted the DAG *after* surfdash and podcast had published, so
|
|
913
843
|
# publish-classify, mark-published, run-report and ledger never ran for an otherwise complete
|
|
914
844
|
# day. Tolerating the failure is safe because a scaffold is an unpublished draft: the prompt
|
|
@@ -918,26 +848,24 @@ nodes:
|
|
|
918
848
|
onError: continue
|
|
919
849
|
role: scribe
|
|
920
850
|
agent: ${vars.agent}
|
|
921
|
-
|
|
922
|
-
#
|
|
923
|
-
#
|
|
924
|
-
#
|
|
925
|
-
# the residue visible instead of a silent skip.
|
|
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.
|
|
926
855
|
input: &translate-input |
|
|
927
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:
|
|
928
857
|
English: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_en-post-path.txt
|
|
929
858
|
Japanese: ${vars.work_dir}/31-publish/${vars.run_date}_20_translate-localize_ja-post-path.txt
|
|
930
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.
|
|
931
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.
|
|
932
|
-
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 "…".
|
|
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.
|
|
933
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.
|
|
934
863
|
Do not wait for operator confirmation. Each edited file must end fully in its target language.
|
|
935
864
|
|
|
936
865
|
# 2026-10-24: the locale translation carried `onError: continue` with NO retry sibling, so a
|
|
937
|
-
# failed executor left the en/ja drafts untranslated and
|
|
938
|
-
#
|
|
939
|
-
#
|
|
940
|
-
# rather than failing the DAG) and runs only when a scaffolded draft is still untranslated.
|
|
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.
|
|
941
869
|
- id: translate-retry
|
|
942
870
|
description: "Retry the en/ja locale translation on the capable tier when the primary left a scaffolded draft untranslated (2026-10-24)"
|
|
943
871
|
dependsOn: [translate]
|
|
@@ -953,26 +881,144 @@ nodes:
|
|
|
953
881
|
onError: continue
|
|
954
882
|
role: reviewer
|
|
955
883
|
agent: ${vars.agent_retry}
|
|
884
|
+
timeoutMs: 600000
|
|
956
885
|
input: *translate-input
|
|
957
886
|
|
|
958
|
-
- id: translate-
|
|
959
|
-
description: "
|
|
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"
|
|
960
889
|
dependsOn: [translate, translate-retry]
|
|
890
|
+
condition:
|
|
891
|
+
kind: shell
|
|
892
|
+
options:
|
|
893
|
+
command: 'test "${vars.publish_enabled}" = "true"'
|
|
961
894
|
action:
|
|
962
895
|
kind: shell
|
|
896
|
+
onError: continue
|
|
963
897
|
options:
|
|
964
898
|
command: >-
|
|
965
|
-
kk stage daily-
|
|
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"'
|
|
986
|
+
action:
|
|
987
|
+
kind: shell
|
|
988
|
+
options:
|
|
989
|
+
command: >-
|
|
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}"
|
|
966
1012
|
|
|
967
1013
|
# ---- fan-in: classify the publish outcome, then close the run ----
|
|
968
1014
|
- id: publish-classify
|
|
969
1015
|
description: >-
|
|
970
|
-
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`:
|
|
971
1017
|
classifies the sequenced surfdash → podcast outcomes into publish-status.txt full|partial|none
|
|
972
|
-
(exit 0 in all three), records
|
|
1018
|
+
(exit 0 in all three), records side-channel/locale completeness, and folds in the
|
|
973
1019
|
surfdash post-patching + the partial-publish marker that voice1 ran as separate states (a
|
|
974
1020
|
classify is the only node that knows the status). Non-publishing runs skip the whole subtree.
|
|
975
|
-
dependsOn: [publish-podcast, publish-surfdash, xhs-publish, wechat-publish, translate-
|
|
1021
|
+
dependsOn: [publish-podcast, publish-surfdash, xhs-publish, wechat-publish, translate-validate]
|
|
976
1022
|
dependencyPolicy: all
|
|
977
1023
|
condition:
|
|
978
1024
|
kind: shell
|
|
@@ -997,7 +1043,8 @@ nodes:
|
|
|
997
1043
|
kind: shell
|
|
998
1044
|
options:
|
|
999
1045
|
command: >-
|
|
1000
|
-
|
|
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
|
|
1001
1048
|
&& test "${vars.publish_enabled}" = "true"
|
|
1002
1049
|
action:
|
|
1003
1050
|
kind: shell
|