@gobing-ai/knowledge-kit 0.0.37 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.css +654 -16
  3. package/dist/board/collect/index.js +191 -64
  4. package/dist/board/daily-voice/index.css +679 -12
  5. package/dist/board/daily-voice/index.js +185 -66
  6. package/dist/board/itc/index.css +679 -13
  7. package/dist/board/itc/index.js +188 -66
  8. package/dist/index.js +2864 -139
  9. package/package.json +3 -2
  10. package/plugins/generations/content-gen/dist/index.js +158 -12
  11. package/plugins/generations/core-facts-gen/dist/index.js +158 -12
  12. package/plugins/generations/daily-article-gen/dist/index.js +158 -12
  13. package/plugins/generations/dailynews-gen/dist/index.js +158 -12
  14. package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
  15. package/plugins/generations/gemini-tts/README.md +4 -3
  16. package/plugins/generations/gemini-tts/dist/index.js +158 -12
  17. package/plugins/generations/image-gen/dist/index.js +158 -12
  18. package/plugins/generations/news-report-gen/dist/index.js +158 -12
  19. package/plugins/generations/omni-voice-gen/README.md +15 -4
  20. package/plugins/generations/omni-voice-gen/profiles.json +1 -2
  21. package/plugins/generations/voice-gen/dist/index.js +158 -12
  22. package/plugins/generations/voxcpm-tts/README.md +21 -1
  23. package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
  24. package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
  25. package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
  26. package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
  27. package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
  28. package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
  29. package/plugins/ingestions/web-search/dist/index.js +158 -12
  30. package/plugins/kk/commands/workflow-run.md +56 -18
  31. package/plugins/kk/decisions/kk-itc.yaml +46 -0
  32. package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
  33. package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
  34. package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
  35. package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
  36. package/plugins/kk/plugin.json +1 -1
  37. package/plugins/kk/publish-presets/chinese-article.json +14 -0
  38. package/plugins/kk/publish-presets/technical-article.json +14 -0
  39. package/plugins/kk/references/smart-seo-attribution.md +85 -0
  40. package/plugins/kk/scripts/daily-stages.ts +42 -2
  41. package/plugins/kk/scripts/itc-stages.ts +25 -118
  42. package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
  43. package/plugins/kk/scripts/seo-decisions.ts +477 -0
  44. package/plugins/kk/scripts/seo-enrich.ts +728 -0
  45. package/plugins/kk/scripts/seo-facts.ts +610 -0
  46. package/plugins/kk/scripts/seo-fetch.ts +766 -0
  47. package/plugins/kk/scripts/seo-report.ts +460 -0
  48. package/plugins/kk/scripts/seo-rules.ts +1882 -0
  49. package/plugins/kk/scripts/seo-stages.ts +1044 -0
  50. package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
  51. package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
  52. package/plugins/kk/skills/smart-seo/references/method.md +126 -0
  53. package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
  54. package/plugins/kk/skills/smart-seo/references/operations.md +202 -0
  55. package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
  56. package/plugins/kk/workflows/feature-verification.yaml +28 -24
  57. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +85 -17
  58. package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
  59. package/plugins/kk/workflows/kk-itc.yaml +208 -682
  60. package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
  61. package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
  62. package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
  63. package/plugins/publishings/bilibili-pub/package.json +20 -0
  64. package/plugins/publishings/bilibili-pub/plugin.json +12 -0
  65. package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
  66. package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
  67. package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
  68. package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
  69. package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
  70. package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
  71. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
  72. package/plugins/publishings/cnblogs-pub/package.json +19 -0
  73. package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
  74. package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
  75. package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
  76. package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
  77. package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
  78. package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
  79. package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
  80. package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
  81. package/plugins/publishings/csdn-pub/package.json +19 -0
  82. package/plugins/publishings/csdn-pub/plugin.json +12 -0
  83. package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
  84. package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
  85. package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
  86. package/plugins/publishings/emdash-pub/dist/index.js +158 -12
  87. package/plugins/publishings/emdash-pub/plugin.json +22 -1
  88. package/plugins/publishings/infoq-pub/dist/index.js +160 -12
  89. package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
  90. package/plugins/publishings/infoq-pub/plugin.json +21 -0
  91. package/plugins/publishings/jike-pub/dist/index.js +21379 -0
  92. package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
  93. package/plugins/publishings/jike-pub/package.json +19 -0
  94. package/plugins/publishings/jike-pub/plugin.json +12 -0
  95. package/plugins/publishings/jike-pub/src/index.ts +800 -0
  96. package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
  97. package/plugins/publishings/jike-pub/tsconfig.json +4 -0
  98. package/plugins/publishings/juejin-pub/dist/index.js +160 -12
  99. package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
  100. package/plugins/publishings/juejin-pub/plugin.json +21 -0
  101. package/plugins/publishings/medium-pub/dist/index.js +158 -12
  102. package/plugins/publishings/medium-pub/plugin.json +22 -1
  103. package/plugins/publishings/podcast-pub/dist/index.js +158 -12
  104. package/plugins/publishings/qiita-pub/dist/index.js +158 -12
  105. package/plugins/publishings/qiita-pub/plugin.json +24 -1
  106. package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
  107. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
  108. package/plugins/publishings/shipinhao-pub/package.json +19 -0
  109. package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
  110. package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
  111. package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
  112. package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
  113. package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
  114. package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
  115. package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
  116. package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
  117. package/plugins/publishings/sspai-pub/package.json +19 -0
  118. package/plugins/publishings/sspai-pub/plugin.json +12 -0
  119. package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
  120. package/plugins/publishings/sspai-pub/src/index.ts +167 -0
  121. package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
  122. package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
  123. package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
  124. package/plugins/publishings/substack-pub/dist/index.js +160 -12
  125. package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
  126. package/plugins/publishings/substack-pub/plugin.json +21 -0
  127. package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
  128. package/plugins/publishings/surfdash-pub/plugin.json +22 -1
  129. package/plugins/publishings/threads-pub/dist/index.js +158 -12
  130. package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
  131. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
  132. package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
  133. package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
  134. package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
  135. package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
  136. package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
  137. package/plugins/publishings/weibo-pub/dist/index.js +160 -12
  138. package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
  139. package/plugins/publishings/weibo-pub/plugin.json +35 -0
  140. package/plugins/publishings/x-pub/dist/index.js +160 -12
  141. package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
  142. package/plugins/publishings/x-pub/plugin.json +40 -0
  143. package/plugins/publishings/xhs-pub/dist/index.js +160 -12
  144. package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
  145. package/plugins/publishings/xhs-pub/plugin.json +37 -0
  146. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
  147. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
  148. package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
  149. package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
  150. package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
  151. package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
  152. package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
  153. package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
  154. package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
  155. package/plugins/publishings/zenn-pub/dist/index.js +158 -12
  156. package/plugins/publishings/zenn-pub/plugin.json +22 -1
  157. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
