@topmindspace/tms-skills 0.1.1 → 0.1.3
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/CHANGELOG.md +82 -0
- package/README.md +13 -3
- package/bin/tms-skills.js +7 -2
- package/package.json +1 -1
- package/top-ppt-html/README.md +69 -148
- package/top-ppt-html/SKILL.md +42 -23
- package/top-ppt-html/agents/openai.yaml +5 -0
- package/top-ppt-html/assets/theme-overview-architecture.png +0 -0
- package/top-ppt-html/assets/theme-overview-research.png +0 -0
- package/top-ppt-html/assets/theme-overview.png +0 -0
- package/top-ppt-html/evals/trigger-queries.json +181 -0
- package/top-ppt-html/package.json +3 -2
- package/top-ppt-html/references/chart-decision-tree.md +42 -0
- package/top-ppt-html/references/charts-discipline.md +3 -1
- package/top-ppt-html/references/charts.md +24 -15
- package/top-ppt-html/references/content-rules.md +54 -34
- package/top-ppt-html/references/default-surface.md +62 -0
- package/top-ppt-html/references/design-system.md +2 -2
- package/top-ppt-html/references/failure-modes.md +14 -1
- package/top-ppt-html/references/icons.md +56 -277
- package/top-ppt-html/references/illustration-layout.md +39 -0
- package/top-ppt-html/references/layout-grammar.md +16 -6
- package/top-ppt-html/references/modes.md +16 -11
- package/top-ppt-html/references/outline-design.md +24 -0
- package/top-ppt-html/references/page-type-matrix.md +35 -0
- package/top-ppt-html/references/playbook.md +42 -86
- package/top-ppt-html/references/pptx-export.md +20 -2
- package/top-ppt-html/references/presentation-craft.md +77 -0
- package/top-ppt-html/references/tech-design.md +11 -5
- package/top-ppt-html/scripts/audit_docs.py +1 -1
- package/top-ppt-html/scripts/audit_skill.py +45 -6
- package/top-ppt-html/scripts/build_examples.py +39 -2260
- package/top-ppt-html/scripts/build_pptx.js +6 -0
- package/top-ppt-html/scripts/check_triggers.py +153 -0
- package/top-ppt-html/scripts/checks_html.py +39 -0
- package/top-ppt-html/scripts/cross_verify.py +5 -1
- package/top-ppt-html/scripts/extract_snippet.py +2 -2
- package/top-ppt-html/scripts/layout-constants.json +111 -18
- package/top-ppt-html/scripts/lib_layout_regions.js +7 -9
- package/top-ppt-html/scripts/negative_tests.py +137 -1
- package/top-ppt-html/scripts/package_skill.py +29 -10
- package/top-ppt-html/scripts/quality_gate.py +11 -3
- package/top-ppt-html/scripts/recommend_layout.py +384 -0
- package/top-ppt-html/scripts/regression.py +1 -1
- package/top-ppt-html/scripts/smoke_pptx.sh +30 -0
- package/top-ppt-html/scripts/sync_runtime.py +22 -25
- package/top-ppt-html/scripts/validate_pptx.py +69 -0
- package/top-ppt-html/scripts/validate_report.py +336 -10
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.html +0 -3926
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.model.json +0 -168
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.html +0 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.model.json +0 -321
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.html +0 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.model.json +0 -321
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.model.json +0 -914
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.model.json +0 -914
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.html +0 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.model.json +0 -914
- package/top-ppt-html/references/design-system-engine.md +0 -235
- package/top-ppt-html/references/industry-benchmark.md +0 -105
- package/top-ppt-html/references/reform-plan.md +0 -252
- package/top-ppt-html/scripts/layout_slots.json +0 -830
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
N12 模型 theme 与 data-theme 矛盾
|
|
21
21
|
N13 模型 mode 与 data-mode 矛盾
|
|
22
22
|
N14 内部锚点断裂(链接目标 id 不存在)
|
|
23
|
+
L4 截断迹象(列表项省略号砍义)
|
|
24
|
+
L5 溢出未拆页
|
|
25
|
+
L6 混排缺对齐(ALIGN_RHYTHM)
|
|
26
|
+
N18 配图页缺 caption/图注(IMAGE_CAPTION)
|
|
23
27
|
|
|
24
28
|
用法: python scripts/negative_tests.py
|
|
25
29
|
"""
|
|
@@ -62,6 +66,25 @@ def expect_fail(name: str, path: Path, keyword: str) -> bool:
|
|
|
62
66
|
return ok
|
|
63
67
|
|
|
64
68
|
|
|
69
|
+
|
|
70
|
+
def expect_layout_qa_fail(name: str, path: Path, keyword: str) -> bool:
|
|
71
|
+
"""Run validate_report --strict --layout-qa and require FAIL containing keyword."""
|
|
72
|
+
r = subprocess.run(
|
|
73
|
+
[PY, str(ROOT / 'scripts' / 'validate_report.py'), str(path),
|
|
74
|
+
'--strict', '--layout-qa'],
|
|
75
|
+
capture_output=True, text=True, encoding='utf-8', errors='replace')
|
|
76
|
+
out = (r.stdout or '') + (r.stderr or '')
|
|
77
|
+
hit = [ln for ln in out.splitlines()
|
|
78
|
+
if keyword in ln and ('[FAIL]' in ln or 'FAIL' in ln)]
|
|
79
|
+
ok = r.returncode != 0 and bool(hit)
|
|
80
|
+
print(f' [{"OK" if ok else "FAIL"}] {name}')
|
|
81
|
+
if hit:
|
|
82
|
+
print(f' 抓到: {hit[0].strip()[:120]}')
|
|
83
|
+
elif not ok:
|
|
84
|
+
print(f' layout-qa 未触发(期望含「{keyword}」)')
|
|
85
|
+
return ok
|
|
86
|
+
|
|
87
|
+
|
|
65
88
|
def main() -> int:
|
|
66
89
|
base = ROOT / 'assets' / 'examples' / '2026-09-09-research-mckinsey.html'
|
|
67
90
|
if not base.exists():
|
|
@@ -154,6 +177,15 @@ def main() -> int:
|
|
|
154
177
|
'"footnote": "<strong>x</strong> ', 1)
|
|
155
178
|
cases.append(('N17 模型夹带标签', t17, 'HTML_TAG_IN_TEXT'))
|
|
156
179
|
|
|
180
|
+
# N18 配图页缺 caption(有 <img>/media 无图注类)
|
|
181
|
+
t18 = txt.replace(
|
|
182
|
+
'</body>',
|
|
183
|
+
'<section class="band" data-page-type="image">'
|
|
184
|
+
'<div class="media media--full">'
|
|
185
|
+
'<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="注入无图注">'
|
|
186
|
+
'</div></section></body>', 1)
|
|
187
|
+
cases.append(('N18 配图缺 caption', t18, 'IMAGE_CAPTION'))
|
|
188
|
+
|
|
157
189
|
ok = True
|
|
158
190
|
for name, content, keyword in cases:
|
|
159
191
|
if content == txt:
|
|
@@ -163,7 +195,111 @@ def main() -> int:
|
|
|
163
195
|
p.write_text(content, encoding='utf-8')
|
|
164
196
|
ok = expect_fail(name, p, keyword) and ok
|
|
165
197
|
|
|
166
|
-
|
|
198
|
+
|
|
199
|
+
# ── Batch 2 · layout-qa 专用负例 ─────────────────────────────────────
|
|
200
|
+
# L1 管线产物缺 data-skel:先造带 skel 的页,再撕掉其中一页的标记
|
|
201
|
+
import tempfile
|
|
202
|
+
scaf = OUT / 'L1-scaffold.html'
|
|
203
|
+
subprocess.run(
|
|
204
|
+
[PY, str(ROOT / 'scripts' / 'scaffold_report.py'),
|
|
205
|
+
'--mode', 'presentation', '--style', 'business-blue', '--theme', 'light',
|
|
206
|
+
'--title', 'LayoutQA Neg', '--sections', '5', '--out', str(scaf)],
|
|
207
|
+
capture_output=True, text=True)
|
|
208
|
+
if scaf.exists() and 'data-skel="' in scaf.read_text(encoding='utf-8'):
|
|
209
|
+
t_l1 = scaf.read_text(encoding='utf-8')
|
|
210
|
+
# 撕掉第一个内容页的 data-skel
|
|
211
|
+
t_l1b = re.sub(r'(id="s1") data-skel="P\d+"', r'\1', t_l1, count=1)
|
|
212
|
+
p_l1 = OUT / 'L1-missing-skel.html'
|
|
213
|
+
p_l1.write_text(t_l1b, encoding='utf-8')
|
|
214
|
+
ok = expect_layout_qa_fail('L1 缺 data-skel', p_l1, 'LAYOUT_QA_MISSING_SKEL') and ok
|
|
215
|
+
# L2 连续 3 页同一 skel
|
|
216
|
+
t_l2 = t_l1
|
|
217
|
+
# force s1,s2,s3 all P4
|
|
218
|
+
t_l2 = re.sub(r'(id="s1"[^>]*data-skel=")P\d+"', r'\1P4"', t_l2, count=1)
|
|
219
|
+
t_l2 = re.sub(r'(id="s2"[^>]*data-skel=")P\d+"', r'\1P4"', t_l2, count=1)
|
|
220
|
+
t_l2 = re.sub(r'(id="s3"[^>]*data-skel=")P\d+"', r'\1P4"', t_l2, count=1)
|
|
221
|
+
# also rewrite if attribute order is data-skel before id
|
|
222
|
+
t_l2 = re.sub(r'(data-skel=")P\d+(" data-page-type="[^"]+" id="s1")', r'\1P4\2', t_l2)
|
|
223
|
+
t_l2 = re.sub(r'(data-skel=")P\d+("([^>]*) id="s1")', r'\1P4\2', t_l2)
|
|
224
|
+
# simpler: replace first three data-skel values
|
|
225
|
+
def _force_p4(src, n=3):
|
|
226
|
+
out, c = [], 0
|
|
227
|
+
for part in re.split(r'(data-skel="P\d+")', src):
|
|
228
|
+
if part.startswith('data-skel="') and c < n:
|
|
229
|
+
out.append('data-skel="P4"'); c += 1
|
|
230
|
+
else:
|
|
231
|
+
out.append(part)
|
|
232
|
+
return ''.join(out)
|
|
233
|
+
t_l2 = _force_p4(t_l1, 3)
|
|
234
|
+
p_l2 = OUT / 'L2-skel-streak.html'
|
|
235
|
+
p_l2.write_text(t_l2, encoding='utf-8')
|
|
236
|
+
ok = expect_layout_qa_fail('L2 连续同 skel', p_l2, 'LAYOUT_QA_SKEL_STREAK') and ok
|
|
237
|
+
# L3 极偏 donut in REPORT_MODEL
|
|
238
|
+
t_l3 = t_l1
|
|
239
|
+
if 'window.REPORT_MODEL' in t_l3:
|
|
240
|
+
# inject a skewed donut section into model JSON
|
|
241
|
+
inj = (
|
|
242
|
+
'{"type":"donut","title":"极偏环",'
|
|
243
|
+
'"chart":{"type":"donut","labels":["A","B"],"values":[0.5,99.5]}},'
|
|
244
|
+
)
|
|
245
|
+
t_l3 = re.sub(r'("sections"\s*:\s*\[)', r'\1' + inj, t_l3, count=1)
|
|
246
|
+
p_l3 = OUT / 'L3-skew-donut.html'
|
|
247
|
+
p_l3.write_text(t_l3, encoding='utf-8')
|
|
248
|
+
ok = expect_layout_qa_fail('L3 极偏 donut', p_l3, 'LAYOUT_QA_SKEW_DONUT') and ok
|
|
249
|
+
|
|
250
|
+
# L4 截断迹象:列表项以省略号砍义
|
|
251
|
+
t_l4 = t_l1
|
|
252
|
+
inj4 = '<div class="card"><ul><li class="card__li">这项证据其实很长但被故意截断了…</li></ul></div>'
|
|
253
|
+
t_l4 = re.sub(r'(id="s1"[^>]*>)', lambda m: m.group(1) + inj4, t_l4, count=1)
|
|
254
|
+
p_l4 = OUT / 'L4-truncation.html'
|
|
255
|
+
p_l4.write_text(t_l4, encoding='utf-8')
|
|
256
|
+
ok = expect_layout_qa_fail('L4 截断迹象', p_l4, 'LAYOUT_QA_TRUNCATION') and ok
|
|
257
|
+
|
|
258
|
+
# L5 溢出未拆页:塞入超长正文使页高估算爆掉且无续页信号
|
|
259
|
+
t_l5 = t_l1
|
|
260
|
+
wall = '论证要点' + ('详细证据与口径说明,必须完整保留不得删减。' * 40)
|
|
261
|
+
t_l5 = re.sub(
|
|
262
|
+
r'(id="s1"[^>]*>)',
|
|
263
|
+
lambda m: m.group(1) + '<div class="card"><p>' + wall + '</p></div>'
|
|
264
|
+
+ '<div class="card"><p>' + wall + '</p></div>'
|
|
265
|
+
+ '<div class="card"><p>' + wall + '</p></div>',
|
|
266
|
+
t_l5, count=1)
|
|
267
|
+
p_l5 = OUT / 'L5-overflow-nosplit.html'
|
|
268
|
+
p_l5.write_text(t_l5, encoding='utf-8')
|
|
269
|
+
ok = expect_layout_qa_fail('L5 溢出未拆页', p_l5, 'LAYOUT_QA_OVERFLOW_NO_SPLIT') and ok
|
|
270
|
+
|
|
271
|
+
# L6 混排缺对齐:图+卡同页但无对齐类
|
|
272
|
+
t_l6 = t_l1
|
|
273
|
+
inj6 = (
|
|
274
|
+
'<div class="grid g-2">'
|
|
275
|
+
'<div class="fig"><svg class="chart" data-chart="bar" style="height:200px"></svg></div>'
|
|
276
|
+
'<div class="card"><ul><li>证据甲</li><li>证据乙</li></ul></div>'
|
|
277
|
+
'</div>'
|
|
278
|
+
)
|
|
279
|
+
t_l6 = re.sub(r'(id="s1"[^>]*>)', lambda m: m.group(1) + inj6, t_l6, count=1)
|
|
280
|
+
# 撕掉 s1 段内对齐类,确保 ALIGN_RHYTHM 能抓住
|
|
281
|
+
def _strip_align_s1(src: str) -> str:
|
|
282
|
+
m = re.search(r'(<section[^>]*id="s1"[^>]*>)([\s\S]*?)(</section>)', src)
|
|
283
|
+
if not m:
|
|
284
|
+
# attribute order may be data-skel before id
|
|
285
|
+
m = re.search(r'(<section[^>]*id="s1"[^>]*>)([\s\S]*?)(</section>)', src)
|
|
286
|
+
if not m:
|
|
287
|
+
return src
|
|
288
|
+
body = m.group(2)
|
|
289
|
+
body = re.sub(r'\ba-start\b', '', body)
|
|
290
|
+
body = re.sub(r'\ba-c\b', '', body)
|
|
291
|
+
body = re.sub(r'\ba-end\b', '', body)
|
|
292
|
+
body = body.replace('align-items:start', '').replace('align-items: center', '')
|
|
293
|
+
return src[:m.start()] + m.group(1) + body + m.group(3) + src[m.end():]
|
|
294
|
+
t_l6 = _strip_align_s1(t_l6)
|
|
295
|
+
p_l6 = OUT / 'L6-align-rhythm.html'
|
|
296
|
+
p_l6.write_text(t_l6, encoding='utf-8')
|
|
297
|
+
ok = expect_layout_qa_fail('L6 混排缺对齐', p_l6, 'LAYOUT_QA_ALIGN_RHYTHM') and ok
|
|
298
|
+
|
|
299
|
+
else:
|
|
300
|
+
print(' [SKIP] layout-qa 负例(scaffold 未产出 data-skel)')
|
|
301
|
+
|
|
302
|
+
ok = _pptx_notes_case() and ok
|
|
167
303
|
ok = _chart_data_case() and ok
|
|
168
304
|
ok = _title_pattern_case() and ok
|
|
169
305
|
ok = _font_scale_case() and ok
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
README.md 人类视角的简介 / 安装 / 开发 / 打包
|
|
18
18
|
package.json Node 依赖声明(pptxgenjs)与常用命令
|
|
19
19
|
assets/templates/ 三份模式模板 + engine.css / ui.js(公共片段注入源)
|
|
20
|
-
assets/examples/
|
|
20
|
+
assets/examples/ 3 份黄金样张 HTML + model(每模式 1;风格见 style-gallery)
|
|
21
21
|
assets/pptx-export.js PPTX 预览运行时(注入源)
|
|
22
22
|
assets/style-gallery.html 风格 × 模式 × 亮暗主题交互画廊
|
|
23
23
|
assets/theme-overview*.png 3 张主题参考图(整体 + research + architecture;演示态与整体图相同)
|
|
24
|
-
references/*.md
|
|
24
|
+
references/*.md 规范全文(Batch3 起 ≥20 篇;playbook.md = L1;其余 L2 按需)
|
|
25
25
|
scripts/ 生成 / 校验 / 回归 / 维护工具(py + js + json 单源)
|
|
26
26
|
scripts/extract_snippet.py L2 节级片段抽取(减少整读大规范)
|
|
27
|
-
|
|
27
|
+
# layoutSlots 已并入 layout-constants.json(Batch 3)
|
|
28
28
|
|
|
29
29
|
────────────────────────────────────────────────────────────────────────
|
|
30
30
|
不进包(构建产物 / 本地状态 / 缓存,与 .gitignore 口径一致)
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
────────────────────────────────────────────────────────────────────────
|
|
43
43
|
· SKILL.md frontmatter 含 name / description;name 与分发包目录名一致;
|
|
44
44
|
description 为单行双引号字符串且 ≤ 1024 字符(平台截断阈值)
|
|
45
|
-
· references(含 layout-grammar)、3 份模式模板 + engine.css / ui.js、
|
|
45
|
+
· references(含 layout-grammar)、3 份模式模板 + engine.css / ui.js、3 组黄金样张、3 张参考图齐全
|
|
46
46
|
· 关键脚本齐全(单源注入 / 审计 / 示例重建 / 回归 / 双校验器 / 精导 / 打包)
|
|
47
47
|
"""
|
|
48
48
|
import fnmatch
|
|
@@ -87,6 +87,7 @@ INCLUDE = [
|
|
|
87
87
|
'references/*',
|
|
88
88
|
'scripts/*',
|
|
89
89
|
'evals/*',
|
|
90
|
+
'agents/*',
|
|
90
91
|
]
|
|
91
92
|
|
|
92
93
|
# 出包规则(命中即跳过;与 .gitignore 口径一致)
|
|
@@ -118,37 +119,45 @@ REQUIRED = [
|
|
|
118
119
|
'assets/templates/ui.js',
|
|
119
120
|
'references/playbook.md',
|
|
120
121
|
'references/layout-grammar.md',
|
|
122
|
+
'references/default-surface.md',
|
|
123
|
+
'references/presentation-craft.md',
|
|
124
|
+
'references/illustration-layout.md',
|
|
125
|
+
'references/page-type-matrix.md',
|
|
126
|
+
'references/chart-decision-tree.md',
|
|
121
127
|
'references/modes.md', 'references/outline-design.md', 'references/design-system.md',
|
|
122
128
|
'references/styles.md', 'references/content-rules.md', 'references/components.md',
|
|
123
129
|
'references/charts.md',
|
|
124
130
|
'references/infographics.md', 'references/icons.md', 'references/pptx-export.md',
|
|
125
131
|
'references/high-fidelity.md', 'references/failure-modes.md',
|
|
126
|
-
'references/tech-design.md',
|
|
132
|
+
'references/tech-design.md',
|
|
127
133
|
'scripts/env_probe.py', 'scripts/audit_docs.py', 'scripts/audit_skill.py',
|
|
128
134
|
'scripts/scaffold_report.py',
|
|
135
|
+
'scripts/render_from_model.py',
|
|
136
|
+
'scripts/recommend_layout.py',
|
|
129
137
|
'scripts/sync_runtime.py', 'scripts/audit_styles.py', 'scripts/build_examples.py',
|
|
130
138
|
'scripts/regression.py', 'scripts/validate_report.py', 'scripts/validate_pptx.py',
|
|
131
139
|
'scripts/extract_model.py', 'scripts/build_pptx.js', 'scripts/gen_channel_a.js',
|
|
132
140
|
'scripts/cross_verify.py', 'scripts/prepare_images.py', 'scripts/probe_image_export.py',
|
|
133
141
|
'scripts/render_compare.py', 'scripts/capture_theme_overview.js', 'scripts/package_skill.py',
|
|
134
|
-
'scripts/layout-constants.json', 'scripts/model-schema.json',
|
|
142
|
+
'scripts/layout-constants.json', 'scripts/model-schema.json',
|
|
135
143
|
'scripts/lib_layout_regions.js', 'scripts/extract_snippet.py', 'scripts/quality_gate.py',
|
|
136
144
|
'scripts/checks_html.py', 'scripts/section-file-map.json',
|
|
137
145
|
'scripts/measure_height.py', 'scripts/audit_css.py', 'scripts/negative_tests.py',
|
|
138
146
|
'references/components-atoms.md', 'references/layouts-research.md',
|
|
139
147
|
'references/layouts-architecture.md', 'references/layouts-combo.md',
|
|
140
148
|
'references/charts-basic.md', 'references/charts-extended.md', 'references/charts-discipline.md',
|
|
141
|
-
'references/design-system-engine.md',
|
|
142
149
|
'references/infographics-stats.md', 'references/infographics-structure.md',
|
|
143
150
|
'evals/prompts.csv', 'evals/run_evals.py', 'evals/rubric.schema.json',
|
|
144
151
|
'evals/trace.example.json',
|
|
152
|
+
'evals/trigger-queries.json',
|
|
153
|
+
'scripts/check_triggers.py',
|
|
145
154
|
]
|
|
146
155
|
|
|
147
156
|
# 必须达到最小数量的集合(名称含日期,故按数量校验)
|
|
148
157
|
MIN_COUNTS = {
|
|
149
|
-
'assets/examples/*.html':
|
|
150
|
-
'assets/examples/*.model.json':
|
|
151
|
-
'references/*.md':
|
|
158
|
+
'assets/examples/*.html': 3, # Batch3 每模式 1 份黄金样张
|
|
159
|
+
'assets/examples/*.model.json': 3,
|
|
160
|
+
'references/*.md': 25, # + page-type-matrix + chart-decision-tree;≥25 防误删
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
|
|
@@ -252,6 +261,16 @@ def main():
|
|
|
252
261
|
return 1
|
|
253
262
|
print(' 校验通过:必需文件齐全 · frontmatter 合规 · 无临时/缓存路径')
|
|
254
263
|
|
|
264
|
+
# P0-1 trigger heuristic gate(廉价、无 LLM)
|
|
265
|
+
trig = ROOT / 'scripts' / 'check_triggers.py'
|
|
266
|
+
if trig.exists():
|
|
267
|
+
import subprocess
|
|
268
|
+
r = subprocess.run([sys.executable, str(trig)], cwd=str(ROOT))
|
|
269
|
+
if r.returncode != 0:
|
|
270
|
+
print(' ✗ trigger coverage 失败(evals/trigger-queries.json)')
|
|
271
|
+
return 1
|
|
272
|
+
print(' ✓ trigger coverage 通过')
|
|
273
|
+
|
|
255
274
|
if check_only:
|
|
256
275
|
print('\n(--check 模式:仅校验,不打包)')
|
|
257
276
|
return 0
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
用法:
|
|
6
6
|
python scripts/quality_gate.py report.html
|
|
7
|
+
# Mode A / presentation 自动对 validate_report 附加 --layout-qa
|
|
7
8
|
python scripts/quality_gate.py report.html --pptx report.pptx --model report.model.json
|
|
8
9
|
python scripts/quality_gate.py report.html --trace trace.json --json
|
|
9
10
|
python scripts/quality_gate.py report.html --require-rubric 60
|
|
@@ -241,10 +242,17 @@ def main() -> int:
|
|
|
241
242
|
print(f'交付质量门禁 · {html.name}')
|
|
242
243
|
print('-' * 60)
|
|
243
244
|
|
|
244
|
-
# ① HTML strict
|
|
245
|
-
|
|
245
|
+
# ① HTML strict(Mode A / presentation 自动附带 --layout-qa)
|
|
246
|
+
head_snip = html.read_text(encoding='utf-8')[:2500]
|
|
247
|
+
mode_m = re.search(r'data-mode="([^"]+)"', head_snip)
|
|
248
|
+
mode = mode_m.group(1) if mode_m else 'presentation'
|
|
249
|
+
vcmd = [sys.executable, str(ROOT / 'scripts' / 'validate_report.py'), str(html), '--strict']
|
|
250
|
+
if mode == 'presentation':
|
|
251
|
+
vcmd.append('--layout-qa')
|
|
252
|
+
rc, out, el = run(vcmd)
|
|
253
|
+
gate_name = 'validate_report --strict' + (' --layout-qa' if mode == 'presentation' else '')
|
|
246
254
|
tail = [ln for ln in out.splitlines() if ln.startswith('PASS ') or ln.startswith('结论')]
|
|
247
|
-
gate(
|
|
255
|
+
gate(gate_name, rc == 0, tail[-1] if tail else f'exit {rc}', el)
|
|
248
256
|
|
|
249
257
|
# ② PPTX strict(可选)
|
|
250
258
|
if args.pptx:
|