@gobing-ai/knowledge-kit 0.1.7 → 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.
Files changed (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.js +744 -1107
  3. package/dist/board/daily-voice/index.js +741 -107
  4. package/dist/board/itc/index.js +32 -6
  5. package/dist/index.js +1793 -103
  6. package/package.json +1 -1
  7. package/plugins/ingestions/horizon-ingest/dist/index.js +24 -1
  8. package/plugins/ingestions/horizon-ingest/src/index.ts +34 -0
  9. package/plugins/kk/config.example.yaml +8 -0
  10. package/plugins/kk/decisions/extract/ai-news.yaml +7 -1
  11. package/plugins/kk/plugin.json +1 -1
  12. package/plugins/kk/publish-presets/daily-ai-voice.json +123 -0
  13. package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +20 -0
  14. package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +17 -0
  15. package/plugins/kk/scripts/daily-stages.ts +664 -83
  16. package/plugins/kk/scripts/kk-workflow-stages.ts +11 -1
  17. package/plugins/kk/scripts/topic-boundary.json +18 -83
  18. package/plugins/kk/workflows/kk-collect.yaml +2 -0
  19. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +82 -8
  20. package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +842 -0
  21. package/plugins/kk/workflows/kk-daily-publish3.yaml +282 -0
  22. package/plugins/publishings/podcast-pub/README.md +65 -0
  23. package/plugins/publishings/podcast-pub/dist/index.js +1709 -274
  24. package/plugins/publishings/podcast-pub/plugin.json +1 -1
  25. package/plugins/publishings/podcast-pub/src/backfill.ts +465 -0
  26. package/plugins/publishings/podcast-pub/src/index.ts +211 -34
  27. package/plugins/publishings/podcast-pub/src/microfeed-client.ts +32 -3
  28. package/plugins/publishings/podcast-pub/src/seo.ts +1081 -0
  29. package/plugins/publishings/podcast-pub/src/show-notes.ts +71 -6
  30. package/plugins/publishings/surfdash-pub/dist/index.js +66 -5
  31. package/plugins/publishings/surfdash-pub/src/index.ts +94 -10
  32. package/plugins/publishings/wechatmp-pub/dist/index.js +54 -30
  33. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +8 -3
  34. package/plugins/publishings/wechatmp-pub/src/enrich.ts +58 -38
  35. package/plugins/publishings/wechatmp-pub/src/flow.ts +18 -1
  36. package/plugins/publishings/wechatmp-pub/src/profile-check.ts +7 -1
@@ -37,6 +37,14 @@ function storeSelectionsQueryString(query) {
37
37
  }
38
38
  return params.toString();
39
39
  }
40
+ function storeChannelsQueryString(query) {
41
+ const params = new URLSearchParams({
42
+ from: String(query.fromMs),
43
+ until: String(query.untilMs),
44
+ page: String(query.page)
45
+ });
46
+ return params.toString();
47
+ }
40
48
  var DEFAULT_READER_ORIGIN = "http://127.0.0.1:4340";
41
49
  function readerOriginFrom(search, defaultOrigin = DEFAULT_READER_ORIGIN) {
42
50
  const override = new URLSearchParams(search).get("kkApi");
@@ -67,6 +75,19 @@ function isLoopbackHttpOrigin(origin) {
67
75
  return false;
68
76
  return url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "[::1]";
69
77
  }
78
+ function isHttpOrigin(origin) {
79
+ let url;
80
+ try {
81
+ url = new URL(origin);
82
+ } catch {
83
+ return false;
84
+ }
85
+ if (url.protocol !== "http:" && url.protocol !== "https:")
86
+ return false;
87
+ if (url.username !== "" || url.password !== "")
88
+ return false;
89
+ return url.pathname === "/" && url.search === "" && url.hash === "";
90
+ }
70
91
  function origin(base) {
71
92
  return base.replace(/\/+$/, "");
72
93
  }
@@ -90,16 +111,20 @@ function parseSpurDbRows(pageData) {
90
111
  });
91
112
  }