@@ -1,733 +1,259 @@
1
1
  ---
2
- "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
3
2
  name: kk-itc
4
- kind: state-machine
3
+ kind: dag
4
+ version: "2"
5
5
  description: >-
6
- IT content authoring pipeline using topic/itc-generating, Spur HITL gates, and optional judging;
7
- an auto path (auto-picked outline, unslop pass, judge revise loop) ends in a run report
8
- initialState: prepare
9
- terminalStates:
10
- - done
11
- - failed
12
- failureStates:
13
- - failed
6
+ IT content authoring and publication DAG: prepare -> child authoring FSM -> verify-child-freeze
7
+ -> illustration/SEO branches -> materialize & plan -> target check -> fan-out send -> reconcile-settle -> report
14
8
 
15
9
  vars:
10
+ # Config-backed options default to "" so `workflows.kk-itc` and compiled defaults apply (0303);
11
+ # the frozen `$work_dir/itc-options.json` is the single source every downstream node reads.
16
12
  topic: ""
17
13
  input_file: ""
14
+ from_collect: ""
15
+ topic_profile: ""
16
+ run_date: ""
18
17
  work_dir: ""
19
- writer: "itc-generating"
20
- playbook: "generic"
21
- research: "false"
22
- judge: "false"
23
- auto: "false" # auto mode: writer-recommended outline, no draft-review pause, mandatory judge, auto revise loop
24
- unslop: "false" # run kk:taste-unslop on the draft before review/judge
25
- max_revisions: "2" # cap on the auto revise loop (interactive stays bounded by the existing 3)
18
+ writer: ""
19
+ playbook: ""
20
+ rubric: "tech-accuracy"
26
21
  outline: ""
22
+ research: ""
23
+ judge: ""
24
+ auto: ""
25
+ unslop: ""
27
26
  force: "false"
28
- rubric: "tech-accuracy"
29
- verdictFile: "" # empty -> ${vars.work_dir}/.itc-verdict.json (set by prepare)
30
- agent: ""
31
- __runId: "" # Engine-injected run id; its presence captures the run for the Board (G8/0254 §9)
32
- __definitionDigest: "" # Executed definition digest; the capture compares it with the catalog (G8 §9)
33
- __hitlAnswer: ""
34
- __hitlInput: ""
35
- __verdictFeedback: "" # raw .itc-verdict.json, read by judge so draft-revise can show the feedback
36
- publish_targets: "" # comma list of publish plugin names; empty = no publish tail (V1)
37
- publish_live: "false" # live instead of draft; launch-only, never implied by auto (V4)
38
- source_locale: "en" # locale of the authored draft (en|zh|ja)
39
- cover_enabled: "false" # generate a cover via kk:image-authoring — this is the spend approval (R7)
40
- inline_images: "false" # also generate inline illustrations — also a spend approval
41
- browser_target: "" # internal cursor for the browser publish loop (written by itc-publish-next)
42
-
43
- states:
27
+ max_revisions: ""
28
+ source_locale: ""
29
+ publish_targets: ""
30
+ publish_preset: ""
31
+ publish_live: ""
32
+ cover_enabled: ""
33
+ inline_images: ""
34
+ seo_mode: ""
35
+ agent: "inline"
36
+ __runId: ""
37
+ __definitionDigest: ""
38
+
39
+ nodes:
44
40
  - id: prepare
