psyclaw 0.24.0 → 0.25.1

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 (277) hide show
  1. package/README.md +46 -53
  2. package/{dist/apps → apps}/panel/index.html +28 -50
  3. package/package.json +10 -20
  4. package/scripts/__pycache__/build_dist.cpython-314.pyc +0 -0
  5. package/scripts/__pycache__/build_whitepaper_docx.cpython-314.pyc +0 -0
  6. package/scripts/build_preprint_docx.py +729 -0
  7. package/scripts/build_preprint_figures.py +238 -0
  8. package/scripts/build_three_line_table_fixture.py +56 -0
  9. package/scripts/build_whitepaper_docx.py +768 -0
  10. package/scripts/check-branding.mjs +159 -0
  11. package/scripts/copy-panel.mjs +11 -0
  12. package/scripts/rebrand-pi.mjs +291 -0
  13. package/scripts/verify_docx.py +41 -0
  14. package/skills/core/citation-audit/SKILL.md +9 -9
  15. package/skills/core/evidence-capture/SKILL.md +9 -9
  16. package/skills/core/manifest.json +15 -15
  17. package/skills/core/research-brief/SKILL.md +8 -8
  18. package/skills/core/research-intake/SKILL.md +9 -9
  19. package/dist/src/adapters/pi/extension.d.ts +0 -19
  20. package/dist/src/adapters/pi/extension.js +0 -735
  21. package/dist/src/adapters/pi/extension.js.map +0 -1
  22. package/dist/src/adapters/pi/model.d.ts +0 -72
  23. package/dist/src/adapters/pi/model.js +0 -152
  24. package/dist/src/adapters/pi/model.js.map +0 -1
  25. package/dist/src/adapters/pi/resources.d.ts +0 -23
  26. package/dist/src/adapters/pi/resources.js +0 -54
  27. package/dist/src/adapters/pi/resources.js.map +0 -1
  28. package/dist/src/adapters/pi/rpc.d.ts +0 -56
  29. package/dist/src/adapters/pi/rpc.js +0 -248
  30. package/dist/src/adapters/pi/rpc.js.map +0 -1
  31. package/dist/src/adapters/pi/session.d.ts +0 -30
  32. package/dist/src/adapters/pi/session.js +0 -64
  33. package/dist/src/adapters/pi/session.js.map +0 -1
  34. package/dist/src/agents/catalog.d.ts +0 -30
  35. package/dist/src/agents/catalog.js +0 -118
  36. package/dist/src/agents/catalog.js.map +0 -1
  37. package/dist/src/agents/discover.d.ts +0 -28
  38. package/dist/src/agents/discover.js +0 -127
  39. package/dist/src/agents/discover.js.map +0 -1
  40. package/dist/src/agents/import.d.ts +0 -67
  41. package/dist/src/agents/import.js +0 -801
  42. package/dist/src/agents/import.js.map +0 -1
  43. package/dist/src/analysis/hooks.d.ts +0 -81
  44. package/dist/src/analysis/hooks.js +0 -139
  45. package/dist/src/analysis/hooks.js.map +0 -1
  46. package/dist/src/analysis/pre-registration.d.ts +0 -18
  47. package/dist/src/analysis/pre-registration.js +0 -86
  48. package/dist/src/analysis/pre-registration.js.map +0 -1
  49. package/dist/src/branding.d.ts +0 -43
  50. package/dist/src/branding.js +0 -127
  51. package/dist/src/branding.js.map +0 -1
  52. package/dist/src/chat.d.ts +0 -16
  53. package/dist/src/chat.js +0 -67
  54. package/dist/src/chat.js.map +0 -1
  55. package/dist/src/cli.d.ts +0 -2
  56. package/dist/src/cli.js +0 -316
  57. package/dist/src/cli.js.map +0 -1
  58. package/dist/src/core/citations.d.ts +0 -44
  59. package/dist/src/core/citations.js +0 -48
  60. package/dist/src/core/citations.js.map +0 -1
  61. package/dist/src/core/contracts.d.ts +0 -119
  62. package/dist/src/core/contracts.js +0 -2
  63. package/dist/src/core/contracts.js.map +0 -1
  64. package/dist/src/core/docx.d.ts +0 -11
  65. package/dist/src/core/docx.js +0 -46
  66. package/dist/src/core/docx.js.map +0 -1
  67. package/dist/src/core/doi.d.ts +0 -41
  68. package/dist/src/core/doi.js +0 -82
  69. package/dist/src/core/doi.js.map +0 -1
  70. package/dist/src/core/evidence-policy.d.ts +0 -24
  71. package/dist/src/core/evidence-policy.js +0 -308
  72. package/dist/src/core/evidence-policy.js.map +0 -1
  73. package/dist/src/core/filekind.d.ts +0 -15
  74. package/dist/src/core/filekind.js +0 -82
  75. package/dist/src/core/filekind.js.map +0 -1
  76. package/dist/src/core/hash.d.ts +0 -2
  77. package/dist/src/core/hash.js +0 -17
  78. package/dist/src/core/hash.js.map +0 -1
  79. package/dist/src/core/pricing.d.ts +0 -38
  80. package/dist/src/core/pricing.js +0 -110
  81. package/dist/src/core/pricing.js.map +0 -1
  82. package/dist/src/core/redact.d.ts +0 -12
  83. package/dist/src/core/redact.js +0 -48
  84. package/dist/src/core/redact.js.map +0 -1
  85. package/dist/src/core/references.d.ts +0 -55
  86. package/dist/src/core/references.js +0 -102
  87. package/dist/src/core/references.js.map +0 -1
  88. package/dist/src/core/schemas.d.ts +0 -162
  89. package/dist/src/core/schemas.js +0 -160
  90. package/dist/src/core/schemas.js.map +0 -1
  91. package/dist/src/extension.d.ts +0 -1
  92. package/dist/src/extension.js +0 -2
  93. package/dist/src/extension.js.map +0 -1
  94. package/dist/src/index.d.ts +0 -72
  95. package/dist/src/index.js +0 -73
  96. package/dist/src/index.js.map +0 -1
  97. package/dist/src/install/installer.d.ts +0 -147
  98. package/dist/src/install/installer.js +0 -743
  99. package/dist/src/install/installer.js.map +0 -1
  100. package/dist/src/integrations/contracts.d.ts +0 -35
  101. package/dist/src/integrations/contracts.js +0 -27
  102. package/dist/src/integrations/contracts.js.map +0 -1
  103. package/dist/src/integrations/mcp.d.ts +0 -155
  104. package/dist/src/integrations/mcp.js +0 -826
  105. package/dist/src/integrations/mcp.js.map +0 -1
  106. package/dist/src/knowledge-map/contracts.d.ts +0 -20
  107. package/dist/src/knowledge-map/contracts.js +0 -2
  108. package/dist/src/knowledge-map/contracts.js.map +0 -1
  109. package/dist/src/knowledge-map/index.d.ts +0 -4
  110. package/dist/src/knowledge-map/index.js +0 -39
  111. package/dist/src/knowledge-map/index.js.map +0 -1
  112. package/dist/src/literature/contracts.d.ts +0 -106
  113. package/dist/src/literature/contracts.js +0 -2
  114. package/dist/src/literature/contracts.js.map +0 -1
  115. package/dist/src/literature/fulltext.d.ts +0 -7
  116. package/dist/src/literature/fulltext.js +0 -24
  117. package/dist/src/literature/fulltext.js.map +0 -1
  118. package/dist/src/literature/knowledge-map.d.ts +0 -2
  119. package/dist/src/literature/knowledge-map.js +0 -20
  120. package/dist/src/literature/knowledge-map.js.map +0 -1
  121. package/dist/src/literature/ledger.d.ts +0 -3
  122. package/dist/src/literature/ledger.js +0 -17
  123. package/dist/src/literature/ledger.js.map +0 -1
  124. package/dist/src/literature/metadata.d.ts +0 -6
  125. package/dist/src/literature/metadata.js +0 -32
  126. package/dist/src/literature/metadata.js.map +0 -1
  127. package/dist/src/memory/store.d.ts +0 -22
  128. package/dist/src/memory/store.js +0 -73
  129. package/dist/src/memory/store.js.map +0 -1
  130. package/dist/src/orchestration/contracts.d.ts +0 -61
  131. package/dist/src/orchestration/contracts.js +0 -2
  132. package/dist/src/orchestration/contracts.js.map +0 -1
  133. package/dist/src/orchestration/pi-executor.d.ts +0 -28
  134. package/dist/src/orchestration/pi-executor.js +0 -169
  135. package/dist/src/orchestration/pi-executor.js.map +0 -1
  136. package/dist/src/orchestration/runner.d.ts +0 -196
  137. package/dist/src/orchestration/runner.js +0 -991
  138. package/dist/src/orchestration/runner.js.map +0 -1
  139. package/dist/src/orchestration/scheduler.d.ts +0 -8
  140. package/dist/src/orchestration/scheduler.js +0 -335
  141. package/dist/src/orchestration/scheduler.js.map +0 -1
  142. package/dist/src/panel/events.d.ts +0 -20
  143. package/dist/src/panel/events.js +0 -93
  144. package/dist/src/panel/events.js.map +0 -1
  145. package/dist/src/panel/extension.d.ts +0 -8
  146. package/dist/src/panel/extension.js +0 -114
  147. package/dist/src/panel/extension.js.map +0 -1
  148. package/dist/src/panel/literature-map.d.ts +0 -26
  149. package/dist/src/panel/literature-map.js +0 -34
  150. package/dist/src/panel/literature-map.js.map +0 -1
  151. package/dist/src/panel/projection.d.ts +0 -51
  152. package/dist/src/panel/projection.js +0 -288
  153. package/dist/src/panel/projection.js.map +0 -1
  154. package/dist/src/panel/server.d.ts +0 -46
  155. package/dist/src/panel/server.js +0 -1610
  156. package/dist/src/panel/server.js.map +0 -1
  157. package/dist/src/panel/usage.d.ts +0 -53
  158. package/dist/src/panel/usage.js +0 -150
  159. package/dist/src/panel/usage.js.map +0 -1
  160. package/dist/src/project/bootstrap.d.ts +0 -10
  161. package/dist/src/project/bootstrap.js +0 -83
  162. package/dist/src/project/bootstrap.js.map +0 -1
  163. package/dist/src/project/hitl.d.ts +0 -12
  164. package/dist/src/project/hitl.js +0 -43
  165. package/dist/src/project/hitl.js.map +0 -1
  166. package/dist/src/project/jsonl.d.ts +0 -16
  167. package/dist/src/project/jsonl.js +0 -93
  168. package/dist/src/project/jsonl.js.map +0 -1
  169. package/dist/src/project/manuscript.d.ts +0 -21
  170. package/dist/src/project/manuscript.js +0 -74
  171. package/dist/src/project/manuscript.js.map +0 -1
  172. package/dist/src/project/paths.d.ts +0 -38
  173. package/dist/src/project/paths.js +0 -166
  174. package/dist/src/project/paths.js.map +0 -1
  175. package/dist/src/project/versions.d.ts +0 -10
  176. package/dist/src/project/versions.js +0 -14
  177. package/dist/src/project/versions.js.map +0 -1
  178. package/dist/src/psyclaw-theme.d.ts +0 -88
  179. package/dist/src/psyclaw-theme.js +0 -89
  180. package/dist/src/psyclaw-theme.js.map +0 -1
  181. package/dist/src/research/brief.d.ts +0 -11
  182. package/dist/src/research/brief.js +0 -81
  183. package/dist/src/research/brief.js.map +0 -1
  184. package/dist/src/research/ledger.d.ts +0 -12
  185. package/dist/src/research/ledger.js +0 -43
  186. package/dist/src/research/ledger.js.map +0 -1
  187. package/dist/src/run.d.ts +0 -20
  188. package/dist/src/run.js +0 -34
  189. package/dist/src/run.js.map +0 -1
  190. package/dist/src/setup.d.ts +0 -54
  191. package/dist/src/setup.js +0 -221
  192. package/dist/src/setup.js.map +0 -1
  193. package/dist/src/skills/contracts.d.ts +0 -105
  194. package/dist/src/skills/contracts.js +0 -3
  195. package/dist/src/skills/contracts.js.map +0 -1
  196. package/dist/src/skills/preflight.d.ts +0 -10
  197. package/dist/src/skills/preflight.js +0 -39
  198. package/dist/src/skills/preflight.js.map +0 -1
  199. package/dist/src/skills/registry.d.ts +0 -28
  200. package/dist/src/skills/registry.js +0 -556
  201. package/dist/src/skills/registry.js.map +0 -1
  202. package/dist/src/style/artifact.d.ts +0 -6
  203. package/dist/src/style/artifact.js +0 -61
  204. package/dist/src/style/artifact.js.map +0 -1
  205. package/dist/src/style/cli-ui.d.ts +0 -41
  206. package/dist/src/style/cli-ui.js +0 -119
  207. package/dist/src/style/cli-ui.js.map +0 -1
  208. package/dist/src/style/contracts.d.ts +0 -34
  209. package/dist/src/style/contracts.js +0 -2
  210. package/dist/src/style/contracts.js.map +0 -1
  211. package/dist/src/style/registry.d.ts +0 -16
  212. package/dist/src/style/registry.js +0 -50
  213. package/dist/src/style/registry.js.map +0 -1
  214. package/dist/src/trust/manifest.d.ts +0 -42
  215. package/dist/src/trust/manifest.js +0 -53
  216. package/dist/src/trust/manifest.js.map +0 -1
  217. package/dist/src/trust/sbom.d.ts +0 -54
  218. package/dist/src/trust/sbom.js +0 -77
  219. package/dist/src/trust/sbom.js.map +0 -1
  220. package/dist/src/tui/app.d.ts +0 -3
  221. package/dist/src/tui/app.js +0 -103
  222. package/dist/src/tui/app.js.map +0 -1
  223. package/dist/src/tui/model.d.ts +0 -13
  224. package/dist/src/tui/model.js +0 -25
  225. package/dist/src/tui/model.js.map +0 -1
  226. package/dist/src/updates/check.d.ts +0 -43
  227. package/dist/src/updates/check.js +0 -68
  228. package/dist/src/updates/check.js.map +0 -1
  229. package/dist/src/updates/manifest.d.ts +0 -19
  230. package/dist/src/updates/manifest.js +0 -58
  231. package/dist/src/updates/manifest.js.map +0 -1
  232. package/dist/src/updates/registry.d.ts +0 -21
  233. package/dist/src/updates/registry.js +0 -82
  234. package/dist/src/updates/registry.js.map +0 -1
  235. package/dist/src/updates/skills.d.ts +0 -15
  236. package/dist/src/updates/skills.js +0 -39
  237. package/dist/src/updates/skills.js.map +0 -1
  238. package/dist/src/updates/status.d.ts +0 -7
  239. package/dist/src/updates/status.js +0 -37
  240. package/dist/src/updates/status.js.map +0 -1
  241. package/dist/src/updates/update.d.ts +0 -43
  242. package/dist/src/updates/update.js +0 -126
  243. package/dist/src/updates/update.js.map +0 -1
  244. package/dist/src/wizard.d.ts +0 -7
  245. package/dist/src/wizard.js +0 -90
  246. package/dist/src/wizard.js.map +0 -1
  247. package/dist/src/workflows/analysis-delegation.d.ts +0 -3
  248. package/dist/src/workflows/analysis-delegation.js +0 -96
  249. package/dist/src/workflows/analysis-delegation.js.map +0 -1
  250. package/dist/src/workflows/expert-review-runner.d.ts +0 -29
  251. package/dist/src/workflows/expert-review-runner.js +0 -35
  252. package/dist/src/workflows/expert-review-runner.js.map +0 -1
  253. package/dist/src/workflows/expert-review.d.ts +0 -140
  254. package/dist/src/workflows/expert-review.js +0 -111
  255. package/dist/src/workflows/expert-review.js.map +0 -1
  256. package/dist/src/workflows/institutional-fulltext.d.ts +0 -16
  257. package/dist/src/workflows/institutional-fulltext.js +0 -110
  258. package/dist/src/workflows/institutional-fulltext.js.map +0 -1
  259. package/dist/src/workflows/literature-review.d.ts +0 -3
  260. package/dist/src/workflows/literature-review.js +0 -76
  261. package/dist/src/workflows/literature-review.js.map +0 -1
  262. package/dist/src/workflows/meta-analysis.d.ts +0 -38
  263. package/dist/src/workflows/meta-analysis.js +0 -185
  264. package/dist/src/workflows/meta-analysis.js.map +0 -1
  265. package/dist/src/workflows/publish.d.ts +0 -51
  266. package/dist/src/workflows/publish.js +0 -153
  267. package/dist/src/workflows/publish.js.map +0 -1
  268. package/dist/src/workflows/spec.d.ts +0 -51
  269. package/dist/src/workflows/spec.js +0 -157
  270. package/dist/src/workflows/spec.js.map +0 -1
  271. package/dist/src/workflows/style-contract.d.ts +0 -24
  272. package/dist/src/workflows/style-contract.js +0 -23
  273. package/dist/src/workflows/style-contract.js.map +0 -1
  274. package/dist/src/workflows/writing-review.d.ts +0 -15
  275. package/dist/src/workflows/writing-review.js +0 -82
  276. package/dist/src/workflows/writing-review.js.map +0 -1
  277. package/package.json.psyclaw +0 -21
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """生成 PsyClaw 预印本的示意图(图1 架构与数据流;图2 三理念协同)。
4
+ 输出 300 dpi PNG 至 figures/。中文用 macOS 自带 Hiragino/Arial Unicode。
5
+ 重跑:UV_DEFAULT_INDEX=https://mirrors.aliyun.com/pypi/simple/ \
6
+ uv run --python 3.12 --with matplotlib python scripts/build_preprint_figures.py
7
+ """
8
+ import os
9
+ import matplotlib
10
+ matplotlib.use("Agg")
11
+ import matplotlib.pyplot as plt
12
+ from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
13
+ from matplotlib.font_manager import FontProperties
14
+
15
+ # ---- 中文字体 ----
16
+ _CANDIDATES = [
17
+ "/System/Library/Fonts/Hiragino Sans GB.ttc",
18
+ "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
19
+ "/System/Library/Fonts/STHeiti Medium.ttc",
20
+ ]
21
+ _FP = next((FontProperties(fname=p) for p in _CANDIDATES if os.path.exists(p)), FontProperties())
22
+ def F(size, weight="normal"):
23
+ fp = _FP.copy(); fp.set_size(size); fp.set_weight(weight); return fp
24
+
25
+ # ---- 品牌配色 ----
26
+ TEAL = "#0F8B8D" # 主青
27
+ TEAL_D = "#0A5F61" # 深青
28
+ TEAL_L = "#D8ECEC" # 浅青底
29
+ INK = "#22303A" # 墨
30
+ GREY = "#5B6B75" # 次要文字
31
+ PAPER = "#FBFCFC" # 底
32
+ BAND = "#F0F4F5" # 层带底
33
+ GOLD = "#C7853B" # 强调(核查/阻断)
34
+ GOLD_L = "#F6EBDA"
35
+
36
+ OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "figures")
37
+ os.makedirs(OUT, exist_ok=True)
38
+
39
+
40
+ def _box(ax, x, y, w, h, fc, ec, lw=1.4, r=0.035, z=2):
41
+ p = FancyBboxPatch((x, y), w, h, boxstyle=f"round,pad=0.0,rounding_size={r}",
42
+ linewidth=lw, edgecolor=ec, facecolor=fc, zorder=z)
43
+ ax.add_patch(p)
44
+ return p
45
+
46
+
47
+ def _txt(ax, x, y, s, fp, color=INK, ha="center", va="center", z=5):
48
+ ax.text(x, y, s, fontproperties=fp, color=color, ha=ha, va=va, zorder=z)
49
+
50
+
51
+ def _arrow(ax, xy1, xy2, color=TEAL_D, lw=1.6, style="-|>", ms=11, ls="-", rad=0.0, z=4):
52
+ a = FancyArrowPatch(xy1, xy2, arrowstyle=style, mutation_scale=ms, lw=lw,
53
+ color=color, linestyle=ls, zorder=z,
54
+ connectionstyle=f"arc3,rad={rad}", shrinkA=2, shrinkB=2)
55
+ ax.add_patch(a)
56
+
57
+
58
+ # =========================================================================
59
+ # 图 1:四层 + 两横切架构与数据流
60
+ # =========================================================================
61
+ def figure1():
62
+ fig, ax = plt.subplots(figsize=(11.2, 7.6))
63
+ ax.set_xlim(0, 100); ax.set_ylim(0, 100); ax.axis("off")
64
+ fig.patch.set_facecolor(PAPER); ax.set_facecolor(PAPER)
65
+
66
+ _txt(ax, 50, 96.5, "图 1 PsyClaw 四层加两横切架构与数据流", F(15, "bold"), INK)
67
+
68
+ # 用户 / CLI 入口
69
+ _box(ax, 6, 87.5, 66, 6.6, "#FFFFFF", INK, 1.4)
70
+ _txt(ax, 10, 90.8, "研究者", F(11, "bold"), INK, ha="left")
71
+ _txt(ax, 39, 90.8, "自然语言 · chat / run / auto", F(10.5), GREY)
72
+
73
+ # 横切两条:Harness(左)Memory(右)—— 贯穿四层背景
74
+ band_top, band_bot = 84.0, 12.0
75
+ _box(ax, 4.0, band_bot, 6.4, band_top - band_bot, GOLD_L, GOLD, 1.3, r=0.02, z=1)
76
+ ax.text(7.2, (band_top+band_bot)/2, "Harness 横切\n每步前置检查(失败即阻断)\n机器可读总验收",
77
+ fontproperties=F(9.2, "bold"), color=GOLD, ha="center", va="center",
78
+ rotation=90, zorder=3)
79
+ _box(ax, 89.6, band_bot, 6.4, band_top - band_bot, TEAL_L, TEAL_D, 1.3, r=0.02, z=1)
80
+ ax.text(92.8, (band_top+band_bot)/2, "Memory 横切\n三层记忆\n方法学·期刊·教训",
81
+ fontproperties=F(9.2, "bold"), color=TEAL_D, ha="center", va="center",
82
+ rotation=90, zorder=3)
83
+
84
+ # 四层主栈(在两横切之间)
85
+ lx, lw = 11.6, 76.8
86
+ layers = [
87
+ ("L0 路由 Router", "三入口共享路由定义 modes.py:chat(对话)· run(声明式流程)· auto(自主)",
88
+ 77.0, 6.4, TEAL, "#FFFFFF"),
89
+ ("L1 流程 Workflow", "每类研究 = 一份纯数据(步骤 + 前置检查);引擎逐步跑 + HITL + 断点恢复 + 总验收",
90
+ 67.6, 6.4, TEAL_L, INK),
91
+ ("L2 子功能 Step", "每步薄封装:可单用(多为 CLI 命令)· 可拆(纯函数)· 可拼(进任意流程)",
92
+ 58.2, 6.4, TEAL_L, INK),
93
+ ("L3 实现 Skill + MCP", "既有命令 / 技能 / MCP 后端;统计计算整体落此层",
94
+ 48.8, 6.4, TEAL_L, INK),
95
+ ]
96
+ for title, sub, y, h, fc, tc in layers:
97
+ _box(ax, lx, y, lw, h, fc, TEAL_D, 1.5)
98
+ _txt(ax, lx + 2.5, y + h*0.66, title, F(11.5, "bold"), tc, ha="left")
99
+ _txt(ax, lx + 2.5, y + h*0.26, sub, F(8.8), tc if tc != "#FFFFFF" else "#EAF6F6", ha="left")
100
+
101
+ # 竖向数据流箭头(逐层)与回流(上行)
102
+ cx = lx + lw*0.30
103
+ for y0, y1 in [(77.0, 74.0), (67.6, 64.6), (58.2, 55.2)]:
104
+ _arrow(ax, (cx, y0), (cx, y1), TEAL_D, 1.8, ms=12)
105
+ ux = lx + lw*0.70
106
+ for y0, y1 in [(55.2, 58.2), (64.6, 67.6), (74.0, 77.0)]:
107
+ _arrow(ax, (ux, y0), (ux, y1), GREY, 1.3, ms=10, ls=(0, (4, 2)))
108
+ _txt(ax, cx - 1.2, 71.0, "逐层委托", F(8.2), TEAL_D, ha="right")
109
+ _txt(ax, ux + 1.2, 71.0, "结果 / 证据回流", F(8.2), GREY, ha="left")
110
+
111
+ # L3 之下:外部实现(统计外移)——标题独占一行,库框在下
112
+ _box(ax, lx, 39.6, lw, 7.6, "#FFFFFF", INK, 1.4)
113
+ _txt(ax, lx + lw/2, 46.1, "统计外移 —— 本仓不实现任何统计算法", F(10.5, "bold"), GOLD)
114
+ ext = [("pingouin", 13.5), ("statsmodels", 27.5), ("SciPy", 41.5),
115
+ ("MNE-MCP", 54.5), ("SPSS/Stata/Mplus\n(MCP)", 70.5)]
116
+ for name, x in ext:
117
+ _box(ax, x, 40.4, 12.4, 4.1, TEAL_L, TEAL_D, 1.2, r=0.05)
118
+ _txt(ax, x + 6.2, 42.45, name, F(8.6, "bold"), TEAL_D)
119
+ for x in [19.7, 33.7, 47.7, 60.7, 76.7]:
120
+ _arrow(ax, (x, 48.6), (x, 47.5), TEAL_D, 1.2, ms=9, ls=(0, (3, 2)))
121
+
122
+ # 四类声明式流程(L1 展开示例)
123
+ _box(ax, lx, 27.5, lw, 9.5, BAND, GREY, 1.2, r=0.02, z=1)
124
+ _txt(ax, lx + lw/2, 34.7, "L1 的四类声明式研究流程", F(10, "bold"), INK)
125
+ flows = [
126
+ ("文献综述", "clarify → 检索(PRISMA) →\n筛选 → 合成 → 评审"),
127
+ ("元分析", "校验效应量 → 委托\nstatsmodels 随机效应"),
128
+ ("实证分析", "数据画像 → 推荐 →\n委托 pingouin/SciPy"),
129
+ ("质性分析", "转录本 → LLM 辅助\n编码/主题(HITL)"),
130
+ ]
131
+ fw = 17.6
132
+ for i, (nm, desc) in enumerate(flows):
133
+ x = lx + 2.6 + i * (fw + 1.4)
134
+ _box(ax, x, 28.4, fw, 4.9, "#FFFFFF", TEAL_D, 1.2, r=0.05)
135
+ _txt(ax, x + fw/2, 32.4, nm, F(9.3, "bold"), TEAL_D)
136
+ _txt(ax, x + fw/2, 30.2, desc, F(7.6), GREY)
137
+
138
+ # 产物
139
+ _box(ax, lx, 13.5, lw, 6.2, GOLD_L, GOLD, 1.4)
140
+ _txt(ax, lx + lw/2, 16.6, "可复现产物", F(10.5, "bold"), GOLD)
141
+ _txt(ax, lx + lw/2, 14.5,
142
+ "等效脚本 + 数据指纹 · 机器可读总验收 workflow_summary.json · 按序号归档",
143
+ F(8.8), INK)
144
+
145
+ # 全局竖向主脉:入口→L0→…→产物
146
+ _arrow(ax, (lx + lw*0.5, 87.5), (lx + lw*0.5, 83.5), INK, 1.6, ms=11)
147
+ _arrow(ax, (lx + lw*0.5, 48.8), (lx + lw*0.5, 47.3), INK, 1.4, ms=10)
148
+ _arrow(ax, (lx + lw*0.5, 39.6), (lx + lw*0.5, 37.1), INK, 1.4, ms=10)
149
+ _arrow(ax, (lx + lw*0.5, 27.5), (lx + lw*0.5, 19.8), INK, 1.4, ms=10)
150
+
151
+ # gate 标记(Harness 对每层的约束)示意:右向三角,指向各层
152
+ for y in [80.2, 70.8, 61.4, 52.0]:
153
+ ax.plot(10.9, y, marker=">", markersize=9, color=GOLD,
154
+ markeredgecolor=GOLD, zorder=6)
155
+
156
+ fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
157
+ path = os.path.join(OUT, "psyclaw_fig1_architecture.png")
158
+ fig.savefig(path, dpi=300, facecolor=PAPER)
159
+ plt.close(fig)
160
+ return path
161
+
162
+
163
+ # =========================================================================
164
+ # 图 2:三理念协同(声明式流程 · 失败即阻断的核查 · 统计外移)
165
+ # =========================================================================
166
+ def figure2():
167
+ fig, ax = plt.subplots(figsize=(11.2, 6.6))
168
+ ax.set_xlim(0, 100); ax.set_ylim(0, 100); ax.axis("off")
169
+ fig.patch.set_facecolor(PAPER); ax.set_facecolor(PAPER)
170
+
171
+ _txt(ax, 50, 95.5, "图 2 三项核心设计理念在一个流程步骤上的协同", F(15, "bold"), INK)
172
+
173
+ # 中央:一个「流程步骤」
174
+ _box(ax, 38, 44, 24, 14, TEAL, TEAL_D, 1.8, r=0.06)
175
+ _txt(ax, 50, 53.3, "一个流程步骤", F(12.5, "bold"), "#FFFFFF")
176
+ _txt(ax, 50, 49.6, "Step", F(9.5), "#EAF6F6")
177
+ _txt(ax, 50, 46.4, "(如「分析」「合成」)", F(8.6), "#DCEFEF")
178
+
179
+ # 理念一:声明式流程(上)——步骤的来源
180
+ _box(ax, 30, 78, 40, 12.5, TEAL_L, TEAL_D, 1.5, r=0.05)
181
+ _txt(ax, 50, 86.4, "① 声明式研究流程", F(12, "bold"), TEAL_D)
182
+ _txt(ax, 50, 82.7, "流程 = 纯数据(步骤 + 前置检查);引擎逐步执行,", F(8.9), INK)
183
+ _txt(ax, 50, 80.2, "可重放、可改装、末尾出机器可读总验收", F(8.9), INK)
184
+ _arrow(ax, (50, 78), (50, 58.3), TEAL_D, 2.0, ms=13)
185
+ _txt(ax, 51.5, 68, "定义并驱动", F(8.6), TEAL_D, ha="left")
186
+
187
+ # 理念二:失败即阻断的核查(左)——步骤执行前的关卡
188
+ _box(ax, 3.5, 40, 26, 22, GOLD_L, GOLD, 1.6, r=0.05)
189
+ _txt(ax, 16.5, 58.0, "② 学术诚信核查", F(12, "bold"), GOLD)
190
+ for i, s in enumerate([
191
+ "效应量 + CI 必报",
192
+ "区分探索 / 确证",
193
+ "确证须预注册",
194
+ "相关 ≠ 因果",
195
+ "图件完整性",
196
+ "PRISMA · APA7 · 复现脚本",
197
+ ]):
198
+ _txt(ax, 6.0, 53.6 - i*2.55, "•", F(9.5, "bold"), GOLD, ha="left")
199
+ _txt(ax, 8.2, 53.6 - i*2.55, s, F(8.7), INK, ha="left")
200
+ # 核查把关箭头(阻断/放行)
201
+ _arrow(ax, (29.5, 51), (38, 51), GOLD, 2.0, ms=13)
202
+ _txt(ax, 33.5, 53.2, "PASS→放行", F(8.0), TEAL_D)
203
+ _arrow(ax, (35, 47.5), (31, 43), GOLD, 1.8, ms=12, style="-|>", rad=0.3)
204
+ _txt(ax, 36.0, 41.0, "FAIL→阻断 + 修复", F(8.0), GOLD, ha="center")
205
+
206
+ # 理念三:统计外移(右)——步骤把计算委托出去
207
+ _box(ax, 70.5, 40, 26, 22, TEAL_L, TEAL_D, 1.6, r=0.05)
208
+ _txt(ax, 83.5, 58.0, "③ 统计外移", F(12, "bold"), TEAL_D)
209
+ _txt(ax, 83.5, 54.6, "本仓不实现任何统计算法", F(8.9, "bold"), GOLD)
210
+ _txt(ax, 83.5, 51.8, "委托成熟库 / MCP 后端:", F(8.6), INK)
211
+ for i, s in enumerate(["pingouin · statsmodels · SciPy",
212
+ "MNE-MCP · SPSS / Stata / Mplus"]):
213
+ _txt(ax, 83.5, 48.6 - i*2.6, s, F(8.4), TEAL_D)
214
+ _txt(ax, 83.5, 42.7, "数值正确性归外部库承担", F(8.2), GREY)
215
+ _arrow(ax, (62, 51), (70.5, 51), TEAL_D, 2.0, ms=13)
216
+ _txt(ax, 66.2, 53.2, "委托计算", F(8.0), TEAL_D)
217
+ _arrow(ax, (70.5, 47.5), (62, 45), GREY, 1.4, ms=10, ls=(0, (4, 2)), rad=-0.25)
218
+ _txt(ax, 66.2, 43.4, "结果回流", F(7.8), GREY)
219
+
220
+ # 底部:三者合力 → 产物
221
+ _box(ax, 22, 12, 56, 12, "#FFFFFF", INK, 1.5, r=0.04)
222
+ _txt(ax, 50, 20.6, "合力产出", F(11.5, "bold"), INK)
223
+ _txt(ax, 50, 17.3, "工作流可复现(等效脚本 + 指纹 + 归档)", F(9.0), TEAL_D)
224
+ _txt(ax, 50, 14.4, "× 内容合规(核查把关) × 数值可信(外部库)", F(9.0), GOLD)
225
+ _arrow(ax, (50, 44), (50, 24.2), INK, 1.8, ms=12)
226
+
227
+ fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
228
+ path = os.path.join(OUT, "psyclaw_fig2_principles.png")
229
+ fig.savefig(path, dpi=300, facecolor=PAPER)
230
+ plt.close(fig)
231
+ return path
232
+
233
+
234
+ if __name__ == "__main__":
235
+ p1 = figure1()
236
+ p2 = figure2()
237
+ print("wrote:", p1)
238
+ print("wrote:", p2)
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ """Build a small DOCX fixture that exercises APA three-line tables."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from psyclaw.output.apa7 import APA7Document
9
+ from psyclaw.output.docx_contract import assert_docx_contract
10
+
11
+
12
+ def main() -> int:
13
+ parser = argparse.ArgumentParser()
14
+ parser.add_argument("output", nargs="?", default="outputs/three-line-table-test.docx")
15
+ args = parser.parse_args()
16
+
17
+ output = Path(args.output)
18
+ output.parent.mkdir(parents=True, exist_ok=True)
19
+
20
+ doc = APA7Document(
21
+ title="APA 三线表生成测试",
22
+ authors="PsyClaw DOCX regression fixture",
23
+ affiliation="Local verification",
24
+ )
25
+ doc.add_heading("结果", 1)
26
+ doc.add_paragraph("以下表格用于检查边框、固定列宽、中文换行与跨页稳定性。")
27
+ doc.add_stat_table(
28
+ "Table 1\n描述性统计",
29
+ ["变量", "n", "M", "SD", "95% CI"],
30
+ [
31
+ ["抑郁症状", "128", "14.20", "3.40", "[13.60, 14.80]"],
32
+ ["焦虑症状", "128", "11.10", "2.90", "[10.59, 11.61]"],
33
+ ["生活满意度(较长的中文变量名称,用于测试自动换行)", "128", "24.80", "5.10", "[23.90, 25.70]"],
34
+ ],
35
+ )
36
+ doc.add_stat_table(
37
+ "Table 2\n相关矩阵",
38
+ ["变量", "1", "2", "3"],
39
+ [
40
+ ["1. 抑郁", "—", "", ""],
41
+ ["2. 焦虑", ".45", "—", ""],
42
+ ["3. 满意度", "-.31", "-.27", "—"],
43
+ ],
44
+ )
45
+ doc.add_stat_table("Table 3\n仅表头边界情况", ["变量", "结果"], [])
46
+
47
+ doc.to_docx(output)
48
+ result = assert_docx_contract(output)
49
+ print(f"generated={output.resolve()}")
50
+ print(f"sha256={result['sha256']}")
51
+ print(f"tables={result['tables']}")
52
+ return 0
53
+
54
+
55
+ if __name__ == "__main__":
56
+ raise SystemExit(main())