92
113
  function createBoardClient(options) {
93
- if (!isLoopbackHttpOrigin(options.readerOrigin)) {
114
+ const reader = origin(options.readerOrigin);
115
+ const board = origin(options.boardOrigin);
116
+ const isNativeSpur = reader === board;
117
+ if (!isHttpOrigin(options.boardOrigin)) {
118
+ throw new Error(`board origin must be an http origin, got '${options.boardOrigin}'`);
119
+ }
120
+ if (!isNativeSpur && !isLoopbackHttpOrigin(options.readerOrigin)) {
94
121
  throw new Error(`reader origin must be loopback http, got '${options.readerOrigin}'`);
95
122
  }
96
- if (!isLoopbackHttpOrigin(options.boardOrigin)) {
97
- throw new Error(`board origin must be loopback http, got '${options.boardOrigin}'`);
123
+ const aggregateReader = origin(options.aggregateReaderOrigin ?? DEFAULT_READER_ORIGIN);
124
+ if (!isLoopbackHttpOrigin(options.aggregateReaderOrigin ?? DEFAULT_READER_ORIGIN)) {
125
+ throw new Error(`aggregate reader origin must be loopback http, got '${options.aggregateReaderOrigin}'`);
98
126
  }
99
- const reader = origin(options.readerOrigin);
100
- const board = origin(options.boardOrigin);
101
127
  const doFetch = options.fetchImpl ?? fetch;
102
- const isNativeSpur = reader === board;
103
128
  async function getJson(url) {
104
129
  const response = await doFetch(url, { headers: { accept: "application/json" } });
105
130
  if (!response.ok) {
@@ -493,11 +518,190 @@ function createBoardClient(options) {
493
518
  return getJson(path);
494
519
  },
495
520
  storeSelections: (query = {}) => isNativeSpur ? nativeStoreSelections(query) : getJson(`${reader}/api/workflows/kk-collect/store/selections?${storeSelectionsQueryString(query)}`),
521
+ storeChannels: (query) => getJson(`${aggregateReader}/api/workflows/kk-collect/store/channels?${storeChannelsQueryString(query)}`),
496
522
  runDetail: (runId) => getJson(`${board}/api/runs/${encodeURIComponent(runId)}`),
497
523
  progress: (runId) => getJson(`${board}/api/runs/${encodeURIComponent(runId)}/progress`)
498
524
  };
499
525
  }
500
526
 
527
+ // ../board/src/shared/channel-yield.tsx
528
+ import { jsx, jsxs } from "react/jsx-runtime";
529
+ function localDayKey(ms, timeZone) {
530
+ return new Intl.DateTimeFormat("en-CA", {
531
+ timeZone,
532
+ year: "numeric",
533
+ month: "2-digit",
534
+ day: "2-digit"
535
+ }).format(ms);
536
+ }
537
+ function isLocalMidnight(ms, timeZone) {
538
+ return new Intl.DateTimeFormat("en-GB", {
539
+ timeZone,
540
+ hour: "2-digit",
541
+ minute: "2-digit",
542
+ second: "2-digit",
543
+ hour12: false
544
+ }).format(ms) === "00:00:00";
545
+ }
546
+ function isPartialDay(day, data) {
547
+ const fromDay = localDayKey(data.fromMs, data.timeZone);
548
+ const untilDay = localDayKey(Math.max(data.fromMs, data.untilMs - 1), data.timeZone);
549
+ if (day === fromDay && !isLocalMidnight(data.fromMs, data.timeZone))
550
+ return true;
551
+ if (day === untilDay && !isLocalMidnight(data.untilMs, data.timeZone))
552
+ return true;
553
+ return false;
554
+ }
555
+ function lastPositiveLabel(at) {
556
+ return at === null ? "unknown" : `${new Date(at).toLocaleString()} (${new Date(at).toISOString()})`;
557
+ }
558
+ function ChannelYieldTable({ data }) {
559
+ const { sources, byDay, bySource } = data;
560
+ if (byDay.length === 0) {
561
+ return /* @__PURE__ */ jsx("p", {
562
+ className: "kk-board-state",
563
+ children: "No new items in this range."
564
+ });
565
+ }
566
+ const grandTotal = sources.reduce((sum, source) => sum + (bySource[source] ?? 0), 0);
567
+ return /* @__PURE__ */ jsxs("table", {
568
+ className: "kk-table kk-channel-yield-table",
569
+ children: [
570
+ /* @__PURE__ */ jsx("thead", {
571
+ children: /* @__PURE__ */ jsxs("tr", {
572
+ children: [
573
+ /* @__PURE__ */ jsx("th", {
574
+ scope: "col",
575
+ children: "Day"
576
+ }),
577
+ sources.map((source) => /* @__PURE__ */ jsx("th", {
578
+ scope: "col",
579
+ children: source
580
+ }, source)),
581
+ /* @__PURE__ */ jsx("th", {
582
+ scope: "col",
583
+ children: "Total"
584
+ })
585
+ ]
586
+ })
587
+ }),
588
+ /* @__PURE__ */ jsxs("tbody", {
589
+ children: [
590
+ byDay.map((day) => /* @__PURE__ */ jsxs("tr", {
591
+ children: [
592
+ /* @__PURE__ */ jsxs("td", {
593
+ children: [
594
+ day.day,
595
+ isPartialDay(day.day, data) ? " (partial)" : ""
596
+ ]
597
+ }),
598
+ sources.map((source) => /* @__PURE__ */ jsx("td", {
599
+ children: day.sources[source] ?? 0
600
+ }, source)),
601
+ /* @__PURE__ */ jsx("td", {
602
+ children: day.total
603
+ })
604
+ ]
605
+ }, day.day)),
606
+ /* @__PURE__ */ jsxs("tr", {
607
+ className: "kk-table-total",
608
+ children: [
609
+ /* @__PURE__ */ jsx("td", {
610
+ children: "Total"
611
+ }),
612
+ sources.map((source) => /* @__PURE__ */ jsx("td", {
613
+ children: bySource[source] ?? 0
614
+ }, source)),
615
+ /* @__PURE__ */ jsx("td", {
616
+ children: grandTotal
617
+ })
618
+ ]
619
+ })
620
+ ]
621
+ })
622
+ ]
623
+ });
624
+ }
625
+ function PluginRunTable({ data }) {
626
+ const { plugins, runsByDay, lastPositiveAt } = data;
627
+ if (runsByDay.length === 0) {
628
+ return /* @__PURE__ */ jsx("p", {
629
+ className: "kk-board-state",
630
+ children: "No collection ticks in this range."
631
+ });
632
+ }
633
+ return /* @__PURE__ */ jsxs("div", {
634
+ className: "kk-plugin-run",
635
+ children: [
636
+ /* @__PURE__ */ jsxs("table", {
637
+ className: "kk-table kk-plugin-run-table",
638
+ children: [
639
+ /* @__PURE__ */ jsx("caption", {
640
+ className: "kk-plugin-run-caption",
641
+ children: "Zero recorded contributions — plugin invocation health unknown."
642
+ }),
643
+ /* @__PURE__ */ jsx("thead", {
644
+ children: /* @__PURE__ */ jsxs("tr", {
645
+ children: [
646
+ /* @__PURE__ */ jsx("th", {
647
+ scope: "col",
648
+ children: "Day"
649
+ }),
650
+ /* @__PURE__ */ jsx("th", {
651
+ scope: "col",
652
+ children: "Runs"
653
+ }),
654
+ plugins.map((plugin) => /* @__PURE__ */ jsx("th", {
655
+ scope: "col",
656
+ children: plugin
657
+ }, plugin))
658
+ ]
659
+ })
660
+ }),
661
+ /* @__PURE__ */ jsx("tbody", {
662
+ children: runsByDay.map((day) => {
663
+ const pluginCounts = day.plugins;
664
+ return /* @__PURE__ */ jsxs("tr", {
665
+ children: [
666
+ /* @__PURE__ */ jsx("td", {
667
+ children: day.day
668
+ }),
669
+ /* @__PURE__ */ jsx("td", {
670
+ children: day.runs
671
+ }),
672
+ pluginCounts === null ? /* @__PURE__ */ jsx("td", {
673
+ colSpan: plugins.length,
674
+ className: "kk-board-state",
675
+ children: "unknown"
676
+ }) : plugins.map((plugin) => /* @__PURE__ */ jsx("td", {
677
+ children: pluginCounts[plugin] ?? 0
678
+ }, plugin))
679
+ ]
680
+ }, day.day);
681
+ })
682
+ })
683
+ ]
684
+ }),
685
+ /* @__PURE__ */ jsx("dl", {
686
+ className: "kk-board-facts",
687
+ children: plugins.map((plugin) => /* @__PURE__ */ jsxs("div", {
688
+ children: [
689
+ /* @__PURE__ */ jsxs("dt", {
690
+ children: [
691
+ "Last positive ",
692
+ plugin
693
+ ]
694
+ }),
695
+ /* @__PURE__ */ jsx("dd", {
696
+ children: lastPositiveLabel(lastPositiveAt[plugin] ?? null)
697
+ })
698
+ ]
699
+ }, plugin))
700
+ })
701
+ ]
702
+ });
703
+ }
704
+
501
705
  // ../board/src/shared/format.ts
502
706
  function availabilityLabel(availability) {
503
707
  switch (availability) {
@@ -568,6 +772,10 @@ function artifactViews(results, readerOrigin) {
568
772
  var ACTIVE_POLL_MS = 5000;
569
773
  var HISTORY_POLL_MS = 30000;
570
774
  var MAX_BACKOFF_MS = 30000;
775
+ function requestSequence() {
776
+ let newest = 0;
777
+ return { begin: () => ++newest, isCurrent: (token) => token === newest };
778
+ }
571
779
  function initialRefresh(intervalMs) {
572
780
  return { pending: false, failures: 0, backoffMs: intervalMs, lastSuccessAt: null, failingSince: null };
573
781
  }
@@ -836,9 +1044,79 @@ function dailySummaryFacts(results, now) {
836
1044
  ];
837
1045
  return facts;
838
1046
  }
1047
+ var GATE_NAMES = {
1048
+ "episode-duration": "Duration floor refusal",
1049
+ script: "Script refusal",
1050
+ "qc-floor": "QC floor refusal",
1051
+ "empty-pool": "Empty pool",
1052
+ "qc-empty": "QC emptied the pool",
1053
+ unknown: "unknown — no gate proven"
1054
+ };
1055
+ function isFunnelGate(value) {
1056
+ return value === "episode-duration" || value === "script" || value === "qc-floor" || value === "empty-pool" || value === "qc-empty" || value === "unknown";
1057
+ }
1058
+ function funnelView(value) {
1059
+ const record = asRecord(value);
1060
+ const gate = isFunnelGate(record?.gate) ? record.gate : null;
1061
+ return {
1062
+ poolCount: asNumber(record?.poolCount),
1063
+ annotatedCount: asNumber(record?.annotatedCount),
1064
+ qcRejectedCount: asNumber(record?.qcRejectedCount),
1065
+ topicKeptCount: asNumber(record?.topicKeptCount),
1066
+ dedupKeptCount: asNumber(record?.dedupKeptCount),
1067
+ plannedCount: asNumber(record?.plannedCount),
1068
+ estimatedMinutes: asNumber(record?.estimatedMinutes),
1069
+ measuredMinutes: asNumber(record?.measuredMinutes),
1070
+ minMinutes: asNumber(record?.minMinutes),
1071
+ maxMinutes: asNumber(record?.maxMinutes),
1072
+ gate,
1073
+ gateName: gate === null ? "not applicable — a content run has no gate" : GATE_NAMES[gate],
1074
+ exclusions: Array.isArray(record?.exclusions) ? record.exclusions.map((entry) => {
1075
+ const row = asRecord(entry);
1076
+ return row === null || typeof row.rule !== "string" ? null : { rule: row.rule, matched: typeof row.matched === "string" ? row.matched : "" };
1077
+ }).filter((row) => row !== null) : [],
1078
+ evidence: record === null ? {} : Object.fromEntries(Object.entries(record.evidence ?? {}).filter((entry) => typeof entry[1] === "string")),
1079
+ diagnostics: Array.isArray(record?.diagnostics) ? record.diagnostics.map((entry) => {
1080
+ const row = asRecord(entry);
1081
+ return row === null ? null : { code: String(row.code ?? ""), message: String(row.message ?? "") };
1082
+ }).filter((row) => row !== null) : []
1083
+ };
1084
+ }
1085
+ var WINDOW_UNITS = {
1086
+ s: 1000,
1087
+ m: 60000,
1088
+ h: 3600000,
1089
+ d: 86400000
1090
+ };
1091
+ function parseWindowMs(raw) {
1092
+ if (raw === null || raw === undefined)
1093
+ return null;
1094
+ const match = /^(\d+)([smhd])$/.exec(raw.trim());
1095
+ if (match === null)
1096
+ return null;
1097
+ const [, amountText, unitText] = match;
1098
+ const unit = unitText === "s" || unitText === "m" || unitText === "h" || unitText === "d" ? unitText : null;
1099
+ if (amountText === undefined || unit === null)
1100
+ return null;
1101
+ const milliseconds = Number(amountText) * WINDOW_UNITS[unit];
1102
+ return milliseconds > 0 ? milliseconds : null;
1103
+ }
1104
+ function supplyContextOf(windowSetting, startedAt) {
1105
+ const windowMs = parseWindowMs(windowSetting);
1106
+ const started = startedAt === null || startedAt === undefined ? null : Date.parse(startedAt);
1107
+ if (windowMs === null || started === null || !Number.isFinite(started))
1108
+ return null;
1109
+ return {
1110
+ windowMs,
1111
+ fromMs: started - windowMs,
1112
+ untilMs: started,
1113
+ fromLabel: formatTimestamp(new Date(started - windowMs).toISOString()),
1114
+ untilLabel: formatTimestamp(new Date(started).toISOString())
1115
+ };
1116
+ }
839
1117
 
840
1118
  // ../board/src/daily-voice/index.tsx
841
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1119
+ import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
842
1120
  var WORKFLOW = "kk-daily-ai-voice2";
843
1121
  var MODULE_PATH = "/board/modules/kk-daily-ai-voice2";
844
1122
  var TAB_LABELS = {
@@ -850,6 +1128,9 @@ var TAB_LABELS = {
850
1128
  function messageOf(error) {
851
1129
  return error instanceof Error ? error.message : String(error);
852
1130
  }
1131
+ function isOfflineError(message) {
1132
+ return message.includes("Failed to fetch") || message.includes("NetworkError") || message.includes("ECONNREFUSED");
1133
+ }
853
1134
  function usePolling(loader, intervalMs, refresh) {
854
1135
  const loaderRef = useRef(loader);
855
1136
  loaderRef.current = loader;
@@ -874,20 +1155,30 @@ function usePolling(loader, intervalMs, refresh) {
874
1155
  function DailyVoiceModule() {
875
1156
  const [link] = useState(() => parseDeepLink(window.location.search));
876
1157
  const [reader] = useState(() => readerOriginFrom(window.location.search));
1158
+ const [aggregateReader] = useState(() => readerOriginFrom(window.location.search, DEFAULT_READER_ORIGIN));
877
1159
  const readerOrigin = reader.origin;
878
- const client = useMemo(() => createBoardClient({ readerOrigin, boardOrigin: window.location.origin }), [readerOrigin]);
1160
+ const client = useMemo(() => createBoardClient({
1161
+ readerOrigin,
1162
+ aggregateReaderOrigin: aggregateReader.origin,
1163
+ boardOrigin: window.location.origin
1164
+ }), [readerOrigin, aggregateReader.origin]);
879
1165
  const [rows, setRows] = useState([]);
880
1166
  const [cursor, setCursor] = useState(null);
881
1167
  const [selection, setSelection] = useState({ runId: link.runId ?? null, tab: link.tab ?? "summary" });
882
1168
  const [diagnostics, setDiagnostics] = useState(() => [
883
1169
  ...link.diagnostics,
884
- ...reader.diagnostic === null ? [] : [reader.diagnostic]
1170
+ ...reader.diagnostic === null ? [] : [reader.diagnostic],
1171
+ ...aggregateReader.diagnostic === null ? [] : [aggregateReader.diagnostic]
885
1172
  ]);
886
1173
  const [historyRefresh, setHistoryRefresh] = useState(() => initialRefresh(HISTORY_POLL_MS));
887
1174
  const [readerRefresh, setReaderRefresh] = useState(() => initialRefresh(ACTIVE_POLL_MS));
888
1175
  const [results, setResults] = useState(null);
889
1176
  const [config, setConfig] = useState(null);
890
1177
  const [filterQuery, setFilterQuery] = useState("");
1178
+ const [supply, setSupply] = useState(null);
1179
+ const [storeOffline, setStoreOffline] = useState(false);
1180
+ const supplyRequests = useRef(requestSequence());
1181
+ const supplyKeyRef = useRef(null);
891
1182
  const historyBusy = useRef(false);
892
1183
  const readerBusy = useRef(false);
893
1184
  const selectionRef = useRef(selection);
@@ -920,15 +1211,24 @@ function DailyVoiceModule() {
920
1211
  return;
921
1212
  readerBusy.current = true;
922
1213
  setReaderRefresh((state) => tick(state));
1214
+ let stale = false;
923
1215
  try {
924
1216
  const [nextResults, nextConfig] = await Promise.all([
925
1217
  client.results(WORKFLOW, runId),
926
1218
  client.config(WORKFLOW, runId)
927
1219
  ]);
1220
+ if (selectionRef.current.runId !== runId) {
1221
+ stale = true;
1222
+ return;
1223
+ }
928
1224
  setResults(nextResults);
929
1225
  setConfig(nextConfig);
930
1226
  setReaderRefresh(() => succeeded(Date.now(), ACTIVE_POLL_MS));
931
1227
  } catch (error) {
1228
+ if (selectionRef.current.runId !== runId) {
1229
+ stale = true;
1230
+ return;
1231
+ }
932
1232
  setDiagnostics((existing) => [
933
1233
  ...existing,
934
1234
  { code: "board.reader.unavailable", message: messageOf(error) }
@@ -936,6 +1236,8 @@ function DailyVoiceModule() {
936
1236
  setReaderRefresh((state) => failed(state, Date.now()));
937
1237
  } finally {
938
1238
  readerBusy.current = false;
1239
+ if (stale)
1240
+ loadReader();
939
1241
  }
940
1242
  }, [client]);
941
1243
  usePolling(loadHistory, HISTORY_POLL_MS, historyRefresh);
@@ -952,6 +1254,42 @@ function DailyVoiceModule() {
952
1254
  const now = Date.now();
953
1255
  const summary = results === null ? null : dailySummary(results);
954
1256
  const facts = results === null ? [] : dailySummaryFacts(results, now);
1257
+ const funnel = funnelView(results?.summary.funnel);
1258
+ const context = supplyContextOf(config?.settings.window ?? null, summary?.startedAt ?? null);
1259
+ useEffect(() => {
1260
+ const bounds = tab === "summary" && context !== null ? context : null;
1261
+ const key = bounds === null ? null : `${selection.runId}:${bounds.fromMs}:${bounds.untilMs}`;
1262
+ if (key === supplyKeyRef.current)
1263
+ return;
1264
+ supplyKeyRef.current = key;
1265
+ if (bounds === null) {
1266
+ setSupply(null);
1267
+ return;
1268
+ }
1269
+ const token = supplyRequests.current.begin();
1270
+ let cancelled = false;
1271
+ (async () => {
1272
+ try {
1273
+ const page = await client.storeChannels({ fromMs: bounds.fromMs, untilMs: bounds.untilMs, page: 1 });
1274
+ if (cancelled || !supplyRequests.current.isCurrent(token))
1275
+ return;
1276
+ setSupply(page);
1277
+ setStoreOffline(false);
1278
+ } catch (error) {
1279
+ if (cancelled || !supplyRequests.current.isCurrent(token))
1280
+ return;
1281
+ setSupply(null);
1282
+ const message = messageOf(error);
1283
+ if (isOfflineError(message))
1284
+ setStoreOffline(true);
1285
+ else
1286
+ setDiagnostics((existing) => [...existing, { code: "board.store.channels-unavailable", message }]);
1287
+ }
1288
+ })();
1289
+ return () => {
1290
+ cancelled = true;
1291
+ };
1292
+ }, [client, tab, context, selection.runId]);
955
1293
  const artifacts = results === null ? [] : artifactViews(results, readerOrigin);
956
1294
  const selectedRow = rows.find((row) => row.id === selection.runId);
957
1295
  const filteredRows = useMemo(() => {
@@ -960,31 +1298,31 @@ function DailyVoiceModule() {
960
1298
  return rows;
961
1299
  return rows.filter((r) => r.id.toLowerCase().includes(q) || r.status.toLowerCase().includes(q));
962
1300
  }, [rows, filterQuery]);
963
- return /* @__PURE__ */ jsxs("div", {
1301
+ return /* @__PURE__ */ jsxs2("div", {
964
1302
  className: "kk-board kk-board-daily-voice",
965
1303
  children: [
966
- /* @__PURE__ */ jsxs("header", {
1304
+ /* @__PURE__ */ jsxs2("header", {
967
1305
  className: "kk-board-header",
968
1306
  children: [
969
- /* @__PURE__ */ jsxs("div", {
1307
+ /* @__PURE__ */ jsxs2("div", {
970
1308
  className: "kk-board-header-left",
971
1309
  children: [
972
- /* @__PURE__ */ jsxs("div", {
1310
+ /* @__PURE__ */ jsxs2("div", {
973
1311
  className: "kk-board-header-title-row",
974
1312
  children: [
975
- /* @__PURE__ */ jsx("h1", {
1313
+ /* @__PURE__ */ jsx2("h1", {
976
1314
  children: "Daily AI Voice"
977
1315
  }),
978
- selectedRow ? /* @__PURE__ */ jsx("span", {
1316
+ selectedRow ? /* @__PURE__ */ jsx2("span", {
979
1317
  className: `kk-badge ${selectedRow.status === "done" ? "kk-badge-success" : selectedRow.status === "failed" ? "kk-badge-failed" : "kk-badge-running"}`,
980
1318
  children: selectedRow.status
981
1319
  }) : null
982
1320
  ]
983
1321
  }),
984
- /* @__PURE__ */ jsxs("p", {
1322
+ /* @__PURE__ */ jsxs2("p", {
985
1323
  children: [
986
1324
  "Run ",
987
- /* @__PURE__ */ jsx("code", {
1325
+ /* @__PURE__ */ jsx2("code", {
988
1326
  children: selection.runId ?? "none selected"
989
1327
  }),
990
1328
  " · ",
@@ -994,13 +1332,13 @@ function DailyVoiceModule() {
994
1332
  })
995
1333
  ]
996
1334
  }),
997
- /* @__PURE__ */ jsxs("div", {
1335
+ /* @__PURE__ */ jsxs2("div", {
998
1336
  className: "kk-board-header-right",
999
1337
  children: [
1000
- /* @__PURE__ */ jsxs("div", {
1338
+ /* @__PURE__ */ jsxs2("div", {
1001
1339
  className: "kk-board-run-filter-group",
1002
1340
  children: [
1003
- /* @__PURE__ */ jsx("input", {
1341
+ /* @__PURE__ */ jsx2("input", {
1004
1342
  type: "text",
1005
1343
  className: "kk-board-run-filter-input",
1006
1344
  placeholder: "Filter runs...",
@@ -1008,19 +1346,19 @@ function DailyVoiceModule() {
1008
1346
  onChange: (e) => setFilterQuery(e.target.value),
1009
1347
  "aria-label": "Filter runs"
1010
1348
  }),
1011
- /* @__PURE__ */ jsxs("label", {
1349
+ /* @__PURE__ */ jsxs2("label", {
1012
1350
  className: "kk-board-run-selector",
1013
1351
  children: [
1014
1352
  "Run",
1015
- /* @__PURE__ */ jsxs("select", {
1353
+ /* @__PURE__ */ jsxs2("select", {
1016
1354
  value: selection.runId ?? "",
1017
1355
  onChange: (event) => setSelection((state) => ({ ...state, runId: event.target.value || null })),
1018
1356
  children: [
1019
- /* @__PURE__ */ jsx("option", {
1357
+ /* @__PURE__ */ jsx2("option", {
1020
1358
  value: "",
1021
1359
  children: rows.length === 0 ? "No runs recorded" : filteredRows.length === 0 ? "No matching runs" : "Select a run"
1022
1360
  }),
1023
- filteredRows.map((row) => /* @__PURE__ */ jsxs("option", {
1361
+ filteredRows.map((row) => /* @__PURE__ */ jsxs2("option", {
1024
1362
  value: row.id,
1025
1363
  children: [
1026
1364
  row.id,
@@ -1034,13 +1372,13 @@ function DailyVoiceModule() {
1034
1372
  })
1035
1373
  ]
1036
1374
  }),
1037
- /* @__PURE__ */ jsx("button", {
1375
+ /* @__PURE__ */ jsx2("button", {
1038
1376
  type: "button",
1039
1377
  onClick: () => void loadReader(),
1040
1378
  title: "Refresh run data",
1041
1379
  children: "↻ Refresh"
1042
1380
  }),
1043
- selection.runId === null ? null : /* @__PURE__ */ jsx("a", {
1381
+ selection.runId === null ? null : /* @__PURE__ */ jsx2("a", {
1044
1382
  href: selectionPath(selection, window.location.search, MODULE_PATH),
1045
1383
  className: "kk-btn-action",
1046
1384
  title: "Permanent link",
@@ -1050,53 +1388,58 @@ function DailyVoiceModule() {
1050
1388
  })
1051
1389
  ]
1052
1390
  }),
1053
- /* @__PURE__ */ jsx("nav", {
1391
+ /* @__PURE__ */ jsx2("nav", {
1054
1392
  className: "kk-board-tabs",
1055
1393
  "aria-label": "Daily AI Voice tabs",
1056
- children: BOARD_TABS.map((candidate) => /* @__PURE__ */ jsx("button", {
1394
+ children: BOARD_TABS.map((candidate) => /* @__PURE__ */ jsx2("button", {
1057
1395
  type: "button",
1058
1396
  "aria-current": candidate === tab ? "page" : undefined,
1059
1397
  onClick: () => setSelection((state) => ({ ...state, tab: candidate })),
1060
1398
  children: TAB_LABELS[candidate]
1061
1399
  }, candidate))
1062
1400
  }),
1063
- rows.length === 0 ? /* @__PURE__ */ jsxs("p", {
1401
+ rows.length === 0 ? /* @__PURE__ */ jsxs2("p", {
1064
1402
  className: "kk-board-empty",
1065
1403
  children: [
1066
1404
  "No runs recorded for kk-daily-ai-voice2. Configure the workflow in",
1067
1405
  " ",
1068
- /* @__PURE__ */ jsx("a", {
1406
+ /* @__PURE__ */ jsx2("a", {
1069
1407
  href: "/board/settings/workflows",
1070
1408
  children: "Settings → Workflows"
1071
1409
  }),
1072
1410
  "."
1073
1411
  ]
1074
1412
  }) : null,
1075
- tab === "summary" ? /* @__PURE__ */ jsx(SummaryPanel, {
1413
+ tab === "summary" ? /* @__PURE__ */ jsx2(SummaryPanel, {
1076
1414
  facts,
1077
- results
1415
+ results,
1416
+ funnel,
1417
+ artifacts,
1418
+ context,
1419
+ supply,
1420
+ storeOffline
1078
1421
  }) : null,
1079
- tab === "results" ? /* @__PURE__ */ jsx(ResultsPanel, {
1422
+ tab === "results" ? /* @__PURE__ */ jsx2(ResultsPanel, {
1080
1423
  artifacts,
1081
1424
  channels: summary?.channels ?? []
1082
1425
  }) : null,
1083
- tab === "progress" ? /* @__PURE__ */ jsx(ProgressPanel, {
1426
+ tab === "progress" ? /* @__PURE__ */ jsx2(ProgressPanel, {
1084
1427
  progress: summary?.progress ?? null
1085
1428
  }) : null,
1086
- tab === "config" ? /* @__PURE__ */ jsx(ConfigPanel, {
1429
+ tab === "config" ? /* @__PURE__ */ jsx2(ConfigPanel, {
1087
1430
  config
1088
1431
  }) : null,
1089
- dedupeDiagnostics([...diagnostics, ...results?.diagnostics ?? []]).map((diagnostic) => /* @__PURE__ */ jsx("p", {
1432
+ dedupeDiagnostics([...diagnostics, ...results?.diagnostics ?? []]).map((diagnostic) => /* @__PURE__ */ jsx2("p", {
1090
1433
  role: "status",
1091
1434
  className: "kk-board-diagnostic",
1092
1435
  children: diagnosticLine(diagnostic)
1093
1436
  }, diagnostic.code)),
1094
- cursor === null ? null : /* @__PURE__ */ jsx("button", {
1437
+ cursor === null ? null : /* @__PURE__ */ jsx2("button", {
1095
1438
  type: "button",
1096
1439
  onClick: () => void loadHistory(),
1097
1440
  children: "Load more runs"
1098
1441
  }),
1099
- /* @__PURE__ */ jsxs("p", {
1442
+ /* @__PURE__ */ jsxs2("p", {
1100
1443
  className: "kk-board-footnote",
1101
1444
  children: [
1102
1445
  "Run history every ",
@@ -1109,44 +1452,52 @@ function DailyVoiceModule() {
1109
1452
  ]
1110
1453
  });
1111
1454
  }
1112
- function SummaryPanel({ facts, results }) {
1455
+ function SummaryPanel({
1456
+ facts,
1457
+ results,
1458
+ funnel,
1459
+ artifacts,
1460
+ context,
1461
+ supply,
1462
+ storeOffline
1463
+ }) {
1113
1464
  if (results === null)
1114
- return /* @__PURE__ */ jsx("p", {
1465
+ return /* @__PURE__ */ jsx2("p", {
1115
1466
  children: "No captured results for the selected run."
1116
1467
  });
1117
1468
  const summary = dailySummary(results);
1118
1469
  const liveChannels = summary.channels.filter((c) => c.state === "live").length;
1119
- return /* @__PURE__ */ jsxs("div", {
1470
+ return /* @__PURE__ */ jsxs2("div", {
1120
1471
  style: { display: "flex", flexDirection: "column", gap: "0.875rem" },
1121
1472
  children: [
1122
- /* @__PURE__ */ jsxs("div", {
1473
+ /* @__PURE__ */ jsxs2("div", {
1123
1474
  className: "kk-stat-grid",
1124
1475
  children: [
1125
- /* @__PURE__ */ jsxs("div", {
1476
+ /* @__PURE__ */ jsxs2("div", {
1126
1477
  className: "kk-stat-card",
1127
1478
  children: [
1128
- /* @__PURE__ */ jsx("span", {
1479
+ /* @__PURE__ */ jsx2("span", {
1129
1480
  className: "kk-stat-label",
1130
1481
  children: "Episode Outcome"
1131
1482
  }),
1132
- /* @__PURE__ */ jsx("span", {
1483
+ /* @__PURE__ */ jsx2("span", {
1133
1484
  className: "kk-stat-value",
1134
1485
  children: summary.outcome ?? "—"
1135
1486
  }),
1136
- /* @__PURE__ */ jsx("span", {
1487
+ /* @__PURE__ */ jsx2("span", {
1137
1488
  className: "kk-stat-sub",
1138
1489
  children: summary.articleTitle ? summary.articleTitle.slice(0, 32) : "Daily podcast"
1139
1490
  })
1140
1491
  ]
1141
1492
  }),
1142
- /* @__PURE__ */ jsxs("div", {
1493
+ /* @__PURE__ */ jsxs2("div", {
1143
1494
  className: "kk-stat-card",
1144
1495
  children: [
1145
- /* @__PURE__ */ jsx("span", {
1496
+ /* @__PURE__ */ jsx2("span", {
1146
1497
  className: "kk-stat-label",
1147
1498
  children: "Distribution"
1148
1499
  }),
1149
- /* @__PURE__ */ jsxs("span", {
1500
+ /* @__PURE__ */ jsxs2("span", {
1150
1501
  className: "kk-stat-value",
1151
1502
  children: [
1152
1503
  liveChannels,
@@ -1154,41 +1505,41 @@ function SummaryPanel({ facts, results }) {
1154
1505
  summary.channels.length
1155
1506
  ]
1156
1507
  }),
1157
- /* @__PURE__ */ jsx("span", {
1508
+ /* @__PURE__ */ jsx2("span", {
1158
1509
  className: "kk-stat-sub",
1159
1510
  children: "Channels published live"
1160
1511
  })
1161
1512
  ]
1162
1513
  }),
1163
- /* @__PURE__ */ jsxs("div", {
1514
+ /* @__PURE__ */ jsxs2("div", {
1164
1515
  className: "kk-stat-card",
1165
1516
  children: [
1166
- /* @__PURE__ */ jsx("span", {
1517
+ /* @__PURE__ */ jsx2("span", {
1167
1518
  className: "kk-stat-label",
1168
1519
  children: "Quality Score"
1169
1520
  }),
1170
- /* @__PURE__ */ jsx("span", {
1521
+ /* @__PURE__ */ jsx2("span", {
1171
1522
  className: "kk-stat-value",
1172
1523
  children: summary.quality?.overallScore ?? "—"
1173
1524
  }),
1174
- /* @__PURE__ */ jsx("span", {
1525
+ /* @__PURE__ */ jsx2("span", {
1175
1526
  className: "kk-stat-sub",
1176
1527
  children: summary.quality?.passed ? "Passed QC standard" : "Under evaluation"
1177
1528
  })
1178
1529
  ]
1179
1530
  }),
1180
- /* @__PURE__ */ jsxs("div", {
1531
+ /* @__PURE__ */ jsxs2("div", {
1181
1532
  className: "kk-stat-card",
1182
1533
  children: [
1183
- /* @__PURE__ */ jsx("span", {
1534
+ /* @__PURE__ */ jsx2("span", {
1184
1535
  className: "kk-stat-label",
1185
1536
  children: "Safety Verdict"
1186
1537
  }),
1187
- /* @__PURE__ */ jsx("span", {
1538
+ /* @__PURE__ */ jsx2("span", {
1188
1539
  className: "kk-stat-value",
1189
1540
  children: summary.safety?.verdict ?? "—"
1190
1541
  }),
1191
- /* @__PURE__ */ jsxs("span", {
1542
+ /* @__PURE__ */ jsxs2("span", {
1192
1543
  className: "kk-stat-sub",
1193
1544
  children: [
1194
1545
  "Confidence: ",
@@ -1199,18 +1550,18 @@ function SummaryPanel({ facts, results }) {
1199
1550
  })
1200
1551
  ]
1201
1552
  }),
1202
- /* @__PURE__ */ jsx("dl", {
1553
+ /* @__PURE__ */ jsx2("dl", {
1203
1554
  className: "kk-board-facts",
1204
- children: facts.map((fact) => /* @__PURE__ */ jsxs("div", {
1555
+ children: facts.map((fact) => /* @__PURE__ */ jsxs2("div", {
1205
1556
  children: [
1206
- /* @__PURE__ */ jsx("dt", {
1557
+ /* @__PURE__ */ jsx2("dt", {
1207
1558
  children: fact.label
1208
1559
  }),
1209
- /* @__PURE__ */ jsxs("dd", {
1560
+ /* @__PURE__ */ jsxs2("dd", {
1210
1561
  children: [
1211
1562
  fact.value,
1212
1563
  " ",
1213
- /* @__PURE__ */ jsx("span", {
1564
+ /* @__PURE__ */ jsx2("span", {
1214
1565
  className: "kk-board-state",
1215
1566
  children: fact.availability
1216
1567
  })
@@ -1218,25 +1569,308 @@ function SummaryPanel({ facts, results }) {
1218
1569
  })
1219
1570
  ]
1220
1571
  }, fact.label))
1572
+ }),
1573
+ /* @__PURE__ */ jsx2(FunnelPanel, {
1574
+ funnel,
1575
+ artifacts
1576
+ }),
1577
+ /* @__PURE__ */ jsx2(SupplyPanel, {
1578
+ context,
1579
+ supply,
1580
+ storeOffline
1581
+ })
1582
+ ]
1583
+ });
1584
+ }
1585
+ function funnelCount(value) {
1586
+ return value === null ? "unavailable" : String(value);
1587
+ }
1588
+ function funnelMinutes(value) {
1589
+ return value === null ? "unavailable" : `${value.toFixed(1)} min`;
1590
+ }
1591
+ function EvidenceChip({
1592
+ field,
1593
+ funnel,
1594
+ artifacts
1595
+ }) {
1596
+ const evidenceId = funnel.evidence[field];
1597
+ if (evidenceId === undefined)
1598
+ return /* @__PURE__ */ jsx2("span", {
1599
+ className: "kk-board-state",
1600
+ children: "no evidence"
1601
+ });
1602
+ if (evidenceId === "captured-settings")
1603
+ return /* @__PURE__ */ jsx2("code", {
1604
+ children: "captured-settings"
1605
+ });
1606
+ const artifact = artifacts.find((candidate) => candidate.id === evidenceId);
1607
+ return artifact === undefined || artifact.href === null ? /* @__PURE__ */ jsxs2("code", {
1608
+ children: [
1609
+ evidenceId,
1610
+ " (",
1611
+ artifact?.state ?? "missing",
1612
+ ")"
1613
+ ]
1614
+ }) : /* @__PURE__ */ jsx2("a", {
1615
+ href: artifact.href,
1616
+ children: /* @__PURE__ */ jsx2("code", {
1617
+ children: evidenceId
1618
+ })
1619
+ });
1620
+ }
1621
+ function FunnelPanel({ funnel, artifacts }) {
1622
+ const rows = [
1623
+ { label: "1 · Episode pool", value: funnelCount(funnel.poolCount), field: "poolCount" },
1624
+ { label: "2 · Annotated", value: funnelCount(funnel.annotatedCount), field: "annotatedCount" },
1625
+ { label: "3 · Topic filter kept", value: funnelCount(funnel.topicKeptCount), field: "topicKeptCount" },
1626
+ { label: "4 · QC rejected", value: funnelCount(funnel.qcRejectedCount), field: "qcRejectedCount" },
1627
+ {
1628
+ label: "5 · Post-dedup survivors",
1629
+ value: funnel.dedupKeptCount === null && (funnel.gate === "episode-duration" || funnel.gate === "script" || funnel.gate === "qc-floor") ? "unknown — cleared by the refusal" : funnel.dedupKeptCount === null && funnel.plannedCount !== null ? "unknown — plan recorded" : funnelCount(funnel.dedupKeptCount),
1630
+ field: "dedupKeptCount"
1631
+ },
1632
+ { label: "6 · Episode plan", value: funnelCount(funnel.plannedCount), field: "plannedCount" }
1633
+ ];
1634
+ return /* @__PURE__ */ jsxs2("section", {
1635
+ className: "kk-card kk-daily-funnel",
1636
+ children: [
1637
+ /* @__PURE__ */ jsxs2("div", {
1638
+ className: "kk-card-header",
1639
+ children: [
1640
+ /* @__PURE__ */ jsx2("h2", {
1641
+ children: "Episode funnel"
1642
+ }),
1643
+ /* @__PURE__ */ jsx2("span", {
1644
+ className: "kk-stat-sub",
1645
+ children: "artifact-backed by this run"
1646
+ })
1647
+ ]
1648
+ }),
1649
+ /* @__PURE__ */ jsxs2("dl", {
1650
+ children: [
1651
+ rows.map((row) => /* @__PURE__ */ jsxs2("div", {
1652
+ children: [
1653
+ /* @__PURE__ */ jsx2("dt", {
1654
+ children: row.label
1655
+ }),
1656
+ /* @__PURE__ */ jsxs2("dd", {
1657
+ children: [
1658
+ row.value,
1659
+ " · evidence",
1660
+ " ",
1661
+ /* @__PURE__ */ jsx2(EvidenceChip, {
1662
+ field: row.field,
1663
+ funnel,
1664
+ artifacts
1665
+ })
1666
+ ]
1667
+ })
1668
+ ]
1669
+ }, row.label)),
1670
+ /* @__PURE__ */ jsxs2("div", {
1671
+ children: [
1672
+ /* @__PURE__ */ jsx2("dt", {
1673
+ children: "Duration ladder"
1674
+ }),
1675
+ /* @__PURE__ */ jsxs2("dd", {
1676
+ children: [
1677
+ "measured ",
1678
+ funnelMinutes(funnel.measuredMinutes),
1679
+ " · estimated",
1680
+ " ",
1681
+ funnelMinutes(funnel.estimatedMinutes),
1682
+ " · floor ",
1683
+ funnelMinutes(funnel.minMinutes),
1684
+ " · ceiling",
1685
+ " ",
1686
+ funnelMinutes(funnel.maxMinutes)
1687
+ ]
1688
+ })
1689
+ ]
1690
+ }, "minutes"),
1691
+ /* @__PURE__ */ jsxs2("div", {
1692
+ children: [
1693
+ /* @__PURE__ */ jsx2("dt", {
1694
+ children: "No-content gate"
1695
+ }),
1696
+ /* @__PURE__ */ jsxs2("dd", {
1697
+ children: [
1698
+ funnel.gateName,
1699
+ " · evidence ",
1700
+ /* @__PURE__ */ jsx2(EvidenceChip, {
1701
+ field: "gate",
1702
+ funnel,
1703
+ artifacts
1704
+ })
1705
+ ]
1706
+ })
1707
+ ]
1708
+ }, "gate")
1709
+ ]
1710
+ }),
1711
+ funnel.exclusions.length === 0 ? null : /* @__PURE__ */ jsxs2("div", {
1712
+ children: [
1713
+ /* @__PURE__ */ jsx2("h3", {
1714
+ children: "Topic-filter exclusions"
1715
+ }),
1716
+ /* @__PURE__ */ jsx2("ul", {
1717
+ className: "kk-board-channels",
1718
+ children: funnel.exclusions.map((exclusion) => /* @__PURE__ */ jsxs2("li", {
1719
+ children: [
1720
+ /* @__PURE__ */ jsx2("strong", {
1721
+ children: exclusion.rule
1722
+ }),
1723
+ exclusion.matched === "" ? null : ` — matched “${exclusion.matched}”`
1724
+ ]
1725
+ }, `${exclusion.rule}::${exclusion.matched}`))
1726
+ })
1727
+ ]
1728
+ }),
1729
+ funnel.diagnostics.length === 0 ? null : /* @__PURE__ */ jsx2("ul", {
1730
+ children: funnel.diagnostics.map((diagnostic) => /* @__PURE__ */ jsxs2("li", {
1731
+ role: "status",
1732
+ className: "kk-board-diagnostic",
1733
+ children: [
1734
+ diagnostic.code,
1735
+ ": ",
1736
+ diagnostic.message
1737
+ ]
1738
+ }, diagnostic.code))
1739
+ })
1740
+ ]
1741
+ });
1742
+ }
1743
+ function SupplyPanel({
1744
+ context,
1745
+ supply,
1746
+ storeOffline
1747
+ }) {
1748
+ return /* @__PURE__ */ jsxs2("section", {
1749
+ className: "kk-card kk-daily-supply",
1750
+ children: [
1751
+ /* @__PURE__ */ jsxs2("div", {
1752
+ className: "kk-card-header",
1753
+ children: [
1754
+ /* @__PURE__ */ jsx2("h2", {
1755
+ children: "Supply context (configured)"
1756
+ }),
1757
+ /* @__PURE__ */ jsx2("span", {
1758
+ className: "kk-stat-sub",
1759
+ children: "read from the kk-collect store"
1760
+ })
1761
+ ]
1762
+ }),
1763
+ context === null ? /* @__PURE__ */ jsx2("p", {
1764
+ children: "The configured supply interval is unknown — no captured window setting or run start was recorded, so no store query was issued."
1765
+ }) : /* @__PURE__ */ jsxs2("p", {
1766
+ children: [
1767
+ "Window ",
1768
+ /* @__PURE__ */ jsx2("code", {
1769
+ children: context.fromLabel
1770
+ }),
1771
+ " → ",
1772
+ /* @__PURE__ */ jsx2("code", {
1773
+ children: context.untilLabel
1774
+ }),
1775
+ supply === null ? "" : ` · time zone ${supply.timeZone}`,
1776
+ " — configured context; the effective extraction window (ladder bounds) is unknown, not persisted."
1777
+ ]
1778
+ }),
1779
+ storeOffline ? /* @__PURE__ */ jsx2("p", {
1780
+ children: "The store reader is offline; supply counts are unavailable."
1781
+ }) : null,
1782
+ supply !== null && supply.availability === "unavailable" ? /* @__PURE__ */ jsx2("p", {
1783
+ children: "The store could not be read for this window; no counts are shown."
1784
+ }) : null,
1785
+ supply !== null && supply.availability === "unavailable" && supply.diagnostics.length > 0 ? /* @__PURE__ */ jsx2("ul", {
1786
+ children: supply.diagnostics.map((diagnostic) => /* @__PURE__ */ jsxs2("li", {
1787
+ className: "kk-board-diagnostic",
1788
+ children: [
1789
+ diagnostic.code,
1790
+ ": ",
1791
+ diagnostic.message
1792
+ ]
1793
+ }, diagnostic.code))
1794
+ }) : null,
1795
+ supply !== null && supply.availability === "available" ? /* @__PURE__ */ jsxs2("div", {
1796
+ className: "kk-summary-grids",
1797
+ children: [
1798
+ /* @__PURE__ */ jsxs2("div", {
1799
+ className: "kk-card",
1800
+ children: [
1801
+ /* @__PURE__ */ jsxs2("div", {
1802
+ className: "kk-card-header",
1803
+ children: [
1804
+ /* @__PURE__ */ jsx2("h2", {
1805
+ children: "New items by day and source"
1806
+ }),
1807
+ /* @__PURE__ */ jsxs2("span", {
1808
+ className: "kk-stat-sub",
1809
+ children: [
1810
+ supply.sources.length,
1811
+ " sources"
1812
+ ]
1813
+ })
1814
+ ]
1815
+ }),
1816
+ /* @__PURE__ */ jsx2("div", {
1817
+ className: "kk-table-container",
1818
+ children: /* @__PURE__ */ jsx2(ChannelYieldTable, {
1819
+ data: supply
1820
+ })
1821
+ })
1822
+ ]
1823
+ }),
1824
+ /* @__PURE__ */ jsxs2("div", {
1825
+ className: "kk-card",
1826
+ children: [
1827
+ /* @__PURE__ */ jsxs2("div", {
1828
+ className: "kk-card-header",
1829
+ children: [
1830
+ /* @__PURE__ */ jsx2("h2", {
1831
+ children: "Collection ticks and plugin contributions"
1832
+ }),
1833
+ /* @__PURE__ */ jsxs2("span", {
1834
+ className: "kk-stat-sub",
1835
+ children: [
1836
+ supply.plugins.length,
1837
+ " plugins"
1838
+ ]
1839
+ })
1840
+ ]
1841
+ }),
1842
+ /* @__PURE__ */ jsx2("div", {
1843
+ className: "kk-table-container",
1844
+ children: /* @__PURE__ */ jsx2(PluginRunTable, {
1845
+ data: supply
1846
+ })
1847
+ })
1848
+ ]
1849
+ })
1850
+ ]
1851
+ }) : null,
1852
+ /* @__PURE__ */ jsx2("p", {
1853
+ className: "kk-board-footnote",
1854
+ children: "A source absent from this window recorded 0 contributions; its health is unknown. The episode funnel above stays artifact-backed — supply here is context, not the run’s consumption record."
1221
1855
  })
1222
1856
  ]
1223
1857
  });
1224
1858
  }
1225
1859
  function ChannelList({ channels }) {
1226
1860
  if (channels.length === 0)
1227
- return /* @__PURE__ */ jsx("p", {
1861
+ return /* @__PURE__ */ jsx2("p", {
1228
1862
  children: "No channel outcomes recorded for the selected run."
1229
1863
  });
1230
- return /* @__PURE__ */ jsx("ul", {
1864
+ return /* @__PURE__ */ jsx2("ul", {
1231
1865
  className: "kk-board-channels",
1232
- children: channels.map((channel) => /* @__PURE__ */ jsxs("li", {
1866
+ children: channels.map((channel) => /* @__PURE__ */ jsxs2("li", {
1233
1867
  children: [
1234
- /* @__PURE__ */ jsx("strong", {
1868
+ /* @__PURE__ */ jsx2("strong", {
1235
1869
  children: channel.label
1236
1870
  }),
1237
1871
  " — ",
1238
1872
  channel.state,
1239
- channel.detail === null ? null : /* @__PURE__ */ jsxs("span", {
1873
+ channel.detail === null ? null : /* @__PURE__ */ jsxs2("span", {
1240
1874
  className: "kk-board-state",
1241
1875
  children: [
1242
1876
  " (",
@@ -1244,10 +1878,10 @@ function ChannelList({ channels }) {
1244
1878
  ")"
1245
1879
  ]
1246
1880
  }),
1247
- channel.url === null ? null : /* @__PURE__ */ jsxs(Fragment, {
1881
+ channel.url === null ? null : /* @__PURE__ */ jsxs2(Fragment, {
1248
1882
  children: [
1249
1883
  " ",
1250
- /* @__PURE__ */ jsx("a", {
1884
+ /* @__PURE__ */ jsx2("a", {
1251
1885
  href: channel.url,
1252
1886
  rel: "noreferrer",
1253
1887
  children: "open"
@@ -1263,30 +1897,30 @@ function ResultsPanel({
1263
1897
  channels
1264
1898
  }) {
1265
1899
  if (artifacts.length === 0)
1266
- return /* @__PURE__ */ jsx("p", {
1900
+ return /* @__PURE__ */ jsx2("p", {
1267
1901
  children: "No artifact evidence for the selected run."
1268
1902
  });
1269
1903
  const media = artifacts.filter((artifact) => artifact.kind === "image" || artifact.kind === "audio");
1270
1904
  const files = artifacts.filter((artifact) => artifact.kind !== "image" && artifact.kind !== "audio");
1271
- return /* @__PURE__ */ jsxs("div", {
1905
+ return /* @__PURE__ */ jsxs2("div", {
1272
1906
  className: "kk-board-results",
1273
1907
  children: [
1274
- media.map((artifact) => artifact.href === null ? /* @__PURE__ */ jsxs("p", {
1908
+ media.map((artifact) => artifact.href === null ? /* @__PURE__ */ jsxs2("p", {
1275
1909
  children: [
1276
1910
  artifact.label,
1277
1911
  " — ",
1278
1912
  artifact.state
1279
1913
  ]
1280
- }, artifact.id) : artifact.kind === "audio" ? /* @__PURE__ */ jsxs("p", {
1914
+ }, artifact.id) : artifact.kind === "audio" ? /* @__PURE__ */ jsxs2("p", {
1281
1915
  children: [
1282
- /* @__PURE__ */ jsx("audio", {
1916
+ /* @__PURE__ */ jsx2("audio", {
1283
1917
  controls: true,
1284
1918
  src: artifact.href,
1285
- children: /* @__PURE__ */ jsx("track", {
1919
+ children: /* @__PURE__ */ jsx2("track", {
1286
1920
  kind: "captions"
1287
1921
  })
1288
1922
  }),
1289
- /* @__PURE__ */ jsxs("a", {
1923
+ /* @__PURE__ */ jsxs2("a", {
1290
1924
  href: artifact.href,
1291
1925
  download: true,
1292
1926
  children: [
@@ -1298,9 +1932,9 @@ function ResultsPanel({
1298
1932
  "— ",
1299
1933
  artifact.state
1300
1934
  ]
1301
- }, artifact.id) : /* @__PURE__ */ jsxs("p", {
1935
+ }, artifact.id) : /* @__PURE__ */ jsxs2("p", {
1302
1936
  children: [
1303
- /* @__PURE__ */ jsx("img", {
1937
+ /* @__PURE__ */ jsx2("img", {
1304
1938
  src: artifact.href,
1305
1939
  alt: artifact.label
1306
1940
  }),
@@ -1310,16 +1944,16 @@ function ResultsPanel({
1310
1944
  artifact.state
1311
1945
  ]
1312
1946
  }, artifact.id)),
1313
- /* @__PURE__ */ jsx("ul", {
1947
+ /* @__PURE__ */ jsx2("ul", {
1314
1948
  className: "kk-board-artifacts",
1315
- children: files.map((artifact) => /* @__PURE__ */ jsx("li", {
1316
- children: artifact.href === null ? /* @__PURE__ */ jsxs("span", {
1949
+ children: files.map((artifact) => /* @__PURE__ */ jsx2("li", {
1950
+ children: artifact.href === null ? /* @__PURE__ */ jsxs2("span", {
1317
1951
  children: [
1318
1952
  artifact.label,
1319
1953
  " — ",
1320
1954
  artifact.state
1321
1955
  ]
1322
- }) : /* @__PURE__ */ jsxs("a", {
1956
+ }) : /* @__PURE__ */ jsxs2("a", {
1323
1957
  href: artifact.href,
1324
1958
  children: [
1325
1959
  artifact.label,
@@ -1329,7 +1963,7 @@ function ResultsPanel({
1329
1963
  })
1330
1964
  }, artifact.id))
1331
1965
  }),
1332
- /* @__PURE__ */ jsx(ChannelList, {
1966
+ /* @__PURE__ */ jsx2(ChannelList, {
1333
1967
  channels
1334
1968
  })
1335
1969
  ]
@@ -1337,24 +1971,24 @@ function ResultsPanel({
1337
1971
  }
1338
1972
  function ProgressPanel({ progress }) {
1339
1973
  if (progress === null || progress.nodes.length === 0) {
1340
- return /* @__PURE__ */ jsx("p", {
1974
+ return /* @__PURE__ */ jsx2("p", {
1341
1975
  children: "Recorded progress is unavailable for the selected run."
1342
1976
  });
1343
1977
  }
1344
- return /* @__PURE__ */ jsxs("div", {
1978
+ return /* @__PURE__ */ jsxs2("div", {
1345
1979
  className: "kk-board-progress",
1346
1980
  children: [
1347
- /* @__PURE__ */ jsx("ul", {
1348
- children: progress.nodes.map((node) => /* @__PURE__ */ jsxs("li", {
1981
+ /* @__PURE__ */ jsx2("ul", {
1982
+ children: progress.nodes.map((node) => /* @__PURE__ */ jsxs2("li", {
1349
1983
  children: [
1350
- /* @__PURE__ */ jsx("strong", {
1984
+ /* @__PURE__ */ jsx2("strong", {
1351
1985
  children: node.id
1352
1986
  }),
1353
1987
  " — ",
1354
1988
  node.status,
1355
1989
  " · ",
1356
1990
  node.timing,
1357
- node.attempts > 1 ? /* @__PURE__ */ jsxs("span", {
1991
+ node.attempts > 1 ? /* @__PURE__ */ jsxs2("span", {
1358
1992
  className: "kk-board-state",
1359
1993
  children: [
1360
1994
  " ",
@@ -1365,30 +1999,30 @@ function ProgressPanel({ progress }) {
1365
1999
  ]
1366
2000
  }, node.id))
1367
2001
  }),
1368
- progress.edgesKnown && progress.edges !== null ? /* @__PURE__ */ jsx("ul", {
2002
+ progress.edgesKnown && progress.edges !== null ? /* @__PURE__ */ jsx2("ul", {
1369
2003
  className: "kk-board-edges",
1370
- children: progress.edges.map((edge) => /* @__PURE__ */ jsxs("li", {
2004
+ children: progress.edges.map((edge) => /* @__PURE__ */ jsxs2("li", {
1371
2005
  children: [
1372
2006
  edge.from,
1373
2007
  " → ",
1374
2008
  edge.to
1375
2009
  ]
1376
2010
  }, `${edge.from}->${edge.to}`))
1377
- }) : /* @__PURE__ */ jsxs("p", {
2011
+ }) : /* @__PURE__ */ jsxs2("p", {
1378
2012
  children: [
1379
2013
  "Dependency edges are unknown — no digest-verified topology was captured. See",
1380
2014
  " ",
1381
- /* @__PURE__ */ jsx("a", {
2015
+ /* @__PURE__ */ jsx2("a", {
1382
2016
  href: "/board/settings/workflows",
1383
2017
  children: "Settings → Workflows"
1384
2018
  }),
1385
2019
  " for the current graph."
1386
2020
  ]
1387
2021
  }),
1388
- progress.childRunId === null ? null : /* @__PURE__ */ jsxs("p", {
2022
+ progress.childRunId === null ? null : /* @__PURE__ */ jsxs2("p", {
1389
2023
  children: [
1390
2024
  "Nested safety run: ",
1391
- /* @__PURE__ */ jsx("code", {
2025
+ /* @__PURE__ */ jsx2("code", {
1392
2026
  children: progress.childRunId
1393
2027
  })
1394
2028
  ]
@@ -1398,44 +2032,44 @@ function ProgressPanel({ progress }) {
1398
2032
  }
1399
2033
  function ConfigPanel({ config }) {
1400
2034
  if (config === null)
1401
- return /* @__PURE__ */ jsx("p", {
2035
+ return /* @__PURE__ */ jsx2("p", {
1402
2036
  children: "No captured configuration for the selected run."
1403
2037
  });
1404
2038
  const entries = Object.entries(config.settings);
1405
- return /* @__PURE__ */ jsxs("div", {
2039
+ return /* @__PURE__ */ jsxs2("div", {
1406
2040
  className: "kk-board-config",
1407
2041
  children: [
1408
- /* @__PURE__ */ jsxs("p", {
2042
+ /* @__PURE__ */ jsxs2("p", {
1409
2043
  children: [
1410
2044
  "Captured ",
1411
2045
  formatTimestamp(config.capturedAt)
1412
2046
  ]
1413
2047
  }),
1414
- entries.length === 0 ? /* @__PURE__ */ jsx("p", {
2048
+ entries.length === 0 ? /* @__PURE__ */ jsx2("p", {
1415
2049
  children: "Recorded configuration unavailable for this run."
1416
2050
  }) : null,
1417
- /* @__PURE__ */ jsx("dl", {
1418
- children: entries.map(([key, value]) => /* @__PURE__ */ jsxs("div", {
2051
+ /* @__PURE__ */ jsx2("dl", {
2052
+ children: entries.map(([key, value]) => /* @__PURE__ */ jsxs2("div", {
1419
2053
  children: [
1420
- /* @__PURE__ */ jsx("dt", {
2054
+ /* @__PURE__ */ jsx2("dt", {
1421
2055
  children: key
1422
2056
  }),
1423
- /* @__PURE__ */ jsx("dd", {
2057
+ /* @__PURE__ */ jsx2("dd", {
1424
2058
  children: value
1425
2059
  })
1426
2060
  ]
1427
2061
  }, key))
1428
2062
  }),
1429
- config.unavailableKeys.length === 0 ? null : /* @__PURE__ */ jsxs("p", {
2063
+ config.unavailableKeys.length === 0 ? null : /* @__PURE__ */ jsxs2("p", {
1430
2064
  children: [
1431
2065
  "Uncaptured keys: ",
1432
2066
  config.unavailableKeys.join(", ")
1433
2067
  ]
1434
2068
  }),
1435
- /* @__PURE__ */ jsxs("p", {
2069
+ /* @__PURE__ */ jsxs2("p", {
1436
2070
  children: [
1437
2071
  "Current definitions and defaults live in ",
1438
- /* @__PURE__ */ jsx("a", {
2072
+ /* @__PURE__ */ jsx2("a", {
1439
2073
  href: "/board/settings/workflows",
1440
2074
  children: "Settings → Workflows"
1441
2075
  }),