45
- description: "mkdir work_dir, 2-outline; validate XOR topic/input_file; write brief.md"
46
- onEnter:
47
- - kind: shell
48
- options:
49
- command: >-
50
- kbin="$(command -v kk 2>/dev/null)";
51
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
52
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
53
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
54
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
55
- bun "$st" prepare-itc "${vars.work_dir}" "${vars.topic}" "${vars.input_file}" "${vars.force}" &&
56
- { [ -z "${vars.__runId}" ] || [ -z "${vars.__definitionDigest}" ] || kk board capture --workflow kk-itc --run "${vars.__runId}" --work-dir "${vars.work_dir}" --stamp "$(date +%Y%m%d)" --definition-digest "${vars.__definitionDigest}" --setting research="${vars.research}" --setting judge="${vars.judge}" --setting auto="${vars.auto}" --setting unslop="${vars.unslop}" --setting force="${vars.force}" --setting publish_live="${vars.publish_live}" --setting cover_enabled="${vars.cover_enabled}" --setting inline_images="${vars.inline_images}" --setting max_revisions="${vars.max_revisions}" --setting source_locale="${vars.source_locale}" || echo "kk-itc: Board capture skipped (workspace is not a trusted-root child); previews disabled" >&2; }
57
-
58
- - id: research
59
- description: "Conduct preliminary topic research via storm-research (agent.run)"
60
- onEnter:
61
- - kind: agent.run
62
- options:
63
- role: scribe
64
- agent: ${vars.agent}
65
- input: |
66
- Invoke the kk:storm-research skill.
67
- Run storm-research on the topic or input file specified in the brief at ${vars.work_dir}/brief.md.
68
-
69
- - id: outline-write
70
- description: "Generate outline options a, b (and optional c) via writer skill (agent.run)"
71
- onEnter:
72
- - kind: agent.run
73
- options:
74
- role: scribe
75
- agent: ${vars.agent}
76
- input: |
77
- Invoke the kk:${vars.writer} skill.
78
- Generate outline options (outline-option-a.md, outline-option-b.md, and optionally outline-option-c.md) in ${vars.work_dir}/2-outline/ from the brief at ${vars.work_dir}/brief.md using playbook ${vars.playbook}.
79
- When auto mode is on (auto=true), also write 2-outline/outline-recommendation.json as {"pick": "a|b|c", "rationale": "<why this option is strongest>"} recommending the option you would commit.
80
- Do not wait for operator selection; write the option files.
81
- expectFile: ${vars.work_dir}/2-outline/outline-option-a.md
82
- - kind: shell
83
- options:
84
- command: >-
85
- test -f "${vars.work_dir}/2-outline/outline-option-b.md" || { echo "missing required outline-option-b.md" >&2; exit 1; }
86
-
87
- - id: outline-select
88
- description: "Operator selects preferred outline option"
89
- pause: true
90
- onEnter:
91
- - kind: hitl.select
92
- options:
93
- prompt: "Select an approved outline option from ${vars.work_dir}/2-outline/ (outline-option-a.md, outline-option-b.md, or outline-option-c.md):"
94
- options:
95
- - a
96
- - b
97
- - c
98
- var: __hitlAnswer
99
-
100
- - id: outline-autopick
101
- description: "Auto mode — validate the writer's outline-recommendation.json and bind the pick as the selected outline"
102
- onEnter:
103
- - kind: shell
104
- options:
105
- command: >-
106
- kk stage itc-outline-pick "${vars.work_dir}"
107
- - kind: file.read.into-var
108
- options:
109
- path: ${vars.work_dir}/2-outline/.auto-pick
110
- var: outline
111
-
112
- - id: outline-commit
113
- description: "Copy selected outline to outline-approved.md"
114
- onEnter:
115
- - kind: shell
116
- options:
117
- command: >-
118
- sel="${vars.outline}";
119
- if [ -z "$sel" ]; then sel="${vars.__hitlAnswer}"; fi;
120
- if [ -z "$sel" ]; then sel="a"; fi;
121
- src="${vars.work_dir}/2-outline/outline-option-$sel.md";
122
- if [ ! -f "$src" ]; then
123
- echo "selected outline file not found: $src" >&2; exit 1;
124
- fi;
125
- cp "$src" "${vars.work_dir}/2-outline/outline-approved.md";
126
-
127
- - id: draft-write
128
- description: "Author full technical draft from approved outline (agent.run)"
129
- onEnter:
130
- - kind: agent.run
131
- options:
132
- role: scribe
133
- agent: ${vars.agent}
134
- input: |
135
- Invoke the kk:${vars.writer} skill.
136
- Author the technical draft at ${vars.work_dir}/3-draft-draft-article.md based on approved outline ${vars.work_dir}/2-outline/outline-approved.md and brief ${vars.work_dir}/brief.md using playbook ${vars.playbook}.
137
- Revision feedback: ${vars.__hitlInput}
138
- expectFile: ${vars.work_dir}/3-draft-draft-article.md
139
-
140
- - id: unslop
141
- description: "De-slop pass — kk:taste-unslop line-edits the draft (writes a new file, then the swap makes it in place)"
142
- onEnter:
143
- - kind: agent.run
144
- options:
145
- role: scribe
146
- agent: ${vars.agent}
147
- input: |
148
- Invoke the kk:taste-unslop skill.
149
- Read ${vars.work_dir}/3-draft-draft-article.md and write the line-edited version to ${vars.work_dir}/3-draft-draft-article.new.md, removing slop (AI-tell phrasing, filler, hedging) while preserving the technical claims, code blocks, links, and the approved outline's structure. Do not modify or delete the original file; the pipeline swaps the edited file in.
150
- expectFile: ${vars.work_dir}/3-draft-draft-article.new.md
151
- - kind: shell
152
- options:
153
- command: mv -f "${vars.work_dir}/3-draft-draft-article.new.md" "${vars.work_dir}/3-draft-draft-article.md"
154
-
155
- - id: draft-review
156
- description: "Operator reviews and approves or requests revision on draft-article.md"
157
- pause: true
158
- onEnter:
159
- - kind: hitl.confirm
160
- options:
161
- prompt: "Review draft at ${vars.work_dir}/3-draft-draft-article.md. Approve?"
162
- var: __hitlAnswer
163
-
164
- - id: draft-revise
165
- description: "Collect revision feedback from operator (bounded by 3 rounds)"
166
- pause: true
167
- onEnter:
168
- - kind: shell
169
- options:
170
- command: >-
171
- n=$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0);
172
- echo $((n + 1)) > "${vars.work_dir}/.revise-count";
173
- - kind: hitl.input
174
- options:
175
- prompt: "Judge feedback (from ${vars.work_dir}/.itc-verdict.json): ${vars.__verdictFeedback}\nProvide specific revision feedback for ${vars.work_dir}/3-draft-draft-article.md:"
176
- var: __hitlInput
177
-
178
- - id: draft-autorevise
179
- description: "Auto mode — apply the judge feedback[] to the draft and consume one revision from the budget"
180
- onEnter:
181
- - kind: shell
182
- options:
183
- command: >-
184
- n=$(cat "${vars.work_dir}/.auto-revise-count" 2>/dev/null || echo 0);
185
- echo $((n + 1)) > "${vars.work_dir}/.auto-revise-count";
186
- - kind: agent.run
187
- options:
188
- role: scribe
189
- agent: ${vars.agent}
190
- input: |
191
- Invoke the kk:${vars.writer} skill.
192
- Read the draft at ${vars.work_dir}/3-draft-draft-article.md and write a revised version to ${vars.work_dir}/3-draft-draft-article.new.md, applying every item of feedback[] in ${vars.work_dir}/.itc-verdict.json. Do not modify or delete the original file; the pipeline swaps the revised file in.
193
- expectFile: ${vars.work_dir}/3-draft-draft-article.new.md
194
- - kind: shell
195
- options:
196
- command: mv -f "${vars.work_dir}/3-draft-draft-article.new.md" "${vars.work_dir}/3-draft-draft-article.md"
197
-
198
- - id: publish-entry
199
- description: "Publish tail entry — targets are set; route into illustration/materialization"
200
-
201
- - id: illustrate
202
- description: "Optional cover/inline illustrations via kk:image-authoring — entered only when a spend var is on (R7)"
203
- onEnter:
204
- - kind: agent.run
205
- options:
206
- role: scribe
207
- agent: ${vars.agent}
208
- input: |
209
- Invoke the kk:image-authoring skill with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 (non-interactive, auto-confirm — the operator approved spend via the cover_enabled/inline_images vars).
210
- Create ${vars.work_dir}/4-illustrations/ and generate the cover illustration into it as cover.png (cover.jpg acceptable), sized for an article hero image.
211
- When inline_images=true, also generate inline figures for the draft's key sections as inline-<slug>.png in the same directory.
212
- Print each written path as the last line of stdout.
213
- - kind: shell
214
- options:
215
- command: >-
216
- test "${vars.cover_enabled}" = "true" || exit 0;
217
- ls "${vars.work_dir}"/4-illustrations/cover.* >/dev/null 2>&1 || { echo "cover_enabled=true but no 4-illustrations/cover.* in ${vars.work_dir}" >&2; exit 1; }
218
-
219
- - id: materialize
220
- description: "Materialize the approved draft to 4-publish/content.json"
221
- onEnter:
222
- - kind: shell
223
- options:
224
- command: >-
225
- kk stage itc-materialize "${vars.work_dir}" "${vars.publish_live}"
226
-
227
- - id: publish-plan
228
- description: "Split targets into fan-out groups and browser targets (4-publish/publish-plan.json)"
229
- onEnter:
230
- - kind: shell
231
- options:
232
- command: >-
233
- kk stage itc-publish-plan "${vars.work_dir}" "${vars.publish_targets}" "${vars.source_locale}" "${vars.publish_live}"
234
-
235
- - id: adapt
236
- description: "Write one adapted Content file per plan variant with adapt:true — kk:article-adapt (R9)"
237
- onEnter:
238
- - kind: agent.run
239
- options:
240
- role: scribe
241
- agent: ${vars.agent}
242
- input: |
243
- Invoke the kk:article-adapt skill.
244
- Read ${vars.work_dir}/4-publish/publish-plan.json and, for every entry in variants with adapt=true, read the source ${vars.work_dir}/4-publish/content.json and write ${vars.work_dir}/4-publish/<entry.file>: translate title and body to the variant locale, apply the entry's limits, and keep code blocks, URLs and references unchanged.
245
- Set format to the variant key's format suffix. Never set metadata.published, metadata.publishStatus or metadata.operation — itc-publish-fanout stamps the draft/live switches at send time.
246
- - kind: shell
247
- options:
248
- command: >-
249
- kk stage itc-adapt-verify "${vars.work_dir}"
250
-
251
- - id: publish-gate
252
- description: "Route to the operator confirm unless auto publishes drafts"
253
-
254
- - id: publish-confirm
255
- description: "Operator confirms the send — every target, draft or live mode"
256
- pause: true
257
- onEnter:
258
- - kind: hitl.confirm
259
- options:
260
- prompt: >-
261
- Send to targets [${vars.publish_targets}] in ${vars.publish_live} mode (false = draft,
262
- true = LIVE)? Review ${vars.work_dir}/4-publish/publish-plan.json first.
263
- var: __hitlAnswer
264
- - kind: shell
265
- options:
266
- command: >-
267
- test "${vars.__hitlAnswer}" = "yes" || printf declined > "${vars.work_dir}/4-publish/.publish-declined"
268
-
269
- - id: publish-fanout
270
- description: "Send each fan-out group through kk executor fan-out — target failures are data"
271
- onEnter:
272
- - kind: shell
273
- options:
274
- command: >-
275
- kk stage itc-publish-fanout "${vars.work_dir}"
276
-
277
- - id: publish-retry
278
- description: "Re-send failed fan-out groups once through --retry-from (V5)"
279
- onEnter:
280
- - kind: shell
281
- options:
282
- command: >-
283
- n=$(cat "${vars.work_dir}/.publish-retry-count" 2>/dev/null || echo 0);
284
- echo $((n + 1)) > "${vars.work_dir}/.publish-retry-count";
285
- kk stage itc-publish-fanout "${vars.work_dir}" --retry
286
-
287
- - id: publish-agent
288
- description: "Publish one pending browser target via kk:publish — file-cursor loop bounded by the plan"
289
- onEnter:
290
- - kind: file.read.into-var
291
- options:
292
- path: ${vars.work_dir}/4-publish/.next-browser
293
- var: browser_target
294
- - kind: agent.run
295
- options:
296
- role: scribe
297
- agent: ${vars.agent}
298
- input: |
299
- Invoke the kk:publish skill for the browser target ${vars.browser_target}.
300
- Read ${vars.work_dir}/4-publish/publish-plan.json, find the browser entry whose target is ${vars.browser_target}, and use that variant's file under ${vars.work_dir}/4-publish/ as the Content input.
301
- When the plan's live is true pass the skill's --live flag; otherwise publish as a draft (V4).
302
- Always write the Result to ${vars.work_dir}/4-publish/result.${vars.browser_target}.json, including {ok: false, error: ...} when the publish fails.
303
- expectFile: ${vars.work_dir}/4-publish/result.${vars.browser_target}.json
304
- - kind: shell
305
- options:
306
- command: 'kk stage itc-publish-next "${vars.work_dir}"'
307
-
308
- - id: judge
309
- description: "Evaluate technical draft with content-judge (agent.run)"
310
- onEnter:
311
- - kind: agent.run
312
- options:
313
- role: reviewer
314
- agent: ${vars.agent}
315
- input: |
316
- Invoke the kk:content-judge skill.
317
- Evaluate the draft at ${vars.work_dir}/3-draft-draft-article.md against the ${vars.rubric} rubric.
318
- Research evidence: if ${vars.work_dir}/docs.json exists, read it and weigh it when judging.
319
- Write the verdict JSON to ${vars.work_dir}/.itc-verdict.json per the skill's verdict contract.
320
- Print the verdict path as the last line of stdout.
321
- expectFile: ${vars.work_dir}/.itc-verdict.json
322
- - kind: file.read.into-var
323
- options:
324
- path: ${vars.work_dir}/.itc-verdict.json
325
- var: __verdictFeedback
326
-
327
- - id: report
328
- description: "Write the run report, then route to done or failed on its outcome"
329
- onEnter:
330
- - kind: shell
331
- options:
332
- command: >-
333
- kk stage itc-report "${vars.work_dir}" "${vars.max_revisions}"
334
-
335
- - id: done
336
- description: "Terminal — IT content draft generated and approved successfully"
337
- - id: failed
338
- description: "Terminal — IT content workflow failed or rejected"
339
-
340
- transitions:
341
- - from: prepare
342
- to: research
343
- description: "Research enabled -> proceed to storm-research"
344
- guard:
345
- kind: shell
346
- options:
347
- command: 'test "${vars.research}" = "true"'
348
-
349
- - from: prepare
350
- to: outline-write
351
- description: "Research disabled -> proceed directly to outline generation"
352
- guard:
353
- kind: shell
354
- options:
355
- command: 'test "${vars.research}" != "true"'
356
-
357
- - from: research
358
- to: outline-write
359
- description: "Research completed -> proceed to outline generation"
360
- guard:
361
- kind: always
362
-
363
- - from: outline-write
364
- to: outline-autopick
365
- description: "Auto mode with no pre-selected outline -> validate the writer recommendation and bind it"
366
- guard:
367
- kind: shell
368
- options:
369
- command: 'test "${vars.auto}" = "true" -a -z "${vars.outline}"'
370
-
371
- - from: outline-write
372
- to: outline-select
373
- description: "Interactive mode with no pre-selected outline -> pause for operator selection"
374
- guard:
375
- kind: shell
376
- options:
377
- command: 'test "${vars.auto}" != "true" -a -z "${vars.outline}"'
378
-
379
- - from: outline-write
380
- to: outline-commit
381
- description: "Outline pre-selected -> skip interactive selection"
382
- guard:
383
- kind: shell
384
- options:
385
- command: 'test -n "${vars.outline}"'
386
-
387
- - from: outline-select
388
- to: outline-commit
389
- description: "Outline selected -> commit approved outline"
390
- guard:
391
- kind: always
392
-
393
- - from: outline-autopick
394
- to: outline-commit
395
- description: "Writer recommendation validated and bound -> commit approved outline"
396
- guard:
397
- kind: always
398
-
399
- - from: outline-commit
400
- to: draft-write
401
- description: "Approved outline committed -> write draft"
402
- guard:
403
- kind: always
404
-
405
- - from: draft-write
406
- to: unslop
407
- description: "Unslop enabled -> de-slop the fresh draft"
408
- guard:
409
- kind: shell
410
- options:
411
- command: 'test "${vars.unslop}" = "true"'
412
-
413
- - from: draft-write
414
- to: draft-review
415
- description: "Interactive mode without unslop -> pause for operator review"
416
- guard:
417
- kind: shell
418
- options:
419
- command: 'test "${vars.auto}" != "true" -a "${vars.unslop}" != "true"'
420
-
421
- - from: draft-write
422
- to: judge
423
- description: "Auto mode without unslop -> straight to the judge"
424
- guard:
425
- kind: shell
426
- options:
427
- command: 'test "${vars.auto}" = "true" -a "${vars.unslop}" != "true"'
428
-
429
- - from: unslop
430
- to: judge
431
- description: "Auto mode -> the de-slopped draft goes to the judge"
432
- guard:
433
- kind: shell
434
- options:
435
- command: 'test "${vars.auto}" = "true"'
436
-
437
- - from: unslop
438
- to: draft-review
439
- description: "Interactive mode -> pause for operator review of the de-slopped draft"
440
- guard:
41
+ description: "Freeze the effective options (itc-prepare) bound to this run, then prepare the brief"
42
+ action:
43
+ kind: shell
44
+ options:
45
+ # The entry normally freezes itc-options.json before launch (a collected topic must be
46
+ # chosen first); a direct launch freezes it here. A snapshot from another run is refused.
47
+ command: |
48
+ wd="${vars.work_dir}"; run="${vars.__runId}"; o="$wd/itc-options.json"
49
+ test -n "$wd" || { echo "work_dir is required (derive it with kk stage run itc-prepare)" >&2; exit 1; }
50
+ if [ ! -f "$o" ]; then
51
+ req="$(mktemp)"
52
+ jq -n --arg topic "${vars.topic}" --arg inputFile "${vars.input_file}" --arg fromCollect "${vars.from_collect}" \
53
+ --arg topicProfile "${vars.topic_profile}" --arg runDate "${vars.run_date}" --arg workDir "$wd" --arg parentRunId "$run" \
54
+ --arg writer "${vars.writer}" --arg playbook "${vars.playbook}" --arg sourceLocale "${vars.source_locale}" \
55
+ --arg publishTargets "${vars.publish_targets}" --arg publishPreset "${vars.publish_preset}" --arg seo "${vars.seo_mode}" \
56
+ --arg maxRevisions "${vars.max_revisions}" --arg research "${vars.research}" --arg judge "${vars.judge}" \
57
+ --arg auto "${vars.auto}" --arg unslop "${vars.unslop}" --arg force "${vars.force}" --arg publishLive "${vars.publish_live}" \
58
+ --arg coverEnabled "${vars.cover_enabled}" --arg inlineImages "${vars.inline_images}" \
59
+ '$ARGS.named | with_entries(select(.value != ""))
60
+ | with_entries(if (.key | IN("research","judge","auto","unslop","force","publishLive","coverEnabled","inlineImages"))
61
+ then .value = (.value == "true") else . end)
62
+ | if has("maxRevisions") then .maxRevisions |= tonumber else . end' > "$req" || { rm -f "$req"; exit 1; }
63
+ kk stage run itc-prepare --request "$req" > /dev/null; rc=$?; rm -f "$req"; test "$rc" -eq 0 || exit 1
64
+ fi
65
+ test -f "$o" || { echo "a collected topic must be chosen before launch: run the entry's intake, then itc-prepare --chosen" >&2; exit 1; }
66
+ pr=$(jq -r '.parentRunId' "$o")
67
+ if [ "$pr" != "$run" ] && [ "${vars.force}" != "true" ]; then
68
+ echo "$o belongs to run '$pr', not '$run'; pass force=true to adopt it" >&2; exit 1
69
+ fi
70
+ kind=$(jq -r '.sourceKind' "$o"); val=$(jq -r '.sourceValue' "$o")
71
+ case "$kind" in
72
+ input_file) kk stage run prepare-itc "$wd" "" "$val" "${vars.force}" ;;
73
+ from_collect) kk stage run prepare-itc "$wd" "$(jq -r '.displayTitle' "$o")" "" "${vars.force}" ;;
74
+ *) kk stage run prepare-itc "$wd" "$val" "" "${vars.force}" ;;
75
+ esac
76
+
77
+ - id: child-author
78
+ description: "Run child authoring state machine (outline -> draft -> unslop -> review/judge)"
79
+ dependsOn:
80
+ - prepare
81
+ action:
82
+ kind: shell
83
+ options:
84
+ # The child id is derived from the parent before launch, so an interrupted parent queries
85
+ # and continues the SAME child instead of starting a second one; a paused child stays a
86
+ # distinct non-success for the parent until it is answered and reaches done. The child
87
+ # runs as an owned process: a cancelled parent signals it and cancels its run.
88
+ command: |
89
+ cid="${vars.__runId}-author"
90
+ v=$(jq -c --arg wd "${vars.work_dir}" --arg agent "${vars.agent}" --arg rubric "${vars.rubric}" --arg outline "${vars.outline}" \
91
+ --arg run "${vars.__runId}" --arg dd "${vars.__definitionDigest}" \
92
+ '{work_dir:$wd, agent:$agent, rubric:$rubric, outline:$outline, writer:.writer, playbook:.playbook,
93
+ auto:(.auto|tostring), unslop:(.unslop|tostring), judge:(.judge|tostring), max_revisions:(.maxRevisions|tostring),
94
+ parent_run_id:$run, child_run_id:($run + "-author"), definition_digest:$dd, options_digest:.optionsDigest}' \
95
+ "${vars.work_dir}/itc-options.json") || exit 1
96
+ owned() {
97
+ env -u SPUR_WORKFLOW_RUN_ACTIVE -u SPUR_EXPECTED_DEFINITION_DIGEST "$@" &
98
+ pid=$!
99
+ trap 'kill -TERM "$pid" 2>/dev/null; spur workflow cancel "$cid" >/dev/null 2>&1; exit 143' TERM INT HUP
100
+ wait "$pid"; rc=$?
101
+ trap - TERM INT HUP
102
+ return "$rc"
103
+ }
104
+ st=$(spur workflow trace "$cid" --json 2>/dev/null | jq -r '.run.status // empty')
105
+ case "$st" in
106
+ done) exit 0 ;;
107
+ paused) owned spur workflow continue "$cid" --yes ;;
108
+ "") owned spur workflow run plugins/kk/workflows/kk-itc-author.yaml --run-id "$cid" --vars "$v" ;;
109
+ *) echo "child $cid is $st; refusing to relaunch it" >&2; exit 1 ;;
110
+ esac
111
+ st=$(spur workflow trace "$cid" --json 2>/dev/null | jq -r '.run.status // empty')
112
+ if [ "$st" = paused ]; then echo "child $cid paused: answer it with spur workflow continue $cid, then resume this run" >&2; exit 1; fi
113
+ test "$st" = done || { echo "child $cid ended $st" >&2; exit 1; }
114
+
115
+ - id: verify-child-freeze
116
+ description: "Verify child authoring completion receipt and freeze draft/outline digests"
117
+ dependsOn:
118
+ - child-author
119
+ action:
441
120
  kind: shell
