minimal-agent 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/package.json +3 -2
  2. package/plugins/workflow-runner/src/expressions.js +29 -8
  3. package/plugins/workflow-runner/src/expressions.ts +28 -10
  4. package/plugins/workflow-runner/src/index.js +45 -17
  5. package/plugins/workflow-runner/src/index.ts +44 -18
  6. package/plugins/workflow-runner/src/recovery.js +9 -2
  7. package/plugins/workflow-runner/src/recovery.ts +9 -2
  8. package/plugins/workflow-runner/src/runner.js +31 -5
  9. package/plugins/workflow-runner/src/runner.ts +33 -5
  10. package/plugins/workflow-runner/src/stepExecutors/assert.js +2 -1
  11. package/plugins/workflow-runner/src/stepExecutors/assert.ts +3 -1
  12. package/plugins/workflow-runner/src/stepExecutors/tool.js +29 -10
  13. package/plugins/workflow-runner/src/stepExecutors/tool.ts +29 -11
  14. package/plugins/workflow-runner/src/stepExecutors/verify.js +6 -3
  15. package/plugins/workflow-runner/src/stepExecutors/verify.ts +7 -3
  16. package/plugins/workflow-runner/src/taskIdentity.js +197 -21
  17. package/plugins/workflow-runner/src/taskIdentity.ts +201 -20
  18. package/plugins/workflow-runner/src/types.js +113 -7
  19. package/plugins/workflow-runner/src/types.ts +111 -7
  20. package/plugins/workflow-runner/src/workflowState.js +455 -54
  21. package/plugins/workflow-runner/src/workflowState.ts +508 -63
  22. package/plugins/workflow-runner/test/cli.e2e.test.ts +20 -0
  23. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +314 -0
  24. package/plugins/workflow-runner/test/engineRobustness.test.ts +9 -1
  25. package/plugins/workflow-runner/test/expressions.test.ts +42 -0
  26. package/plugins/workflow-runner/test/illustrate.load.test.ts +2 -2
  27. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +19 -0
  28. package/plugins/workflow-runner/test/ppt.load.test.ts +15 -15
  29. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +3 -2
  30. package/plugins/workflow-runner/test/research.load.test.ts +328 -0
  31. package/plugins/workflow-runner/test/runnerPreflight.test.ts +68 -11
  32. package/plugins/workflow-runner/test/stateLifecycle.test.ts +60 -12
  33. package/plugins/workflow-runner/test/taskArbitration.test.ts +19 -3
  34. package/plugins/workflow-runner/test/taskLifecycle.test.ts +61 -7
  35. package/plugins/workflow-runner/test/webppt.load.test.ts +4 -4
  36. package/plugins/workflow-runner/test/word.load.test.ts +9 -9
  37. package/skills/skill-creator/scripts/run_eval.py +3 -2
  38. package/src/cli/print.js +17 -0
  39. package/src/cli/print.ts +25 -0
  40. package/src/config/configService.js +24 -0
  41. package/src/config/configService.ts +23 -0
  42. package/src/infra/verification.js +25 -18
  43. package/src/infra/verification.ts +50 -18
  44. package/src/plugins/pluginRunner.js +18 -7
  45. package/src/plugins/pluginRunner.ts +20 -6
  46. package/src/prompts/policyRegistry.js +28 -1
  47. package/src/prompts/policyRegistry.ts +28 -1
  48. package/src/tools/advisor/advisor.js +76 -9
  49. package/src/tools/advisor/advisor.ts +79 -9
  50. package/src/tools/bash/bash.js +51 -6
  51. package/src/tools/bash/bash.ts +56 -7
  52. package/src/tools/powershell/powershell.js +18 -1
  53. package/src/tools/powershell/powershell.ts +18 -2
  54. package/src/tools/read/read.js +9 -4
  55. package/src/tools/read/read.ts +9 -4
  56. package/src/tools/shared/permanentDeleteGuard.js +34 -3
  57. package/src/tools/shared/permanentDeleteGuard.ts +43 -6
  58. package/src/tools/webbrowser/browserControl.js +5 -1
  59. package/src/tools/webbrowser/browserControl.ts +7 -1
  60. package/src/tools/webbrowser/webbrowser.js +8 -4
  61. package/src/tools/webbrowser/webbrowser.ts +8 -4
  62. package/src/types.ts +10 -4
  63. package/workflows/adaptive-research.yaml +5 -5
  64. package/workflows/book-review-short.yaml +5 -5
  65. package/workflows/e2e-write-greet.yaml +2 -2
  66. package/workflows/illustrate.yaml +12 -12
  67. package/workflows/plan.yaml +18 -18
  68. package/workflows/ppt.yaml +201 -201
  69. package/workflows/ralph-loop.yaml +3 -1
  70. package/workflows/research.md +449 -0
  71. package/workflows/research.yaml +873 -0
  72. package/workflows/webppt.yaml +47 -47
  73. package/workflows/word.yaml +80 -80
  74. package/workflows/youtube-shorts.yaml +25 -25
@@ -20,7 +20,7 @@ version: "0.1"
20
20
  # 中间 scratch(draft/design/deck.html/img_plan/assets/export)故意不声明——收尾 cleanup 删;
21
21
  # 声明进来会被「声明产物必须存在」判缺失而 halt。
22
22
  artifacts:
23
- - "./out/deck.pptx"
23
+ - "out/ppt/${session}/deck.pptx"
24
24
 
25
25
  # 自愈:LLM/网络瞬态故障 in-run 重跑(端点会分钟级拥塞波,给恢复窗口)。
26
26
  self_heal:
@@ -54,7 +54,7 @@ inputs:
54
54
  description: "【高级·可选 --input】AI 生图张数控制:默认 -1=最佳质量、不设上限;0=完全不生 AI 图(仅纯示意主题);N>0=显式上限 N 张。只要页面需要真实画面、产品感、场景、操作、安装、环境、特写、氛围背景,且客户图没有合适素材,就必须积极文生图;流程图、数据图表、架构图、关系图、时间线、参数轴等精确信息必须 HTML/SVG,不占额度。"
55
55
  - name: out
56
56
  type: string
57
- default: "./out/deck.pptx"
57
+ default: "out/ppt/${session}/deck.pptx"
58
58
  description: "【高级·可选 --input】输出 PPTX 路径"
59
59
  - name: image_intensity
60
60
  type: enum
@@ -70,7 +70,7 @@ inputs:
70
70
  type: enum
71
71
  values: ["true", "false"]
72
72
  default: "true"
73
- description: "【调试·可选 --input】是否保留 ./build 中间产物。默认 true,便于检查 deck.html、design.json、截图和报告;设为 false 时最终 PPT 生成后清理 ./build。"
73
+ description: "【调试·可选 --input】是否保留 out/ppt/${session}/build 中间产物。默认 true,便于检查 deck.html、design.json、截图和报告;设为 false 时最终 PPT 生成后清理 out/ppt/${session}/build。"
74
74
 
75
75
 
76
76
  steps:
@@ -149,31 +149,31 @@ steps:
149
149
  args:
150
150
  command: |
151
151
  set -e
152
- mkdir -p ./build ./build/assets
152
+ mkdir -p out/ppt/${session}/build out/ppt/${session}/build/assets
153
153
  # 只清理本次运行的状态/哨兵/旧报告,不删除 build 全目录,保留用户排障需要的中间产物。
154
154
  rm -f \
155
- ./build/build_complete \
156
- ./build/layout_ok \
157
- ./build/visual_quality_ok \
158
- ./build/visual_quality_state.json \
159
- ./build/visual_quality_report.json \
160
- ./build/visual_quality_report.md \
161
- ./build/visual_quality_residual.md \
162
- ./build/image_generation_report.json \
163
- ./build/generated_image_review.json \
164
- ./build/generated_image_semantic_retry_report.json \
165
- ./build/export-summary.md \
166
- ./build/build_probe.json \
167
- ./build/build_probe.txt \
168
- ./build/design_repair_done \
169
- ./build/design_repair_residual.md \
170
- ./build/design_json_ok \
171
- ./build/design_json_gate.json \
172
- ./build/design_json_gate.md \
173
- ./build/design.raw.json \
174
- ./build/design.draft.json \
175
- ./build/img_route_ok
176
- rm -rf ./build/export ./build/generated-image-reviews
155
+ out/ppt/${session}/build/build_complete \
156
+ out/ppt/${session}/build/layout_ok \
157
+ out/ppt/${session}/build/visual_quality_ok \
158
+ out/ppt/${session}/build/visual_quality_state.json \
159
+ out/ppt/${session}/build/visual_quality_report.json \
160
+ out/ppt/${session}/build/visual_quality_report.md \
161
+ out/ppt/${session}/build/visual_quality_residual.md \
162
+ out/ppt/${session}/build/image_generation_report.json \
163
+ out/ppt/${session}/build/generated_image_review.json \
164
+ out/ppt/${session}/build/generated_image_semantic_retry_report.json \
165
+ out/ppt/${session}/build/export-summary.md \
166
+ out/ppt/${session}/build/build_probe.json \
167
+ out/ppt/${session}/build/build_probe.txt \
168
+ out/ppt/${session}/build/design_repair_done \
169
+ out/ppt/${session}/build/design_repair_residual.md \
170
+ out/ppt/${session}/build/design_json_ok \
171
+ out/ppt/${session}/build/design_json_gate.json \
172
+ out/ppt/${session}/build/design_json_gate.md \
173
+ out/ppt/${session}/build/design.raw.json \
174
+ out/ppt/${session}/build/design.draft.json \
175
+ out/ppt/${session}/build/img_route_ok
176
+ rm -rf out/ppt/${session}/build/export out/ppt/${session}/build/generated-image-reviews
177
177
  RESOLVE="skills/ui-design/scripts/resolve_source_input.py"
178
178
  EXTRACT="skills/ui-design/scripts/extract_source_assets.py"
179
179
  STATS="skills/image-stats/scripts/image_stats.py"
@@ -189,16 +189,16 @@ steps:
189
189
  --source "$RAW_SOURCE" \
190
190
  --topic "$RAW_TOPIC" \
191
191
  --cwd . \
192
- --out ./build/source_resolution.json
193
- SOURCE="$(python3 -c 'import json; data=json.load(open("./build/source_resolution.json", encoding="utf-8")); print(data.get("resolved_path") or "")')"
192
+ --out out/ppt/${session}/build/source_resolution.json
193
+ SOURCE="$(python3 -c 'import json; data=json.load(open("out/ppt/${session}/build/source_resolution.json", encoding="utf-8")); print(data.get("resolved_path") or "")')"
194
194
  if [ -n "$SOURCE" ]; then
195
- python3 "$EXTRACT" "$SOURCE" --out ./build -v
195
+ python3 "$EXTRACT" "$SOURCE" --out out/ppt/${session}/build -v
196
196
  else
197
- python3 "$EXTRACT" . --scan --out ./build -v
197
+ python3 "$EXTRACT" . --scan --out out/ppt/${session}/build -v
198
198
  fi
199
- python3 -c 'import json,sys; r=json.load(open("./build/source_resolution.json", encoding="utf-8")); a=json.load(open("./build/source_assets.json", encoding="utf-8")); ok=not (r.get("requires_source") and not a.get("has_source")); print("PPT_SOURCE_REQUIRED_BUT_EMPTY: 用户明确要求使用素材文件,但抽取结果为空。", file=sys.stderr) if not ok else None; print(json.dumps(r, ensure_ascii=False, indent=2), file=sys.stderr) if not ok else None; sys.exit(0 if ok else 3)'
200
- python3 "$STATS" ./build/assets --scan --json > ./build/source_stats.json
201
- cat > ./build/user_request.md <<'EOF'
199
+ python3 -c 'import json,sys; r=json.load(open("out/ppt/${session}/build/source_resolution.json", encoding="utf-8")); a=json.load(open("out/ppt/${session}/build/source_assets.json", encoding="utf-8")); ok=not (r.get("requires_source") and not a.get("has_source")); print("PPT_SOURCE_REQUIRED_BUT_EMPTY: 用户明确要求使用素材文件,但抽取结果为空。", file=sys.stderr) if not ok else None; print(json.dumps(r, ensure_ascii=False, indent=2), file=sys.stderr) if not ok else None; sys.exit(0 if ok else 3)'
200
+ python3 "$STATS" out/ppt/${session}/build/assets --scan --json > out/ppt/${session}/build/source_stats.json
201
+ cat > out/ppt/${session}/build/user_request.md <<'EOF'
202
202
  # 用户原始输入与目标
