@gobing-ai/knowledge-kit 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +114 -38
- package/package.json +1 -1
- package/plugins/generations/daily-article-gen/dist/index.js +45 -5
- package/plugins/generations/daily-article-gen/src/index.ts +59 -4
- package/plugins/generations/dailynews-gen/dist/index.js +46 -9
- package/plugins/generations/dailynews-gen/src/index.ts +36 -1
- package/plugins/generations/dailynews-gen/src/script-builder.ts +15 -12
- package/plugins/generations/dailynews-gen/src/script-plan.ts +21 -3
- package/plugins/generations/episode-plan-gen/dist/index.js +55 -4
- package/plugins/generations/episode-plan-gen/src/index.ts +80 -2
- package/plugins/generations/image-gen/assets/fonts/NotoSansSC-Bold.otf +0 -0
- package/plugins/generations/image-gen/assets/fonts/NotoSansSC-Regular.otf +0 -0
- package/plugins/generations/image-gen/assets/fonts/OFL.txt +92 -0
- package/plugins/generations/image-gen/assets/fonts/SOURCE.md +15 -0
- package/plugins/generations/image-gen/dist/index.js +700 -22
- package/plugins/generations/image-gen/package.json +1 -0
- package/plugins/generations/image-gen/presets/formats/daily-news-cover.yaml +2 -2
- package/plugins/generations/image-gen/presets/formats/xhs-card.yaml +19 -11
- package/plugins/generations/image-gen/presets/styles/xhs-knowledge.yaml +2 -1
- package/plugins/generations/image-gen/scripts/compose-e2e-artifact.ts +135 -0
- package/plugins/generations/image-gen/src/bytes.ts +116 -0
- package/plugins/generations/image-gen/src/compose/card.ts +294 -0
- package/plugins/generations/image-gen/src/compose/chromium.ts +147 -0
- package/plugins/generations/image-gen/src/compose/cmap.ts +177 -0
- package/plugins/generations/image-gen/src/index.ts +211 -13
- package/plugins/generations/image-gen/src/job.ts +12 -0
- package/plugins/generations/image-gen/src/presets.ts +53 -13
- package/plugins/generations/image-gen/src/providers/minimax.ts +12 -4
- package/plugins/generations/image-gen/src/providers/types.ts +12 -1
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-voice-style.zh.md +27 -14
- package/plugins/kk/scripts/itc-stages.ts +21 -3
- package/plugins/kk/scripts/kk-workflow-stages.ts +461 -10
- package/plugins/kk/scripts/topic-boundary.json +96 -0
- package/plugins/kk/scripts/validate-voicescript.ts +15 -0
- package/plugins/kk/skills/content-judge/SKILL.md +30 -0
- package/plugins/kk/skills/content-judge/references/workflow-integration.md +9 -2
- package/plugins/kk/skills/content-safety/SKILL.md +163 -0
- package/plugins/kk/skills/content-safety/references/platforms/apple-podcasts.md +36 -0
- package/plugins/kk/skills/content-safety/references/platforms/netease-music.md +31 -0
- package/plugins/kk/skills/content-safety/references/platforms/wechatmp.md +32 -0
- package/plugins/kk/skills/content-safety/references/platforms/xiaoyuzhou.md +35 -0
- package/plugins/kk/skills/publish/SKILL.md +1 -1
- package/plugins/kk/skills/xhs-images/SKILL.md +11 -7
- package/plugins/kk/skills/xhs-images/references/card-layouts.md +4 -4
- package/plugins/kk/workflows/judge-gated-publish-example.yaml +18 -10
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +139 -24
- package/plugins/kk/workflows/kk-itc.yaml +21 -8
- package/plugins/publishings/podcast-pub/dist/index.js +4 -0
- package/plugins/publishings/podcast-pub/src/index.ts +2 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +6 -0
- package/plugins/publishings/xhs-pub/dist/index.js +1 -1
- package/plugins/publishings/xhs-pub/src/automation-guard.ts +2 -2
- package/plugins/publishings/xhs-pub/src/index.ts +1 -1
- package/plugins/publishings/xhs-pub/src/profile-check.ts +2 -2
- package/plugins/sp/lib/env.ts +0 -46
- package/plugins/sp/lib/inline-run.generated.mjs +0 -1489
- package/plugins/sp/scripts/batch-preflight.mjs +0 -346
- package/plugins/sp/scripts/batch-preflight.ts +0 -459
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -615
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -846
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -223
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -367
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -132
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -169
- package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -171
- package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
- package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -285
- package/plugins/sp/scripts/feature-sync-bounded.ts +0 -478
- package/plugins/sp/scripts/feature-verification-steps.ts +0 -275
- package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -902
- package/plugins/sp/scripts/history-anatomy-cache.ts +0 -1028
- package/plugins/sp/scripts/idea-handoff.mjs +0 -22
- package/plugins/sp/scripts/idea-handoff.ts +0 -44
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -185
- package/plugins/sp/scripts/inline-run-setup.ts +0 -722
- package/plugins/sp/scripts/pr-reviewing.mjs +0 -769
- package/plugins/sp/scripts/pr-reviewing.ts +0 -925
- package/plugins/sp/scripts/quality-gate.mjs +0 -179
- package/plugins/sp/scripts/quality-gate.ts +0 -217
- package/plugins/sp/scripts/script-contract-check.ts +0 -319
- package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
- package/plugins/sp/scripts/surface-drift-inventory.ts +0 -929
- package/plugins/sp/scripts/task-evidence-precheck.ts +0 -181
- package/plugins/sp/scripts/task-size-precheck.ts +0 -175
- package/plugins/sp/scripts/transition-shim-check.ts +0 -238
- package/plugins/sp/scripts/validate-commands.ts +0 -689
- package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
- package/plugins/sp/scripts/verify-answer-lint.ts +0 -530
- package/plugins/sp/scripts/workflow-step-profile.mjs +0 -316
- package/plugins/sp/scripts/workflow-step-profile.ts +0 -456
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
- package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -258
- package/plugins/sp/scripts/wrapup-steps.mjs +0 -444
- package/plugins/sp/scripts/wrapup-steps.ts +0 -552
|
@@ -31,6 +31,28 @@ export interface FormatVariable {
|
|
|
31
31
|
default: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Optional compose block in a format preset (task 0180, F616).
|
|
36
|
+
* When present, image-gen typesets the on-card copy from Noto Sans SC via
|
|
37
|
+
* headless Chromium instead of generating text as pixels. Requires `width`
|
|
38
|
+
* and `height` on the format (enforced by FormatPresetSchema refine).
|
|
39
|
+
*/
|
|
40
|
+
export interface ComposeConfig {
|
|
41
|
+
/** The only v1 engine value; zod literal enforces it. */
|
|
42
|
+
engine: 'text-card';
|
|
43
|
+
/** Safe-area inset in px on all sides. */
|
|
44
|
+
safeArea: number;
|
|
45
|
+
/** Headline minimum font size in px. */
|
|
46
|
+
headlineMinPx: number;
|
|
47
|
+
/** Body/bullets minimum font size in px. */
|
|
48
|
+
bodyMinPx: number;
|
|
49
|
+
/** Height in px of the illustration band (top); text panel takes the rest. */
|
|
50
|
+
illustrationHeight: number;
|
|
51
|
+
palette: { background: string; text: string; accent: string };
|
|
52
|
+
/** Font filenames under `assets/fonts/`. */
|
|
53
|
+
fonts: { headline: string; body: string };
|
|
54
|
+
}
|
|
55
|
+
|
|
34
56
|
/** R1 — `formats/<name>.yaml`: the job skeleton (template, keywords, variables). */
|
|
35
57
|
export interface FormatPreset {
|
|
36
58
|
/** Filename stem must equal this. */
|
|
@@ -51,6 +73,8 @@ export interface FormatPreset {
|
|
|
51
73
|
variables: Record<string, FormatVariable>;
|
|
52
74
|
/** Prompt body; YAML block scalar, embedded newlines preserved. */
|
|
53
75
|
template: string;
|
|
76
|
+
/** Optional compose block (0180): when present, image-gen typesets card copy from a real font. */
|
|
77
|
+
compose?: ComposeConfig;
|
|
54
78
|
}
|
|
55
79
|
|
|
56
80
|
/** R1 — `styles/<name>.yaml`; `modifiers` is the only required prompt field. */
|
|
@@ -76,21 +100,37 @@ const PresetSourceSchema = z.object({
|
|
|
76
100
|
confidence: z.enum(['high', 'medium', 'low']).optional(),
|
|
77
101
|
});
|
|
78
102
|
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
keywords: z.array(z.string()),
|
|
89
|
-
outputFilename: z.string(),
|
|
90
|
-
variables: z.record(z.string(), z.object({ description: z.string(), default: z.string() })),
|
|
91
|
-
template: z.string(),
|
|
103
|
+
/** The zod schema for the optional compose block (0180). */
|
|
104
|
+
const ComposeConfigSchema = z.object({
|
|
105
|
+
engine: z.literal('text-card'),
|
|
106
|
+
safeArea: z.number(),
|
|
107
|
+
headlineMinPx: z.number(),
|
|
108
|
+
bodyMinPx: z.number(),
|
|
109
|
+
illustrationHeight: z.number(),
|
|
110
|
+
palette: z.object({ background: z.string(), text: z.string(), accent: z.string() }),
|
|
111
|
+
fonts: z.object({ headline: z.string(), body: z.string() }),
|
|
92
112
|
});
|
|
93
113
|
|
|
114
|
+
/** Parse gate for `formats/<name>.yaml`. */
|
|
115
|
+
export const FormatPresetSchema = z
|
|
116
|
+
.object({
|
|
117
|
+
name: z.string(),
|
|
118
|
+
description: z.string(),
|
|
119
|
+
job: z.enum(['free', 'cover', 'illustration']),
|
|
120
|
+
aspectRatio: z.string(),
|
|
121
|
+
width: z.number().optional(),
|
|
122
|
+
height: z.number().optional(),
|
|
123
|
+
defaultStyle: z.string(),
|
|
124
|
+
keywords: z.array(z.string()),
|
|
125
|
+
outputFilename: z.string(),
|
|
126
|
+
variables: z.record(z.string(), z.object({ description: z.string(), default: z.string() })),
|
|
127
|
+
template: z.string(),
|
|
128
|
+
compose: ComposeConfigSchema.optional(),
|
|
129
|
+
})
|
|
130
|
+
.refine((data) => data.compose === undefined || (data.width !== undefined && data.height !== undefined), {
|
|
131
|
+
message: 'A format preset with a compose block requires both width and height to be declared',
|
|
132
|
+
});
|
|
133
|
+
|
|
94
134
|
/** Parse gate for `styles/<name>.yaml`. */
|
|
95
135
|
export const StylePresetSchema = z.object({
|
|
96
136
|
name: z.string(),
|
|
@@ -50,11 +50,17 @@ export function parsePixelSize(size: string): { width: number; height: number }
|
|
|
50
50
|
return { width, height };
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** 0182 R4: image-01 accepts an exact canvas of 512..2048 per edge, divisible by 8. */
|
|
54
|
+
function isAcceptablePixelSize(width: number, height: number): boolean {
|
|
55
|
+
return width >= 512 && width <= 2048 && height >= 512 && height <= 2048 && width % 8 === 0 && height % 8 === 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Throwing twin of {@link isAcceptablePixelSize} for the request-body path; messages are frozen (tests pin them). */
|
|
53
59
|
function validatePixelSize(width: number, height: number): void {
|
|
54
|
-
if (width
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
if (!isAcceptablePixelSize(width, height)) {
|
|
61
|
+
if (width < 512 || width > 2048 || height < 512 || height > 2048) {
|
|
62
|
+
throw new Error('MiniMax custom size must keep width and height between 512 and 2048.');
|
|
63
|
+
}
|
|
58
64
|
throw new Error('MiniMax custom size requires width and height divisible by 8.');
|
|
59
65
|
}
|
|
60
66
|
}
|
|
@@ -146,6 +152,8 @@ export const createMinimax: ProviderFactory = (options?: ProviderOptions): Image
|
|
|
146
152
|
defaultModel: () => DEFAULT_MODEL,
|
|
147
153
|
isAvailable: () => readEnv('MINIMAX_API_KEY') !== '',
|
|
148
154
|
supportsReferenceImages: () => true,
|
|
155
|
+
exactSize: (model: string, width: number, height: number): boolean =>
|
|
156
|
+
model === 'image-01' && isAcceptablePixelSize(width, height),
|
|
149
157
|
validate(req: ImageRequest): void {
|
|
150
158
|
requestBody(req.prompt, req.model, req);
|
|
151
159
|
},
|
|
@@ -32,7 +32,11 @@ export interface ImageRequest {
|
|
|
32
32
|
model: string;
|
|
33
33
|
/** e.g. '21:9' — from FormatPreset.aspectRatio (0102). */
|
|
34
34
|
aspectRatio?: string;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Declared preset canvas (0182 R1). Sent only when the resolved provider's
|
|
37
|
+
* `exactSize(model, width, height)` returns true — with `aspectRatio` omitted;
|
|
38
|
+
* every other provider receives the aspect-only request shape and never sees them.
|
|
39
|
+
*/
|
|
36
40
|
width?: number;
|
|
37
41
|
height?: number;
|
|
38
42
|
quality?: ImageQuality;
|
|
@@ -64,6 +68,13 @@ export interface ImageProvider {
|
|
|
64
68
|
isAvailable(): boolean;
|
|
65
69
|
/** Queryable ref-image capability; 0105 routes `--ref` work on this fact. */
|
|
66
70
|
supportsReferenceImages(model: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Exact-size capability (0182 R1/R4): can this adapter deliver the exact pixel canvas
|
|
73
|
+
* for this model? Absent = aspect-only — `processGeneratorIO` then sends only
|
|
74
|
+
* `aspectRatio`, byte-for-byte as before 0182. Adapters opt in one at a time, on
|
|
75
|
+
* measured evidence (task 0182 Q&A, closed).
|
|
76
|
+
*/
|
|
77
|
+
exactSize?(model: string, width: number, height: number): boolean;
|
|
67
78
|
/** Throw on an impossible combination (e.g. Imagen + referenceImages) before spending money. */
|
|
68
79
|
validate?(req: ImageRequest): void;
|
|
69
80
|
generate(req: ImageRequest): Promise<ImageResult>;
|
package/plugins/kk/plugin.json
CHANGED
|
@@ -1,37 +1,45 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AI 乱炖 · 主持风格契约(zh)
|
|
2
2
|
|
|
3
|
-
`kk-daily-ai-voice` 的每一句口播都受这份契约约束(F613 R1
|
|
4
|
-
`dailynews-gen` 的校验器读取同一份机器可读块,因此提示词与校验永远一致。
|
|
3
|
+
`kk-daily-ai-voice` 的每一句口播都受这份契约约束(F613 R1;0189 R1 改定位)。`script-write`
|
|
4
|
+
提示词与 `dailynews-gen` 的校验器读取同一份机器可读块,因此提示词与校验永远一致。
|
|
5
|
+
|
|
6
|
+
## 栏目定位(0189 R1)
|
|
7
|
+
|
|
8
|
+
「AI 乱炖」是「咸话咸说」下的 AI 技术分享栏目,副题「工具、论文与实践随聊」。口播是开发者
|
|
9
|
+
视角的技术分享,不是新闻通报:不播报时事,不以任何代称、绰号或「闲聊」包装政治、外交、
|
|
10
|
+
军事、公共政策争议或社会公共事件主线的内容——这类条目在选题/QC 阶段就应被排除(0189 R2)。
|
|
11
|
+
身份与经历必须真实:只讲真实做过的实测与实践,AI 配音参与按真实程度说明。
|
|
5
12
|
|
|
6
13
|
## 人设与语域
|
|
7
14
|
|
|
8
|
-
-
|
|
15
|
+
- 口语化普通话,第一人称「我」,像一位冷静、有依据的 AI 技术分享主播。
|
|
9
16
|
- 平实、克制、不炒作:不夸大单一消息的意义,不使用惊叹式渲染或营销话术。
|
|
10
|
-
-
|
|
17
|
+
- 面向普通听众说明「这件事为什么重要」或「怎么用得上」,而不是堆砌术语。
|
|
11
18
|
|
|
12
19
|
## 固定开场与收尾
|
|
13
20
|
|
|
14
21
|
开场问候与收尾致谢由 builder 固定输出,计划文本只填充两者之间的内容,节目身份永不漂移:
|
|
15
22
|
|
|
16
|
-
-
|
|
23
|
+
- 开场:`大家好,今天是<日期>,欢迎收听「咸话咸说」的「AI 乱炖」栏目,我是<主持人>。`
|
|
17
24
|
- 收尾:`感谢您的收听,我是<主持人>,祝您拥有充实高效的一天,我们下期节目不见不散!`
|
|
18
25
|
|
|
19
26
|
## 段落角色
|
|
20
27
|
|
|
21
|
-
- `intro
|
|
22
|
-
- `lead
|
|
23
|
-
- `insight
|
|
24
|
-
- `outro`:简短收束语,接在固定致谢之前,不超过
|
|
28
|
+
- `intro`:开场导语,串起今天几个主题,接在固定问候之后,不超过 150 字。
|
|
29
|
+
- `lead`:一句话口语桥段,引出下一个主题的【标题】;只做铺垫,不得复述标题本身,不超过 80 字。
|
|
30
|
+
- `insight`:只基于这一条自身可核事实的点评(3–6 句),展开这一条自身的机制→意义→实用角度;不得挪用其他条目的话题,不超过 380 字。时长契约:全片按 15–20 分钟口播设计,insight 应写到接近上限——宁可深挖这一条的事实,也不要浅尝辄止。字数按配音驱动折算:gemini ≈3.3 字/秒(正文约 4300–5800 字);Voicebox robin-news ≈6.3 字/秒(正文约 5700–7600 字,或增加条目数)。
|
|
31
|
+
- `outro`:简短收束语,接在固定致谢之前,不超过 150 字。
|
|
25
32
|
|
|
26
33
|
## 机器可读契约
|
|
27
34
|
|
|
28
35
|
`caps` 是各字段的字数上限;`banned` 是任何计划字段都不得出现的短语——包括旧模板的全部罐头
|
|
29
|
-
点评、旧模板的 首先/接着/最后
|
|
30
|
-
`banned
|
|
36
|
+
点评、旧模板的 首先/接着/最后 开头、0133 的研究脚手架标签与对冲语(校验规则
|
|
37
|
+
`banned:<短语>`),以及 0189 R4 退役的旧栏目名(「每日 AI 资讯」「AI科技漫谈」
|
|
38
|
+
「AI Daily Briefing」):任何路径都不得让旧新闻定位复活。
|
|
31
39
|
|
|
32
40
|
```json
|
|
33
41
|
{
|
|
34
|
-
"caps": { "intro":
|
|
42
|
+
"caps": { "intro": 150, "outro": 150, "lead": 80, "insight": 380 },
|
|
35
43
|
"banned": [
|
|
36
44
|
"在各种技术光环背后,正是这样专注而不知疲倦的底层攻坚者,为今天整个大模型生态奠定了最坚实的技术基石。",
|
|
37
45
|
"这其实释放了一个非常明确的信号:AI竞争的下半场,已经不再只是单纯拼模型参数,更是电力、能源和全栈工业基础设施的硬核较量。",
|
|
@@ -46,7 +54,12 @@
|
|
|
46
54
|
"背景",
|
|
47
55
|
"潜在影响",
|
|
48
56
|
"读者宜另行核实",
|
|
49
|
-
"仍待验证"
|
|
57
|
+
"仍待验证",
|
|
58
|
+
"每日AI资讯",
|
|
59
|
+
"每日 AI 资讯",
|
|
60
|
+
"今日AI科技漫谈",
|
|
61
|
+
"AI科技漫谈",
|
|
62
|
+
"AI Daily Briefing"
|
|
50
63
|
]
|
|
51
64
|
}
|
|
52
65
|
```
|
|
@@ -131,7 +131,18 @@ export function itcReport(args: string[]): void {
|
|
|
131
131
|
const [workDir = '', maxRevisions = ''] = args;
|
|
132
132
|
if (!workDir) fail('usage: itc-report <work_dir> [max_revisions]');
|
|
133
133
|
const verdictPath = join(workDir, '.itc-verdict.json');
|
|
134
|
-
const
|
|
134
|
+
const verdictJson = existsSync(verdictPath)
|
|
135
|
+
? (readJson(verdictPath) as { verdict?: unknown; confidence?: unknown })
|
|
136
|
+
: {};
|
|
137
|
+
const verdict = verdictJson.verdict ?? '';
|
|
138
|
+
// Same enum as the workflow guards and `daily-safety-gate`: a PASS without a HIGH/MEDIUM/LOW
|
|
139
|
+
// confidence never cleared the judge gate, so the report must not claim a publish-ready draft.
|
|
140
|
+
// An absent verdict file stays the dry-run PASS convention (verdict '').
|
|
141
|
+
const unusablePass =
|
|
142
|
+
verdict === 'PASS' &&
|
|
143
|
+
verdictJson.confidence !== 'HIGH' &&
|
|
144
|
+
verdictJson.confidence !== 'MEDIUM' &&
|
|
145
|
+
verdictJson.confidence !== 'LOW';
|
|
135
146
|
// Auto runs count on .auto-revise-count; interactive runs keep the existing .revise-count.
|
|
136
147
|
const revisions = readCount(join(workDir, '.auto-revise-count')) ?? readCount(join(workDir, '.revise-count')) ?? 0;
|
|
137
148
|
// Exhaustion only fails the run when the last verdict still asks for another revision —
|
|
@@ -186,7 +197,8 @@ export function itcReport(args: string[]): void {
|
|
|
186
197
|
}
|
|
187
198
|
|
|
188
199
|
const publishFailed = targets.some((t) => !t.ok);
|
|
189
|
-
const outcome =
|
|
200
|
+
const outcome =
|
|
201
|
+
verdict === 'FAIL' || unusablePass || exhausted || publishFailed ? 'failed' : declined ? 'declined' : 'done';
|
|
190
202
|
const reportDir = join(workDir, '5-report');
|
|
191
203
|
const jsonPath = join(reportDir, 'report.json');
|
|
192
204
|
const publish = hasPlan ? { targets, retryCommands } : undefined;
|
|
@@ -198,7 +210,13 @@ export function itcReport(args: string[]): void {
|
|
|
198
210
|
);
|
|
199
211
|
writeFileSync(
|
|
200
212
|
join(reportDir, 'report.md'),
|
|
201
|
-
`# kk-itc run report\n\n- Outcome: ${outcome}\n- Verdict: ${
|
|
213
|
+
`# kk-itc run report\n\n- Outcome: ${outcome}\n- Verdict: ${
|
|
214
|
+
verdict === ''
|
|
215
|
+
? 'PASS (empty)'
|
|
216
|
+
: unusablePass
|
|
217
|
+
? 'PASS (no HIGH/MEDIUM/LOW confidence — the judge gate never cleared)'
|
|
218
|
+
: verdict
|
|
219
|
+
}\n- Revisions: ${revisions}\n` +
|
|
202
220
|
(declined
|
|
203
221
|
? '\n## Publish\n\n- Declined by the operator — nothing was sent.\n'
|
|
204
222
|
: targets.length > 0
|