442
121
  options:
443
- command: 'test "${vars.auto}" != "true"'
444
-
445
- - from: draft-review
446
- to: judge
447
- description: "Draft approved and judge enabled -> evaluate quality"
448
- guard:
449
- kind: shell
450
- options:
451
- command: '{ test -z "${vars.__hitlAnswer}" || test "${vars.__hitlAnswer}" = "yes"; } && test "${vars.judge}" = "true"'
452
-
453
- - from: draft-review
454
- to: done
455
- description: "Draft approved, judge disabled, no publish targets -> complete workflow"
456
- guard:
457
- kind: shell
458
- options:
459
- command: '{ test -z "${vars.__hitlAnswer}" || test "${vars.__hitlAnswer}" = "yes"; } && test "${vars.judge}" != "true" && test -z "${vars.publish_targets}"'
460
-
461
- - from: draft-review
462
- to: publish-entry
463
- description: "Draft approved, judge disabled, targets set -> enter the publish tail"
464
- guard:
465
- kind: shell
466
- options:
467
- command: '{ test -z "${vars.__hitlAnswer}" || test "${vars.__hitlAnswer}" = "yes"; } && test "${vars.judge}" != "true" && test -n "${vars.publish_targets}"'
468
-
469
- - from: draft-review
470
- to: draft-revise
471
- description: "Draft rejected with revision budget remaining -> collect feedback"
472
- guard:
473
- kind: shell
474
- options:
475
- command: 'test "${vars.__hitlAnswer}" = "no" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -lt 3'
476
-
477
- - from: draft-review
478
- to: failed
479
- terminalReason: failed-guard
480
- description: "Draft rejected with revision budget exhausted -> fail"
481
- guard:
482
- kind: shell
483
- options:
484
- command: 'test "${vars.__hitlAnswer}" = "no" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -ge 3'
485
-
486
- - from: draft-revise
487
- to: draft-write
488
- description: "Feedback collected -> rewrite draft"
489
- guard:
490
- kind: always
122
+ command: >-
123
+ kk stage run itc-author-complete "${vars.work_dir}" "${vars.__runId}"
491
124
 
