@gobing-ai/knowledge-kit 0.1.8 → 0.1.9
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/board/collect/index.js +725 -1101
- package/dist/board/daily-voice/index.js +722 -101
- package/dist/board/itc/index.js +13 -0
- package/dist/index.js +1792 -102
- package/package.json +1 -1
- package/plugins/ingestions/horizon-ingest/dist/index.js +24 -1
- package/plugins/ingestions/horizon-ingest/src/index.ts +34 -0
- package/plugins/kk/config.example.yaml +8 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/publish-presets/daily-ai-voice.json +123 -0
- package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +20 -0
- package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +17 -0
- package/plugins/kk/scripts/daily-stages.ts +580 -78
- package/plugins/kk/scripts/kk-workflow-stages.ts +11 -1
- package/plugins/kk/scripts/topic-boundary.json +18 -83
- package/plugins/kk/workflows/kk-collect.yaml +2 -0
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +75 -6
- package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +842 -0
- package/plugins/kk/workflows/kk-daily-publish3.yaml +282 -0
- package/plugins/publishings/podcast-pub/README.md +65 -0
- package/plugins/publishings/podcast-pub/dist/index.js +1709 -274
- package/plugins/publishings/podcast-pub/plugin.json +1 -1
- package/plugins/publishings/podcast-pub/src/backfill.ts +465 -0
- package/plugins/publishings/podcast-pub/src/index.ts +211 -34
- package/plugins/publishings/podcast-pub/src/microfeed-client.ts +32 -3
- package/plugins/publishings/podcast-pub/src/seo.ts +1081 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +71 -6
- package/plugins/publishings/surfdash-pub/dist/index.js +66 -5
- package/plugins/publishings/surfdash-pub/src/index.ts +94 -10
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kk-daily-publish3
|
|
3
|
+
kind: dag
|
|
4
|
+
version: "1"
|
|
5
|
+
description: >-
|
|
6
|
+
The publish half of voice3 (design `docs/design/kk-daily-ai-voice3.md` §5, §5a, §5b, task 0323):
|
|
7
|
+
three windows over one frozen bundle. `after-generate` drafts the draft-capable channels right
|
|
8
|
+
after generation, `cn-peak` goes live at 09:00 Asia/Shanghai, and `overseas-rss` runs behind the
|
|
9
|
+
podcast RSS gate. The channel matrix lives in the `daily-ai-voice` publish preset, never here.
|
|
10
|
+
Re-enter a failed window with
|
|
11
|
+
`spur workflow run plugins/kk/workflows/kk-daily-publish3.yaml --vars '{"work_dir":"<day dir>","window":"after-generate"}'`
|
|
12
|
+
— receipts make the rerun a no-op for anything already delivered.
|
|
13
|
+
|
|
14
|
+
# ADR-022: live is launch-only. A workflow file never yields a live send; the operator's launch var
|
|
15
|
+
# (or the installed schedule entry that carries it) is the consent. `dummy` is the rehearsal switch.
|
|
16
|
+
vars:
|
|
17
|
+
work_dir: ""
|
|
18
|
+
run_date: ""
|
|
19
|
+
window: after-generate
|
|
20
|
+
# Empty = the operator's `workflows.kk-daily-ai-voice3.publish_preset` selector decides.
|
|
21
|
+
preset: ""
|
|
22
|
+
channels: ""
|
|
23
|
+
live_channels: ""
|
|
24
|
+
dummy: "false"
|
|
25
|
+
agent: "inline"
|
|
26
|
+
__runId: ""
|
|
27
|
+
__definitionDigest: ""
|
|
28
|
+
|
|
29
|
+
nodes:
|
|
30
|
+
# ── cn-peak: the schedule fires in local time, the audience peaks in Shanghai ──────────────
|
|
31
|
+
- id: wait-until
|
|
32
|
+
description: >-
|
|
33
|
+
cn-peak only: the launchd entry fires at 17:00 local because launchd intervals follow the
|
|
34
|
+
machine's zone, so this node sleeps the remainder to 09:00 Asia/Shanghai (DST-safe, no-op when
|
|
35
|
+
already past).
|
|
36
|
+
condition:
|
|
37
|
+
kind: shell
|
|
38
|
+
options:
|
|
39
|
+
command: 'test "${vars.window}" = "cn-peak"'
|
|
40
|
+
|
|
41
|
+
action:
|
|
42
|
+
kind: shell
|
|
43
|
+
options:
|
|
44
|
+
command: 'kk stage run publish-matrix-wait-until 09:00 Asia/Shanghai'
|
|
45
|
+
|
|
46
|
+
# ── overseas-rss: never publish an episode the feed cannot prove ──────────────────────────
|
|
47
|
+
- id: rss-gate
|
|
48
|
+
description: >-
|
|
49
|
+
The overseas RSS gate, as a node that ALWAYS succeeds: for after-generate and cn-peak it is a
|
|
50
|
+
no-op, and for overseas-rss it records the verdict in `4-publish/rss-gate.json` (a miss is a
|
|
51
|
+
verdict, not a node failure). The graph routes on that artifact — `plan` requires a hit,
|
|
52
|
+
`diagnose` requires a miss — because a failing node would instead cascade-skip its dependents
|
|
53
|
+
through the engine's all-deps-skipped rule and take the incident path with it. `run_date` is
|
|
54
|
+
the previous day by the time this window runs, so the caller passes it; the gate never guesses.
|
|
55
|
+
action:
|
|
56
|
+
kind: shell
|
|
57
|
+
options:
|
|
58
|
+
command: >-
|
|
59
|
+
test "${vars.window}" = "overseas-rss" || { echo "publish3: rss-gate not applicable to ${vars.window}"; exit 0; };
|
|
60
|
+
test -n "${vars.run_date}" || { echo "publish3: overseas-rss needs run_date" >&2; exit 1; };
|
|
61
|
+
kk stage run publish-matrix-rss-gate "${vars.work_dir}" --date "${vars.run_date}" || true
|
|
62
|
+
|
|
63
|
+
# ── every window: plan → check → send-confirm → send ──────────────────────────────────────
|
|
64
|
+
- id: plan
|
|
65
|
+
description: >-
|
|
66
|
+
Freeze this window's target rows and their payloads from the bundle and the preset
|
|
67
|
+
(`4-publish/plan.<window>.json`). Never runs for a gated-miss overseas day: the RSS gate's
|
|
68
|
+
non-zero exit skips its dependents, so an unpublished episode cannot be planned.
|
|
69
|
+
dependsOn: [rss-gate]
|
|
70
|
+
condition:
|
|
71
|
+
kind: shell
|
|
72
|
+
options:
|
|
73
|
+
# A gated miss plans nothing: overseas targets only exist once the feed proves the episode.
|
|
74
|
+
command: >-
|
|
75
|
+
test "${vars.window}" != "overseas-rss"
|
|
76
|
+
|| jq -e '.hit == true' "${vars.work_dir}/4-publish/rss-gate.json" >/dev/null 2>&1
|
|
77
|
+
|
|
78
|
+
action:
|
|
79
|
+
kind: shell
|
|
80
|
+
options:
|
|
81
|
+
command: >-
|
|
82
|
+
BUNDLE="${vars.work_dir}/bundle.json";
|
|
83
|
+
test -f "$BUNDLE" || { echo "publish3: no bundle at $BUNDLE" >&2; exit 1; };
|
|
84
|
+
ARGS="--bundle $BUNDLE --window ${vars.window}";
|
|
85
|
+
test -z "${vars.preset}" || ARGS="$ARGS --preset ${vars.preset}";
|
|
86
|
+
test -z "${vars.channels}" || ARGS="$ARGS --channels ${vars.channels}";
|
|
87
|
+
test -z "${vars.live_channels}" || ARGS="$ARGS --live-channels ${vars.live_channels}";
|
|
88
|
+
test "${vars.dummy}" != "true" || ARGS="$ARGS --dummy";
|
|
89
|
+
kk stage run publish-matrix-plan "${vars.work_dir}" $ARGS
|
|
90
|
+
|
|
91
|
+
- id: check
|
|
92
|
+
description: >-
|
|
93
|
+
Readiness probes for the planned non-dummy channels. Warn-only by contract: a stale session is
|
|
94
|
+
recorded in `4-publish/check.<window>.json` and the window still runs, because the send's own
|
|
95
|
+
receipt is the authority on whether a channel could publish.
|
|
96
|
+
dependsOn: [plan]
|
|
97
|
+
action:
|
|
98
|
+
kind: shell
|
|
99
|
+
options:
|
|
100
|
+
command: 'kk stage run publish-matrix-check "${vars.work_dir}" --window "${vars.window}" || true'
|
|
101
|
+
|
|
102
|
+
- id: send-confirm
|
|
103
|
+
description: >-
|
|
104
|
+
Operator approval of a LIVE send: `pause: true` stops the run and RESUME is the approval;
|
|
105
|
+
cancelling is the rejection. Skipped for a draft-only window, so an unattended draft run and
|
|
106
|
+
every rehearsal proceed without a human.
|
|
107
|
+
dependsOn: [check]
|
|
108
|
+
pause: true
|
|
109
|
+
condition:
|
|
110
|
+
kind: shell
|
|
111
|
+
options:
|
|
112
|
+
command: >-
|
|
113
|
+
jq -e '[.rows[] | select(.mode == "live")] | length > 0'
|
|
114
|
+
"${vars.work_dir}/4-publish/plan.${vars.window}.json" >/dev/null 2>&1
|
|
115
|
+
|
|
116
|
+
- id: send
|
|
117
|
+
description: >-
|
|
118
|
+
Dispatch in tier order: tier 0 serial and blocking, later tiers after the tier-0 links resolve.
|
|
119
|
+
Receipts key every target, so a rerun never resends what was delivered, and a dummy row never
|
|
120
|
+
spawns a plugin. `check` is listed beside `send-confirm` on purpose: the engine skips a node only
|
|
121
|
+
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.
|
|
123
|
+
dependsOn: [check, send-confirm]
|
|
124
|
+
action:
|
|
125
|
+
kind: shell
|
|
126
|
+
options:
|
|
127
|
+
command: 'kk stage run publish-matrix-send "${vars.work_dir}" --window "${vars.window}"'
|
|
128
|
+
|
|
129
|
+
- id: classify
|
|
130
|
+
description: >-
|
|
131
|
+
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
|
+
dependsOn: [send]
|
|
134
|
+
action:
|
|
135
|
+
kind: shell
|
|
136
|
+
options:
|
|
137
|
+
command: >-
|
|
138
|
+
test -n "${vars.run_date}" || { echo "publish3: classify needs run_date" >&2; exit 1; };
|
|
139
|
+
kk stage run publish-matrix-classify "${vars.work_dir}" --window "${vars.window}" --run-date "${vars.run_date}"
|
|
140
|
+
|
|
141
|
+
# ── after-generate only: the voice2-era tail stages keep working on the bridged names ─────
|
|
142
|
+
- id: surfdash-patch
|
|
143
|
+
description: >-
|
|
144
|
+
after-generate only: cover and podcast cross-link patching plus the CMS re-sync, whenever tier 0
|
|
145
|
+
published something. Reads the legacy `31-publish` names the classifier bridged, so voice2's own
|
|
146
|
+
stage body is reused unchanged.
|
|
147
|
+
dependsOn: [classify]
|
|
148
|
+
condition:
|
|
149
|
+
kind: shell
|
|
150
|
+
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
|
|
154
|
+
|
|
155
|
+
action:
|
|
156
|
+
kind: shell
|
|
157
|
+
options:
|
|
158
|
+
command: >-
|
|
159
|
+
kk stage run daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "$KK_SURFDASH_ROOT" ""
|
|
160
|
+
|
|
161
|
+
- id: partial-marker
|
|
162
|
+
description: "after-generate only: record the partial-publish marker when the classifier said partial."
|
|
163
|
+
dependsOn: [classify]
|
|
164
|
+
condition:
|
|
165
|
+
kind: shell
|
|
166
|
+
options:
|
|
167
|
+
command: >-
|
|
168
|
+
test "${vars.window}" = "after-generate"
|
|
169
|
+
&& grep -qx 'partial' "${vars.work_dir}/4-publish/publish-status.after-generate.txt" 2>/dev/null
|
|
170
|
+
|
|
171
|
+
action:
|
|
172
|
+
kind: shell
|
|
173
|
+
options:
|
|
174
|
+
command: 'kk stage run daily-publish-partial "${vars.work_dir}" "${vars.run_date}"'
|
|
175
|
+
|
|
176
|
+
- id: mark-published
|
|
177
|
+
description: >-
|
|
178
|
+
after-generate only: mark the run's items published in the store once the owned platforms
|
|
179
|
+
delivered. Keyed by the voice3 run id the bundle records, never by this publish run.
|
|
180
|
+
dependsOn: [classify]
|
|
181
|
+
condition:
|
|
182
|
+
kind: shell
|
|
183
|
+
options:
|
|
184
|
+
command: >-
|
|
185
|
+
test "${vars.window}" = "after-generate"
|
|
186
|
+
&& grep -qxE 'full|partial' "${vars.work_dir}/4-publish/publish-status.after-generate.txt" 2>/dev/null
|
|
187
|
+
|
|
188
|
+
action:
|
|
189
|
+
kind: shell
|
|
190
|
+
options:
|
|
191
|
+
command: >-
|
|
192
|
+
PARENT="$(jq -r '.parentRunId // .runId' "${vars.work_dir}/bundle.json" 2>/dev/null)";
|
|
193
|
+
test -n "$PARENT" || { echo "publish3: bundle has no run id" >&2; exit 1; };
|
|
194
|
+
kk store mark-published --topic ai-news --run "$PARENT"
|
|
195
|
+
|
|
196
|
+
# ── the incident path: a gated miss is diagnosed, never silently dropped ─────────────────
|
|
197
|
+
- id: diagnose
|
|
198
|
+
description: >-
|
|
199
|
+
overseas-rss only, on a gate miss: classify why the episode is not in the feed
|
|
200
|
+
(`no-run | generation-failed@<node> | no-content | t0-failed | rss-lag`) and write
|
|
201
|
+
`4-publish/incident.json` + `incident.md`. `onError: continue` — a diagnosis must never mask the
|
|
202
|
+
miss it is describing.
|
|
203
|
+
dependsOn: [rss-gate]
|
|
204
|
+
condition:
|
|
205
|
+
kind: shell
|
|
206
|
+
options:
|
|
207
|
+
command: >-
|
|
208
|
+
test "${vars.window}" = "overseas-rss"
|
|
209
|
+
&& jq -e '.hit == false' "${vars.work_dir}/4-publish/rss-gate.json" >/dev/null 2>&1
|
|
210
|
+
|
|
211
|
+
action:
|
|
212
|
+
kind: agent.run
|
|
213
|
+
onError: continue
|
|
214
|
+
options:
|
|
215
|
+
role: scribe
|
|
216
|
+
agent: ${vars.agent}
|
|
217
|
+
input: |
|
|
218
|
+
Diagnose a missed overseas publish window. Read only these paths:
|
|
219
|
+
- ${vars.work_dir}/4-publish/rss-gate.json (the miss reason)
|
|
220
|
+
- ${vars.work_dir}/bundle.json (absent = no generation ran)
|
|
221
|
+
- ${vars.work_dir}/4-publish/outcome.after-generate.json and receipts/ (tier-0 evidence)
|
|
222
|
+
Run `spur workflow trace --json` for the voice3 run named in bundle.json if it exists.
|
|
223
|
+
Write ${vars.work_dir}/4-publish/incident.json with exactly
|
|
224
|
+
{"schemaVersion":1,"date":"<run_date>","class":"<one of no-run|generation-failed|<node>|no-content|t0-failed|rss-lag>","reason":"<one line>","evidence":["<paths>"]}
|
|
225
|
+
and ${vars.work_dir}/4-publish/incident.md with 3 sentences of operator prose: what is missing,
|
|
226
|
+
which class, and the one action that would fix it. Never regenerate content.
|
|
227
|
+
expectFile: ${vars.work_dir}/4-publish/incident.json
|
|
228
|
+
|
|
229
|
+
- id: repair
|
|
230
|
+
description: >-
|
|
231
|
+
overseas-rss only, on a gate miss: the ONE safe automatic repair — resend tier-0 targets whose
|
|
232
|
+
receipt is `effect: none`. It never regenerates content and never resends an uncertain live send
|
|
233
|
+
(the shared `--retry` rules), so a lost generation stays an operator decision.
|
|
234
|
+
dependsOn: [diagnose]
|
|
235
|
+
action:
|
|
236
|
+
kind: shell
|
|
237
|
+
options:
|
|
238
|
+
command: 'kk stage run publish-matrix-send "${vars.work_dir}" --window after-generate --retry --tier 0'
|
|
239
|
+
|
|
240
|
+
# ── traceability: one episode, several publish runs ───────────────────────────────────────
|
|
241
|
+
- id: bundle-runs
|
|
242
|
+
description: >-
|
|
243
|
+
Append this run id to `bundle.json` `runs[]`, idempotently, so one day's episode is traceable
|
|
244
|
+
across its generation run and its windows.
|
|
245
|
+
dependsOn: [classify]
|
|
246
|
+
action:
|
|
247
|
+
kind: shell
|
|
248
|
+
options:
|
|
249
|
+
command: >-
|
|
250
|
+
BUNDLE="${vars.work_dir}/bundle.json";
|
|
251
|
+
test -f "$BUNDLE" || exit 0;
|
|
252
|
+
jq --arg id "${vars.__runId}" 'if (.runs // []) | index($id) then . else .runs = ((.runs // []) + [$id]) end'
|
|
253
|
+
"$BUNDLE" > "$BUNDLE.tmp" && mv "$BUNDLE.tmp" "$BUNDLE"
|
|
254
|
+
|
|
255
|
+
- id: report
|
|
256
|
+
description: >-
|
|
257
|
+
The publish half's report: per-window status and every receipt, on one page. It also depends on
|
|
258
|
+
`diagnose`, so a gated-miss day (skipped `bundle-runs`, executed `diagnose`) still writes its
|
|
259
|
+
page — the same all-deps-skipped rule, read the other way.
|
|
260
|
+
dependsOn: [bundle-runs, diagnose]
|
|
261
|
+
action:
|
|
262
|
+
kind: shell
|
|
263
|
+
options:
|
|
264
|
+
# Straight statements, no `{ … }` group and no heredoc: the engine's shell wrapper treats a
|
|
265
|
+
# multi-line group as an interactive read and never returns.
|
|
266
|
+
command: >-
|
|
267
|
+
OUT="${vars.work_dir}/5-report/publish-${vars.window}.md";
|
|
268
|
+
mkdir -p "${vars.work_dir}/5-report";
|
|
269
|
+
STATUS="$(cat "${vars.work_dir}/4-publish/publish-status.${vars.window}.txt" 2>/dev/null || echo unknown)";
|
|
270
|
+
printf '# Publish report — %s — %s\n\n- status: %s\n\n| target | tier | status | effect | dummy |\n| --- | --- | --- | --- | --- |\n'
|
|
271
|
+
"${vars.window}" "${vars.run_date}" "$STATUS" > "$OUT";
|
|
272
|
+
jq -r '.rows[] | "| \(.id) | \(.tier) | \(.status) | \(.effect // "-") | \(.dummy) |"'
|
|
273
|
+
"${vars.work_dir}/4-publish/outcome.${vars.window}.json" >> "$OUT" 2>/dev/null || true
|
|
274
|
+
|
|
275
|
+
- id: ledger
|
|
276
|
+
description: "Append the window's row to the workflow ledger; `onError: continue` — bookkeeping never fails a publish."
|
|
277
|
+
dependsOn: [report]
|
|
278
|
+
action:
|
|
279
|
+
kind: shell
|
|
280
|
+
options:
|
|
281
|
+
command: >-
|
|
282
|
+
kk ledger append --workflow kk-daily-publish3 --work-dir "${vars.work_dir}" --run "${vars.__runId}" || true
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# podcast-pub
|
|
2
|
+
|
|
3
|
+
Publish a podcast episode to a self-hosted microfeed 1.0.13 instance, and repair
|
|
4
|
+
SEO fields on episodes that are already there.
|
|
5
|
+
|
|
6
|
+
Credentials: `MICROFEED_BASE_URL`, `MICROFEED_API_KEY`. Optional
|
|
7
|
+
`MICROFEED_TIMEOUT_MS` (default 300000).
|
|
8
|
+
|
|
9
|
+
## Operations
|
|
10
|
+
|
|
11
|
+
`options.operation` selects the call. The default is `publish`.
|
|
12
|
+
|
|
13
|
+
| Operation | What it does |
|
|
14
|
+
| --------- | ------------ |
|
|
15
|
+
| `publish` | Create or upload an episode. A pattern slug is sent only on the first create. |
|
|
16
|
+
| `show-notes` | Render plan `Doc[]` to markdown. No network. A bare JSON array `--in` selects this. |
|
|
17
|
+
| `list` / `get` / `update` / `delete` | Feed and item management. |
|
|
18
|
+
| `validate` | `POST /api/v1/items/validate`. |
|
|
19
|
+
| `backfill-seo` | Fill `_microfeed.seo` and missing iTunes numbers. **Dry-run unless `apply` is set.** |
|
|
20
|
+
|
|
21
|
+
`backfill-seo` options:
|
|
22
|
+
|
|
23
|
+
| Key | Default | Effect |
|
|
24
|
+
| --- | ------- | ------ |
|
|
25
|
+
| `apply` | false | `true` / `"true"` / `"1"` sends the PUTs |
|
|
26
|
+
| `since` / `until` | unset | Inclusive `YYYY-MM-DD` window |
|
|
27
|
+
| `ids` | unset | Item ids to touch |
|
|
28
|
+
| `out` | unset | Write the plan JSON `{ dryRun, items }` |
|
|
29
|
+
| `plan` | unset | With `apply`, PUT this reviewed plan instead of recomputing |
|
|
30
|
+
| `retitle` | false | Also change the RSS title |
|
|
31
|
+
| `rewriteBody` | false | Insert the summary paragraph and move the timeline `<style>` |
|
|
32
|
+
| `applySlug` | false | Set `_microfeed.slug` (moves the public URL; a 409 retries as `-2`) |
|
|
33
|
+
| `overrides` | unset | `[{ id, title?, titleKeywords?, summaryClauses? }]`. `title` is kept verbatim after sanitising (the 10-06 escape hatch) |
|
|
34
|
+
|
|
35
|
+
The repair always proposes SEO fields, even when `PODCAST_PUB_SEO=false`.
|
|
36
|
+
It does not change `guid`, attachments, `date_published`, `status`, or `url`.
|
|
37
|
+
|
|
38
|
+
## SEO environment
|
|
39
|
+
|
|
40
|
+
Item `metadata.podcast.*` wins over these variables. Empty string means unset.
|
|
41
|
+
Invalid values fail the run and name the key. The full table and the legacy
|
|
42
|
+
combination that reproduces the pre-D9 payload are in `docs/design/podcast-pub.md` (D9).
|
|
43
|
+
|
|
44
|
+
| Key | Default |
|
|
45
|
+
| --- | ------- |
|
|
46
|
+
| `PODCAST_PUB_SEO` | `true` |
|
|
47
|
+
| `PODCAST_PUB_EPISODE_TITLE_MODE` | `as-is` (`headline` builds `{series} · {date}:{keywords}`) |
|
|
48
|
+
| `PODCAST_PUB_EPISODE_TITLE_PATTERN` | `{series} · {date}:{keywords}` |
|
|
49
|
+
| `PODCAST_PUB_SERIES_NAME` / `PODCAST_PUB_SHOW_NAME` | `每日AI乱炖` / `咸话咸说` |
|
|
50
|
+
| `PODCAST_PUB_TITLE_MAX` / `PODCAST_PUB_TITLE_MAX_KEYWORDS` | `55` / `2` |
|
|
51
|
+
| `PODCAST_PUB_SEO_TITLE_SUFFIX` / `PODCAST_PUB_SEO_TITLE_MAX` | `|咸话咸说` / `60` |
|
|
52
|
+
| `PODCAST_PUB_SEO_DESCRIPTION_MIN` / `MAX` / `SOURCE` | `120` / `155` / `plan` |
|
|
53
|
+
| `PODCAST_PUB_SUMMARY_PARAGRAPH` | `true` |
|
|
54
|
+
| `PODCAST_PUB_TIMELINE_INLINE_STYLE` | `after` (`before` is the old position, `omit` drops the style) |
|
|
55
|
+
| `PODCAST_PUB_AUTO_EPISODE_NUMBER` | `true` |
|
|
56
|
+
| `PODCAST_PUB_TITLE_SANITIZE` | `normalize` (`reject` fails on `"`, `\`, `<>`, or control characters) |
|
|
57
|
+
| `PODCAST_PUB_LINK_FIELD` | `none` (`url` copies `articleUrl` onto the item and warns) |
|
|
58
|
+
| `PODCAST_PUB_SLUG` / `PODCAST_PUB_SLUG_PATTERN` | `pattern` / `daily-ai-{date}` |
|
|
59
|
+
| `PODCAST_PUB_SOCIAL_IMAGE` | `false` |
|
|
60
|
+
| `PODCAST_PUB_ROUND_DURATION` | `true` |
|
|
61
|
+
|
|
62
|
+
`PODCAST_PUB_TITLE` is the show-notes H1. It is not the episode title pattern.
|
|
63
|
+
|
|
64
|
+
Do not point this plugin at a live feed from tests. `backfill-seo` against
|
|
65
|
+
production is an operator step, after a dry-run plan has been reviewed.
|