203
203
 
204
204
  - 主题/指令:${inputs.topic}
@@ -209,12 +209,12 @@ steps:
209
209
 
210
210
  ## 素材解析结果
211
211
  EOF
212
- printf '\n```json\n' >> ./build/user_request.md
213
- cat ./build/source_resolution.json >> ./build/user_request.md
214
- printf '\n```\n\n## 素材抽取结果\n\n```json\n' >> ./build/user_request.md
215
- cat ./build/source_assets.json >> ./build/user_request.md
216
- printf '\n```\n' >> ./build/user_request.md
217
- cat >> ./build/user_request.md <<'EOF'
212
+ printf '\n```json\n' >> out/ppt/${session}/build/user_request.md
213
+ cat out/ppt/${session}/build/source_resolution.json >> out/ppt/${session}/build/user_request.md
214
+ printf '\n```\n\n## 素材抽取结果\n\n```json\n' >> out/ppt/${session}/build/user_request.md
215
+ cat out/ppt/${session}/build/source_assets.json >> out/ppt/${session}/build/user_request.md
216
+ printf '\n```\n' >> out/ppt/${session}/build/user_request.md
217
+ cat >> out/ppt/${session}/build/user_request.md <<'EOF'
218
218
 
219
219
 
220
220
  ## 阶段1澄清结论
@@ -228,7 +228,7 @@ steps:
228
228
  3. 客户图片必须直接使用或通过图生图增强派生后使用,不能无故丢弃。
229
229
  EOF
230
230
  echo "PPT_SOURCE_EXTRACT_READY"
231
- test -f ./build/source_assets.json
231
+ test -f out/ppt/${session}/build/source_assets.json
232
232
  onError: halt # 用户明确提到素材但解析/抽取失败时必须停下,避免伪装成纯 topic
233
233
 
234
234
  # ---------- 阶段3:逐图多模态识图(best-effort)—— for-each 遍历 readJson 拿到的真实图片清单 ----------
@@ -236,9 +236,9 @@ steps:
236
236
  # 写 build/vision/<file>.json;无多模态模型 / 单图识图失败都不阻断(onError:continue)。images:[] 时 0 轮(when 守门)。
237
237
  - id: stage_vision_loop
238
238
  title: "阶段3/22·逐图识图(内容+重点区+背景适配)"
239
- when: "fileExists('./build/source_assets.json')"
239
+ when: "fileExists('out/ppt/${session}/build/source_assets.json')"
240
240
  type: loop
241
- over: "${asArray(readJson('./build/source_assets.json', 'images'))}"
241
+ over: "${asArray(readJson('out/ppt/${session}/build/source_assets.json', 'images'))}"
242
242
  as: img
243
243
  fresh_context: true
244
244
  do:
@@ -279,7 +279,7 @@ steps:
279
279
  - `notes`:其它对排版有用的观察(如"右上角有大片留白可放标题"),没有就空串。
280
280
  capture:
281
281
  text: vision_json
282
- images: ["./build/assets/${img.file}"]
282
+ images: ["out/ppt/${session}/build/assets/${img.file}"]
283
283
  image_detail: high
284
284
  onError: continue # 无多模态模型 / 单图识图失败 → 跳过该图,下游容缺(只用文字+统计)
285
285
  - id: write_vision_json
@@ -287,7 +287,7 @@ steps:
287
287
  when: "length(vision_json) > 0"
288
288
  tool: Write
289
289
  args:
290
- file_path: "./build/vision/${img.file}.json"
290
+ file_path: "out/ppt/${session}/build/vision/${img.file}.json"
291
291
  content: "${vision_json}"
292
292
  onError: continue
293
293
  # 写后消毒校验(2026-07-02 事故:src_6 识图结论正确但 JSON 引号未转义 → assemble 静默吞 None →
@@ -299,7 +299,7 @@ steps:
299
299
  tool: Bash
300
300
  args:
301
301
  command: |
302
- python3 - "./build/vision/${img.file}.json" <<'PYEOF'
302
+ python3 - "out/ppt/${session}/build/vision/${img.file}.json" <<'PYEOF'
303
303
  import json, re, sys
304
304
  path = sys.argv[1]
305
305
  try:
@@ -324,7 +324,7 @@ steps:
324
324
  # 坏 JSON 重识别一次(强化 JSON 纪律的短 prompt);仍坏则删除,assemble 阶段逐图 WARNING + 盲态保守。
325
325
  - id: vision_one_retry
326
326
  title: "阶段3/22·重识别 ${img.file}(上次 JSON 非法)"
327
- when: "length(vision_json) > 0 && !fileExists('./build/vision/${img.file}.json')"
327
+ when: "length(vision_json) > 0 && !fileExists('out/ppt/${session}/build/vision/${img.file}.json')"
328
328
  llm: |
329
329
  上一次你对图片 `${img.file}` 的识图输出不是合法 JSON(多半是字符串里出现了未转义的英文双引号)。
330
330
  重新只输出**一段合法 JSON**(无解释、无代码围栏;字符串内部引号一律用中文「」),字段:
@@ -333,15 +333,15 @@ steps:
333
333
  relevant_to_topic(true|false) / recommended_text_zone / palette_impression / notes。
334
334
  capture:
335
335
  text: vision_json_retry
336
- images: ["./build/assets/${img.file}"]
336
+ images: ["out/ppt/${session}/build/assets/${img.file}"]
337
337
  image_detail: high
338
338
  onError: continue
339
339
  - id: rewrite_vision_json
340
340
  title: "阶段3/22·重写识图 JSON ${img.file}"
341
- when: "length(vision_json_retry) > 0 && !fileExists('./build/vision/${img.file}.json')"
341
+ when: "length(vision_json_retry) > 0 && !fileExists('out/ppt/${session}/build/vision/${img.file}.json')"
342
342
  tool: Write
343
343
  args:
344
- file_path: "./build/vision/${img.file}.json"
344
+ file_path: "out/ppt/${session}/build/vision/${img.file}.json"
345
345
  content: "${vision_json_retry}"
346
346
  onError: continue
347
347
  - id: sanitize_vision_json_retry
@@ -350,7 +350,7 @@ steps:
350
350
  tool: Bash
351
351
  args:
352
352
  command: |
353
- python3 - "./build/vision/${img.file}.json" <<'PYEOF'
353
+ python3 - "out/ppt/${session}/build/vision/${img.file}.json" <<'PYEOF'
354
354
  import json, re, sys
355
355
  path = sys.argv[1]
356
356
  try:
@@ -381,34 +381,34 @@ steps:
381
381
  # 收成确定性(治根因:低清图被硬铺当背景、该图生图的没图生图、提示词写不稳),设计步从此「按算好的选模板」。
382
382
  - id: stage_assemble_material
383
383
  title: "阶段4/22·合并统一材料 + 客户图版式/图生图规划"
384
- when: "fileExists('./build/source_assets.json')"
384
+ when: "fileExists('out/ppt/${session}/build/source_assets.json')"
385
385
  tool: Bash
386
386
  args:
387
387
  command: |
388
388
  set -e
389
- python3 skills/ui-design/scripts/assemble_material.py --build ./build
390
- test -f ./build/material.json
389
+ python3 skills/ui-design/scripts/assemble_material.py --build out/ppt/${session}/build
390
+ test -f out/ppt/${session}/build/material.json
391
391
  # 确定性客户图版式/图生图规划(恒产出 source_layout_plan.json/md,无客户图则空规划)。
392
- python3 skills/ui-design/scripts/plan_source_layout.py --build ./build || true
392
+ python3 skills/ui-design/scripts/plan_source_layout.py --build out/ppt/${session}/build || true
393
393
  echo "PPT_MATERIAL_ASSEMBLED"
394
- sed -n '1,120p' ./build/material.md || true
395
- sed -n '1,80p' ./build/source_layout_plan.md 2>/dev/null || true
394
+ sed -n '1,120p' out/ppt/${session}/build/material.md || true
395
+ sed -n '1,80p' out/ppt/${session}/build/source_layout_plan.md 2>/dev/null || true
396
396
  onError: continue # 合并失败不阻断(脚本恒产出,最坏是空材料,下游退化纯 topic)
397
397
 
398
398
  # ---------- 阶段5:基于客户材料撰写知乎深度长文式基础稿件(默认不联网) ----------
399
399
  - id: stage_draft_from_material
400
400
  title: "阶段5/22·客户材料成稿(默认不联网)"
401
401
  agent: |
402
- 为主题「${inputs.topic}」撰写一篇**知乎高赞深度长文式**的基础稿件,落到 `./build/draft.md`。
402
+ 为主题「${inputs.topic}」撰写一篇**知乎高赞深度长文式**的基础稿件,落到 `out/ppt/${session}/build/draft.md`。
403
403
  这篇稿件是后续整套 PPT 的**唯一内容源**——它薄,PPT 必然肤浅;它厚且有料,PPT 才立得住。
404
404
  成败标准只有一个:**专业、有深度、有观点、有据可依,绝不灌水、绝不百科式罗列。**
405
405
 
406
406
  ## -1、先回看用户目标(最高权重)
407
- **先 Read `./build/user_request.md`**。后续所有取舍都以这里记录的用户原始输入、素材路径、视觉偏好和澄清结论为最高权重。
407
+ **先 Read `out/ppt/${session}/build/user_request.md`**。后续所有取舍都以这里记录的用户原始输入、素材路径、视觉偏好和澄清结论为最高权重。
408
408
  如果材料与模型常识冲突,以客户材料为准;如果用户目标与材料范围冲突,写稿时明确采用用户目标做取舍。
409
409
 
410
410
  ## 〇、先看「用户原始材料」(素材驱动的关键)
411
- **先 Read `./build/material.md`**(统一中间材料:文字主干 + 每张客户图的识图详述/重点区/统计):
411
+ **先 Read `out/ppt/${session}/build/material.md`**(统一中间材料:文字主干 + 每张客户图的识图详述/重点区/统计):
412
412
  - **有素材(material.md 有实质文字或图片)**:**以它为内容主干**——
413
413
  **忠实保留其要点/数据/结构/事实(高权重,这是用户的原始资料,不得丢弃或随意改写)**;
414
414
  你的工作是把它**消化、补全、深化**成知乎长文式稿件(理顺逻辑、补背景与机制、补必要常识),
@@ -434,7 +434,7 @@ steps:
434
434
  涉及最新价格、政策、版本等强时效信息时,不要编造;写成「需按用户最新资料或公开信息复核」,除非用户在 topic 里明确要求联网查新。
435
435
  不要整段复制材料原文;要把材料消化成适合 PPT 叙事的论点、证据和例子。
436
436
 
437
- ## 四、按"知乎深度长文"结构成稿(用 Write 写 `./build/draft.md`)
437
+ ## 四、按"知乎深度长文"结构成稿(用 Write 写 `out/ppt/${session}/build/draft.md`)
438
438
  简体中文、Markdown,**篇幅按主题复杂度自适应**:默认 `quality=fast` 写 1200–2500 字的高密度成稿(快、够用、不注水);
439
439
  `quality=best` 才写 2500–6000 字深稿(愿意等待时用)。结构:
440
440
  1. **引子**:抛真问题 / 认知张力 / 反直觉点(不要"随着……的发展"套话)。
@@ -449,7 +449,7 @@ steps:
449
449
  完成后简述:字数、核心论点、几个论点小节、使用了哪些客户材料和图片信息。
450
450
  allowed_tools: [Read, Write, Glob]
451
451
  maxTurns: 24
452
- done_when: "fileExists('./build/draft.md')"
452
+ done_when: "fileExists('out/ppt/${session}/build/draft.md')"
453
453
  # 地基步:稿件是后续全流程的唯一内容源。LLM 调用失败(如 minimax 端点拥塞/空闲超时)时**必须 halt 保留现场**,
454
454
  # 不能 continue——否则没 draft.md 会让后面 5 个内容步全因 when=false 跳过、空手撞到导出步「假装推进、实则烂尾」。
455
455
  # halt 后重跑按任务身份从本步续跑(错峰/网络恢复即可成)。max_retries 给 2 次 in-run 重试吸收瞬态拥塞。
@@ -463,25 +463,25 @@ steps:
463
463
  # (这是用户点名最高优先级问题,不能只在 best 生效);查无遗漏时零开销(when 直接短路,跳过修复步)。
464
464
  - id: stage_draft_coverage
465
465
  title: "阶段5/22·原文主题覆盖检查"
466
- when: "fileExists('./build/draft.md')"
466
+ when: "fileExists('out/ppt/${session}/build/draft.md')"
467
467
  tool: Bash
468
468
  args:
469
469
  command: |
470
- python3 skills/ui-design/scripts/draft_coverage_check.py --build ./build -v
470
+ python3 skills/ui-design/scripts/draft_coverage_check.py --build out/ppt/${session}/build -v
471
471
  onError: continue
472
472
 
473
473
  - id: stage_draft_coverage_repair
474
474
  title: "阶段5/22·回填未覆盖主题"
475
- when: "fileExists('./build/draft_coverage_report.json') && readJson('./build/draft_coverage_report.json', 'ok') == false"
475
+ when: "fileExists('out/ppt/${session}/build/draft_coverage_report.json') && readJson('out/ppt/${session}/build/draft_coverage_report.json', 'ok') == false"
476
476
  agent: |
477
- `./build/draft_coverage_report.md` 列出了原文档里"有章节标题/有客户图表核心结论,但在
478
- `./build/draft.md` 里找不到对应内容"的主题——这类主题多半靠图片本身说话、原文写得模糊,
477
+ `out/ppt/${session}/build/draft_coverage_report.md` 列出了原文档里"有章节标题/有客户图表核心结论,但在
478
+ `out/ppt/${session}/build/draft.md` 里找不到对应内容"的主题——这类主题多半靠图片本身说话、原文写得模糊,
479
479
  上一步写 draft.md 时被判定"缺乏明确论点"而略过了,但客户图还在等着这些内容配页。
480
480
 
481
481
  必读:
482
- - `./build/draft_coverage_report.md`
483
- - `./build/material.md`(找回原文对应段落 + 相关客户图的识图详述/图表核心结论)
484
- - `./build/draft.md`
482
+ - `out/ppt/${session}/build/draft_coverage_report.md`
483
+ - `out/ppt/${session}/build/material.md`(找回原文对应段落 + 相关客户图的识图详述/图表核心结论)
484
+ - `out/ppt/${session}/build/draft.md`
485
485
 
486
486
  对报告里每个未覆盖主题:回 `material.md` 找到对应原文段落或客户图的 `key_message`/识图详述,
487
487
  提炼成一条论点/证据/例子,**补进 draft.md 现有相关小节,或新增一个简短小节**(用 Edit 或 Write)。
@@ -501,14 +501,14 @@ steps:
501
501
  # **保留 visual.{route,side,note} 字段名** → 下游 image_plan_lint.py(--outline 参数)直接吃 design.json,零适配。
502
502
  - id: stage_design_doc
503
503
  title: "阶段6/22·Web 页面设计文档(先设计后实现)"
504
- when: "fileExists('./build/draft.md')"
504
+ when: "fileExists('out/ppt/${session}/build/draft.md')"
505
505
  agent: |
506
- 读 `./build/draft.md`,产出一份**详细的 Web 页面设计文档**——这是后续"做网页"的唯一蓝图。
506
+ 读 `out/ppt/${session}/build/draft.md`,产出一份**详细的 Web 页面设计文档**——这是后续"做网页"的唯一蓝图。
507
507
  关键心态:**先把每一页设计清楚(装什么内容、用什么构图、每块怎么呈现、配什么图、背景什么变化),再去实现**。
508
508
  不要在本步写 deck.html / 生图(那是后续阶段)。本步只产设计文档 + 配图计划。
509
509
 
510
510
  ## -1、先回看用户目标
511
- 先 Read `./build/user_request.md`,确认用户原始输入、素材路径、视觉风格和阶段1澄清结论。后续设计取舍优先满足用户目标,
511
+ 先 Read `out/ppt/${session}/build/user_request.md`,确认用户原始输入、素材路径、视觉风格和阶段1澄清结论。后续设计取舍优先满足用户目标,
512
512
  不要在长流程里忘掉用户最初说法。
513
513
 
514
514
  ## 先读设计规则(单一规则源)
@@ -516,8 +516,8 @@ steps:
516
516
  (构图法枚举 + 每页内容预算 + 元素渲染决策 + 配图分流 + 生图提示词配方 + 构图均衡)。严格照其执行。
517
517
 
518
518
  ## 〇、先盘点「用户原始材料」——决定客户图怎么高权重用(素材驱动的关键)
519
- **先 Read `./build/material.json`**(富 manifest:每张客户图带 base 字段 + `vision`[识图详述/key_elements/key_region/suitable_as_background/recommended_text_zone] + `stats`[average_color/dominant_colors/tone/brightness],图已在 `./build/assets/`):
520
- - **有客户图(material.json images 非空)**:**再 Read `./build/source_layout_plan.md`(确定性版式/图生图规划)和 ui-design 技能目录下的 `references/source-image-layout.md`**(客户图布局铁律:全图生图重绘到槽位比例 + 默认 `object-fit:cover` 填满槽、`object-position` 保重点区,仅 dense-info 数据图 contain + 按朝向/重点区图文混排 + 配色随图 + 文字避让重点区 + 宜做背景者铺背景)。
519
+ **先 Read `out/ppt/${session}/build/material.json`**(富 manifest:每张客户图带 base 字段 + `vision`[识图详述/key_elements/key_region/suitable_as_background/recommended_text_zone] + `stats`[average_color/dominant_colors/tone/brightness],图已在 `out/ppt/${session}/build/assets/`):
520
+ - **有客户图(material.json images 非空)**:**再 Read `out/ppt/${session}/build/source_layout_plan.md`(确定性版式/图生图规划)和 ui-design 技能目录下的 `references/source-image-layout.md`**(客户图布局铁律:全图生图重绘到槽位比例 + 默认 `object-fit:cover` 填满槽、`object-position` 保重点区,仅 dense-info 数据图 contain + 按朝向/重点区图文混排 + 配色随图 + 文字避让重点区 + 宜做背景者铺背景)。
521
521
  🚨 **逐图决策已由 `source_layout_plan.md`/`.json` 算定,你【直接照它执行、不要自己重算分辨率/比例】**——每张图它都给了:
522
522
  `usage_intent`(用途)、`enhance_required`(是否必须图生图)、`enhance_target_aspect`(目标比例)、`recommended_templates`(推荐图文混排模板,对齐 layout-composition §2.2)、`text_zone`(文字落区,避让重点区)、`text_color`(深墨字/白字)。
523
523
  你的工作只剩两件:① **章节锚定配页(治张冠李戴·第一优先级)**:每张客户图先看规划里的 `section`(来源章节)+ `near_text`(原文邻近文案)——**必须配到覆盖该章节内容的页**(客户图是给原文特定章节配的,排到别的主题页 = 张冠李戴);vision.description(识图内容)只用于「该章节对应多页时挑最贴的一页」;**仅当**该章节无对应页、或图无 section(独立散图/页号锚)时,才按 vision.description ↔ key_point 语义契合配页。配好后从 `recommended_templates` 里挑一个落地;② 让该页文案呼应 vision.description、配色随 stats。
@@ -687,9 +687,9 @@ steps:
687
687
  **相邻页不要完全一样**;但不破坏色系一致(仍是同一 VI)。
688
688
 
689
689
  ## 五、产物(严格落这些路径)
690
- 1. 用 Write 写 `./build/design.md`(人读):逐页 `## 第N页 · <标题>` + archetype + composition + key_point + 要点 +
690
+ 1. 用 Write 写 `out/ppt/${session}/build/design.md`(人读):逐页 `## 第N页 · <标题>` + archetype + composition + key_point + 要点 +
691
691
  各块 render + 配图说明 + 背景意图,便于人复核。
692
- 2. 用 Write 写 `./build/design.json`(机读,**严格 JSON、无注释、无 ``` 包裹**):
692
+ 2. 用 Write 写 `out/ppt/${session}/build/design.json`(机读,**严格 JSON、无注释、无 ``` 包裹**):
693
693
  - 可以写成多行缩进的漂亮 JSON;下一阶段会用 `normalize_design_json.py` 做硬解析、规范化和结构验收。
694
694
  不要追求单行大 JSON;单行过长更容易被弱模型写坏。**但必须是真 JSON:不能有注释、Markdown、尾随解释文字。**
695
695
  - 字段精炼(key_point/points/evidence/note/prompt 都用短句,别灌长文);下面是**字段结构示例**:
@@ -708,17 +708,17 @@ steps:
708
708
  ⚠️ **`aspect` 必须 = 该图所在槽位的比例**(你为它选的模板/grid 单元决定,如全幅背景 16:9、左右半 4:5、上下横带 21:9)——图生图据此**重绘到槽位所需比例/分辨率**,落进槽位**正好填满、不留白不裁切**;`prompt` 用 `source_layout_plan.json` 里算好的 `enhance_prompt`(保留原图内容 + 高清重绘 + 缺失区域智能补全),别自己另写。
709
709
  - `key_point` 每页必填;`evidence` 内容页必填、封面/章节页可 [];route=ai-* 或 source-enhanced 才需 visual.prompt(route=source 用 source_file、不需 prompt)。
710
710
  - `speaker_notes` 内容页建议必写(封面/章节可省):**演讲讲稿**,把 draft 的论证/机制/出处收进来——它是「上屏短语化」的另一半,没有它深度就只能塞上屏。不计入 max_visible_words。
711
- 3. 用 Write 写 `./build/img_plan.json`(机读,配图计划):**只写 route=source-enhanced 客户图增强条目**(best-effort——阶段9 `--ensure-img-plan` 还会从 source_layout_plan 兜底 union,写不全没关系)。
711
+ 3. 用 Write 写 `out/ppt/${session}/build/img_plan.json`(机读,配图计划):**只写 route=source-enhanced 客户图增强条目**(best-effort——阶段9 `--ensure-img-plan` 还会从 source_layout_plan 兜底 union,写不全没关系)。
712
712
  🚫 **route=ai-A/ai-B 的 AI 底图条目【不用你写进 img_plan】**——阶段9 `--ensure-ai-img-plan` 读 design.json 按**约定名 `ai_p<page>.png`** 确定性注入(弱模型手写 ai-* 易整批丢失 / 起错名致重复生两张,故收归确定性);你只要把 `route`/`prompt`/`aspect` 写进 design.json 的 `visual` 即可,文件名由约定派定。
713
713
  (`illustrations=${inputs.illustrations}`:-1=由内容定、0=不主动配 AI 图;N>0=AI 图不超 N,source-enhanced 不计入浪费额度),图生图增强每条:
714
714
  - 图生图增强:`{"file":"enhanced_src_K.png","mode":"image-to-image","source_file":"src_K.png","aspect":"<目标比例>","prompt":"<扩图/换背景/清晰化/比例修复提示词>"}`
715
- ⚠️ **只要有任何 route=ai-* 或 source-enhanced 页 → 必须写出 `./build/img_plan.json`(无客户图增强就写 `[]` 空数组也行)**,保证阶段9 闸门触发、ai-* 被注入;纯示意主题且零 AI/客户图才可不写此文件。
715
+ ⚠️ **只要有任何 route=ai-* 或 source-enhanced 页 → 必须写出 `out/ppt/${session}/build/img_plan.json`(无客户图增强就写 `[]` 空数组也行)**,保证阶段9 闸门触发、ai-* 被注入;纯示意主题且零 AI/客户图才可不写此文件。
716
716
 
717
717
  完成后简述:共几页、是否含综述+总结页、各页 archetype+composition、其中几页 route=source/source-enhanced(客户图)/ai-A/ai-B、img_plan.json 几条。
718
718
  allowed_tools: [Read, Write, Glob]
719
719
  maxTurns: 20
720
720
  capture: design_notes
721
- done_when: "fileExists('./build/design.md') && fileExists('./build/design.json')"
721
+ done_when: "fileExists('out/ppt/${session}/build/design.md') && fileExists('out/ppt/${session}/build/design.json')"
722
722
  onError: halt
723
723
  # 重活步(读长 draft+layout+vi、出 16 页 JSON)拥塞下首字节延迟易撞空闲超时 → 给 2 次 in-run 重试吸收瞬态拥塞。
724
724
  max_retries: 2
@@ -728,20 +728,20 @@ steps:
728
728
  # 当场修订到位。不引入人工 pause(webppt 定位=一句话自动产出);不重跑研究,只打磨设计文档。
729
729
  - id: stage_design_json_gate
730
730
  title: "阶段7/22·设计 JSON 硬验收 + 确定性预检"
731
- when: "fileExists('./build/design.json')"
731
+ when: "fileExists('out/ppt/${session}/build/design.json')"
732
732
  tool: Bash
733
733
  args:
734
734
  command: |
735
735
  set -e
736
- mkdir -p ./build
737
- python3 skills/ui-design/scripts/normalize_design_json.py --build ./build
738
- test -f ./build/design_json_ok
739
- BUDGET_LOG=./build/design_budget_check.txt
740
- STYLE_LOG=./build/design_style_check.txt
736
+ mkdir -p out/ppt/${session}/build
737
+ python3 skills/ui-design/scripts/normalize_design_json.py --build out/ppt/${session}/build
738
+ test -f out/ppt/${session}/build/design_json_ok
739
+ BUDGET_LOG=out/ppt/${session}/build/design_budget_check.txt
740
+ STYLE_LOG=out/ppt/${session}/build/design_style_check.txt
741
741
  set +e
742
- python3 skills/ui-design/scripts/design_budget_check.py ./build/design.json -v > "$BUDGET_LOG" 2>&1
742
+ python3 skills/ui-design/scripts/design_budget_check.py out/ppt/${session}/build/design.json -v > "$BUDGET_LOG" 2>&1
743
743
  BUDGET_STATUS=$?
744
- python3 skills/ui-design/scripts/design_style_check.py ./build/design.json -v > "$STYLE_LOG" 2>&1
744
+ python3 skills/ui-design/scripts/design_style_check.py out/ppt/${session}/build/design.json -v > "$STYLE_LOG" 2>&1
745
745
  STYLE_STATUS=$?
746
746
  set -e
747
747
  export BUDGET_STATUS STYLE_STATUS
@@ -750,7 +750,7 @@ steps:
750
750
  import os
751
751
  from pathlib import Path
752
752
 
753
- build = Path("./build")
753
+ build = Path("out/ppt/${session}/build")
754
754
  budget_status = int(os.environ.get("BUDGET_STATUS", "1"))
755
755
  style_status = int(os.environ.get("STYLE_STATUS", "1"))
756
756
  budget_log = (build / "design_budget_check.txt").read_text(encoding="utf-8", errors="replace")
@@ -781,11 +781,11 @@ steps:
781
781
 
782
782
  - id: stage_design_review
783
783
  title: "阶段8/22·对抗评审设计文档(短修订)"
784
- when: "inputs.quality == 'best' && fileExists('./build/design_json_ok')"
784
+ when: "inputs.quality == 'best' && fileExists('out/ppt/${session}/build/design_json_ok')"
785
785
  agent: |
786
786
  你是一名挑剔的**设计评审 + 内容总监**,对刚产出的 Web 页面设计文档做**对抗式审查并当场修订定稿**。
787
- 先 Read `./build/user_request.md`、`./build/design_review_checks.md`、`./build/design.md` 与 `./build/design.json`,
788
- 再按需 Read `./build/material.json`、`./build/source_layout_plan.json`(确定性版式/图生图规划——逐张核对设计是否服从它)。
787
+ 先 Read `out/ppt/${session}/build/user_request.md`、`out/ppt/${session}/build/design_review_checks.md`、`out/ppt/${session}/build/design.md` 与 `out/ppt/${session}/build/design.json`,
788
+ 再按需 Read `out/ppt/${session}/build/material.json`、`out/ppt/${session}/build/source_layout_plan.json`(确定性版式/图生图规划——逐张核对设计是否服从它)。
789
789
 
790
790
  ## 最高裁决顺序:非破坏性评审,防止把好设计改坏
791
791
  本步是**非破坏性评审**:你可以修 `design.json`,但只能修**证据明确**违反下面顺序的地方,不能为了"空"、"不满"、
@@ -806,7 +806,7 @@ steps:
806
806
  - 拿不准就保留原设计,写入"存疑,交末端构图门复核",不要硬改。
807
807
 
808
808
  ## 第零步:只读预检报告,不再自己跑脚本
809
- `./build/design_review_checks.md/json` 已由上一步确定性生成,里面包含 `design_budget_check.py` 与 `design_style_check.py`
809
+ `out/ppt/${session}/build/design_review_checks.md/json` 已由上一步确定性生成,里面包含 `design_budget_check.py` 与 `design_style_check.py`
810
810
  的结果。你只根据报告中的明确问题修 `design.json`/`design.md`:
811
811
  - budget 问题:只修文字过量、标题/要点过长、可能顶出画布;**不为填满页面加内容**。
812
812
  - style 问题:只修缺字段、卡片墙/大表格/文字墙、缺主视觉、缺客户图增强或缺 AI 主视觉。
@@ -883,29 +883,29 @@ steps:
883
883
 
884
884
  - id: stage_design_recheck
885
885
  title: "阶段9/22·设计文档确定性复核"
886
- when: "fileExists('./build/design_json_ok')"
886
+ when: "fileExists('out/ppt/${session}/build/design_json_ok')"
887
887
  tool: Bash
888
888
  args:
889
889
  command: |
890
890
  set -e
891
- mkdir -p ./build
892
- python3 skills/ui-design/scripts/normalize_design_json.py --build ./build
893
- test -f ./build/design_json_ok
894
- BUDGET_LOG=./build/design_budget_recheck.txt
895
- STYLE_LOG=./build/design_style_recheck.txt
891
+ mkdir -p out/ppt/${session}/build
892
+ python3 skills/ui-design/scripts/normalize_design_json.py --build out/ppt/${session}/build
893
+ test -f out/ppt/${session}/build/design_json_ok
894
+ BUDGET_LOG=out/ppt/${session}/build/design_budget_recheck.txt
895
+ STYLE_LOG=out/ppt/${session}/build/design_style_recheck.txt
896
896
  if [ "${inputs.quality}" != "best" ]; then
897
- cp ./build/design_budget_check.txt "$BUDGET_LOG" 2>/dev/null || true
898
- cp ./build/design_style_check.txt "$STYLE_LOG" 2>/dev/null || true
899
- cp ./build/design_review_checks.json ./build/design_review_recheck.json 2>/dev/null || true
900
- cp ./build/design_review_checks.md ./build/design_review_recheck.md 2>/dev/null || true
901
- cp ./build/design_review_checks.md ./build/design_repair_residual.md 2>/dev/null || true
897
+ cp out/ppt/${session}/build/design_budget_check.txt "$BUDGET_LOG" 2>/dev/null || true
898
+ cp out/ppt/${session}/build/design_style_check.txt "$STYLE_LOG" 2>/dev/null || true
899
+ cp out/ppt/${session}/build/design_review_checks.json out/ppt/${session}/build/design_review_recheck.json 2>/dev/null || true
900
+ cp out/ppt/${session}/build/design_review_checks.md out/ppt/${session}/build/design_review_recheck.md 2>/dev/null || true
901
+ cp out/ppt/${session}/build/design_review_checks.md out/ppt/${session}/build/design_repair_residual.md 2>/dev/null || true
902
902
  echo "DESIGN_REVIEW_RECHECK_SKIPPED_FAST"
903
903
  exit 0
904
904
  fi
905
905
  set +e
906
- python3 skills/ui-design/scripts/design_budget_check.py ./build/design.json -v > "$BUDGET_LOG" 2>&1
906
+ python3 skills/ui-design/scripts/design_budget_check.py out/ppt/${session}/build/design.json -v > "$BUDGET_LOG" 2>&1
907
907
  BUDGET_STATUS=$?
908
- python3 skills/ui-design/scripts/design_style_check.py ./build/design.json -v > "$STYLE_LOG" 2>&1
908
+ python3 skills/ui-design/scripts/design_style_check.py out/ppt/${session}/build/design.json -v > "$STYLE_LOG" 2>&1
909
909
  STYLE_STATUS=$?
910
910
  set -e
911
911
  export BUDGET_STATUS STYLE_STATUS
@@ -914,7 +914,7 @@ steps:
914
914
  import os
915
915
  from pathlib import Path
916
916
 
917
- build = Path("./build")
917
+ build = Path("out/ppt/${session}/build")
918
918
  budget_status = int(os.environ.get("BUDGET_STATUS", "1"))
919
919
  style_status = int(os.environ.get("STYLE_STATUS", "1"))
920
920
  budget_log = (build / "design_budget_recheck.txt").read_text(encoding="utf-8", errors="replace")
@@ -945,7 +945,7 @@ steps:
945
945
 
946
946
  - id: stage_design_repair
947
947
  title: "阶段10/22·设计文档短修复(fast只写残留·best最多3轮)"
948
- when: "inputs.quality == 'best' && fileExists('./build/design_review_recheck.json') && readJson('./build/design_review_recheck.json', 'ok') == false"
948
+ when: "inputs.quality == 'best' && fileExists('out/ppt/${session}/build/design_review_recheck.json') && readJson('out/ppt/${session}/build/design_review_recheck.json', 'ok') == false"
949
949
  type: loop
950
950
  max_iterations: 3
951
951
  fresh_context: true
@@ -955,19 +955,19 @@ steps:
955
955
  tool: Bash
956
956
  args:
957
957
  command: |
958
- python3 skills/ui-design/scripts/design_repair_hints.py --build ./build --max-pages 3
958
+ python3 skills/ui-design/scripts/design_repair_hints.py --build out/ppt/${session}/build --max-pages 3
959
959
  onError: continue
960
960
  - id: design_repair_batch
961
961
  title: "阶段10/22·短修复本批页面"
962
- when: "inputs.quality == 'best' && fileExists('./build/design_repair_batch.md') && readJson('./build/design_review_recheck.json', 'ok') == false"
962
+ when: "inputs.quality == 'best' && fileExists('out/ppt/${session}/build/design_repair_batch.md') && readJson('out/ppt/${session}/build/design_review_recheck.json', 'ok') == false"
963
963
  agent: |
964
964
  你只做**本批页面短修复**,不要重写全稿。
965
965
 