492
- - from: draft-autorevise
493
- to: unslop
494
- description: "Unslop enabled -> de-slop the auto-revised draft"
495
- guard:
125
+ - id: illustrate
126
+ description: "Optional cover/inline illustrations via kk:image-authoring — entered only when a spend var is on (R7)"
127
+ dependsOn:
128
+ - verify-child-freeze
129
+ condition:
496
130
  kind: shell
497
131
  options:
498
- command: 'test "${vars.unslop}" = "true"'
499
-
500
- - from: draft-autorevise
501
- to: judge
502
- description: "Auto mode without unslop -> back to the judge"
503
- guard:
504
- kind: shell
132
+ command: 'jq -e ".coverEnabled or .inlineImages" "${vars.work_dir}/itc-options.json" >/dev/null'
133
+ action:
134
+ kind: agent.run
505
135
  options:
506
- command: 'test "${vars.auto}" = "true" -a "${vars.unslop}" != "true"'
136
+ role: scribe
137
+ agent: ${vars.agent}
138
+ input: |
139
+ Invoke the kk:image-authoring skill with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 (non-interactive, auto-confirm — the operator approved spend via the cover_enabled/inline_images vars).
140
+ Create ${vars.work_dir}/4-illustrations/ and generate the cover illustration into it as cover.png (cover.jpg acceptable), sized for an article hero image.
141
+ When inline_images=true, also generate inline figures for the draft's key sections as inline-<slug>.png in the same directory.
142
+ Print each written path as the last line of stdout.
507
143
 
