dsh-aris-panel 0.1.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 (442) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/README_CN.md +87 -0
  4. package/dsh/checkout.patch.yml +38 -0
  5. package/dsh/client.js +634 -0
  6. package/dsh/cordis.patch.yml +44 -0
  7. package/dsh/index.mjs +76 -0
  8. package/dsh/run-status.mjs +182 -0
  9. package/dsh/scope-limits.mjs +50 -0
  10. package/dsh/workbench.mjs +291 -0
  11. package/mcp-servers/claude-review/README.md +93 -0
  12. package/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
  13. package/mcp-servers/claude-review/server.py +718 -0
  14. package/mcp-servers/codex-image2/README.md +65 -0
  15. package/mcp-servers/codex-image2/server.py +893 -0
  16. package/mcp-servers/feishu-bridge/requirements.txt +1 -0
  17. package/mcp-servers/feishu-bridge/server.py +240 -0
  18. package/mcp-servers/gemini-review/README.md +171 -0
  19. package/mcp-servers/gemini-review/server.py +1856 -0
  20. package/mcp-servers/llm-chat/requirements.txt +1 -0
  21. package/mcp-servers/llm-chat/server.py +664 -0
  22. package/mcp-servers/manual-review/README.md +133 -0
  23. package/mcp-servers/manual-review/server.py +910 -0
  24. package/mcp-servers/manual-review/ui.html +279 -0
  25. package/mcp-servers/minimax-chat/requirements.txt +1 -0
  26. package/mcp-servers/minimax-chat/server.py +381 -0
  27. package/package.json +51 -0
  28. package/skills/ablation-planner/SKILL.md +123 -0
  29. package/skills/alphaxiv/SKILL.md +196 -0
  30. package/skills/analyze-results/SKILL.md +46 -0
  31. package/skills/arxiv/SKILL.md +248 -0
  32. package/skills/auto-paper-improvement-loop/SKILL.md +651 -0
  33. package/skills/auto-review-loop/SKILL.md +1137 -0
  34. package/skills/auto-review-loop-llm/SKILL.md +259 -0
  35. package/skills/auto-review-loop-minimax/SKILL.md +302 -0
  36. package/skills/citation-audit/SKILL.md +502 -0
  37. package/skills/claims-drafting/SKILL.md +227 -0
  38. package/skills/comm-lit-review/SKILL.md +297 -0
  39. package/skills/deepxiv/SKILL.md +263 -0
  40. package/skills/dse-loop/SKILL.md +296 -0
  41. package/skills/embodiment-description/SKILL.md +129 -0
  42. package/skills/exa-search/SKILL.md +205 -0
  43. package/skills/experiment-audit/SKILL.md +311 -0
  44. package/skills/experiment-bridge/SKILL.md +376 -0
  45. package/skills/experiment-plan/SKILL.md +249 -0
  46. package/skills/experiment-queue/SKILL.md +431 -0
  47. package/skills/experiment-queue/scripts/build_manifest.py +142 -0
  48. package/skills/experiment-queue/scripts/queue_manager.py +433 -0
  49. package/skills/feishu-notify/SKILL.md +156 -0
  50. package/skills/figure-description/SKILL.md +138 -0
  51. package/skills/figure-spec/SKILL.md +262 -0
  52. package/skills/figure-spec/scripts/figure_renderer.py +799 -0
  53. package/skills/formula-derivation/SKILL.md +280 -0
  54. package/skills/gemini-search/SKILL.md +231 -0
  55. package/skills/grant-proposal/SKILL.md +698 -0
  56. package/skills/idea-creator/SKILL.md +542 -0
  57. package/skills/idea-discovery/SKILL.md +521 -0
  58. package/skills/idea-discovery-robot/SKILL.md +363 -0
  59. package/skills/integrity-forensics/SKILL.md +284 -0
  60. package/skills/interview-cheatsheet/SKILL.md +245 -0
  61. package/skills/invention-structuring/SKILL.md +188 -0
  62. package/skills/jurisdiction-format/SKILL.md +192 -0
  63. package/skills/kill-argument/SKILL.md +437 -0
  64. package/skills/mermaid-diagram/SKILL.md +419 -0
  65. package/skills/meta-apply/SKILL.md +141 -0
  66. package/skills/meta-optimize/SKILL.md +437 -0
  67. package/skills/monitor-experiment/SKILL.md +140 -0
  68. package/skills/novelty-check/SKILL.md +101 -0
  69. package/skills/openalex/SKILL.md +237 -0
  70. package/skills/overleaf-sync/SKILL.md +220 -0
  71. package/skills/paper-claim-audit/SKILL.md +348 -0
  72. package/skills/paper-compile/SKILL.md +266 -0
  73. package/skills/paper-figure/SKILL.md +312 -0
  74. package/skills/paper-illustration/SKILL.md +736 -0
  75. package/skills/paper-illustration-image2/SKILL.md +391 -0
  76. package/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  77. package/skills/paper-plan/SKILL.md +386 -0
  78. package/skills/paper-poster/SKILL.md +19 -0
  79. package/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  80. package/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  81. package/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  82. package/skills/paper-poster-html/NOTICE.md +57 -0
  83. package/skills/paper-poster-html/SKILL.md +323 -0
  84. package/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  85. package/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  86. package/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  87. package/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  88. package/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  89. package/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  90. package/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  91. package/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  92. package/skills/paper-poster-html/scripts/asset_check.py +897 -0
  93. package/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  94. package/skills/paper-poster-html/scripts/poster_check.py +251 -0
  95. package/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  96. package/skills/paper-poster-html/scripts/render_preview.py +217 -0
  97. package/skills/paper-poster-html/scripts/run_gates.py +556 -0
  98. package/skills/paper-poster-html/scripts/style_check.py +1324 -0
  99. package/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  100. package/skills/paper-poster-html/templates/README.md +170 -0
  101. package/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  102. package/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  103. package/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  104. package/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  105. package/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  106. package/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  107. package/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  108. package/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  109. package/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  110. package/skills/paper-slides/SKILL.md +635 -0
  111. package/skills/paper-talk/SKILL.md +381 -0
  112. package/skills/paper-write/SKILL.md +604 -0
  113. package/skills/paper-write/templates/IEEEtran.bst +2409 -0
  114. package/skills/paper-write/templates/IEEEtran.cls +6347 -0
  115. package/skills/paper-write/templates/iclr2026.tex +84 -0
  116. package/skills/paper-write/templates/icml2025.tex +87 -0
  117. package/skills/paper-write/templates/ieee_conference.tex +89 -0
  118. package/skills/paper-write/templates/ieee_journal.tex +93 -0
  119. package/skills/paper-write/templates/math_commands.tex +48 -0
  120. package/skills/paper-write/templates/neurips2025.tex +80 -0
  121. package/skills/paper-writing/SKILL.md +916 -0
  122. package/skills/patent-novelty-check/SKILL.md +153 -0
  123. package/skills/patent-pipeline/SKILL.md +344 -0
  124. package/skills/patent-review/SKILL.md +203 -0
  125. package/skills/pixel-art/SKILL.md +137 -0
  126. package/skills/prior-art-search/SKILL.md +146 -0
  127. package/skills/proof-checker/SKILL.md +866 -0
  128. package/skills/proof-orchestrator/NOTICE.md +24 -0
  129. package/skills/proof-orchestrator/SKILL.md +254 -0
  130. package/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
  131. package/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
  132. package/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
  133. package/skills/proof-orchestrator/references/notation-audit.md +135 -0
  134. package/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  135. package/skills/proof-orchestrator/references/stress-tests.md +38 -0
  136. package/skills/proof-writer/SKILL.md +223 -0
  137. package/skills/qzcli/SKILL.md +324 -0
  138. package/skills/rebuttal/SKILL.md +376 -0
  139. package/skills/render-html/SKILL.md +316 -0
  140. package/skills/render-html/scripts/render_html.py +1006 -0
  141. package/skills/render-html/scripts/templates/academic.html +703 -0
  142. package/skills/render-html/scripts/templates/dashboard.html +333 -0
  143. package/skills/research-lit/SKILL.md +756 -0
  144. package/skills/research-pipeline/SKILL.md +384 -0
  145. package/skills/research-refine/SKILL.md +770 -0
  146. package/skills/research-refine-pipeline/SKILL.md +186 -0
  147. package/skills/research-review/SKILL.md +198 -0
  148. package/skills/research-wiki/SKILL.md +461 -0
  149. package/skills/resubmit-pipeline/SKILL.md +447 -0
  150. package/skills/result-to-claim/SKILL.md +311 -0
  151. package/skills/run-experiment/SKILL.md +313 -0
  152. package/skills/semantic-scholar/SKILL.md +236 -0
  153. package/skills/serverless-modal/SKILL.md +335 -0
  154. package/skills/shared-references/acceptance-gate.md +324 -0
  155. package/skills/shared-references/assurance-contract.md +248 -0
  156. package/skills/shared-references/capture-antipatterns.md +78 -0
  157. package/skills/shared-references/citation-discipline.md +583 -0
  158. package/skills/shared-references/compute-env-contract.md +163 -0
  159. package/skills/shared-references/effort-contract.md +183 -0
  160. package/skills/shared-references/evidence-precheck.md +65 -0
  161. package/skills/shared-references/experiment-integrity.md +49 -0
  162. package/skills/shared-references/external-cadence.md +326 -0
  163. package/skills/shared-references/fan-out-pattern.md +366 -0
  164. package/skills/shared-references/injection-hygiene.md +127 -0
  165. package/skills/shared-references/integration-contract.md +461 -0
  166. package/skills/shared-references/output-composition.md +93 -0
  167. package/skills/shared-references/output-language.md +45 -0
  168. package/skills/shared-references/output-manifest.md +49 -0
  169. package/skills/shared-references/output-versioning.md +111 -0
  170. package/skills/shared-references/patent-format-cn.md +199 -0
  171. package/skills/shared-references/patent-format-ep.md +173 -0
  172. package/skills/shared-references/patent-format-us.md +161 -0
  173. package/skills/shared-references/patent-writing-principles.md +197 -0
  174. package/skills/shared-references/prior-art-databases.md +141 -0
  175. package/skills/shared-references/resumable-runs.md +109 -0
  176. package/skills/shared-references/review-scope-limits.md +81 -0
  177. package/skills/shared-references/review-tracing.md +391 -0
  178. package/skills/shared-references/reviewer-independence.md +79 -0
  179. package/skills/shared-references/reviewer-routing.md +852 -0
  180. package/skills/shared-references/skill-governance.md +104 -0
  181. package/skills/shared-references/taste-calibration.md +85 -0
  182. package/skills/shared-references/venue-checklists.md +114 -0
  183. package/skills/shared-references/wiki-helper-resolution.md +134 -0
  184. package/skills/shared-references/writing-principles.md +525 -0
  185. package/skills/skills-codex/README.md +102 -0
  186. package/skills/skills-codex/README_CN.md +100 -0
  187. package/skills/skills-codex/ablation-planner/SKILL.md +126 -0
  188. package/skills/skills-codex/alphaxiv/SKILL.md +186 -0
  189. package/skills/skills-codex/analyze-results/SKILL.md +45 -0
  190. package/skills/skills-codex/arxiv/SKILL.md +210 -0
  191. package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +574 -0
  192. package/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
  193. package/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
  194. package/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
  195. package/skills/skills-codex/citation-audit/SKILL.md +504 -0
  196. package/skills/skills-codex/claims-drafting/SKILL.md +239 -0
  197. package/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
  198. package/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
  199. package/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
  200. package/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
  201. package/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
  202. package/skills/skills-codex/deepxiv/SKILL.md +142 -0
  203. package/skills/skills-codex/dse-loop/SKILL.md +285 -0
  204. package/skills/skills-codex/embodiment-description/SKILL.md +129 -0
  205. package/skills/skills-codex/exa-search/SKILL.md +192 -0
  206. package/skills/skills-codex/experiment-audit/SKILL.md +286 -0
  207. package/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
  208. package/skills/skills-codex/experiment-plan/SKILL.md +249 -0
  209. package/skills/skills-codex/experiment-queue/SKILL.md +401 -0
  210. package/skills/skills-codex/feishu-notify/SKILL.md +155 -0
  211. package/skills/skills-codex/figure-description/SKILL.md +138 -0
  212. package/skills/skills-codex/figure-spec/SKILL.md +252 -0
  213. package/skills/skills-codex/formula-derivation/SKILL.md +280 -0
  214. package/skills/skills-codex/gemini-search/SKILL.md +205 -0
  215. package/skills/skills-codex/grant-proposal/SKILL.md +626 -0
  216. package/skills/skills-codex/idea-creator/SKILL.md +405 -0
  217. package/skills/skills-codex/idea-discovery/SKILL.md +475 -0
  218. package/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
  219. package/skills/skills-codex/integrity-forensics/SKILL.md +106 -0
  220. package/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
  221. package/skills/skills-codex/invention-structuring/SKILL.md +188 -0
  222. package/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
  223. package/skills/skills-codex/kill-argument/SKILL.md +403 -0
  224. package/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
  225. package/skills/skills-codex/meta-apply/SKILL.md +154 -0
  226. package/skills/skills-codex/meta-optimize/SKILL.md +348 -0
  227. package/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
  228. package/skills/skills-codex/novelty-check/SKILL.md +89 -0
  229. package/skills/skills-codex/openalex/SKILL.md +228 -0
  230. package/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
  231. package/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
  232. package/skills/skills-codex/paper-compile/SKILL.md +253 -0
  233. package/skills/skills-codex/paper-figure/SKILL.md +311 -0
  234. package/skills/skills-codex/paper-illustration/SKILL.md +690 -0
  235. package/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
  236. package/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  237. package/skills/skills-codex/paper-plan/SKILL.md +278 -0
  238. package/skills/skills-codex/paper-poster/SKILL.md +19 -0
  239. package/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
  240. package/skills/skills-codex/paper-slides/SKILL.md +571 -0
  241. package/skills/skills-codex/paper-talk/SKILL.md +381 -0
  242. package/skills/skills-codex/paper-write/SKILL.md +411 -0
  243. package/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
  244. package/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
  245. package/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
  246. package/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
  247. package/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
  248. package/skills/skills-codex/paper-write/templates/acl.sty +312 -0
  249. package/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
  250. package/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
  251. package/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
  252. package/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
  253. package/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
  254. package/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
  255. package/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
  256. package/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
  257. package/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
  258. package/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
  259. package/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
  260. package/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
  261. package/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
  262. package/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
  263. package/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
  264. package/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
  265. package/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
  266. package/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
  267. package/skills/skills-codex/paper-writing/SKILL.md +731 -0
  268. package/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
  269. package/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
  270. package/skills/skills-codex/patent-review/SKILL.md +202 -0
  271. package/skills/skills-codex/pixel-art/SKILL.md +139 -0
  272. package/skills/skills-codex/prior-art-search/SKILL.md +146 -0
  273. package/skills/skills-codex/proof-checker/SKILL.md +554 -0
  274. package/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
  275. package/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
  276. package/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
  277. package/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
  278. package/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
  279. package/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  280. package/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
  281. package/skills/skills-codex/proof-writer/SKILL.md +222 -0
  282. package/skills/skills-codex/qzcli/SKILL.md +324 -0
  283. package/skills/skills-codex/rebuttal/SKILL.md +305 -0
  284. package/skills/skills-codex/render-html/SKILL.md +305 -0
  285. package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
  286. package/skills/skills-codex/render-html/scripts/render_html.py +909 -0
  287. package/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
  288. package/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
  289. package/skills/skills-codex/research-lit/SKILL.md +464 -0
  290. package/skills/skills-codex/research-pipeline/SKILL.md +340 -0
  291. package/skills/skills-codex/research-refine/SKILL.md +721 -0
  292. package/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
  293. package/skills/skills-codex/research-review/SKILL.md +135 -0
  294. package/skills/skills-codex/research-wiki/SKILL.md +421 -0
  295. package/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
  296. package/skills/skills-codex/result-to-claim/SKILL.md +246 -0
  297. package/skills/skills-codex/run-experiment/SKILL.md +236 -0
  298. package/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
  299. package/skills/skills-codex/serverless-modal/SKILL.md +335 -0
  300. package/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
  301. package/skills/skills-codex/shared-references/assurance-contract.md +139 -0
  302. package/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
  303. package/skills/skills-codex/shared-references/citation-discipline.md +452 -0
  304. package/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
  305. package/skills/skills-codex/shared-references/effort-contract.md +143 -0
  306. package/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
  307. package/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
  308. package/skills/skills-codex/shared-references/external-cadence.md +334 -0
  309. package/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
  310. package/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
  311. package/skills/skills-codex/shared-references/integration-contract.md +372 -0
  312. package/skills/skills-codex/shared-references/output-composition.md +98 -0
  313. package/skills/skills-codex/shared-references/output-language.md +45 -0
  314. package/skills/skills-codex/shared-references/output-manifest.md +40 -0
  315. package/skills/skills-codex/shared-references/output-versioning.md +111 -0
  316. package/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
  317. package/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
  318. package/skills/skills-codex/shared-references/patent-format-us.md +161 -0
  319. package/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
  320. package/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
  321. package/skills/skills-codex/shared-references/resumable-runs.md +125 -0
  322. package/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
  323. package/skills/skills-codex/shared-references/review-tracing.md +144 -0
  324. package/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
  325. package/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
  326. package/skills/skills-codex/shared-references/skill-governance.md +119 -0
  327. package/skills/skills-codex/shared-references/taste-calibration.md +90 -0
  328. package/skills/skills-codex/shared-references/venue-checklists.md +73 -0
  329. package/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
  330. package/skills/skills-codex/shared-references/writing-principles.md +525 -0
  331. package/skills/skills-codex/slides-polish/SKILL.md +563 -0
  332. package/skills/skills-codex/specification-writing/SKILL.md +211 -0
  333. package/skills/skills-codex/system-profile/SKILL.md +103 -0
  334. package/skills/skills-codex/training-check/SKILL.md +83 -0
  335. package/skills/skills-codex/vast-gpu/SKILL.md +394 -0
  336. package/skills/skills-codex/web-debug-search/SKILL.md +334 -0
  337. package/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
  338. package/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
  339. package/skills/skills-codex-claude-review/README.md +79 -0
  340. package/skills/skills-codex-claude-review/README_CN.md +78 -0
  341. package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +581 -0
  342. package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +510 -0
  343. package/skills/skills-codex-claude-review/novelty-check/SKILL.md +102 -0
  344. package/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
  345. package/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
  346. package/skills/skills-codex-claude-review/paper-write/SKILL.md +420 -0
  347. package/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
  348. package/skills/skills-codex-claude-review/research-review/SKILL.md +149 -0
  349. package/skills/skills-codex-gemini-review/README.md +176 -0
  350. package/skills/skills-codex-gemini-review/README_CN.md +175 -0
  351. package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +331 -0
  352. package/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
  353. package/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
  354. package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +263 -0
  355. package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
  356. package/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
  357. package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +92 -0
  358. package/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
  359. package/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
  360. package/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
  361. package/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
  362. package/skills/skills-codex-gemini-review/paper-write/SKILL.md +346 -0
  363. package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +312 -0
  364. package/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
  365. package/skills/skills-codex-gemini-review/research-review/SKILL.md +112 -0
  366. package/skills/slides-polish/SKILL.md +565 -0
  367. package/skills/specification-writing/SKILL.md +211 -0
  368. package/skills/system-profile/SKILL.md +103 -0
  369. package/skills/training-check/SKILL.md +132 -0
  370. package/skills/vast-gpu/SKILL.md +394 -0
  371. package/skills/web-debug-search/SKILL.md +334 -0
  372. package/skills/wiki-enrich/SKILL.md +257 -0
  373. package/skills/writing-systems-papers/SKILL.md +184 -0
  374. package/templates/CLAUDE_MD_TEMPLATE.md +29 -0
  375. package/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
  376. package/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
  377. package/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
  378. package/templates/FINDINGS_TEMPLATE.md +52 -0
  379. package/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
  380. package/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
  381. package/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
  382. package/templates/MANIFEST_TEMPLATE.md +7 -0
  383. package/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
  384. package/templates/PAPER_PLAN_TEMPLATE.md +47 -0
  385. package/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
  386. package/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
  387. package/templates/README.md +57 -0
  388. package/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
  389. package/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
  390. package/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
  391. package/templates/claude-hooks/corpus_write_guard.json +16 -0
  392. package/templates/claude-hooks/corpus_write_guard.py +85 -0
  393. package/templates/claude-hooks/meta_logging.json +74 -0
  394. package/templates/gitignore-trace.txt +3 -0
  395. package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
  396. package/tools/arxiv_fetch.py +311 -0
  397. package/tools/capture_filter.py +126 -0
  398. package/tools/check_skills_inventory.py +273 -0
  399. package/tools/convert_skills_to_llm_chat.py +282 -0
  400. package/tools/copilot_native_evidence.py +818 -0
  401. package/tools/deepxiv_fetch.py +213 -0
  402. package/tools/evidence_check.py +212 -0
  403. package/tools/exa_search.py +425 -0
  404. package/tools/experiment_queue/README.md +118 -0
  405. package/tools/experiment_queue/build_manifest.py +44 -0
  406. package/tools/experiment_queue/queue_manager.py +44 -0
  407. package/tools/extract_paper_style.py +560 -0
  408. package/tools/figure_renderer.py +69 -0
  409. package/tools/forensics_gate.py +669 -0
  410. package/tools/generate_codex_claude_review_overrides.py +299 -0
  411. package/tools/idea_discovery_gate.py +256 -0
  412. package/tools/install_aris.ps1 +1372 -0
  413. package/tools/install_aris.sh +1370 -0
  414. package/tools/install_aris_codex.sh +1023 -0
  415. package/tools/install_aris_copilot.sh +1052 -0
  416. package/tools/iteration_log.py +143 -0
  417. package/tools/lint_skills_helpers.sh +84 -0
  418. package/tools/meta_opt/check_ready.sh +80 -0
  419. package/tools/meta_opt/log_event.sh +91 -0
  420. package/tools/meta_opt/trigger_eval.py +280 -0
  421. package/tools/meta_opt/trigger_evals.sample.json +28 -0
  422. package/tools/openalex_fetch.py +326 -0
  423. package/tools/overleaf_audit.sh +104 -0
  424. package/tools/overleaf_setup.sh +150 -0
  425. package/tools/paper_illustration_image2.py +62 -0
  426. package/tools/provenance.py +294 -0
  427. package/tools/research_wiki.py +1720 -0
  428. package/tools/review_gate.py +502 -0
  429. package/tools/run_state.py +399 -0
  430. package/tools/save_trace.sh +477 -0
  431. package/tools/semantic_scholar_fetch.py +438 -0
  432. package/tools/skill-groups.tsv +116 -0
  433. package/tools/skill_picker.py +238 -0
  434. package/tools/smart_update.ps1 +521 -0
  435. package/tools/smart_update.sh +591 -0
  436. package/tools/smart_update_codex.sh +419 -0
  437. package/tools/smart_update_copilot.sh +605 -0
  438. package/tools/threat_scan.py +222 -0
  439. package/tools/verify_paper_audits.sh +487 -0
  440. package/tools/verify_papers.py +613 -0
  441. package/tools/verify_wiki_coverage.sh +176 -0
  442. package/tools/watchdog.py +485 -0