966
966
  必读:
967
- - `./build/user_request.md`
968
- - `./build/design_repair_batch.md`
969
- - `./build/design.json`
970
- - 必要时读 `./build/material.json`、`./build/source_layout_plan.json`
967
+ - `out/ppt/${session}/build/user_request.md`
968
+ - `out/ppt/${session}/build/design_repair_batch.md`
969
+ - `out/ppt/${session}/build/design.json`
970
+ - 必要时读 `out/ppt/${session}/build/material.json`、`out/ppt/${session}/build/source_layout_plan.json`
971
971
 
972
972
  只修 `design_repair_batch.md` 列出的 1–3 页:
973
973
  - budget 失败:删减/改写超长标题、超长要点、文字过量页面;不要新增内容填满画布。
@@ -976,8 +976,8 @@ steps:
976
976
  - 对图片页,优先修 `one_sentence_takeaway / composition_style / page_grammar / visual_focus / safe_text_zone / visual_role / semantic_image_prompt / copy_budget / forbidden_layout`。
977
977
 
978
978
  修复方式:
979
- - 直接用 Write 重写 `./build/design.json` 为严格 JSON。
980
- - 可用 Edit 同步 `./build/design.md` 的人读说明。
979
+ - 直接用 Write 重写 `out/ppt/${session}/build/design.json` 为严格 JSON。
980
+ - 可用 Edit 同步 `out/ppt/${session}/build/design.md` 的人读说明。
981
981
  - 不写 deck.html、不生图;修完只报告本批修了哪些页。
982
982
  allowed_tools: [Read, Edit, Write, Glob]
983
983
  maxTurns: 12
@@ -989,16 +989,16 @@ steps:
989
989
  args:
990
990
  command: |
991
991
  set -e
992
- python3 skills/ui-design/scripts/normalize_design_json.py --build ./build
993
- test -f ./build/design_json_ok
992
+ python3 skills/ui-design/scripts/normalize_design_json.py --build out/ppt/${session}/build
993
+ test -f out/ppt/${session}/build/design_json_ok
994
994
  # 只跑一遍:把退出码落 .status(&&/|| 在 set -e 下安全,$? 取的是检查器真实退出码),
995
995
  # 内联 Python 读状态即可,不再重跑同一对慢检查器(去「耗时高收益小」双跑)。
996
- python3 skills/ui-design/scripts/design_budget_check.py ./build/design.json -v > ./build/design_budget_recheck.txt 2>&1 && echo 0 > ./build/design_budget_recheck.status || echo $? > ./build/design_budget_recheck.status
997
- python3 skills/ui-design/scripts/design_style_check.py ./build/design.json -v > ./build/design_style_recheck.txt 2>&1 && echo 0 > ./build/design_style_recheck.status || echo $? > ./build/design_style_recheck.status
996
+ python3 skills/ui-design/scripts/design_budget_check.py out/ppt/${session}/build/design.json -v > out/ppt/${session}/build/design_budget_recheck.txt 2>&1 && echo 0 > out/ppt/${session}/build/design_budget_recheck.status || echo $? > out/ppt/${session}/build/design_budget_recheck.status
997
+ python3 skills/ui-design/scripts/design_style_check.py out/ppt/${session}/build/design.json -v > out/ppt/${session}/build/design_style_recheck.txt 2>&1 && echo 0 > out/ppt/${session}/build/design_style_recheck.status || echo $? > out/ppt/${session}/build/design_style_recheck.status
998
998
  python3 - <<'PY'
999
999
  import json
1000
1000
  from pathlib import Path
1001
- build = Path("./build")
1001
+ build = Path("out/ppt/${session}/build")
1002
1002
  def _status(name):
1003
1003
  try:
1004
1004
  return int((build / (name + ".status")).read_text(encoding="utf-8").strip())
@@ -1033,13 +1033,13 @@ steps:
1033
1033
  print("DESIGN_REPAIR_RECHECK_OK" if ok else "DESIGN_REPAIR_RECHECK_ISSUES")
1034
1034
  PY
1035
1035
  if [ "${inputs.quality}" != "best" ]; then
1036
- cp ./build/design_review_recheck.md ./build/design_repair_residual.md 2>/dev/null || true
1037
- touch ./build/design_repair_done
1036
+ cp out/ppt/${session}/build/design_review_recheck.md out/ppt/${session}/build/design_repair_residual.md 2>/dev/null || true
1037
+ touch out/ppt/${session}/build/design_repair_done
1038
1038
  fi
1039
1039
  onError: continue
1040
1040
  - id: design_repair_done
1041
1041
  type: break
1042
- when: "fileExists('./build/design_repair_done')"
1042
+ when: "fileExists('out/ppt/${session}/build/design_repair_done')"
1043
1043
 
1044
1044
  # ---------- 阶段6:图生图兜底注入 + 配图分流闸门(结构图必须 HTML,不准当 AI 图生) ----------
1045
1045
  # ① 确定性兜底注入:plan_source_layout.py --ensure-img-plan 把「必须图生图增强」的客户图条目 union 进
@@ -1053,22 +1053,22 @@ steps:
1053
1053
  # when 放宽:img_plan 存在【或】规划里有待增强客户图(length null 安全)→ 即使设计步漏写 img_plan,兜底注入也能创建它。
1054
1054
  - id: stage_img_route_gate
1055
1055
  title: "阶段11/22·图生图兜底注入 + 配图分流闸门"
1056
- when: "fileExists('./build/design_json_ok') && (fileExists('./build/img_plan.json') || length(readJson('./build/source_layout_plan.json', 'enhance_entries')) > 0)"
1056
+ when: "fileExists('out/ppt/${session}/build/design_json_ok') && (fileExists('out/ppt/${session}/build/img_plan.json') || length(readJson('out/ppt/${session}/build/source_layout_plan.json', 'enhance_entries')) > 0)"
1057
1057
  tool: Bash
1058
1058
  args:
1059
1059
  command: |
1060
1060
  set -e
1061
- python3 skills/ui-design/scripts/plan_source_layout.py --ensure-img-plan --build ./build
1062
- python3 skills/ui-design/scripts/plan_source_layout.py --reconcile-extensions --build ./build --design ./build/design.json
1063
- python3 skills/ui-design/scripts/plan_source_layout.py --ensure-ai-img-plan --build ./build --design ./build/design.json
1064
- python3 skills/ui-design/scripts/plan_source_layout.py --ensure-page-backgrounds --build ./build --design ./build/design.json
1065
- python3 skills/ui-design/scripts/image_prompt_rewrite.py --build ./build
1066
- python3 skills/ui-design/scripts/image_plan_lint.py ./build/img_plan.json -v
1061
+ python3 skills/ui-design/scripts/plan_source_layout.py --ensure-img-plan --build out/ppt/${session}/build
1062
+ python3 skills/ui-design/scripts/plan_source_layout.py --reconcile-extensions --build out/ppt/${session}/build --design out/ppt/${session}/build/design.json
1063
+ python3 skills/ui-design/scripts/plan_source_layout.py --ensure-ai-img-plan --build out/ppt/${session}/build --design out/ppt/${session}/build/design.json
1064
+ python3 skills/ui-design/scripts/plan_source_layout.py --ensure-page-backgrounds --build out/ppt/${session}/build --design out/ppt/${session}/build/design.json
1065
+ python3 skills/ui-design/scripts/image_prompt_rewrite.py --build out/ppt/${session}/build
1066
+ python3 skills/ui-design/scripts/image_plan_lint.py out/ppt/${session}/build/img_plan.json -v
1067
1067
  # 章节错位预警(张冠李戴,WARNING 级恒 exit 0):生图**之前**就核对客户图是否配在
1068
1068
  # 其来源章节对应页(词元交集,见 source_usage_check.section_mismatch_warnings),
1069
1069
  # 错位在此最便宜(design 层可改,还没花生图/建页成本)。|| true 双保险,绝不 halt。
1070
- python3 skills/ui-design/scripts/source_usage_check.py --build ./build --design-only || true
1071
- touch ./build/img_route_ok
1070
+ python3 skills/ui-design/scripts/source_usage_check.py --build out/ppt/${session}/build --design-only || true
1071
+ touch out/ppt/${session}/build/img_route_ok
1072
1072
  echo "IMG_ROUTE_GATE_OK"
1073
1073
  onError: halt
1074
1074
  max_retries: 0
@@ -1081,11 +1081,11 @@ steps:
1081
1081
  # 内建优雅退化(单图失败→图生图退原图/文生图退渐变占位,恒不阻断),仅凭 Ctrl+C 中断。
1082
1082
  - id: stage_gen_images
1083
1083
  title: "阶段12/22·生成插图(按比例)"
1084
- when: "fileExists('./build/img_plan.json')"
1084
+ when: "fileExists('out/ppt/${session}/build/img_plan.json')"
1085
1085
  tool: GenerateImage
1086
1086
  args:
1087
- plan: ./build/img_plan.json
1088
- assets: ./build/assets
1087
+ plan: out/ppt/${session}/build/img_plan.json
1088
+ assets: out/ppt/${session}/build/assets
1089
1089
  size: 2k
1090
1090
  timeout_ms: 300000
1091
1091
  wall_budget_ms: 3600000
@@ -1098,15 +1098,15 @@ steps:
1098
1098
  # ---------- 阶段7b:生图质量门(fast 只补一轮降级图;best 的深度复核在后续语义复核阶段) ----------
1099
1099
  - id: stage_image_quality_gate
1100
1100
  title: "阶段13/22·AI 配图质量门(fast一轮补降级图)"
1101
- when: "fileExists('./build/image_generation_report.json')"
1101
+ when: "fileExists('out/ppt/${session}/build/image_generation_report.json')"
1102
1102
  tool: GenerateImage
1103
1103
  args:
1104
- plan: ./build/img_plan.json
1105
- assets: ./build/assets
1104
+ plan: out/ppt/${session}/build/img_plan.json
1105
+ assets: out/ppt/${session}/build/assets
1106
1106
  size: 2k
1107
1107
  timeout_ms: 300000
1108
1108
  wall_budget_ms: 3600000
1109
- retry_degraded_report: ./build/image_generation_report.json
1109
+ retry_degraded_report: out/ppt/${session}/build/image_generation_report.json
1110
1110
  max_rounds: 1
1111
1111
  concurrency: 2
1112
1112
  onError: continue
@@ -1119,22 +1119,22 @@ steps:
1119
1119
  # 恢复 = 三步 when 还原为原条件(记录于各步注释)+ gate 加回 `or not generated_review_ok`。
1120
1120
  - id: stage_generated_image_review
1121
1121
  title: "阶段14/22·生成图语义复核(已屏蔽)"
1122
- # 原条件:"inputs.quality == 'best' && fileExists('./build/img_plan.json')"
1122
+ # 原条件:"inputs.quality == 'best' && fileExists('out/ppt/${session}/build/img_plan.json')"
1123
1123
  when: "false"
1124
1124
  type: loop
1125
- over: "${asArray(readJson('./build/img_plan.json'))}"
1125
+ over: "${asArray(readJson('out/ppt/${session}/build/img_plan.json'))}"
1126
1126
  as: gen_img
1127
1127
  do:
1128
1128
  - id: review_generated_image
1129
1129
  title: "阶段14/22·复核生成图 #${gen_img.file}"
1130
- when: "fileExists('./build/assets/' + gen_img.file)"
1130
+ when: "fileExists('out/ppt/${session}/build/assets/' + gen_img.file)"
1131
1131
  history_mode: isolated
1132
1132
  llm: |
1133
1133
  你是 PPT 产品详情页配图验收员。请只看下方这一张生成图,并结合本轮 PPT 的目标判断它是否适合继续使用。
1134
1134
 
1135
1135
  必读上下文:
1136
- - 用户目标和原始要求:`./build/user_request.md`
1137
- - PPT 设计文档:`./build/design.json`
1136
+ - 用户目标和原始要求:`out/ppt/${session}/build/user_request.md`
1137
+ - PPT 设计文档:`out/ppt/${session}/build/design.json`
1138
1138
  - 图片计划条目:`${json(gen_img)}`
1139
1139
 
1140
1140
  只输出 JSON 对象,不要 markdown:
@@ -1155,15 +1155,15 @@ steps:
1155
1155
  - 不符合当前 VI 的色彩、材质、光线和产品详情页气质。
1156
1156
  - 图像花哨、拼贴、像 UI 卡片/缩略图阵列,不能作为 Apple/DJI 式产品详情页主视觉。
1157
1157
  context_files:
1158
- - path: "./build/user_request.md"
1158
+ - path: "out/ppt/${session}/build/user_request.md"
1159
1159
  hint: "用户原始目标与最高优先级要求"
1160
- - path: "./build/design.json"
1160
+ - path: "out/ppt/${session}/build/design.json"
1161
1161
  hint: "每页文案、视觉焦点与配图意图"
1162
- - path: "./build/img_plan.json"
1162
+ - path: "out/ppt/${session}/build/img_plan.json"
1163
1163
  hint: "当前生成图计划与提示词"
1164
1164
  allowed_tools: [Read]
1165
1165
  images:
1166
- - source: "./build/assets/${gen_img.file}"
1166
+ - source: "out/ppt/${session}/build/assets/${gen_img.file}"
1167
1167
  detail: high
1168
1168
  max_long_side_pixel: 1600
1169
1169
  capture:
@@ -1175,34 +1175,34 @@ steps:
1175
1175
  when: "length(generated_image_review_text) > 0"
1176
1176
  tool: Write
1177
1177
  args:
1178
- file_path: "./build/generated-image-reviews/${gen_img.file}.json"
1178
+ file_path: "out/ppt/${session}/build/generated-image-reviews/${gen_img.file}.json"
1179
1179
  content: "${generated_image_review_text}"
1180
1180
  onError: continue
1181
1181
 
1182
1182
  - id: stage_generated_image_review_collect
1183
1183
  title: "阶段15/22·汇总生成图语义复核(已屏蔽)"
1184
- # 原条件:"inputs.quality == 'best' && fileExists('./build/img_plan.json')"(屏蔽见阶段7c 头注)
1184
+ # 原条件:"inputs.quality == 'best' && fileExists('out/ppt/${session}/build/img_plan.json')"(屏蔽见阶段7c 头注)
1185
1185
  when: "false"
1186
1186
  tool: Bash
1187
1187
  args:
1188
1188
  command: |
1189
1189
  set -e
1190
- python3 skills/ui-design/scripts/generated_image_review_collect.py --build ./build
1190
+ python3 skills/ui-design/scripts/generated_image_review_collect.py --build out/ppt/${session}/build
1191
1191
  onError: continue
1192
1192
  max_retries: 1
1193
1193
 
1194
1194
  - id: stage_generated_image_retry
1195
1195
  title: "阶段16/22·生成图语义失败重试(已屏蔽)"
1196
- # 原条件:"inputs.quality == 'best' && fileExists('./build/generated_image_semantic_retry_report.json')"(屏蔽见阶段7c 头注)
1196
+ # 原条件:"inputs.quality == 'best' && fileExists('out/ppt/${session}/build/generated_image_semantic_retry_report.json')"(屏蔽见阶段7c 头注)
1197
1197
  when: "false"
1198
1198
  tool: GenerateImage
1199
1199
  args:
1200
- plan: ./build/img_plan.json
1201
- assets: ./build/assets
1200
+ plan: out/ppt/${session}/build/img_plan.json
1201
+ assets: out/ppt/${session}/build/assets
1202
1202
  size: 2k
1203
1203
  timeout_ms: 300000
1204
1204
  wall_budget_ms: 3600000
1205
- retry_degraded_report: ./build/generated_image_semantic_retry_report.json
1205
+ retry_degraded_report: out/ppt/${session}/build/generated_image_semantic_retry_report.json
1206
1206
  max_rounds: 3
1207
1207
  onError: continue
1208
1208
  max_retries: 1
@@ -1213,7 +1213,7 @@ steps:
1213
1213
  # (design.json/deck.html 在磁盘,每轮 Read 即可,不靠 history 记忆);loop 级断点支持中断续跑。
1214
1214
  - id: stage_build_loop
1215
1215
  title: "阶段17/22·分批生成网页(fast每轮≤5页·页数+客户图双闸门收口)"
1216
- when: "fileExists('./build/design_json_ok')"
1216
+ when: "fileExists('out/ppt/${session}/build/design_json_ok')"
1217
1217
  type: loop
1218
1218
  max_iterations: 6
1219
1219
  fresh_context: true