508
- - from: judge
509
- to: done
510
- description: "Interactive mode, verdict PASS with a valid confidence (or empty in dry-run), no publish targets -> complete workflow"
511
- guard:
144
+ - id: materialize-plan
145
+ description: "Materialize content.json and resolve publication targets into publish-plan.json"
146
+ dependsOn:
147
+ - verify-child-freeze
148
+ - illustrate
149
+ action:
512
150
  kind: shell
513
151
  options:
514
152
  command: >-
515
- { test "${vars.auto}" != "true"; } &&
516
- { v="$(jq -r 'if (.verdict // "") == "PASS" then (.verdict + (if ((.confidence // "") | test("^(HIGH|MEDIUM|LOW)$")) then "" else "!" end)) else (.verdict // "") end' ${vars.work_dir}/.itc-verdict.json 2>/dev/null)";
517
- test -z "$v" -o "$v" = PASS; } &&
518
- test -z "${vars.publish_targets}"
153
+ kk stage run itc-materialize-plan "${vars.work_dir}"
519
154
 
520
- - from: judge
521
- to: publish-entry
522
- description: "Verdict PASS with a valid confidence and targets set -> enter the publish tail (auto runs report after the tail)"
523
- guard:
155
+ - id: adapt
156
+ description: "Prepare per-platform variant Content files via kk:article-adapt — runs only for adapt:true variants (R9)"
157
+ dependsOn:
158
+ - materialize-plan
159
+ action:
160
+ kind: agent.run
161
+ options:
162
+ role: scribe
163
+ agent: ${vars.agent}
164
+ input: |
165
+ Invoke the kk:article-adapt skill.
166
+ Read ${vars.work_dir}/4-publish/publish-plan.json and, for each entry in variants with adapt=true, read ${vars.work_dir}/4-publish/content.json and write the variant Content file to ${vars.work_dir}/4-publish/<variant.file>.
167
+ Apply the plan's variant limits and translate title and body into the variant's locale. Never set metadata.published; the publishing states set switches.
168
+ Print each written variant file path as the last line of stdout.
169
+
170
+ - id: adapt-verify
171
+ description: "Verify adapted variant content files"
172
+ dependsOn:
173
+ - adapt
174
+ action:
524
175
  kind: shell
