@gobing-ai/knowledge-kit 0.1.10 → 0.1.12
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 +2478 -1737
- package/package.json +1 -1
- package/plugins/generations/image-gen/README.md +108 -0
- package/plugins/generations/image-gen/assets/sample/cover__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/dist/index.js +102 -58
- package/plugins/generations/image-gen/scripts/compose-e2e-artifact.ts +5 -21
- package/plugins/generations/image-gen/scripts/gen-samples.ts +208 -0
- package/plugins/generations/image-gen/src/compose/chromium.ts +2 -3
- package/plugins/generations/image-gen/src/presets.ts +6 -3
- package/plugins/generations/image-gen/src/providers/errors.ts +16 -1
- package/plugins/generations/image-gen/src/providers/minimax.ts +46 -17
- package/plugins/ingestions/web-search/src/index.ts +1 -1
- package/plugins/kk/README.md +18 -0
- package/plugins/kk/commands/image-extract.md +1 -0
- package/plugins/kk/commands/image-generate.md +1 -0
- package/plugins/kk/commands/tell-me.md +1 -0
- package/plugins/kk/commands/workflow-run.md +7 -1
- package/plugins/kk/decisions/extract/it-topics.yaml +24 -0
- package/plugins/kk/decisions/kk-itc.yaml +6 -4
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/publish-presets/daily-ai-voice-migration.json +146 -0
- package/plugins/kk/publish-presets/daily-ai-voice.json +11 -3
- package/plugins/kk/schedules/kk-collect.grok-bot.md.tmpl +69 -0
- package/plugins/kk/schedules/kk-daily-ai-voice3.cron.tmpl +18 -0
- package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +15 -5
- package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +9 -4
- package/plugins/kk/scripts/daily-scheduled-run.ts +298 -0
- package/plugins/kk/scripts/daily-stages.ts +1403 -156
- package/plugins/kk/scripts/kk-workflow-stages.ts +266 -10
- package/plugins/kk/scripts/safety-artifact-check.ts +277 -0
- package/plugins/kk/scripts/voice3-publish-window.ts +202 -0
- package/plugins/kk/skills/article-adapt/SKILL.md +1 -0
- package/plugins/kk/skills/audio-authoring/SKILL.md +1 -0
- package/plugins/kk/skills/content-judge/SKILL.md +1 -0
- package/plugins/kk/skills/content-safety/SKILL.md +1 -0
- package/plugins/kk/skills/explain-things/SKILL.md +1 -0
- package/plugins/kk/skills/image-authoring/SKILL.md +1 -0
- package/plugins/kk/skills/itc-generating/SKILL.md +1 -0
- package/plugins/kk/skills/publish/SKILL.md +1 -0
- package/plugins/kk/skills/smart-seo/SKILL.md +1 -0
- package/plugins/kk/skills/storm-research/SKILL.md +1 -0
- package/plugins/kk/skills/taste-unslop/SKILL.md +1 -0
- package/plugins/kk/skills/topic/SKILL.md +1 -0
- package/plugins/kk/skills/web-automation/SKILL.md +1 -0
- package/plugins/kk/skills/xhs-images/SKILL.md +1 -0
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +177 -128
- package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +161 -61
- package/plugins/kk/workflows/kk-daily-publish3.yaml +82 -26
- package/plugins/kk/workflows/kk-daily-safety.yaml +156 -69
- package/plugins/kk/workflows/kk-itc-author.yaml +75 -0
- package/plugins/kk/workflows/kk-itc-publish-tail.yaml +25 -3
- package/plugins/kk/workflows/kk-itc.yaml +36 -4
- 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/linkedin-pub/dist/index.js +21333 -0
- package/plugins/publishings/linkedin-pub/dist/profile-check.js +21005 -0
- package/plugins/publishings/linkedin-pub/package.json +19 -0
- package/plugins/publishings/linkedin-pub/plugin.json +12 -0
- package/plugins/publishings/linkedin-pub/src/index.ts +705 -0
- package/plugins/publishings/linkedin-pub/src/profile-check.ts +17 -0
- package/plugins/publishings/linkedin-pub/tsconfig.json +4 -0
- 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 +461 -53
- package/plugins/publishings/surfdash-pub/src/digest-check.ts +85 -19
- package/plugins/publishings/surfdash-pub/src/index.ts +592 -65
- package/plugins/publishings/threads-pub/dist/index.js +7 -0
- package/plugins/publishings/threads-pub/src/index.ts +12 -0
- package/plugins/publishings/wechatmp-pub/dist/index.js +936 -98
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +60 -3
- package/plugins/publishings/wechatmp-pub/src/flow.ts +1134 -86
- 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 +9 -1
- package/plugins/publishings/x-pub/dist/profile-check.js +2 -1
- package/plugins/publishings/x-pub/src/index.ts +11 -0
- 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
|
@@ -24,6 +24,12 @@ vars:
|
|
|
24
24
|
publish_window: "after-generate"
|
|
25
25
|
publish_preset: ""
|
|
26
26
|
publish_dummy: "false"
|
|
27
|
+
# 0334 R7: opt-in live consent channel, forwarded verbatim to the nested publish3
|
|
28
|
+
# `live_channels`. Empty by default — this variable alone can never produce a live send
|
|
29
|
+
# (ADR-022: only an operator launch that sets it, with tokens the plan stage validates
|
|
30
|
+
# against the window, promotes rows; `publish_dummy=true` and a non-empty token list
|
|
31
|
+
# refuse each other at plan time).
|
|
32
|
+
publish_live_channels: ""
|
|
27
33
|
# Composed by the `prepare` node (`kk daily prepare`), then read back into vars through the
|
|
28
34
|
# single-node read chain below — shell actions cannot set vars, and a concurrent `setVars` merge
|
|
29
35
|
# is last-writer-wins, so the DAG exchanges data by fixed paths under work_dir, never by vars.
|
|
@@ -140,6 +146,7 @@ nodes:
|
|
|
140
146
|
command: >-
|
|
141
147
|
kk stage daily-preflight plugins/kk/workflows/kk-daily-ai-voice2.yaml
|
|
142
148
|
plugins/kk/workflows/kk-daily-safety.yaml
|
|
149
|
+
plugins/kk/workflows/kk-daily-publish3.yaml
|
|
143
150
|
&& kk daily prepare --workflow kk-daily-ai-voice2 --run-date "${vars.run_date}" --ledger-sync
|
|
144
151
|
--run "${vars.__runId}" --definition-digest "${vars.__definitionDigest}"
|
|
145
152
|
--setting window="${vars.window}" --setting language="${vars.language}" --setting vdriver="${vars.vdriver}"
|
|
@@ -243,6 +250,31 @@ nodes:
|
|
|
243
250
|
command: >-
|
|
244
251
|
kk stage daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}" "${vars.qc_min_episode_items}" "${vars.episode_min_minutes}" "${vars.vdriver}"
|
|
245
252
|
|
|
253
|
+
# One guard for the whole pool (0324 R3; 0329 F01/F15): it sits right after `qc-content`, in
|
|
254
|
+
# front of every token reader, and NEVER exits non-zero — in this engine a failed dependency
|
|
255
|
+
# never releases its dependents, so a failing gate on an empty pool wedges the graph into
|
|
256
|
+
# dag-unreachable and turns a thin day into a failed run. The token carries the verdict
|
|
257
|
+
# instead: PASS opens the content fan-out, EMPTY makes every token-gated node skip, and the
|
|
258
|
+
# run still closes `done` with a no-content run report.
|
|
259
|
+
- id: pool-gate
|
|
260
|
+
description: >-
|
|
261
|
+
The single pool guard (0324 R3, 0329): `kk store pool-count --min 1` over the candidates
|
|
262
|
+
file that `qc-content` wrote, recorded once as a token at
|
|
263
|
+
`.spur/run/<runId>-pool-gate.status`. A token, not a bare dependency, because the engine
|
|
264
|
+
skips a node only when ALL its dependencies were skipped — so a failed gate beside a done
|
|
265
|
+
dependency would still admit the node. The gate itself always exits 0 (0329: an empty pool
|
|
266
|
+
is a no-content day, not a failed run).
|
|
267
|
+
dependsOn: [qc-content]
|
|
268
|
+
action:
|
|
269
|
+
kind: shell
|
|
270
|
+
options:
|
|
271
|
+
command: >-
|
|
272
|
+
OUT=".spur/run/${vars.__runId}-pool-gate.status";
|
|
273
|
+
mkdir -p "$(dirname "$OUT")";
|
|
274
|
+
if kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1;
|
|
275
|
+
then echo PASS > "$OUT";
|
|
276
|
+
else echo EMPTY > "$OUT"; rm -f "${vars.work_dir}/bundle.json"; fi
|
|
277
|
+
|
|
246
278
|
- id: select
|
|
247
279
|
description: >-
|
|
248
280
|
Record today's planned set in the store's topic-scoped `selections` ledger (0228). It runs at
|
|
@@ -250,7 +282,7 @@ nodes:
|
|
|
250
282
|
depends on the DAG reaching its tail: a run that fails, pauses at review or skips publish still
|
|
251
283
|
excludes what it planned. Guarded on the QC candidates — a pool that filtered to zero items
|
|
252
284
|
selects nothing.
|
|
253
|
-
dependsOn: [qc-content]
|
|
285
|
+
dependsOn: [qc-content, pool-gate]
|
|
254
286
|
condition:
|
|
255
287
|
kind: shell
|
|
256
288
|
options:
|
|
@@ -275,7 +307,7 @@ nodes:
|
|
|
275
307
|
|
|
276
308
|
- id: plan-translate
|
|
277
309
|
description: "Translate the needsTranslation plan items into the output plan.json via agent.run; onError: continue — plan-translate-retry below is the loud gate (0238 R2)"
|
|
278
|
-
dependsOn: [qc-content]
|
|
310
|
+
dependsOn: [qc-content, pool-gate]
|
|
279
311
|
condition:
|
|
280
312
|
kind: shell
|
|
281
313
|
options:
|
|
@@ -315,7 +347,7 @@ nodes:
|
|
|
315
347
|
VALID expectFile (missing or contract-violating JSON), retries the same input on a different
|
|
316
348
|
tier, and carries NO onError — a second miss fails the run loudly instead of letting an
|
|
317
349
|
untranslated or truncated plan flow downstream.
|
|
318
|
-
dependsOn: [plan-translate]
|
|
350
|
+
dependsOn: [plan-translate, pool-gate]
|
|
319
351
|
condition:
|
|
320
352
|
kind: shell
|
|
321
353
|
options:
|
|
@@ -335,7 +367,7 @@ nodes:
|
|
|
335
367
|
|
|
336
368
|
- id: article-write
|
|
337
369
|
description: "Write the tech-sharing prose article from the translated plan via agent.run (3-format split: surfdash gets prose, not a news list); onError: continue — article-write-retry below is the loud gate (0238 R2)"
|
|
338
|
-
dependsOn: [qc-content, plan-translate, plan-translate-retry]
|
|
370
|
+
dependsOn: [qc-content, plan-translate, plan-translate-retry, pool-gate]
|
|
339
371
|
condition:
|
|
340
372
|
kind: shell
|
|
341
373
|
options:
|
|
@@ -369,7 +401,7 @@ nodes:
|
|
|
369
401
|
Bounded recovery for article-write (0238 R2): runs only when the primary settled without its
|
|
370
402
|
expectFile, retries the same input on a different tier, and carries NO onError — a second miss
|
|
371
403
|
fails the run loudly instead of publishing an article nobody wrote.
|
|
372
|
-
dependsOn: [article-write]
|
|
404
|
+
dependsOn: [article-write, pool-gate]
|
|
373
405
|
condition:
|
|
374
406
|
kind: shell
|
|
375
407
|
options:
|
|
@@ -394,7 +426,7 @@ nodes:
|
|
|
394
426
|
this node: a done dependency in the mix keeps it runnable) as well as on both authoring nodes
|
|
395
427
|
AND their retry siblings, and it carries the same candidates guard — a pool that filtered to
|
|
396
428
|
zero items must not build an empty article.
|
|
397
|
-
dependsOn: [qc-content, plan-translate, plan-translate-retry, article-write, article-write-retry]
|
|
429
|
+
dependsOn: [qc-content, plan-translate, plan-translate-retry, article-write, article-write-retry, pool-gate]
|
|
398
430
|
condition:
|
|
399
431
|
kind: shell
|
|
400
432
|
options:
|
|
@@ -411,7 +443,7 @@ nodes:
|
|
|
411
443
|
# 0238 R3: reads 21-plan/<date>_03_plan_plan.json, so it must not start before the plan
|
|
412
444
|
# producers settle. `qc-content` stays in the list: a done dependency in the mix keeps this node
|
|
413
445
|
# runnable when the plan-authoring nodes are disabled (all-skipped deps skip a node).
|
|
414
|
-
dependsOn: [qc-content, plan-translate, plan-translate-retry]
|
|
446
|
+
dependsOn: [qc-content, plan-translate, plan-translate-retry, pool-gate]
|
|
415
447
|
condition:
|
|
416
448
|
kind: shell
|
|
417
449
|
options:
|
|
@@ -458,7 +490,7 @@ nodes:
|
|
|
458
490
|
|
|
459
491
|
- id: script-style
|
|
460
492
|
description: "Snapshot the shipped zh style contract into the work dir (separate node: it is also an audit input for the revision loop)"
|
|
461
|
-
dependsOn: [qc-content]
|
|
493
|
+
dependsOn: [qc-content, pool-gate]
|
|
462
494
|
condition:
|
|
463
495
|
kind: shell
|
|
464
496
|
options:
|
|
@@ -475,7 +507,7 @@ nodes:
|
|
|
475
507
|
# 0238 R3: reads 21-plan/<date>_03_plan_plan.json, so it must not start before the plan
|
|
476
508
|
# producers settle. `script-style` stays first in the list: a done dependency in the mix keeps
|
|
477
509
|
# this node runnable when the plan-authoring nodes are disabled (all-skipped deps skip a node).
|
|
478
|
-
dependsOn: [script-style, plan-translate, plan-translate-retry]
|
|
510
|
+
dependsOn: [script-style, plan-translate, plan-translate-retry, article, pool-gate]
|
|
479
511
|
condition:
|
|
480
512
|
kind: shell
|
|
481
513
|
options:
|
|
@@ -505,7 +537,7 @@ nodes:
|
|
|
505
537
|
Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content. Depends on
|
|
506
538
|
`article` so the node still runs when the plan-authoring nodes were disabled or skipped (the
|
|
507
539
|
builder falls back to templates) — but not when the whole content branch is skipped.
|
|
508
|
-
dependsOn: [article, script-style, script-write]
|
|
540
|
+
dependsOn: [article, script-style, script-write, pool-gate]
|
|
509
541
|
condition:
|
|
510
542
|
kind: shell
|
|
511
543
|
options:
|
|
@@ -558,7 +590,7 @@ nodes:
|
|
|
558
590
|
|
|
559
591
|
- id: wrap-docs
|
|
560
592
|
description: "Wrap VoiceScript into Doc[] input for the voice generator plugin (the generate-side variant input)"
|
|
561
|
-
dependsOn: [script, review]
|
|
593
|
+
dependsOn: [script, review, pool-gate]
|
|
562
594
|
condition:
|
|
563
595
|
kind: shell
|
|
564
596
|
options:
|
|
@@ -574,7 +606,7 @@ nodes:
|
|
|
574
606
|
description: "Voice tier, primary driver (vars.vdriver — voxcpm; its 40 ms lattice is known and monitored, see the vdriver comment). onError: continue — the fallback siblings below are the loud chain (same pattern as 0238 R2)."
|
|
575
607
|
# wrap-docs is a data dependency, not a sibling: daily-generate reads its _10_wrap-docs_docs.json
|
|
576
608
|
# as the TTS input. Without this edge the two nodes race in the same wave (0212 dogfood finding).
|
|
577
|
-
dependsOn: [script, safety, review, wrap-docs]
|
|
609
|
+
dependsOn: [script, safety, review, wrap-docs, pool-gate]
|
|
578
610
|
condition:
|
|
579
611
|
kind: shell
|
|
580
612
|
options:
|
|
@@ -582,8 +614,8 @@ nodes:
|
|
|
582
614
|
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
583
615
|
action:
|
|
584
616
|
kind: shell
|
|
617
|
+
onError: continue
|
|
585
618
|
options:
|
|
586
|
-
onError: continue
|
|
587
619
|
command: >-
|
|
588
620
|
OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
|
|
589
621
|
|
|
@@ -595,7 +627,7 @@ nodes:
|
|
|
595
627
|
|
|
596
628
|
- id: generate-fallback
|
|
597
629
|
description: "Voice tier fallback 1: gemini (gemini-tts) when the primary driver left no valid Content — the measured-dry escape hatch from voxcpm's lattice"
|
|
598
|
-
dependsOn: [generate]
|
|
630
|
+
dependsOn: [generate, pool-gate]
|
|
599
631
|
condition:
|
|
600
632
|
kind: shell
|
|
601
633
|
options:
|
|
@@ -605,14 +637,14 @@ nodes:
|
|
|
605
637
|
&& ! jq -e '(.metadata.generator == "kk:voxcpm-tts" or .metadata.generator == "kk:gemini-tts") and .metadata.qc.passed == true' "${vars.work_dir}/30-audio/${vars.run_date}_11_generate_content.json"
|
|
606
638
|
action:
|
|
607
639
|
kind: shell
|
|
640
|
+
onError: continue
|
|
608
641
|
options:
|
|
609
|
-
onError: continue
|
|
610
642
|
command: >-
|
|
611
643
|
kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "gemini" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
|
|
612
644
|
|
|
613
645
|
- id: generate-fallback2
|
|
614
646
|
description: "Voice tier fallback 2 (last, loud): omni-voice-gen when neither voxcpm nor gemini delivered"
|
|
615
|
-
dependsOn: [generate-fallback]
|
|
647
|
+
dependsOn: [generate-fallback, pool-gate]
|
|
616
648
|
condition:
|
|
617
649
|
kind: shell
|
|
618
650
|
options:
|
|
@@ -626,9 +658,17 @@ nodes:
|
|
|
626
658
|
command: >-
|
|
627
659
|
OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "ominivoice" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
|
|
628
660
|
|
|
661
|
+
# 0329 F15: a script-gate refusal produces no audio, but the sting must not fail the day —
|
|
662
|
+
# the refusal artifact admits intro (and duration closes the run as no-content on it).
|
|
629
663
|
- id: intro
|
|
630
664
|
description: "Prepend the 8s intro sting (intro_music var; empty = skip), update duration/audioPath"
|
|
631
665
|
dependsOn: [generate, generate-fallback, generate-fallback2]
|
|
666
|
+
condition:
|
|
667
|
+
kind: shell
|
|
668
|
+
options:
|
|
669
|
+
command: >-
|
|
670
|
+
test -f "${vars.work_dir}/30-audio/${vars.run_date}_11_generate_content.json"
|
|
671
|
+
-o -f "${vars.work_dir}/30-audio/${vars.run_date}_11_generate_content_duration-refusal.json"
|
|
632
672
|
action:
|
|
633
673
|
kind: shell
|
|
634
674
|
options:
|
|
@@ -658,32 +698,39 @@ nodes:
|
|
|
658
698
|
onError: continue
|
|
659
699
|
|
|
660
700
|
- id: duration
|
|
661
|
-
description: "Measured duration gate: under episode_min_minutes the run refuses and closes as no-content (refusal artifact + emptied candidates, exit 0 — 0266); above episode_max_minutes it still fails loud. Duration is authoritative."
|
|
701
|
+
description: "Measured duration gate: under episode_min_minutes the run refuses and closes as no-content (refusal artifact + emptied candidates + publish-path invalidation, exit 0 — 0266/0329); above episode_max_minutes it still fails loud. Duration is authoritative."
|
|
662
702
|
dependsOn: [intro]
|
|
663
703
|
action:
|
|
664
704
|
kind: shell
|
|
665
705
|
options:
|
|
666
706
|
command: >-
|
|
667
|
-
kk stage daily-episode-duration "${vars.work_dir}" "${vars.run_date}" "${vars.episode_min_minutes}" "${vars.episode_max_minutes}" "${vars.vdriver}"
|
|
707
|
+
kk stage daily-episode-duration "${vars.work_dir}" "${vars.run_date}" "${vars.episode_min_minutes}" "${vars.episode_max_minutes}" "${vars.vdriver}" "${vars.__runId}"
|
|
668
708
|
|
|
669
709
|
- id: quality
|
|
670
710
|
description: >-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
711
|
+
Print the daily QC table (daily-quality-report) and then gate on it (0329 F23): the print
|
|
712
|
+
alone never gated anything — a 41/100 episode sailed into show-notes, the bundle and the
|
|
713
|
+
publish windows. The verdict stage fails loud when the generator's audio qc verdict is
|
|
714
|
+
missing or `passed` is false, so a bad episode dies here instead of at a tier-0 send.
|
|
715
|
+
dependsOn: [duration, wrap-docs, pool-gate]
|
|
716
|
+
condition:
|
|
717
|
+
kind: shell
|
|
718
|
+
options:
|
|
719
|
+
# 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
|
|
720
|
+
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
675
721
|
action:
|
|
676
722
|
kind: shell
|
|
677
723
|
options:
|
|
678
724
|
command: >-
|
|
679
725
|
kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
|
|
726
|
+
&& kk stage run daily-quality-verdict "${vars.work_dir}" "${vars.run_date}"
|
|
680
727
|
|
|
681
728
|
- id: show-notes
|
|
682
729
|
description: >-
|
|
683
730
|
Render timeline show notes from the episode plan — content, not publishing: the publish half
|
|
684
731
|
ships them as the episode variant's `metadata.showNotes`, and the link placeholders stay
|
|
685
732
|
unresolved until a send resolves them."
|
|
686
|
-
dependsOn: [quality]
|
|
733
|
+
dependsOn: [quality, pool-gate]
|
|
687
734
|
condition:
|
|
688
735
|
kind: shell
|
|
689
736
|
options:
|
|
@@ -694,13 +741,36 @@ nodes:
|
|
|
694
741
|
options:
|
|
695
742
|
command: >-
|
|
696
743
|
kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
|
|
744
|
+
|
|
745
|
+
# 0329 F02: voice3 lost daily-publish-prep in the migration (voice2 keeps it at its :704), so
|
|
746
|
+
# `31-publish/<date>_13_publish-prep_content.json` — the episode variant's source — was never
|
|
747
|
+
# written by this workflow, and a freeze only succeeded against a stale same-day voice2 prep
|
|
748
|
+
# file. Rebuilt per the voice2 template: after duration (it reads the generate content's
|
|
749
|
+
# mp3Path, and ID3 stamps must land on the final post-intro MP3), pool-gated so a refused
|
|
750
|
+
# episode packages nothing.
|
|
751
|
+
- id: publish-prep
|
|
752
|
+
description: >-
|
|
753
|
+
Package the publish-ready episode Content (ID3 tags on the final post-intro MP3, cover
|
|
754
|
+
path, article link) into 31-publish/<date>_13_publish-prep_content.json — the episode
|
|
755
|
+
variant's source for bundle-variants.
|
|
756
|
+
dependsOn: [duration, article, safety, pool-gate]
|
|
757
|
+
condition:
|
|
758
|
+
kind: shell
|
|
759
|
+
options:
|
|
760
|
+
# 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
|
|
761
|
+
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
762
|
+
action:
|
|
763
|
+
kind: shell
|
|
764
|
+
options:
|
|
765
|
+
command: >-
|
|
766
|
+
kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}" "${vars.podcast_artist}" "${vars.podcast_album}"
|
|
697
767
|
- id: cards-write
|
|
698
768
|
description: "Plan the card series across the episode's plan items via agent.run; onError: continue — a failed plan skips the card series, never the run"
|
|
699
769
|
dependsOn: [script, review, pool-gate]
|
|
700
770
|
condition:
|
|
701
771
|
kind: shell
|
|
702
772
|
options:
|
|
703
|
-
command: 'test "${vars.cards_enabled}" = "true"'
|
|
773
|
+
command: 'test "${vars.cards_enabled}" = "true" && test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
704
774
|
action:
|
|
705
775
|
kind: agent.run
|
|
706
776
|
onError: continue
|
|
@@ -720,14 +790,14 @@ nodes:
|
|
|
720
790
|
|
|
721
791
|
- id: cards-render
|
|
722
792
|
description: "Render the card series through the pinned xhs-card/xhs-knowledge presets, attach the episode cover last and pair every image with its numbered line (0235 note_scope=episode); onError: continue — the publish node names the missing series and stays soft"
|
|
723
|
-
dependsOn: [cards-write]
|
|
793
|
+
dependsOn: [cards-write, pool-gate]
|
|
724
794
|
# A skipped cards-write still admits its dependents, so each card node keeps its own switch:
|
|
725
795
|
# without it a run with cards_enabled=false would re-package a stale same-day 25-xhs/ series.
|
|
726
796
|
# The pool guard is `pool-gate`'s alone in voice3 (0324 R3).
|
|
727
797
|
condition:
|
|
728
798
|
kind: shell
|
|
729
799
|
options:
|
|
730
|
-
command: 'test "${vars.cards_enabled}" = "true"'
|
|
800
|
+
command: 'test "${vars.cards_enabled}" = "true" && test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
731
801
|
action:
|
|
732
802
|
kind: shell
|
|
733
803
|
onError: continue
|
|
@@ -737,31 +807,12 @@ nodes:
|
|
|
737
807
|
|
|
738
808
|
# ---- generate-side payloads and the frozen bundle (0324 R2/R3) ----
|
|
739
809
|
|
|
740
|
-
# One guard for the whole pool (0324 R3): every node that used to repeat `kk store pool-count`
|
|
741
|
-
# depends on this one, so a refused pool empties the candidates file once and the rest of the
|
|
742
|
-
# graph skips through the engine's all-deps-skipped rule.
|
|
743
|
-
- id: pool-gate
|
|
744
|
-
description: >-
|
|
745
|
-
The single pool guard (0324 R3): `kk store pool-count --min 1` over the candidates file that
|
|
746
|
-
`qc-content` wrote, recorded once as a token at `.spur/run/<runId>-pool-gate.status`. voice2
|
|
747
|
-
repeated the same guard on twenty-one conditions. A token, not a bare dependency, because the
|
|
748
|
-
engine skips a node only when ALL its dependencies were skipped — so a failed gate beside a
|
|
749
|
-
done dependency would still admit the node. Everything downstream reads the token.
|
|
750
|
-
dependsOn: [qc-content]
|
|
751
|
-
action:
|
|
752
|
-
kind: shell
|
|
753
|
-
options:
|
|
754
|
-
command: >-
|
|
755
|
-
OUT=".spur/run/${vars.__runId}-pool-gate.status";
|
|
756
|
-
if kk store pool-count "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json" --min 1;
|
|
757
|
-
then echo PASS > "$OUT";
|
|
758
|
-
else echo EMPTY > "$OUT"; exit 1; fi
|
|
759
|
-
|
|
760
810
|
- id: social-write
|
|
761
811
|
description: >-
|
|
762
|
-
Author one short post per social platform (x, weibo, threads) from the approved
|
|
763
|
-
inside each platform's character cap. `daily-bundle-variants` truncates as a
|
|
764
|
-
missed cap degrades rather than fails; a missing file simply skips that
|
|
812
|
+
Author one short post per social platform (x, weibo, threads, linkedin) from the approved
|
|
813
|
+
plan, in zh, inside each platform's character cap. `daily-bundle-variants` truncates as a
|
|
814
|
+
backstop, so a missed cap degrades rather than fails; a missing file simply skips that
|
|
815
|
+
platform's variant.
|
|
765
816
|
dependsOn: [quality]
|
|
766
817
|
action:
|
|
767
818
|
kind: agent.run
|
|
@@ -771,10 +822,11 @@ nodes:
|
|
|
771
822
|
agent: ${vars.agent}
|
|
772
823
|
input: |
|
|
773
824
|
Read the approved episode plan at ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json.
|
|
774
|
-
Write
|
|
825
|
+
Write four short zh posts, one per file:
|
|
775
826
|
- ${vars.work_dir}/26-social/x.md — at most 280 weighted characters (this feed counts CJK as 2, so ~140 汉字 or 280 Latin letters)
|
|
776
827
|
- ${vars.work_dir}/26-social/weibo.md — at most 2000 characters
|
|
777
828
|
- ${vars.work_dir}/26-social/threads.md — at most 500 characters
|
|
829
|
+
- ${vars.work_dir}/26-social/linkedin.md — at most 3000 characters
|
|
778
830
|
Each post: the day's single most interesting item, one concrete number or name from the
|
|
779
831
|
plan, no hashtag spam, no emoji, no "点击链接", no invented facts. Plain text, no markdown
|
|
780
832
|
headings. Deliver with your file-write tool; each file must be non-empty.
|
|
@@ -784,35 +836,83 @@ nodes:
|
|
|
784
836
|
description: >-
|
|
785
837
|
Build the channel payloads from the run's own artifacts (0327 builders): `30-variants/<key>`
|
|
786
838
|
for article / episode / cards plus the channel-specific keys and the social posts, all with
|
|
787
|
-
`{{article_url}}` / `{{podcast_url}}` placeholders because nothing is published yet.
|
|
788
|
-
|
|
839
|
+
`{{article_url}}` / `{{podcast_url}}` placeholders because nothing is published yet. 0329
|
|
840
|
+
F16: depends on every producer it reads (the 0324 file order alone was not a guarantee),
|
|
841
|
+
wipes stale variants first, and follows the cards/cover switches so a disabled producer's
|
|
842
|
+
variant is a skip, not a stale re-read.
|
|
843
|
+
dependsOn: [quality, social-write, publish-prep, cover-normalize, cards-render, pool-gate]
|
|
844
|
+
condition:
|
|
845
|
+
kind: shell
|
|
846
|
+
options:
|
|
847
|
+
# 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
|
|
848
|
+
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
789
849
|
action:
|
|
790
850
|
kind: shell
|
|
791
851
|
options:
|
|
792
852
|
command: >-
|
|
793
853
|
kk stage run daily-bundle-variants "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
|
|
854
|
+
"${vars.cards_enabled}" "${vars.cover_enabled}"
|
|
855
|
+
|
|
856
|
+
- id: safety-variants
|
|
857
|
+
description: >-
|
|
858
|
+
Final-variant safety review through the same nested kk-daily-safety machine (0331): the
|
|
859
|
+
manifest stage registers every `30-variants/*.content.json` first, then the machine runs in
|
|
860
|
+
the variants scope (`safety_scope_dir=4-safety-variants` re-roots its stages, guards and
|
|
861
|
+
prompts; the drafts review above is untouched). On an admitted outcome the machine leaves
|
|
862
|
+
`4-safety-variants/safety.json` — status, exclusion record and the sha256 of every reviewed
|
|
863
|
+
variant's current bytes — which publish-matrix-bundle freezes into the bundle so live
|
|
864
|
+
launches and social edits are gated on exactly the reviewed bytes. Fails closed: a block or
|
|
865
|
+
an integrity failure fails this node (the drafts scope's non-zero = never publish contract).
|
|
866
|
+
dependsOn: [bundle-variants, pool-gate]
|
|
867
|
+
condition:
|
|
868
|
+
kind: shell
|
|
869
|
+
options:
|
|
870
|
+
# 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
|
|
871
|
+
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
872
|
+
action:
|
|
873
|
+
kind: shell
|
|
874
|
+
options:
|
|
875
|
+
command: >-
|
|
876
|
+
kk stage run daily-safety-manifest "${vars.work_dir}" "${vars.run_date}" &&
|
|
877
|
+
env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST
|
|
878
|
+
spur workflow run plugins/kk/workflows/kk-daily-safety.yaml
|
|
879
|
+
--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}\",\"safety_scope_dir\":\"4-safety-variants\"}"
|
|
880
|
+
--quiet --no-plan
|
|
794
881
|
|
|
795
882
|
- id: bundle-freeze
|
|
796
883
|
description: >-
|
|
797
884
|
Freeze `bundle.json` (0321 `BundleSchema`): every `30-variants/*` file with its sha256 plus the
|
|
798
885
|
episode title, so the publish half can run later, or three times a day, against exactly these
|
|
799
886
|
bytes. Fails when the article or episode variant is missing — a bundle never advertises what
|
|
800
|
-
generate did not produce.
|
|
801
|
-
|
|
887
|
+
generate did not produce. 0329 F15: token-gated, so a refused episode never freezes — the
|
|
888
|
+
duration gate also drops this run's bundle.json on a refusal. 0331: after safety-variants, so
|
|
889
|
+
the freeze reads the review's evidence (or the bundle stays without it, on a skipped run).
|
|
890
|
+
dependsOn: [bundle-variants, safety-variants, pool-gate]
|
|
891
|
+
condition:
|
|
892
|
+
kind: shell
|
|
893
|
+
options:
|
|
894
|
+
# 0324 R3: the pool verdict is `pool-gate`'s token, never a repeated `kk store pool-count`.
|
|
895
|
+
command: 'test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
802
896
|
action:
|
|
803
897
|
kind: shell
|
|
804
898
|
options:
|
|
805
899
|
command: >-
|
|
806
|
-
|
|
900
|
+
case "${vars.publish_preset}" in '') ARGS='' ;; *) ARGS="--preset ${vars.publish_preset}" ;; esac;
|
|
901
|
+
kk stage run publish-matrix-bundle "${vars.work_dir}" --run-date "${vars.run_date}" --run-id "${vars.__runId}" $ARGS
|
|
807
902
|
|
|
808
903
|
- id: generation-report
|
|
809
|
-
description: "The generate half's report (the publish half writes its own, per window)."
|
|
810
|
-
dependsOn: [bundle-freeze]
|
|
904
|
+
description: "The generate half's report (the publish half writes its own, per window). 0329 F15: real run-report flags — the phantom skip flag made every invocation exit 1; the pool/candidates/qc inputs are what let the report record a no-content close. Depends on pool-gate beside bundle-freeze: pool-gate always completes, so the report (and the ledger after it) also land on a no-content close — the design requires the report reachable from extract/QC/no-content, not only bundle-freeze."
|
|
905
|
+
dependsOn: [bundle-freeze, pool-gate]
|
|
811
906
|
action:
|
|
812
907
|
kind: shell
|
|
813
908
|
options:
|
|
814
909
|
command: >-
|
|
815
|
-
kk daily run-report --work-dir "${vars.work_dir}" --run-date "${vars.run_date}"
|
|
910
|
+
kk daily run-report --work-dir "${vars.work_dir}" --run-date "${vars.run_date}"
|
|
911
|
+
--pool "${vars.work_dir}/10-collect/pool.json"
|
|
912
|
+
--candidates "${vars.work_dir}/21-plan/${vars.run_date}_04_quality-control-content_candidates.json"
|
|
913
|
+
--plugins-path "${vars.plugins_path}"
|
|
914
|
+
--qc-min-quality "${vars.qc_min_quality}" --qc-min-importance "${vars.qc_min_importance}"
|
|
915
|
+
--qc-min-urgency "${vars.qc_min_urgency}" --qc-min-impact "${vars.qc_min_impact}"
|
|
816
916
|
|
|
817
917
|
- id: publish
|
|
818
918
|
description: >-
|
|
@@ -820,18 +920,18 @@ nodes:
|
|
|
820
920
|
window against the bundle this run just froze. The launcher vars are stripped at the nested
|
|
821
921
|
boundary (the same two voice2 strips for the safety machine), and the nested run's exit code is
|
|
822
922
|
surfaced — a failed publish window is a failed generate run's last step, never a silent one.
|
|
823
|
-
dependsOn: [generation-report]
|
|
923
|
+
dependsOn: [generation-report, pool-gate]
|
|
824
924
|
condition:
|
|
825
925
|
kind: shell
|
|
826
926
|
options:
|
|
827
|
-
command: 'test "${vars.auto_publish}" = "true"'
|
|
927
|
+
command: 'test "${vars.auto_publish}" = "true" && test "$(cat ".spur/run/${vars.__runId}-pool-gate.status" 2>/dev/null)" = PASS'
|
|
828
928
|
action:
|
|
829
929
|
kind: shell
|
|
830
930
|
options:
|
|
831
931
|
command: >-
|
|
832
932
|
env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST
|
|
833
933
|
spur workflow run plugins/kk/workflows/kk-daily-publish3.yaml
|
|
834
|
-
--vars "{\"work_dir\":\"${vars.work_dir}\",\"run_date\":\"${vars.run_date}\",\"window\":\"${vars.publish_window}\",\"preset\":\"${vars.publish_preset}\",\"dummy\":\"${vars.publish_dummy}\"}"
|
|
934
|
+
--vars "{\"work_dir\":\"${vars.work_dir}\",\"run_date\":\"${vars.run_date}\",\"window\":\"${vars.publish_window}\",\"preset\":\"${vars.publish_preset}\",\"dummy\":\"${vars.publish_dummy}\",\"live_channels\":\"${vars.publish_live_channels}\"}"
|
|
835
935
|
|
|
836
936
|
- id: ledger
|
|
837
937
|
description: "Append this run to the workflow ledger; bookkeeping never fails a generation."
|
|
@@ -99,6 +99,18 @@ nodes:
|
|
|
99
99
|
options:
|
|
100
100
|
command: 'kk stage run publish-matrix-check "${vars.work_dir}" --window "${vars.window}" || true'
|
|
101
101
|
|
|
102
|
+
- id: validate
|
|
103
|
+
description: >-
|
|
104
|
+
Bind the bundle's calendar day before any side effect (0332 R1): normalize the run date
|
|
105
|
+
(compact `YYYYMMDD` and ISO `YYYY-MM-DD` both accepted), refuse a garbage one, and write the
|
|
106
|
+
canonical `4-publish/run-date.txt` every later stage derives its day from.
|
|
107
|
+
dependsOn: []
|
|
108
|
+
action:
|
|
109
|
+
kind: shell
|
|
110
|
+
options:
|
|
111
|
+
command: >-
|
|
112
|
+
kk stage run publish-matrix-validate "${vars.work_dir}" --run-date "${vars.run_date}"
|
|
113
|
+
|
|
102
114
|
- id: send-confirm
|
|
103
115
|
description: >-
|
|
104
116
|
Operator approval of a LIVE send: `pause: true` stops the run and RESUME is the approval;
|
|
@@ -119,79 +131,121 @@ nodes:
|
|
|
119
131
|
Receipts key every target, so a rerun never resends what was delivered, and a dummy row never
|
|
120
132
|
spawns a plugin. `check` is listed beside `send-confirm` on purpose: the engine skips a node only
|
|
121
133
|
when ALL its dependencies were skipped, so a draft window (no live row → `send-confirm` skipped)
|
|
122
|
-
still sends, while a gated/blocked plan (both ancestors skipped) still stops here.
|
|
134
|
+
still sends, while a gated/blocked plan (both ancestors skipped) still stops here. A failing
|
|
135
|
+
target does NOT end the run here — `onError: continue` lets the bounded retry and the outcome
|
|
136
|
+
assertion decide, so the evidence (report, ledger, outcome) always survives to the operator.
|
|
123
137
|
dependsOn: [check, send-confirm]
|
|
124
138
|
action:
|
|
125
139
|
kind: shell
|
|
140
|
+
onError: continue
|
|
126
141
|
options:
|
|
127
142
|
command: 'kk stage run publish-matrix-send "${vars.work_dir}" --window "${vars.window}"'
|
|
128
143
|
|
|
144
|
+
- id: send-retry
|
|
145
|
+
description: >-
|
|
146
|
+
The ONE bounded retry (0333): `--retry` resends only rows whose receipt is a live/draft failure
|
|
147
|
+
with `effect: none` — a succeeded row is skipped, a live row with an unknown effect is never
|
|
148
|
+
resent, and a dummy row never spawns a plugin. No unbounded back-edge: one retry node, then the
|
|
149
|
+
outcome assertion rules. A targeted operator rerun of this node is the recovery path for a
|
|
150
|
+
window that still ends non-full.
|
|
151
|
+
dependsOn: [send]
|
|
152
|
+
condition:
|
|
153
|
+
kind: shell
|
|
154
|
+
options:
|
|
155
|
+
command: 'test "${vars.window}" != "overseas-rss"'
|
|
156
|
+
action:
|
|
157
|
+
kind: shell
|
|
158
|
+
options:
|
|
159
|
+
command: 'kk stage run publish-matrix-send "${vars.work_dir}" --window "${vars.window}" --retry'
|
|
160
|
+
|
|
129
161
|
- id: classify
|
|
130
162
|
description: >-
|
|
131
163
|
Turn the window's receipts into `full|partial|none|dummy` (`4-publish/outcome.<window>.json`),
|
|
132
|
-
always exit 0 — routing is this graph's job, not the classifier's.
|
|
133
|
-
|
|
164
|
+
always exit 0 — routing is this graph's job, not the classifier's. The run date comes from the
|
|
165
|
+
plans/`run-date.txt` (0332 R1), so a late `run_date` guard is gone; a provided one must match
|
|
166
|
+
the bundle's own day and the stage refuses a mismatch.
|
|
167
|
+
dependsOn: [send-retry, send]
|
|
134
168
|
action:
|
|
135
169
|
kind: shell
|
|
136
170
|
options:
|
|
137
171
|
command: >-
|
|
138
|
-
test -n "${vars.run_date}" || { echo "publish3: classify needs run_date" >&2; exit 1; };
|
|
139
172
|
kk stage run publish-matrix-classify "${vars.work_dir}" --window "${vars.window}" --run-date "${vars.run_date}"
|
|
140
173
|
|
|
141
174
|
# ── after-generate only: the voice2-era tail stages keep working on the bridged names ─────
|
|
142
|
-
- id: surfdash
|
|
175
|
+
- id: patch-surfdash
|
|
143
176
|
description: >-
|
|
144
|
-
after-generate only: cover and podcast cross-link patching plus the CMS re-sync,
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
after-generate only: cover and podcast cross-link patching plus the CMS re-sync, and only when
|
|
178
|
+
a real surfdash receipt exists. The payload is derived from that receipt's aggregate
|
|
179
|
+
`metadata.postPath` — never from legacy aggregate files or any site-root argument: the plugin
|
|
180
|
+
resolves its own checkout. A dummy or missing podcast episode yields
|
|
181
|
+
explicit empty links; there is nothing to patch without a real post. The stage is its own gate:
|
|
182
|
+
with no real tier-0 post evidence it records an explicit skip and exits 0 (the channel matrix
|
|
183
|
+
lives in the preset, so the graph cannot name channels here).
|
|
147
184
|
dependsOn: [classify]
|
|
148
185
|
condition:
|
|
149
186
|
kind: shell
|
|
150
187
|
options:
|
|
151
|
-
command:
|
|
152
|
-
test "${vars.window}" = "after-generate"
|
|
153
|
-
&& grep -qxE 'full|partial' "${vars.work_dir}/4-publish/publish-status.after-generate.txt" 2>/dev/null
|
|
188
|
+
command: 'test "${vars.window}" = "after-generate"'
|
|
154
189
|
|
|
155
190
|
action:
|
|
156
191
|
kind: shell
|
|
157
192
|
options:
|
|
158
|
-
command:
|
|
159
|
-
kk stage run daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "$KK_SURFDASH_ROOT" ""
|
|
193
|
+
command: 'kk stage run publish-matrix-patch-surfdash "${vars.work_dir}" --window "${vars.window}"'
|
|
160
194
|
|
|
161
195
|
- id: partial-marker
|
|
162
|
-
description:
|
|
196
|
+
description: >-
|
|
197
|
+
Record the partial/none recovery marker when the classifier said so: names the exact failed
|
|
198
|
+
target ids from the receipt-derived outcome and points at the receipt-scoped rerun (`--retry`).
|
|
199
|
+
A full window deletes any stale marker, so the report never re-flags recovered targets.
|
|
163
200
|
dependsOn: [classify]
|
|
164
201
|
condition:
|
|
165
202
|
kind: shell
|
|
166
203
|
options:
|
|
167
204
|
command: >-
|
|
168
|
-
|
|
169
|
-
|
|
205
|
+
grep -qx 'partial' "${vars.work_dir}/4-publish/outcome.${vars.window}.json" 2>/dev/null ||
|
|
206
|
+
grep -qx 'none' "${vars.work_dir}/4-publish/outcome.${vars.window}.json" 2>/dev/null
|
|
170
207
|
|
|
171
208
|
action:
|
|
172
209
|
kind: shell
|
|
173
210
|
options:
|
|
174
|
-
command: 'kk stage run
|
|
211
|
+
command: 'kk stage run publish-matrix-partial "${vars.work_dir}" --window "${vars.window}"'
|
|
175
212
|
|
|
176
|
-
- id:
|
|
213
|
+
- id: settle
|
|
177
214
|
description: >-
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
Promote the store selection only on receipt-proven completion (0333): every required real row
|
|
216
|
+
must carry a CURRENT-bundle receipt that is succeeded/confirmed/live — draft preparation and
|
|
217
|
+
dummy rehearsals HOLD the selection (no store call), never claim a publication. Writes
|
|
218
|
+
`4-publish/settlement.<window>.json` naming each unresolved row. Keyed to the bundle's
|
|
219
|
+
parentRunId, never to this publish run. Exit 0 while holding: the outcome assertion owns the
|
|
220
|
+
run's exit code, so this evidence file always survives.
|
|
180
221
|
dependsOn: [classify]
|
|
181
222
|
condition:
|
|
182
223
|
kind: shell
|
|
183
224
|
options:
|
|
184
|
-
command:
|
|
185
|
-
|
|
186
|
-
|
|
225
|
+
command: 'test "${vars.window}" != "overseas-rss"'
|
|
226
|
+
action:
|
|
227
|
+
kind: shell
|
|
228
|
+
options:
|
|
229
|
+
command: 'kk stage run publish-matrix-settle "${vars.work_dir}" --window "${vars.window}" --topic ai-news'
|
|
230
|
+
|
|
231
|
+
- id: outcome-assert
|
|
232
|
+
description: >-
|
|
233
|
+
The run's honest exit (0333): when an outcome exists, every required real row must be
|
|
234
|
+
succeeded — a partial window with failed required targets fails the run AFTER report and ledger
|
|
235
|
+
are written, with the outcome, receipts and incident evidence preserved. Gated-miss windows
|
|
236
|
+
(no outcome file) stay done: the diagnose/repair path already classified them.
|
|
237
|
+
dependsOn: [ledger]
|
|
238
|
+
condition:
|
|
239
|
+
kind: shell
|
|
240
|
+
options:
|
|
241
|
+
command: 'test -f "${vars.work_dir}/4-publish/outcome.${vars.window}.json"'
|
|
187
242
|
|
|
188
243
|
action:
|
|
189
244
|
kind: shell
|
|
190
245
|
options:
|
|
191
246
|
command: >-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
kk store mark-published --topic ai-news --run "$PARENT"
|
|
247
|
+
jq -e 'all(.rows[]; .required != true or .dummy == true or .status == "succeeded")' "${vars.work_dir}/4-publish/outcome.${vars.window}.json" >/dev/null
|
|
248
|
+
|| { echo "publish3: required targets failed — see outcome.${vars.window}.json and 5-report" >&2; exit 1; }
|
|
195
249
|
|
|
196
250
|
# ── the incident path: a gated miss is diagnosed, never silently dropped ─────────────────
|
|
197
251
|
- id: diagnose
|
|
@@ -270,6 +324,8 @@ nodes:
|
|
|
270
324
|
printf '# Publish report — %s — %s\n\n- status: %s\n\n| target | tier | status | effect | dummy |\n| --- | --- | --- | --- | --- |\n'
|
|
271
325
|
"${vars.window}" "${vars.run_date}" "$STATUS" > "$OUT";
|
|
272
326
|
jq -r '.rows[] | "| \(.id) | \(.tier) | \(.status) | \(.effect // "-") | \(.dummy) |"'
|
|
327
|
+
"${vars.work_dir}/4-publish/outcome.${vars.window}.json" >> "$OUT" 2>/dev/null || true;
|
|
328
|
+
jq -r '.excluded[]? | "- excluded: \(.id) (\(.plugin)) — \(.reason)"'
|
|
273
329
|
"${vars.work_dir}/4-publish/outcome.${vars.window}.json" >> "$OUT" 2>/dev/null || true
|
|
274
330
|
|
|
275
331
|
- id: ledger
|