@@ -1237,8 +1237,8 @@ steps:
1237
1237
  proc = subprocess.run(cmd, cwd=str(root), text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
1238
1238
  return proc.returncode, proc.stdout.strip()
1239
1239
 
1240
- pages_status, pages_log = run(["python3", "skills/ui-design/scripts/deck_pages_check.py", "./build"])
1241
- source_status, source_log = run(["python3", "skills/ui-design/scripts/source_usage_check.py", "--build", "./build"])
1240
+ pages_status, pages_log = run(["python3", "skills/ui-design/scripts/deck_pages_check.py", "out/ppt/${session}/build"])
1241
+ source_status, source_log = run(["python3", "skills/ui-design/scripts/source_usage_check.py", "--build", "out/ppt/${session}/build"])
1242
1242
  pages_complete = pages_status == 0 and "COMPLETE" in pages_log
1243
1243
  source_ok = source_status == 0 and "SOURCE_USAGE_OK" in source_log
1244
1244
  report = {
@@ -1268,19 +1268,19 @@ steps:
1268
1268
  onError: halt
1269
1269
  - id: build_batch
1270
1270
  title: "阶段17/22·生成下一批页面"
1271
- when: "!fileExists('./build/build_complete')"
1271
+ when: "!fileExists('out/ppt/${session}/build/build_complete')"
1272
1272
  agent: |
1273
- 按设计文档**分批**生成 1920x1080 的 HTML deck(单文件 `./build/deck.html`、自包含 CSS)。
1274
- **本轮只做 `./build/build_probe.txt` 指出的缺页或未用客户图(默认 fast 最多 5 页),做完即止**——大 deck 一次写完会撞轮数上限、半途烂尾,分批是为了稳。
1273
+ 按设计文档**分批**生成 1920x1080 的 HTML deck(单文件 `out/ppt/${session}/build/deck.html`、自包含 CSS)。
1274
+ **本轮只做 `out/ppt/${session}/build/build_probe.txt` 指出的缺页或未用客户图(默认 fast 最多 5 页),做完即止**——大 deck 一次写完会撞轮数上限、半途烂尾,分批是为了稳。
1275
1275
 
1276
1276
  动手前从系统提示「可用技能」里 ui-design 的【技能目录】Read `references/layout-composition.md`
1277
1277
  (grid 结构化构图 + 三层语义化 HTML + 构图均衡),严格照其执行。
1278
1278
 
1279
1279
  ## 本轮步骤
1280
- 1. **先 Read `./build/build_probe.txt`**。这个文件由上一小步确定性生成;不要自己重新判断完成状态。
1280
+ 1. **先 Read `out/ppt/${session}/build/build_probe.txt`**。这个文件由上一小步确定性生成;不要自己重新判断完成状态。
1281
1281
  - 若显示页数 MISSING:按下面生成缺页。
1282
1282
  - 若显示 SOURCE_USAGE_UNUSED:把**未用的客户图补进最相关的页**(route=source 直接用,或 route=source-enhanced 增强后用,见下)。
1283
- - 🚫 **客户图引用契约(2026-07-02 坠机原图上墙事故)**:Read `./build/source_layout_plan.json`——
1283
+ - 🚫 **客户图引用契约(2026-07-02 坠机原图上墙事故)**:Read `out/ppt/${session}/build/source_layout_plan.json`——
1284
1284
  规划标 `enhance_required:true` 或 `has_embedded_text:true` 的图**只准引用 `enhanced_src_N`,绝不引用原图 `src_N`**(原图带字幕/水印,字会烧死在页面里);
1285
1285
  `usage_intent:"discard"` 的图**任何页都不准用**;`usage_intent:"direct_contain"`/`content_type:screenshot_slide` 的图只准 contain 框图展示、**绝不铺整页背景、绝不在其上叠正文**
1286
1286
  (视觉门有确定性【版式违规】ERROR 核对——contain/gallery/small_evidence 图出现在 `.bg-img`/CSS 背景必被打回,2026-07-03 第4页影子标题真机事故,写对省一轮);
@@ -1289,8 +1289,8 @@ steps:
1289
1289
  (背景驾驶舱+前景 3 张驾驶舱特写=重复堆料)——前景改放数据/图形/结论或留白,一页一主视觉。
1290
1290
  🚨🚫 **绝不修改 `img_plan.json`(2026-07-06 真机复盘:`build_batch` 往 `img_plan.json` 补了一条 `enhanced_src_4.png` 条目试图"修复"页6缺图,但阶段7 `stage_gen_images` 是只跑一次的确定性工具步、早已跑完,build_batch 阶段再编辑 `img_plan.json` 不会触发任何新的实际生图——结果是 `enhanced_src_4.png` 永远不存在于 `assets/`,deck 却引用了它,`source_usage_check` 判客户图 `src_4.png` 未用上,硬门失败,整个 `-p` 进程直接 halt,比平时"带残留放行"严重得多)**:写 HTML 前先用 Glob/Read 确认 `assets/enhanced_src_N.png` 这类文件**真实存在**再引用;**发现该文件不存在时,绝不要去编辑 `img_plan.json` 加条目"修复"它**(这个阶段编辑该文件毫无效果,只会留下一个此后永远 404 的幽灵引用)——正确做法是**直接引用原始 `assets/src_N.png`**(哪怕带一点原始水印/低清也好过引用不存在的文件,让下一轮 visual_repair_batch 或用户重跑整体流程处理这张图的增强),`img_plan.json` 只能由阶段 7 之前的确定性脚本步骤写,build_batch 对它只读不写。
1291
1291
  - 若两者都 OK,说明 build_probe 会直接 break,本 step 不应运行。
1292
- 2. **若 MISSING**:Read `./build/design.json` 拿到**缺页**(build_probe 列出的页号)的设计(archetype/composition/blocks.render/key_point/points/visual/background);
1293
- Read `./build/deck.html`(若已存在)了解已有结构与**全局 CSS / VI 视觉系统**(保持一致)。
1292
+ 2. **若 MISSING**:Read `out/ppt/${session}/build/design.json` 拿到**缺页**(build_probe 列出的页号)的设计(archetype/composition/blocks.render/key_point/points/visual/background);
1293
+ Read `out/ppt/${session}/build/deck.html`(若已存在)了解已有结构与**全局 CSS / VI 视觉系统**(保持一致)。
1294
1294
  3. **生成缺页里的下一批(默认 fast 最多 5 页)**,每页一个 `<section class="slide" data-screen-label="NN ...">`(NN=该页 page 号两位数、含中文标题),
1295
1295
  必须先读取该页的 `composition_style / page_grammar / one_sentence_takeaway / visual_focus / text_zone / safe_text_zone / visual_role / primary_visual_type / image_weight / text_overlay_mode / max_visible_words / copy_budget / main_visual_layer / supporting_visual_layer / text_overlay_zone / visual_story / semantic_image_prompt / forbidden_layout`,
1296
1296
  然后按这些字段实现;不要只看 `archetype/composition` 自由发挥。
@@ -1371,7 +1371,7 @@ steps:
1371
1371
  - **排不下时的自救顺序(绝不缩字到 <24px、绝不顶出画布)**:① **精炼文案 + 图文并茂**(流程/对比/层级/数据用 HTML 图表/SVG/图标/卡片表达,一图省一段话)→ ② 一列条目多就改 **grid 两列**(`grid-template-columns:1fr 1fr`)→ ③ 仍排不下就**拆成两页**(新增一个 `<section class="slide">`、同一主题分上下篇,别死磕一页)。导出端【超出画布】【字号过小】WARNING 触发时按此序处理。
1372
1372
  - **配色解耦(治一片单色)**:只有背景/页面底板/chrome 用 VI;**图表数据系列用可区分分类色**(见所选 VI 文档「数据可视化分类色」)、非背景插图用画面真实色、辅助解释图形用语义色,都与 VI 协调不冲突(见 §0.9)。
1373
1373
  - **背景按 design 的 background 意图**做同色系深浅/光位变化,相邻页不雷同。
1374
- - **客户图(现已【全部 route=source-enhanced 图生图重绘到槽位比例】)= 必须全用 + 填满槽(`.fig-fill` cover)+ 按朝向/重点区图文混排 + 配色随图**(首轮动手前 Read ui-design 技能目录的 `references/source-image-layout.md`,并 Read `./build/material.json` 取每图的 vision/stats):
1374
+ - **客户图(现已【全部 route=source-enhanced 图生图重绘到槽位比例】)= 必须全用 + 填满槽(`.fig-fill` cover)+ 按朝向/重点区图文混排 + 配色随图**(首轮动手前 Read ui-design 技能目录的 `references/source-image-layout.md`,并 Read `out/ppt/${session}/build/material.json` 取每图的 vision/stats):
1375
1375
  **plan_source_layout 已把所有客户图 `enhance_required=true`**(唯一例外=无 vision 盲态)——用 `<img src="assets/<enhanced_file>">` 并在 `data-source-file="<source_file>"` 保留原图映射(用尽闸门/追溯用)。
1376
1376
  若 `enhanced_file` 生成失败,回退 `<img src="assets/<source_file>">` 的合适布局,不能删除该客户图、不能空占位(缺图兜底脚本会自动 cp 原图顶上,见 stage_export `--reconcile-missing`)。
1377
1377
  **图生图已重绘到槽位目标比例,所以放进 §0.7 `.fig-fill` 用 `object-fit:cover` 填满该槽**(配 `object-position:<key_region 中心%>` 保重点)——**默认 cover 填满、不留白裱框/letterbox 黑边**;**仅 dense-info(截图/图表,需完整保数据)才 `object-fit:contain`**(其槽 `aspect-ratio` 按图比例定、零裁切)。🚫禁 `max-height` 帽、🚫禁 `.src-figure{background:白/surface;padding}` 白裱框(真凶 2)。
@@ -1435,7 +1435,7 @@ steps:
1435
1435
  - **封面/章节文字对比度铁律(治浅底图白字看不清)**:氛围底图亮暗不可控(浅色模板 AI 底图常偏亮),**别默认白字靠弱 scrim**。浅色模板首选 **① 深字(`var(--ink)`) + 浅色磨砂文字板**(`background:rgba(255,255,255,.55);backdrop-filter:blur(6px)`);要白字则 **② scrim/板压到足够暗(不透明度 ≥ .72)**(见 §1.B 两套 CSS)。
1436
1436
  - **同一条铁律对所有内容页同样生效,不止封面/章节**(真机事故:p5「三大结构性盲区」list 页 `deck-title`/`deck-subtitle` 直接叠在全幅 AI 照片背景上、无任何 scrim,像素实测最差对比度 1.00,标题实质不可读)——只要页面结构是「`.edit-bg` 全幅 AI/照片背景 + `.deck-header` 直接叠加」(内容页背景层是硬规则、见上「每个内容页都有背景层」),无论页面 route 是 list/split/dense 哪种,都必须给 `.deck-header` 区域加同等保护(局部 scrim 紧贴 header 区域,或整页 scrim,或深字+磨砂板),不能因为"这是内容页不是封面"就假设背景干净、省掉这层——内容页 AI 背景要求"低干扰"是生图侧的目标,不是版式侧可以省略保护的理由。
1437
1437
  - 🚨 **VI 自己定义的强调色/次要文字色变量(如 `--arsenal-blue`「eyebrow 标签色」、`--ink-soft`/`--muted`「副标/次要说明色」)默认按纯白背景校准,套进 vi_fallback 渐变背景/AI图/照片背景时不能假设天然够对比度(2026-07-07 真机复盘:单份 deck 里 26 处像素实测不可读,锁定 4 个 CSS 变量值——`--arsenal-blue`/`--arsenal-blue-deep`/`--ink-soft`/`--muted`——全 deck 复用,落在灰白渐变/深色照片背景时对比度普遍跌破 3.0,最差仅 1.01)**:这些"品牌强调色/次要色"跟普通正文字一样必须过对比度检查,不能因为"这是 VI 标准配色,看起来专业"就跳过验证——写这类文字前先确认它实际落点的背景亮度,背景不是纯白就要么换成 `var(--ink)`/白色高对比色,要么给这块文字单独加局部 scrim/色块;不要在非白背景上无条件复用这几个 VI 强调色变量。
1438
- 4. **写入 `./build/deck.html`**:首轮若文件不存在 → 新建含 `<head>` + 全局 CSS(§0.7 流式骨架:`body` 深色衬底 + `.slide{position:relative;box-sizing:border-box;width:1920px;height:1080px;margin:0 auto 40px;padding:56px 112px 48px}`(**统一内容内边距:左右 112 不贴边、上 56 下 48 少吃竖向**)纵向排开 + VI 视觉系统 + `[hidden]{display:none!important}`)+ 本批 section;
1438
+ 4. **写入 `out/ppt/${session}/build/deck.html`**:首轮若文件不存在 → 新建含 `<head>` + 全局 CSS(§0.7 流式骨架:`body` 深色衬底 + `.slide{position:relative;box-sizing:border-box;width:1920px;height:1080px;margin:0 auto 40px;padding:56px 112px 48px}`(**统一内容内边距:左右 112 不贴边、上 56 下 48 少吃竖向**)纵向排开 + VI 视觉系统 + `[hidden]{display:none!important}`)+ 本批 section;
1439
1439
  后续轮 → 用 Edit 把本批 `<section>` 追加进 `</body>` 前(保持单文件自包含、CSS 不重复定义)。
1440
1440
  5. **deck 卫生**:不要加 Tweaks/localStorage/postMessage/fitStage/翻页 nav/React/Babel(对导出无用、只干扰)。
1441
1441
  6. 做完本批后不要自判完成,也不要强行输出完成标记。下一轮 `build_probe` 会重新跑页数和客户图闸门,OK 时自动 `build_complete` 并 break。
@@ -1447,12 +1447,12 @@ steps:
1447
1447
  max_retries: 1
1448
1448
  - id: build_done
1449
1449
  type: break
1450
- when: "fileExists('./build/build_complete')"
1450
+ when: "fileExists('out/ppt/${session}/build/build_complete')"
1451
1451
 
1452
1452
  # ---------- 阶段14b:产品详情页视觉硬门(确定性,不再交给 agent 自觉执行) ----------
1453
1453
  - id: stage_visual_quality_gate
1454
1454
  title: "阶段18/22·产品详情页视觉硬门(最多两轮修复)"
1455
- when: "fileExists('./build/deck.html')"
1455
+ when: "fileExists('out/ppt/${session}/build/deck.html')"
1456
1456
  type: loop
1457
1457
  # 轮数 3→2(2026-07-02 用户裁决);修复步同步开放给 fast——上一轮 fast 跑出 41 个 ERROR
1458
1458
  # 却因 when 限死 best 而 0 次修复带残留放行,是缺陷出货的直接原因。
@@ -1470,32 +1470,32 @@ steps:
1470
1470
  # 不会误读上一轮(修复前)的陈旧 report → 不误判。
1471
1471
  # --with-bg-shots:顺带落背景态截图(slide-NN-background.png),像素级文字可读性门
1472
1472
  # (text_readability_pixel.py)按文本矩形采真实像素——CSS 对比度在压图文字上物理测不准。
1473
- mkdir -p ./build/export
1474
- rm -f ./build/export/layout-report.json
1475
- node skills/ui-design/scripts/export_pptx_from_html.mjs ./build/deck.html \
1476
- --measure-only --with-bg-shots --mode hybrid --width 1920 --height 1080 --workdir ./build/export
1473
+ mkdir -p out/ppt/${session}/build/export
1474
+ rm -f out/ppt/${session}/build/export/layout-report.json
1475
+ node skills/ui-design/scripts/export_pptx_from_html.mjs out/ppt/${session}/build/deck.html \
1476
+ --measure-only --with-bg-shots --mode hybrid --width 1920 --height 1080 --workdir out/ppt/${session}/build/export
1477
1477
  onError: continue
1478
1478
  - id: visual_quality_check
1479
1479
  title: "阶段18/22·视觉质量检查"
1480
1480
  tool: Bash
1481
1481
  args:
1482
1482
  command: |
1483
- python3 skills/ui-design/scripts/visual_quality_gate.py --build ./build --max-rounds 2 --strict-product-detail
1483
+ python3 skills/ui-design/scripts/visual_quality_gate.py --build out/ppt/${session}/build --max-rounds 2 --strict-product-detail
1484
1484
  onError: halt
1485
1485
  max_retries: 0 # 确定性检查步:重试=原样重跑纯空转(exit 1 仅剩 hard 档=真缺陷,halt 正确)
1486
1486
  - id: visual_quality_done
1487
1487
  type: break
1488
- when: "fileExists('./build/visual_quality_ok')"
1488
+ when: "fileExists('out/ppt/${session}/build/visual_quality_ok')"
1489
1489
  - id: visual_repair_batch
1490
1490
  title: "阶段18/22·修复前几个视觉问题"
1491
- when: "fileExists('./build/visual_quality_report.md') && !fileExists('./build/visual_quality_ok')"
1491
+ when: "fileExists('out/ppt/${session}/build/visual_quality_report.md') && !fileExists('out/ppt/${session}/build/visual_quality_ok')"
1492
1492
  agent: |
1493
- 只修 `./build/visual_quality_report.md` 里最高优先级的前 3–5 个问题,不要重写整份 deck。
1493
+ 只修 `out/ppt/${session}/build/visual_quality_report.md` 里最高优先级的前 3–5 个问题,不要重写整份 deck。
1494
1494
 
1495
1495
  必读:
1496
- - `./build/visual_quality_report.md`
1497
- - `./build/design.json`
1498
- - `./build/deck.html`
1496
+ - `out/ppt/${session}/build/visual_quality_report.md`
1497
+ - `out/ppt/${session}/build/design.json`
1498
+ - `out/ppt/${session}/build/deck.html`
1499
1499
 
1500
1500
  🚨 **先看报告里有没有"⚠️ N 处文字压图问题共享同一文字颜色 rgb(...)"这行归组提示(2026-07-03 真机
1501
1501
  复盘:27 处 ERROR 里 15+ 处共享同一个 VI 强调色 CSS 变量,逐条加局部 scrim 在 2 轮修复预算内根本
@@ -1537,8 +1537,8 @@ steps:
1537
1537
  - data-visual 缺 SVG/chart/axis/bar/line:补可编辑 HTML/SVG 图形,别用大表格。
1538
1538
  - 【AI 背景图被 scrim 盖到不可辨识】(2026-07-05 复盘:list/metric 数据结构页 `image_weight≈0` 却套了 0.82~0.90 近纯白整页 scrim,生成的低干扰材质背景在截图上完全看不出痕迹,等于图片形同虚设):把该页 `.edit-scrim` 的整页不透明度降到 ≤0.6,让底图纹理隐约可辨;若因此文字/图例对比度不够,改成只在文字/图例紧贴区域加局部深色小背板,不要靠拉高整页不透明度解决。
1539
1539
  - 低对比/小字/孤立连线:统一字体 token、节点和连线归属。
1540
- - **内容飞出画布/整页不可见**(几何 catastrophic,见 `./build/visual_quality_report.md` 的 layout_balance 段:某页 coverage≈0 或元素可见部分<20%):**报告里这条即使文字前缀写的是"WARNING"也必须排进本批最优先修——它对应的是导出阶段 `--catastrophic-only` 硬门(无重试预算,2026-07-05 真机两次因此在 `stage_export` 直接 halt 整个 `-p` 进程),比其它真正的 WARNING 后果严重得多,别被"WARNING"字面误导降低优先级**。多为 CSS Grid 轨道被大图撑爆 → 按 `references/layout-composition.md §0.7` 给 `.edit-content>*` 加 `min-width:0;min-height:0`,或收敛该页图尺寸/改用不炸轨的版式,让标题正文回到画布内。**若该块是 `.fig-contain`/`.fig-fill` 且内联 style 里带了 `aspect-ratio`(2026-07-05 第6/8页事故):删掉这个内联 `aspect-ratio`,让类自带的 `width:100%;height:100%` 生效——这两个类本身已经安全,加 `aspect-ratio` 就是在覆盖它。**
1541
- - 修完本批(3–5 个)后重跑 `python3 skills/ui-design/scripts/deck_visual_style_check.py ./build/deck.html --design ./build/design.json --image-report ./build/image_generation_report.json --source-plan ./build/source_layout_plan.json --strict-product-detail`,确认**本批问题已消**;本批消掉即收尾(不能只写 residual 糊弄),**剩余/新暴露的问题留给外层循环下一轮重新测量再修**——别在本步内追着全量 ERROR 无界修下去(那会撞 maxTurns 被截断)。
1540
+ - **内容飞出画布/整页不可见**(几何 catastrophic,见 `out/ppt/${session}/build/visual_quality_report.md` 的 layout_balance 段:某页 coverage≈0 或元素可见部分<20%):**报告里这条即使文字前缀写的是"WARNING"也必须排进本批最优先修——它对应的是导出阶段 `--catastrophic-only` 硬门(无重试预算,2026-07-05 真机两次因此在 `stage_export` 直接 halt 整个 `-p` 进程),比其它真正的 WARNING 后果严重得多,别被"WARNING"字面误导降低优先级**。多为 CSS Grid 轨道被大图撑爆 → 按 `references/layout-composition.md §0.7` 给 `.edit-content>*` 加 `min-width:0;min-height:0`,或收敛该页图尺寸/改用不炸轨的版式,让标题正文回到画布内。**若该块是 `.fig-contain`/`.fig-fill` 且内联 style 里带了 `aspect-ratio`(2026-07-05 第6/8页事故):删掉这个内联 `aspect-ratio`,让类自带的 `width:100%;height:100%` 生效——这两个类本身已经安全,加 `aspect-ratio` 就是在覆盖它。**
1541
+ - 修完本批(3–5 个)后重跑 `python3 skills/ui-design/scripts/deck_visual_style_check.py out/ppt/${session}/build/deck.html --design out/ppt/${session}/build/design.json --image-report out/ppt/${session}/build/image_generation_report.json --source-plan out/ppt/${session}/build/source_layout_plan.json --strict-product-detail`,确认**本批问题已消**;本批消掉即收尾(不能只写 residual 糊弄),**剩余/新暴露的问题留给外层循环下一轮重新测量再修**——别在本步内追着全量 ERROR 无界修下去(那会撞 maxTurns 被截断)。
1542
1542
 
1543
1543
  不要碰已正常的页面。完成后简述修改页号。
1544
1544
  allowed_tools: [Read, Edit, Write, Glob]
@@ -1561,10 +1561,10 @@ steps:
1561
1561
  set -euo pipefail
1562
1562
  OUT="${inputs.out}"
1563
1563
  MODE="${inputs.mode}"
1564
- mkdir -p "$(dirname "$OUT")" ./build/export
1564
+ mkdir -p "$(dirname "$OUT")" out/ppt/${session}/build/export
1565
1565
 
1566
1566
  echo "[export] deck pages check (完整度硬门 + 页序报告)"
1567
- python3 skills/ui-design/scripts/deck_pages_check.py ./build --check-order || {
1567
+ python3 skills/ui-design/scripts/deck_pages_check.py out/ppt/${session}/build --check-order || {
1568
1568
  rc=$?
1569
1569
  # 完整度缺页(exit 1/2) → 硬门 halt;仅页序错乱(exit 3) → 非阻断报告
1570
1570
  # (批量生成天然乱序,别为顺序反复 halt 烧配额;ORDER_MISMATCH 已在上面日志留痕,建议按编号重排 <section>)。
@@ -1572,43 +1572,43 @@ steps:
1572
1572
  }
1573
1573
 
1574
1574
  echo "[export] ui artifact check"
1575
- python3 skills/ui-design/scripts/check_ui_artifact.py --for-pptx-export ./build/deck.html
1575
+ python3 skills/ui-design/scripts/check_ui_artifact.py --for-pptx-export out/ppt/${session}/build/deck.html
1576
1576
 
1577
1577
  echo "[export] reconcile missing enhanced/source assets"
1578
- python3 skills/ui-design/scripts/plan_source_layout.py --reconcile-missing --build ./build
1578
+ python3 skills/ui-design/scripts/plan_source_layout.py --reconcile-missing --build out/ppt/${session}/build
1579
1579
 
1580
1580
  echo "[export] source usage hard gate"
1581
- python3 skills/ui-design/scripts/source_usage_check.py --build ./build --require-existing-derived
1581
+ python3 skills/ui-design/scripts/source_usage_check.py --build out/ppt/${session}/build --require-existing-derived
1582
1582
 
1583
1583
  # deck_visual_style_check 不在此重跑(减法:与阶段18 视觉门完全同参双跑)——
1584
1584
  # 它已在 stage_visual_quality_gate 有界循环里跑过(fast/best 统一 2 轮修复,2026-07-02 裁决),
1585
1585
  # 达轮数带残留放行(visual_quality_ok + visual_quality_residual.md)。此处只验哨兵。
1586
1586
  echo "[export] visual quality gate must already be accepted"
1587
- test -f ./build/visual_quality_ok
1587
+ test -f out/ppt/${session}/build/visual_quality_ok
1588
1588
 
1589
1589
  echo "[export] measure-only layout report"
1590
- node skills/ui-design/scripts/export_pptx_from_html.mjs ./build/deck.html \
1591
- --measure-only --mode hybrid --width 1920 --height 1080 --workdir ./build/export
1590
+ node skills/ui-design/scripts/export_pptx_from_html.mjs out/ppt/${session}/build/deck.html \
1591
+ --measure-only --mode hybrid --width 1920 --height 1080 --workdir out/ppt/${session}/build/export
1592
1592
 
1593
1593
  echo "[export] layout balance report"
1594
- python3 skills/ui-design/scripts/layout_balance.py ./build/export/layout-report.json --no-occupancy -v \
1595
- --source-layout-plan ./build/source_layout_plan.json || true
1596
- touch ./build/layout_ok
1594
+ python3 skills/ui-design/scripts/layout_balance.py out/ppt/${session}/build/export/layout-report.json --no-occupancy -v \
1595
+ --source-layout-plan out/ppt/${session}/build/source_layout_plan.json || true
1596
+ touch out/ppt/${session}/build/layout_ok
1597
1597
 
1598
1598
  echo "[export] geometry catastrophic hard gate(内容不可见零容忍:严重飞出画布 / 无真实视觉空页 → 拒绝导出)"
1599
1599
  # 只拦「内容根本看不到」的灾难(可见<20% 越界 + content_boxes==0 且无真实视觉),
1600
1600
  # 不含重叠/占空族噪声 → 干净 deck 不误伤、不重烧配额。治本在 layout-composition §0.7 的 min-width:0 骨架;此为兜底网。
1601
- python3 skills/ui-design/scripts/layout_balance.py ./build/export/layout-report.json --catastrophic-only
1601
+ python3 skills/ui-design/scripts/layout_balance.py out/ppt/${session}/build/export/layout-report.json --catastrophic-only
1602
1602
 
1603
1603
  echo "[export] write pptx"
1604
- node skills/ui-design/scripts/export_pptx_from_html.mjs ./build/deck.html \
1605
- --out "$OUT" --mode "$MODE" --width 1920 --height 1080 --workdir ./build/export
1604
+ node skills/ui-design/scripts/export_pptx_from_html.mjs out/ppt/${session}/build/deck.html \
1605
+ --out "$OUT" --mode "$MODE" --width 1920 --height 1080 --workdir out/ppt/${session}/build/export
1606
1606
 
1607
1607
  echo "[export] opc check"
1608
1608
  python3 skills/pptx/scripts/opc_check.py "$OUT"
1609
1609
  rm -f "$OUT.tmp" "$OUT.fixed.pptx" 2>/dev/null || true
1610
1610
 
1611
- cat > ./build/export-summary.md <<EOF
1611
+ cat > out/ppt/${session}/build/export-summary.md <<EOF
1612
1612
  输出路径: $OUT
1613
1613
  导出模式: $MODE
1614
1614
  可编辑性: hybrid=背景不可编辑,文本和部分前景对象可编辑;flat=整页图片不可编辑
@@ -1635,10 +1635,10 @@ steps:
1635
1635
  command: |
1636
1636
  set -euo pipefail
1637
1637
  if [ "${inputs.debug_keep_build}" = "false" ]; then
1638
- rm -rf ./build
1639
- echo "[cleanup] debug_keep_build=false,已清理 ./build,仅保留最终 PPT:${inputs.out}"
1638
+ rm -rf out/ppt/${session}/build
1639
+ echo "[cleanup] debug_keep_build=false,已清理 out/ppt/${session}/build,仅保留最终 PPT:${inputs.out}"
1640
1640
  else
1641
- echo "[cleanup] debug_keep_build=true,保留 ./build 中间产物用于排障:deck.html / design.json / export 报告 / 截图。"
1641
+ echo "[cleanup] debug_keep_build=true,保留 out/ppt/${session}/build 中间产物用于排障:deck.html / design.json / export 报告 / 截图。"
1642
1642
  fi
1643
1643
  onError: continue
1644
1644
 
@@ -1665,6 +1665,6 @@ steps:
1665
1665
  - 流程:摄取素材(抽文字+图片+图像统计)→ 逐图多模态识图 → 合并「用户原始材料」统一文件(material.md/json)→ 客户材料成稿(默认不联网)→ 详细 Web 页面设计文档(客户图按识图内容高权重配页,全部图生图重绘到高清并贴 PPT 风格)→ 配图分流 → 文生图/图生图 → 分批生成网页(页数+客户图用尽双闸门)→ 产品详情页视觉硬门 → 三层导出。
1666
1666
  - 可编辑性:hybrid 三层=背景整页截图垫底(不可编辑),前景每个离散元素(卡片/色块/图标/SVG/插图)为独立可编辑图片对象,
1667
1667
  文本为 PPT 原生可编辑文本框;全幅氛围背景图(B 路线)连同遮罩留背景层;flat=全图不可编辑。
1668
- - 中间产物处理:由 debug_keep_build=${inputs.debug_keep_build} 控制;默认保留 ./build 用于检查 deck.html、design.json、截图和报告,设为 false 时只保留最终 PPT。
1668
+ - 中间产物处理:由 debug_keep_build=${inputs.debug_keep_build} 控制;默认保留 out/ppt/${session}/build 用于检查 deck.html、design.json、截图和报告,设为 false 时只保留最终 PPT。
1669
1669
 
1670
1670
  用 PowerPoint / WPS 打开 [${inputs.out}](${inputs.out}) 核验。