525
176
  options:
526
177
  command: >-
527
- { v="$(jq -r 'if (.verdict // "") == "PASS" then (.verdict + (if ((.confidence // "") | test("^(HIGH|MEDIUM|LOW)$")) then "" else "!" end)) else (.verdict // "") end' ${vars.work_dir}/.itc-verdict.json 2>/dev/null)";
528
- test -z "$v" -o "$v" = PASS; } &&
529
- test -n "${vars.publish_targets}"
178
+ test -f "${vars.work_dir}/4-publish/publish-plan.json" || exit 0;
179
+ kk stage run itc-adapt-verify "${vars.work_dir}"
530
180
 
531
- - from: judge
532
- to: report
533
- description: "Auto mode -> the run ends in the report (valid PASS without targets, FAIL, or exhausted NEEDS_REVISION)"
534
- guard:
181
+ - id: check-targets
182
+ description: "Check readiness probes for plannable publish targets"
183
+ dependsOn:
184
+ - materialize-plan
185
+ action:
535
186
  kind: shell
536
187
  options:
537
188
  command: >-
538
- { test "${vars.auto}" = "true"; } &&
539
- { v="$(jq -r 'if (.verdict // "") == "PASS" then (.verdict + (if ((.confidence // "") | test("^(HIGH|MEDIUM|LOW)$")) then "" else "!" end)) else (.verdict // "") end' ${vars.work_dir}/.itc-verdict.json 2>/dev/null)";
540
- c="$(cat "${vars.work_dir}/.auto-revise-count" 2>/dev/null || echo 0)";
541
- { test -z "$v" -o "$v" = PASS; } || test "$v" = FAIL || { test "$v" = NEEDS_REVISION -a "$c" -ge "${vars.max_revisions}"; }; }
542
-
543
- - from: judge
544
- to: draft-autorevise
545
- description: "Auto mode, NEEDS_REVISION with revise budget left -> apply feedback and re-judge"
546
- guard:
547
- kind: shell
548
- options:
549
- command: '{ test "${vars.auto}" = "true"; } && { v="$(jq -r .verdict ${vars.work_dir}/.itc-verdict.json 2>/dev/null)"; c="$(cat "${vars.work_dir}/.auto-revise-count" 2>/dev/null || echo 0)"; test "$v" = NEEDS_REVISION -a "$c" -lt "${vars.max_revisions}"; }'
550
-
551
- - from: judge
552
- to: draft-revise
553
- description: "Interactive mode, NEEDS_REVISION with revise budget left -> collect revision with the feedback shown"
554
- guard:
555
- kind: shell
556
- options:
557
- command: '{ test "${vars.auto}" != "true"; } && { v="$(jq -r .verdict ${vars.work_dir}/.itc-verdict.json 2>/dev/null)"; c="$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)"; test "$v" = NEEDS_REVISION -a "$c" -lt 3; }'
558
-
559
- - from: judge
560
- to: failed
561
- terminalReason: failed-guard
562
- description: "Interactive mode, verdict FAIL or revise budget exhausted -> fail"
563
- guard:
564
- kind: shell
565
- options:
566
- command: '{ test "${vars.auto}" != "true"; } && { v="$(jq -r .verdict ${vars.work_dir}/.itc-verdict.json 2>/dev/null)"; c="$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)"; test "$v" = FAIL || { test "$v" = NEEDS_REVISION -a "$c" -ge 3; }; }'
567
-
568
- # --- 0151 publish tail: materialize -> plan -> gate -> confirm/fanout -> retry -> browser loop ---
569
-
570
- - from: publish-entry
571
- to: illustrate
572
- description: "Cover or inline images approved -> run the illustration step (R7: no image spend while both vars are false)"
573
- guard:
574
- kind: shell
575
- options:
576
- command: 'test "${vars.cover_enabled}" = "true" -o "${vars.inline_images}" = "true"'
577
-
578
- - from: publish-entry
579
- to: materialize
580
- description: "No illustration spend approved -> materialize directly"
581
- guard:
189
+ test -f "${vars.work_dir}/4-publish/publish-plan.json" || exit 0;
190
+ kk stage run itc-check-targets "${vars.work_dir}"
191
+
192
+ - id: send-confirm
193
+ description: >-
194
+ Operator approval of a LIVE send: `pause: true` stops the run and RESUME is the approval
195
+ (no action); cancelling is the rejection. Skipped for draft-only or empty plans.
196
+ dependsOn:
197
+ - adapt-verify
198
+ - check-targets
199
+ pause: true
200
+ condition:
582
201
  kind: shell
583
202
  options:
584
- command: 'test "${vars.cover_enabled}" != "true" -a "${vars.inline_images}" != "true"'
585
-
586
- - from: illustrate
587
- to: materialize
588
- description: "Illustrations written -> materialize the final draft (metadata.coverImage picks up 4-illustrations/cover.*)"
589
- guard:
590
- kind: always
203
+ command: 'jq -e ".live == true and (.targets // [] | length) > 0" "${vars.work_dir}/4-publish/publish-plan.json" >/dev/null 2>&1'
591
204
 
592
- - from: materialize
593
- to: publish-plan
594
- description: "content.json written -> split targets into groups"
595
- guard:
596
- kind: always
597
-
598
- - from: publish-plan
599
- to: adapt
600
- description: "The plan has at least one adapt:true variant -> write the adapted variant files (R9)"
601
- guard:
205
+ - id: fanout-send
206
+ description: "Dispatch target payloads via executor fan-out and record receipts"
207
+ dependsOn:
208
+ - send-confirm
209
+ action:
602
210
  kind: shell
603
211
  options:
604
212
  command: >-
605
- v="$(jq '[.variants[] | select(.adapt == true)] | length' "${vars.work_dir}/4-publish/publish-plan.json" 2>/dev/null)";
606
- test -n "$v" -a "$v" -gt 0
607
-
608
- - from: publish-plan
609
- to: publish-gate
610
- description: "Nothing needs adaptation -> route through the send gate"
611
- guard:
612
- kind: always
213
+ test -f "${vars.work_dir}/4-publish/publish-plan.json" || exit 0;
214
+ kk stage run itc-send-targets "${vars.work_dir}"
613
215
 