@@ -0,0 +1,498 @@
1
+ """Soft visual-polish gate — runs at Step 6.
2
+
3
+ Three gates the hard alignment gate cannot see:
4
+
5
+ - **Gate A: figure sizing by aspect ratio.** A wide figure (AR > 1.3)
6
+ rendered at 38% of card width wastes 60% of the column even when
7
+ columns align. The defaults match the documented "aim for" lower
8
+ bounds in SKILL.md so any figure inside the recommended range
9
+ passes cleanly.
10
+ - **Gate B: typography orphans.** ``1.18-1.30× ↑`` whose ``↑``
11
+ wrapped alone onto its own line. Detected on elements with
12
+ ``[class*="stat"]`` / ``[class*="num"]`` / ``.takeaway-num`` /
13
+ ``.headline-num`` that end with a known orphan-prone glyph but
14
+ lack ``white-space: nowrap``.
15
+ - **Gate C: space-between fill.** ``justify-content: space-between``
16
+ on a column with one short card produces a giant whitespace gap
17
+ that reads as "this column ran out of things to say". Detected
18
+ when the largest inter-card gap exceeds the column's stated
19
+ ``row-gap`` by > 5% of column height.
20
+
21
+ Warns by default; ``--strict`` to exit non-zero. Hard-fails if the
22
+ poster has no ``[data-measure-role]`` markup at all — a polish PASS on
23
+ "0 figures, 0 columns, 0 stat elements" would be misleading.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import re
29
+ import sys
30
+ from pathlib import Path
31
+ from typing import Any
32
+
33
+ from . import canvas as _canvas
34
+ from . import preflight as _preflight
35
+ from . import render as _render
36
+
37
+
38
+ # Trailing glyphs that orphan when wrapped: arrows, multiplicative
39
+ # cross, division, plus-minus, footnote markers, degree, percent.
40
+ ORPHAN_GLYPHS = "↑↓↔×÷±§¶†‡*°%"
41
+
42
+
43
+ from .textutil import ascii_safe
44
+
45
+
46
+ def _eprint(*args: Any, **kw: Any) -> None:
47
+ print(*args, file=sys.stderr, **kw)
48
+
49
+
50
+ _POLISH_JS = r"""
51
+ () => {
52
+ // ---- 1) Figure sizing ----
53
+ // For each card, list every <img> with rendered size, the card's
54
+ // bounding width (the "budget"), and natural dimensions for AR.
55
+ const figures = [];
56
+ document.querySelectorAll('[data-measure-role="card"]')
57
+ .forEach((card, ci) => {
58
+ const cw = card.getBoundingClientRect().width;
59
+ card.querySelectorAll('img').forEach(img => {
60
+ const r = img.getBoundingClientRect();
61
+ if (r.width < 50) return; // skip inline icons
62
+ figures.push({
63
+ card_index: ci,
64
+ role: 'card',
65
+ src: img.getAttribute('src') || '',
66
+ alt: img.getAttribute('alt') || '',
67
+ fig_layout: img.getAttribute('data-fig-layout') || '',
68
+ rendered_w: r.width,
69
+ rendered_h: r.height,
70
+ card_w: cw,
71
+ natural_w: img.naturalWidth || 0,
72
+ natural_h: img.naturalHeight || 0,
73
+ });
74
+ });
75
+ });
76
+ // Hero-panel images (the main figure of a hero-layout poster) get the
77
+ // broken-image check too -- a blank centerpiece is the worst failure
78
+ // mode and the card-only scan used to miss it. AR sizing gates are
79
+ // skipped for these on the Python side (they are framed as % of card
80
+ // width, which the full-bleed hero panel doesn't have).
81
+ document.querySelectorAll('[data-measure-role="hero"]')
82
+ .forEach(hero => {
83
+ const hw = hero.getBoundingClientRect().width;
84
+ hero.querySelectorAll('img').forEach(img => {
85
+ const r = img.getBoundingClientRect();
86
+ if (r.width < 50) return; // skip venue badges / inline icons
87
+ figures.push({
88
+ card_index: -1,
89
+ role: 'hero',
90
+ src: img.getAttribute('src') || '',
91
+ alt: img.getAttribute('alt') || '',
92
+ fig_layout: img.getAttribute('data-fig-layout') || '',
93
+ rendered_w: r.width,
94
+ rendered_h: r.height,
95
+ card_w: hw,
96
+ natural_w: img.naturalWidth || 0,
97
+ natural_h: img.naturalHeight || 0,
98
+ });
99
+ });
100
+ });
101
+
102
+ // ---- 2) Orphan-prone text elements ----
103
+ const sel = '[class*="stat"], [class*="num"], .num, .takeaway-num,'
104
+ + ' .headline-num';
105
+ const seen = new Set();
106
+ const orphans = [];
107
+ document.querySelectorAll(sel).forEach(el => {
108
+ if (seen.has(el)) return;
109
+ seen.add(el);
110
+ const txt = (el.innerText || '').replace(/\s+$/, '');
111
+ if (!txt || txt.length > 80) return;
112
+ const cs = window.getComputedStyle(el);
113
+ orphans.push({
114
+ tag: el.tagName.toLowerCase(),
115
+ cls: el.className || '',
116
+ text: txt,
117
+ ws: cs.whiteSpace || '',
118
+ });
119
+ });
120
+
121
+ // ---- 3) Space-between fill ----
122
+ const cols = [];
123
+ document.querySelectorAll('[data-measure-role="column"]')
124
+ .forEach((col, ci) => {
125
+ const cs = window.getComputedStyle(col);
126
+ if (cs.justifyContent !== 'space-between') return;
127
+ const colR = col.getBoundingClientRect();
128
+ const children = Array.from(col.children).map(c => {
129
+ const r = c.getBoundingClientRect();
130
+ return {top: r.top, bottom: r.bottom, h: r.height};
131
+ }).filter(c => c.h > 0);
132
+ if (children.length < 2) return;
133
+ const gapPx = parseFloat(cs.rowGap || cs.gap || '0') || 0;
134
+ let maxExcess = 0;
135
+ let pairIdx = -1;
136
+ for (let i = 1; i < children.length; i++) {
137
+ const actual = children[i].top - children[i - 1].bottom;
138
+ const excess = actual - gapPx;
139
+ if (excess > maxExcess) {
140
+ maxExcess = excess;
141
+ pairIdx = i;
142
+ }
143
+ }
144
+ cols.push({
145
+ column_index: ci,
146
+ column_h: colR.height,
147
+ stated_gap_px: gapPx,
148
+ max_excess_px: maxExcess,
149
+ pair_idx: pairIdx,
150
+ });
151
+ });
152
+
153
+ // ---- 4) Card trailing whitespace (single stretched card) ----
154
+ // A card with flex:1 (or any stretch-to-fill) whose content is top-
155
+ // packed leaves blank space below the last line. `measure` only checks
156
+ // the card's bottom edge so it passes; Gate C only looks BETWEEN cards.
157
+ // Skip cards that distribute space on purpose (space-* / center / end)
158
+ // -- that is Gate C's territory or an intentional layout.
159
+ const cards = [];
160
+ document.querySelectorAll('[data-measure-role="card"]')
161
+ .forEach((card, ci) => {
162
+ const cs = window.getComputedStyle(card);
163
+ const jc = cs.justifyContent || '';
164
+ if (jc.indexOf('space') !== -1 || jc === 'center'
165
+ || jc === 'end' || jc === 'flex-end') return;
166
+ const cr = card.getBoundingClientRect();
167
+ if (cr.height <= 0) return;
168
+ const padB = parseFloat(cs.paddingBottom) || 0;
169
+ const padT = parseFloat(cs.paddingTop) || 0;
170
+ const borderB = parseFloat(cs.borderBottomWidth) || 0;
171
+
172
+ // Is `node` inside an absolutely/fixed-positioned subtree within the
173
+ // card? A corner badge / QR / watermark sits at the card bottom but
174
+ // is NOT the normal-flow content bottom -- counting it would mask a
175
+ // top-packed void above it (false negative). Walk parents to card.
176
+ const inAbs = (node) => {
177
+ let el = node.nodeType === 1 ? node : node.parentElement;
178
+ while (el && el !== card) {
179
+ const pos = window.getComputedStyle(el).position;
180
+ if (pos === 'absolute' || pos === 'fixed') return true;
181
+ el = el.parentElement;
182
+ }
183
+ return false;
184
+ };
185
+
186
+ // Bottom-most rendered CONTENT = max over three sources (each kept
187
+ // via `maxB`, so adding a source can only RAISE the content bottom,
188
+ // never hide a void):
189
+ // (1) TEXT, via Range -- a plain-text tail that wraps onto a line
190
+ // BELOW an inline <span>/<b>/<code> is invisible to an element
191
+ // scan (its parent <p> has element children so it's skipped,
192
+ // and the inline leaf sits on an earlier line) -> undershoot.
193
+ // (2) REPLACED media (img/svg/canvas/...) -- even when it has child
194
+ // nodes (e.g. <svg> wrapping <path>s) and so isn't a leaf.
195
+ // (3) LEAF element boxes (no element children) -- re-covers a pure-
196
+ // CSS diagram node (an empty <div> bar/box) that carries no
197
+ // text and isn't replaced, which (1)+(2) alone would miss.
198
+ // Non-leaf, non-replaced CONTAINERS are skipped: a stretched wrapper
199
+ // box would over-measure to the card bottom and mask the void.
200
+ let maxB = cr.top + padT;
201
+ const bump = (r) => {
202
+ if (r && r.height > 0 && r.bottom > maxB) maxB = r.bottom;
203
+ };
204
+ const walker = document.createTreeWalker(card, NodeFilter.SHOW_TEXT);
205
+ for (let tn = walker.nextNode(); tn; tn = walker.nextNode()) {
206
+ if (!tn.nodeValue || !tn.nodeValue.trim()) continue;
207
+ if (inAbs(tn)) continue;
208
+ const rng = document.createRange();
209
+ rng.selectNodeContents(tn);
210
+ const rects = rng.getClientRects();
211
+ for (let i = 0; i < rects.length; i++) bump(rects[i]);
212
+ }
213
+ const REPLACED = /^(IMG|SVG|CANVAS|VIDEO|IFRAME|HR|OBJECT|EMBED)$/;
214
+ card.querySelectorAll('*').forEach(el => {
215
+ if (inAbs(el)) return;
216
+ // tagName is upper-case for HTML, but case-preserved (lower) for
217
+ // SVG elements -- normalise before the replaced-tag test.
218
+ if (!REPLACED.test(el.tagName.toUpperCase()) && el.children.length) {
219
+ return; // a non-replaced container: skip (only leaves + media)
220
+ }
221
+ bump(el.getBoundingClientRect());
222
+ });
223
+
224
+ cards.push({
225
+ card_index: ci,
226
+ card_h: cr.height,
227
+ trailing_px: (cr.bottom - padB - borderB) - maxB,
228
+ });
229
+ });
230
+
231
+ // ---- 5) <br> as a direct child of a flex container ----
232
+ // A <br> that is an in-flow child of display:flex|inline-flex is
233
+ // blockified into a flex ITEM and stops creating a line break -- so
234
+ // intended multi-line content (e.g. an icon + label stacked with <br>)
235
+ // silently collapses onto one row. `measure` can't see it (card bottom
236
+ // is unchanged); only the eye catches it. Report each offending flex
237
+ // parent once. Even in flex-direction:column the <br> does nothing (the
238
+ // text runs already stack as separate items); row is where it visibly
239
+ // breaks, so we report the direction to make the fix obvious.
240
+ const flexbr = [];
241
+ const seenFlexBr = new Set();
242
+ document.querySelectorAll('br').forEach(br => {
243
+ const parent = br.parentElement;
244
+ if (!parent || seenFlexBr.has(parent)) return;
245
+ const cs = window.getComputedStyle(parent);
246
+ if (cs.display === 'flex' || cs.display === 'inline-flex') {
247
+ seenFlexBr.add(parent);
248
+ flexbr.push({
249
+ tag: parent.tagName.toLowerCase(),
250
+ cls: parent.className || '',
251
+ dir: cs.flexDirection || 'row',
252
+ });
253
+ }
254
+ });
255
+
256
+ return {figures, orphans, cols, cards, flexbr};
257
+ }
258
+ """
259
+
260
+
261
+ def cmd_polish(args: argparse.Namespace) -> int:
262
+ try:
263
+ from playwright.sync_api import sync_playwright
264
+ from playwright.sync_api import TimeoutError as PWTimeoutError
265
+ except ImportError:
266
+ _eprint("ERROR: playwright not installed. Run:")
267
+ _eprint(" python -m pip install playwright")
268
+ _eprint(" python -m playwright install chromium")
269
+ return 2
270
+
271
+ html_path = Path(args.html).resolve()
272
+ if not html_path.exists():
273
+ _eprint(f"ERROR: HTML not found: {ascii_safe(html_path)}")
274
+ return 2
275
+
276
+ # Hard-fail if there's no measurement markup at all. A polish PASS
277
+ # on "0 figures, 0 columns, 0 stat-like elements" would be silent
278
+ # success on a file the tool can't reason about.
279
+ role_counts = _preflight.has_required_roles_in_html(html_path)
280
+ must_have = ("poster", "card", "column")
281
+ missing = [r for r in must_have if role_counts.get(r, 0) == 0]
282
+ if missing:
283
+ _eprint(
284
+ f"ERROR: polish requires data-measure-role markup on the "
285
+ f"poster, columns, and cards. Missing or zero-count: "
286
+ f"{missing}. Either add the roles or use a different tool."
287
+ )
288
+ return 2
289
+
290
+ resolved = _canvas.resolve_canvas(
291
+ html_path, args.canvas, label="[polish]"
292
+ )
293
+ if resolved is None:
294
+ _eprint(
295
+ "ERROR: could not find `@page { size: <W> <H> }` in HTML; "
296
+ "pass `--canvas <W>x<H>in` or `--canvas 'A0 portrait'`."
297
+ )
298
+ return 2
299
+ canvas, viewport = resolved
300
+
301
+ with sync_playwright() as p:
302
+ browser, _ctx, page = _render.open_print_emulated_page(p, viewport)
303
+ nav_timed_out = False
304
+ try:
305
+ page.goto(html_path.as_uri(), wait_until="networkidle",
306
+ timeout=args.mathjax_timeout_ms)
307
+ except PWTimeoutError:
308
+ # Don't raw-traceback on a hung/slow resource. Record it and
309
+ # let settle_page surface a MathJax-specific failure first;
310
+ # otherwise fail-fast below. polish must NOT sample a poster
311
+ # that never finished loading -- a blocked remote image or web
312
+ # font would otherwise sneak through as a false PASS.
313
+ nav_timed_out = True
314
+
315
+ settle = _render.settle_page(
316
+ page,
317
+ mathjax_timeout_ms=args.mathjax_timeout_ms,
318
+ settle_ms=args.settle_ms,
319
+ )
320
+ fail = _render.hard_fail_on_settle_problems(
321
+ settle, mathjax_timeout_ms=args.mathjax_timeout_ms,
322
+ )
323
+ if fail is not None:
324
+ browser.close()
325
+ _eprint(f"FAIL: {fail}")
326
+ return 1
327
+ if nav_timed_out:
328
+ browser.close()
329
+ _eprint(
330
+ "FAIL: page did not reach network-idle within "
331
+ f"{args.mathjax_timeout_ms} ms; refusing to polish a "
332
+ "partially loaded poster. A blocked/slow remote resource "
333
+ "(CDN image, web font, MathJax) is the usual cause -- "
334
+ "inline assets, or raise --mathjax-timeout-ms."
335
+ )
336
+ return 1
337
+
338
+ data = page.evaluate(_POLISH_JS)
339
+ browser.close()
340
+
341
+ warns: list[str] = []
342
+
343
+ # ---- Gate A: figure sizing by AR ----
344
+ for f in data.get("figures", []):
345
+ rw = float(f["rendered_w"])
346
+ cw = float(f["card_w"])
347
+ nw = float(f["natural_w"])
348
+ nh = float(f["natural_h"])
349
+ role = f.get("role", "card")
350
+ src_l = str(f["src"]).lower()
351
+ # A vector image (SVG) can legitimately report zero natural size
352
+ # while rendering fine, so never flag it broken. Match the path
353
+ # extension (after stripping any ?query / #fragment) plus inline
354
+ # SVG data URIs. Imperfect: an SVG behind an extensionless URL
355
+ # still slips through; an `img.decode()`-based JS probe would be
356
+ # exact. Covers both card and hero <img> (see _POLISH_JS).
357
+ src_path = src_l.split("?", 1)[0].split("#", 1)[0]
358
+ is_svg = (
359
+ src_path.endswith((".svg", ".svgz"))
360
+ or src_l.startswith("data:image/svg")
361
+ )
362
+ if (nw <= 0 or nh <= 0) and not is_svg:
363
+ warns.append(
364
+ f"FIG/BROKEN: '{ascii_safe(f['src'])}' has zero natural "
365
+ "size -- the image failed to load (missing file, 404, or "
366
+ "an unreachable remote URL); it will be blank in print."
367
+ )
368
+ continue
369
+ # Hero figures get the broken-image check above, but the AR sizing
370
+ # gates below are framed as "% of card width" and don't apply to
371
+ # the full-bleed hero panel. Skip them.
372
+ if role == "hero":
373
+ continue
374
+ # Author opt-out for a DELIBERATE image-left/text-right card: a
375
+ # wide figure that shares its card width with a meaningful text
376
+ # column is sized below the AR thresholds on purpose. Marking the
377
+ # <img> with `data-fig-layout="beside-text"` records that intent
378
+ # in the markup -- so a later edit (human or agent) reads "this is
379
+ # intentionally beside text" and leaves the layout alone instead
380
+ # of widening the figure to silence the warning. It skips only the
381
+ # AR width gates below; the FIG/BROKEN check above still applies
382
+ # (a blank image is a bug regardless of layout). The gate stays
383
+ # strict on the accidental case: a lone wide figure shrunk into a
384
+ # gray margin has no such attribute and still warns.
385
+ if str(f.get("fig_layout", "")).strip() == "beside-text":
386
+ continue
387
+ if cw <= 0 or rw <= 0 or nw <= 0 or nh <= 0:
388
+ continue
389
+ ar = nw / nh
390
+ ratio = rw / cw
391
+ if ar > 1.3 and ratio < args.wide_min_ratio:
392
+ warns.append(
393
+ f"FIG/WIDE: '{ascii_safe(f['src'])}' (AR={ar:.2f}) at "
394
+ f"{ratio * 100:.0f}% of card width -- wide figures "
395
+ f"should sit >= {args.wide_min_ratio * 100:.0f}%. "
396
+ f"Enlarge, or drop the image-left/text-right wrapper."
397
+ )
398
+ elif ar < 0.8 and ratio > args.tall_max_ratio:
399
+ warns.append(
400
+ f"FIG/TALL: '{ascii_safe(f['src'])}' (AR={ar:.2f}) at "
401
+ f"{ratio * 100:.0f}% of card width -- tall figures "
402
+ f"usually pair better with text-right at 45-60%."
403
+ )
404
+ elif 0.8 <= ar <= 1.3 and ratio < args.square_min_ratio:
405
+ warns.append(
406
+ f"FIG/SQUARE: '{ascii_safe(f['src'])}' (AR={ar:.2f}) at "
407
+ f"{ratio * 100:.0f}% of card width -- square figures "
408
+ f"sit better at {args.square_min_ratio * 100:.0f}-75%."
409
+ )
410
+
411
+ # ---- Gate B: typography orphans ----
412
+ for n in data.get("orphans", []):
413
+ txt: str = n["text"]
414
+ if not txt:
415
+ continue
416
+ last = txt[-1]
417
+ if last not in ORPHAN_GLYPHS:
418
+ continue
419
+ if not re.search(r"\s", txt[:-1]):
420
+ continue
421
+ ws = (n["ws"] or "").lower()
422
+ if "nowrap" in ws or "pre" in ws:
423
+ continue
424
+ warns.append(
425
+ f"ORPHAN: <{ascii_safe(n['tag'])} class='{ascii_safe(n['cls'])}'> "
426
+ f"text '{ascii_safe(txt[:48])}' ends with '{ascii_safe(last)}' "
427
+ f"and may wrap alone. Apply `white-space: nowrap` or use &nbsp; "
428
+ f"before the trailing glyph."
429
+ )
430
+
431
+ # ---- Gate C: space-between fill ----
432
+ for c in data.get("cols", []):
433
+ col_h = float(c["column_h"])
434
+ excess = float(c["max_excess_px"])
435
+ if col_h <= 0:
436
+ continue
437
+ fill = excess / col_h
438
+ if fill > args.max_space_between_fill:
439
+ warns.append(
440
+ f"SPACE-BETWEEN: column {c['column_index']} has a "
441
+ f"{excess:.0f} px inter-card gap "
442
+ f"({fill * 100:.1f}% of column height, stated gap "
443
+ f"{c['stated_gap_px']:.0f} px). Balance via "
444
+ f"meaningful content, not justify-content. See "
445
+ f"Gate C in SKILL.md."
446
+ )
447
+
448
+ # ---- Gate C (one card): trailing whitespace below the last line ----
449
+ for c in data.get("cards", []):
450
+ ch = float(c["card_h"])
451
+ tr = float(c["trailing_px"])
452
+ if ch <= 0 or tr <= 0:
453
+ continue
454
+ ratio = tr / ch
455
+ if ratio > args.max_card_trailing:
456
+ warns.append(
457
+ f"CARD/TRAILING: card {c['card_index']} fills only "
458
+ f"{100 - ratio * 100:.0f}% of its height -- {tr:.0f} px "
459
+ f"({ratio * 100:.0f}%) blank below the last line. A card "
460
+ f"stretched to align (flex:1) but padded with whitespace "
461
+ f"clears the bottom-edge gate yet reads as unfinished. Fill "
462
+ f"with real content, grow a figure, or shrink the canvas. "
463
+ f"See Gate C in SKILL.md."
464
+ )
465
+
466
+ # ---- Gate D: <br> inside a flex container ----
467
+ # A <br> that is a direct child of a flex container is blockified into
468
+ # a flex item and creates NO line break, so intended multi-line text
469
+ # collapses onto one row. Detectable only at render time (getComputed-
470
+ # Style), which is why it lives here and not in preflight's static scan.
471
+ for fb in data.get("flexbr", []):
472
+ cls = str(fb.get("cls", ""))
473
+ cls_attr = f' class="{ascii_safe(cls)}"' if cls else ""
474
+ warns.append(
475
+ f"LAYOUT/FLEX-BR: <{ascii_safe(fb['tag'])}{cls_attr}> is "
476
+ f"display:flex (flex-direction:{fb['dir']}) with a direct <br> "
477
+ f"child -- the <br> is blockified into a flex item and creates "
478
+ f"NO line break, so intended multi-line content collapses onto "
479
+ f"one row. Wrap each line in a <span> and use "
480
+ f"flex-direction:column, or make the wrapper a plain block."
481
+ )
482
+
483
+ print(f"[polish] {ascii_safe(html_path.name)}")
484
+ print(f" figures checked : {len(data.get('figures', []))}")
485
+ print(f" stat-like elements : {len(data.get('orphans', []))}")
486
+ print(f" space-between cols : {len(data.get('cols', []))}")
487
+ print(f" cards checked : {len(data.get('cards', []))}")
488
+ print(f" flex/<br> parents : {len(data.get('flexbr', []))}")
489
+ print(f" warnings : {len(warns)}")
490
+ for w in warns:
491
+ print(f" WARN: {w}")
492
+
493
+ if args.strict and warns:
494
+ _eprint("[polish] FAIL -- --strict and warnings present")
495
+ return 1
496
+ print("[polish] PASS" if not warns
497
+ else "[polish] OK (warnings only)")
498
+ return 0