614
- - from: adapt
615
- to: publish-gate
616
- description: "Variant files verified (itc-adapt-verify) -> route through the send gate"
617
- guard:
618
- kind: always
619
-
620
- - from: publish-gate
621
- to: publish-confirm
622
- description: "Interactive run or live publish -> pause for the operator answer (V4)"
623
- guard:
216
+ - id: fanout-recovery
217
+ description: "Retry safe effect-none target failures once within budget"
218
+ dependsOn:
219
+ - fanout-send
220
+ action:
624
221
  kind: shell
625
222
  options:
626
223
  command: >-
627
- test "${vars.auto}" != "true" -o "${vars.publish_live}" = "true"
628
-
629
- - from: publish-gate
630
- to: publish-fanout
631
- description: "Auto draft run -> send without pausing (ADR-022)"
632
- guard:
633
- kind: always
634
-
635
- - from: publish-confirm
636
- to: publish-fanout
637
- description: "Operator confirmed -> send"
638
- guard:
639
- kind: shell
640
- options:
641
- command: 'test "${vars.__hitlAnswer}" = "yes"'
224
+ test -f "${vars.work_dir}/4-publish/publish-plan.json" || exit 0;
225
+ kk stage run itc-send-targets "${vars.work_dir}" --retry
642
226
 
643
- - from: publish-confirm
644
- to: report
645
- description: "Operator declined (or dry-run left no answer) -> report without any publish call"
646
- guard:
227
+ - id: reconcile-settle
228
+ description: "Reconcile target receipts and settle store selection"
229
+ dependsOn:
230
+ - fanout-recovery
231
+ action:
647
232
  kind: shell
648
233
  options:
649
234
  command: >-
650
- test -z "${vars.__hitlAnswer}" -o "${vars.__hitlAnswer}" = "no"
235
+ kk stage run itc-reconcile-receipts "${vars.work_dir}"
651
236
 
652
- - from: publish-fanout
653
- to: publish-retry
654
- description: "A fan-out group has ok:false and the retry budget is untouched -> re-send once (V5)"
655
- guard:
237
+ - id: seo
238
+ description: "Export SEO prepublish request and published pages handoffs"
239
+ dependsOn:
240
+ - verify-child-freeze
241
+ action:
656
242
  kind: shell
657
243
  options:
658
244
  command: >-
659
- test "$(cat "${vars.work_dir}/.publish-retry-count" 2>/dev/null || echo 0)" -lt 1
660
- -a -s "${vars.work_dir}/4-publish/.fanout-failed"
661
-
662
- - from: publish-fanout
663
- to: publish-agent
664
- description: "A browser target has no result.<target>.json -> run the kk:publish loop"
665
- guard:
666
- kind: shell
667
- options:
668
- # .next-browser cursor: seeded by itc-publish-fanout, recomputed after each kk:publish step
669
- command: 'test -s "${vars.work_dir}/4-publish/.next-browser"'
245
+ kk stage run itc-export-seo "${vars.work_dir}" "$(jq -r '.seo' "${vars.work_dir}/itc-options.json")"
670
246
 
671
- - from: publish-fanout
672
- to: report
673
- description: "Otherwise -> report the per-target outcomes"
674
- guard:
675
- kind: always
676
-
677
- - from: publish-retry
678
- to: publish-agent
679
- description: "Retry done, a browser target is still pending -> run the kk:publish loop"
680
- guard:
681
- kind: shell
682
- options:
683
- # .next-browser cursor: seeded by itc-publish-fanout, recomputed after each kk:publish step
684
- command: 'test -s "${vars.work_dir}/4-publish/.next-browser"'
685
-
686
- - from: publish-retry
687
- to: report
688
- description: "Otherwise -> report (remaining fan-out failures are terminal data)"
689
- guard:
690
- kind: always
691
-
692
- - from: publish-agent
693
- to: publish-agent
694
- description: "A browser target is still pending -> next cursor iteration (bounded by the plan, V5)"
695
- guard:
696
- kind: shell
697
- options:
698
- # .next-browser cursor: seeded by itc-publish-fanout, recomputed after each kk:publish step
699
- command: 'test -s "${vars.work_dir}/4-publish/.next-browser"'
700
-
701
- - from: publish-agent
702
- to: report
703
- description: "Browser list exhausted -> report"
704
- guard:
705
- kind: always
706
-
707
- - from: report
708
- to: done
709
- description: "Report outcome done or declined (empty outcome = done in dry-run, mirroring the empty-verdict PASS convention)"
710
- guard:
711
- kind: shell
712
- options:
713
- command: 'o="$(jq -r .outcome ${vars.work_dir}/5-report/report.json 2>/dev/null)"; test -z "$o" -o "$o" = done -o "$o" = declined'
714
-
715
- - from: report
716
- to: failed
717
- terminalReason: failed-guard
718
- description: "Report outcome failed -> fail"
719
- guard:
720
- kind: shell
721
- options:
722
- command: 'o="$(jq -r .outcome ${vars.work_dir}/5-report/report.json 2>/dev/null)"; test -n "$o" -a "$o" != done'
723
-
724
- - from: judge
725
- to: failed
726
- terminalReason: failed-guard
727
- description: "Catch-all: an out-of-contract verdict string fails the run instead of deadlocking the machine (0149 review P3); a PASS whose confidence is missing or outside HIGH/MEDIUM/LOW reads as `PASS!` and lands here"
728
- guard:
247
+ - id: report
248
+ description: "Produce final run report summary"
249
+ # Mandatory freeze plus every branch: a skipped optional branch still satisfies the edge,
250
+ # so an all-skipped publication reports, but a report never races an unfinished send.
251
+ dependsOn:
252
+ - verify-child-freeze
253
+ - reconcile-settle
254
+ - seo
255
+ action:
729
256
  kind: shell
730
257
  options:
731
258
  command: >-
732
- v="$(jq -r 'if (.verdict // "") == "PASS" then (.verdict + (if ((.confidence // "") | test("^(HIGH|MEDIUM|LOW)$")) then "" else "!" end)) else (.verdict // "") end' ${vars.work_dir}/.itc-verdict.json 2>/dev/null)";
733
- test -n "$v" -a "$v" != PASS -a "$v" != FAIL -a "$v" != NEEDS_REVISION
259
+ kk stage run itc-report-summary "${vars.work_dir}" "$(jq -r '.maxRevisions' "${vars.work_dir}/itc-options.json")"