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,669 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ forensics_gate.py — typed policy gate + append-only obligations ledger for the
4
+ /integrity-forensics launcher (Anti-Autoresearch integration).
5
+
6
+ Doctrine (cross-model design review, 2026-07-12):
7
+
8
+ - Anti-Autoresearch's verdict is preserved VERBATIM and never re-labeled. In
9
+ particular `CLEAN_GIVEN_EVIDENCE` maps to `NO_NEW_BLOCKER` — "no flag found in
10
+ the evidence at hand" — NEVER to PASS/accepted. A forensics sweep can raise
11
+ flags; it cannot acquit anything (flags are computable, acquittals are not).
12
+ - Findings become APPEND-ONLY obligations. A re-run may add obligations; a
13
+ finding that disappears from a later report NEVER auto-closes its obligation
14
+ (an LLM asked to "make the flag go away" learns to reword the span faster
15
+ than to fix the number). A vanished, unresolved obligation is marked
16
+ `UNRESOLVED_DISAPPEARANCE` — deterministically recorded, not an accusation.
17
+ - Closure is an explicit, evidence-bearing act: `resolve` (typed fix + evidence
18
+ file hashed at closure time + who verified) or `waive` (human sign-off; a
19
+ waiver is never a resolution and never rewrites the original finding).
20
+
21
+ Gate policy (fixed):
22
+ upstream HARD_FLAGS → BLOCK
23
+ upstream REVIEW_UNAVAILABLE → BLOCK (an incomplete sweep cannot wave a paper through)
24
+ any OPEN critical obligation → BLOCK
25
+ upstream SOFT_FLAGS → WARN (human disposition)
26
+ any OPEN obligation → WARN
27
+ otherwise → NO_NEW_BLOCKER
28
+
29
+ Pure stdlib. Artifacts:
30
+ <paper>/.aris/forensics/gate.json (one per run; overwritten)
31
+ <paper>/.aris/forensics/obligations.json (append-only ledger; never pruned)
32
+ """
33
+ import argparse
34
+ import contextlib
35
+ import hashlib
36
+ import json
37
+ import os
38
+ import re
39
+ import sys
40
+ import tempfile
41
+ from datetime import datetime, timezone
42
+
43
+ try:
44
+ import fcntl # POSIX
45
+ except ImportError: # pragma: no cover
46
+ fcntl = None
47
+
48
+ GATE_VERSION = "1"
49
+ AUDITOR_FAMILY = "openai" # Anti-AR's reviewer pins are GPT-family by upstream contract
50
+
51
+ BLOCK = "BLOCK"
52
+ WARN = "WARN"
53
+ NO_NEW_BLOCKER = "NO_NEW_BLOCKER"
54
+
55
+ FIX_TYPES = ("corrected-from-results", "claim-narrowed", "claim-withdrawn",
56
+ "citation-replaced")
57
+
58
+ _FAMILY_NEEDLES = [
59
+ ("anthropic", ("claude", "opus", "sonnet", "haiku")),
60
+ ("openai", ("gpt", "codex", "oracle", "chatgpt", "o1", "o3", "o4")),
61
+ ("google", ("gemini", "palm", "bard")),
62
+ ]
63
+
64
+
65
+ def _now():
66
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
67
+
68
+
69
+ def _sha256_file(path):
70
+ h = hashlib.sha256()
71
+ with open(path, "rb") as fh:
72
+ for chunk in iter(lambda: fh.read(65536), b""):
73
+ h.update(chunk)
74
+ return h.hexdigest()
75
+
76
+
77
+ def _norm_ws(s):
78
+ return re.sub(r"\s+", " ", (s or "")).strip()
79
+
80
+
81
+ def _executor_family(name):
82
+ n = (name or "").strip().lower()
83
+ hits = {fam for fam, needles in _FAMILY_NEEDLES if any(x in n for x in needles)}
84
+ return next(iter(hits)) if len(hits) == 1 else "unknown"
85
+
86
+
87
+ def _severity(f):
88
+ return f.get("_severity_final") or f.get("severity") or "info"
89
+
90
+
91
+ def _is_obligation_bearing(f):
92
+ """Weight-1, above-info findings become obligations. Zero-weight tracks
93
+ (AIS/advisory) inform; they never gate."""
94
+ if f.get("_verdict_weight", 1) != 1:
95
+ return False
96
+ return _severity(f) in ("critical", "major", "minor")
97
+
98
+
99
+ def fingerprint(f):
100
+ """Stable identity of a finding ACROSS re-runs. Deliberately excludes:
101
+ finding_id (F001... is positional), claim_id (positional in the ledger),
102
+ and artifact_hash (upstream hashes the WHOLE source file, so any unrelated
103
+ edit to the same .tex would re-identify every finding in it — duplicating
104
+ obligations on honest revisions). Identity = which auditor, which pattern,
105
+ the verbatim evidence spans (whitespace-normalized), and their file paths;
106
+ artifact hashes remain recorded on each observation as provenance."""
107
+ ev = sorted(
108
+ (_norm_ws(e.get("span")),
109
+ os.path.normpath((e.get("location") or {}).get("file") or ""))
110
+ for e in (f.get("evidence") or []) if isinstance(e, dict)
111
+ )
112
+ basis = json.dumps({"skill": f.get("skill"), "pattern_id": f.get("pattern_id"),
113
+ "evidence": ev}, sort_keys=True, ensure_ascii=False)
114
+ return hashlib.sha256(basis.encode("utf-8")).hexdigest()[:24]
115
+
116
+
117
+ def _forensics_dir(paper_dir):
118
+ d = os.path.join(paper_dir, ".aris", "forensics")
119
+ os.makedirs(d, exist_ok=True)
120
+ return d
121
+
122
+
123
+ # The compile-input closure PLUS the compiled deliverable PLUS tabular data
124
+ # sources (pgfplots/tables read .csv/.dat at compile time). Numbers moved
125
+ # into a .sty macro, a regenerated figure, an edited data file, or a rebuilt
126
+ # PDF after the sweep must all read as STALE — the sweep audited none of
127
+ # them. .json is deliberately NOT fingerprinted: the sweep's own report.json
128
+ # (and re-run siblings) live in the paper dir and would self-trip the
129
+ # staleness guard; JSON results feeding the paper are covered by resolution
130
+ # evidence re-hashing instead.
131
+ _FINGERPRINT_EXTS = (".tex", ".bib", ".sty", ".cls", ".bst", ".inc", ".def",
132
+ ".tikz", ".pgf", ".pdf", ".png", ".jpg", ".jpeg", ".eps",
133
+ ".svg", ".csv", ".tsv", ".dat")
134
+
135
+
136
+ def _paper_fingerprint(paper_dir):
137
+ """sha256 over the paper's compile inputs + deliverables (sorted relpaths,
138
+ dot-dirs like .aris/.git excluded, symlinked dirs followed) — binds a gate
139
+ artifact to the content it audited, so a paper edited (or recompiled)
140
+ AFTER a clean sweep reads as STALE (via the `fresh` subcommand) instead of
141
+ inheriting the old gate."""
142
+ entries = []
143
+ for root, dirs, files in os.walk(paper_dir, followlinks=True):
144
+ dirs[:] = sorted(x for x in dirs if not x.startswith("."))
145
+ for fn in files:
146
+ if fn.lower().endswith(_FINGERPRINT_EXTS):
147
+ fp = os.path.join(root, fn)
148
+ entries.append((os.path.relpath(fp, paper_dir), _sha256_file(fp)))
149
+ h = hashlib.sha256()
150
+ for rel, sha in sorted(entries):
151
+ h.update("{}\0{}\n".format(rel, sha).encode("utf-8"))
152
+ return h.hexdigest()
153
+
154
+
155
+ def _assert_report_not_stale(report_path, paper_dir):
156
+ """A report older than any audited paper file was generated BEFORE those
157
+ edits — folding or gating it would bind new text to a sweep that never saw
158
+ it. Refuse (fail closed); re-run the sweep instead. NOTE this is an mtime
159
+ net for the honest resumed-run case, not proof of provenance. That is where
160
+ it stops on purpose: this is a research-workflow gate, not a provenance
161
+ system, and upstream deliberately removed its report fingerprints in 2026-08
162
+ because nothing consumed them."""
163
+ rep_mtime = os.path.getmtime(report_path)
164
+ for root, dirs, files in os.walk(paper_dir, followlinks=True):
165
+ dirs[:] = [x for x in dirs if not x.startswith(".")]
166
+ for fn in files:
167
+ fp = os.path.join(root, fn)
168
+ if not fn.lower().endswith(_FINGERPRINT_EXTS):
169
+ continue
170
+ if os.path.samefile(fp, report_path):
171
+ continue # the report itself may be fingerprinted (.json)
172
+ if os.path.getmtime(fp) > rep_mtime:
173
+ raise SystemExit(f"FATAL: {fp} was modified AFTER the report — "
174
+ "stale report; re-run the sweep")
175
+
176
+
177
+ SEV_RANK = {"minor": 1, "major": 2, "critical": 3}
178
+
179
+
180
+ @contextlib.contextmanager
181
+ def _ledger_lock(paper_dir):
182
+ """One lock around every load→mutate→save transaction — concurrent
183
+ update/resolve/waive must never lose records (append-only is a promise)."""
184
+ lock_path = os.path.join(_forensics_dir(paper_dir), ".ledger.lock")
185
+ fh = open(lock_path, "w")
186
+ try:
187
+ if fcntl is not None:
188
+ fcntl.flock(fh, fcntl.LOCK_EX)
189
+ yield
190
+ finally:
191
+ if fcntl is not None:
192
+ fcntl.flock(fh, fcntl.LOCK_UN)
193
+ fh.close()
194
+
195
+
196
+ def _load_ledger(path):
197
+ if os.path.isfile(path):
198
+ with open(path, encoding="utf-8") as fh:
199
+ data = json.load(fh)
200
+ if not isinstance(data, dict) or not isinstance(data.get("obligations"), list):
201
+ raise SystemExit(f"FATAL: {path} is not an obligations ledger")
202
+ for i, o in enumerate(data["obligations"]):
203
+ if not isinstance(o, dict) or not isinstance(o.get("obligation_id"), str):
204
+ raise SystemExit(f"FATAL: {path} obligations[{i}] is malformed")
205
+ return data
206
+ return {"ledger_version": "1", "obligations": []}
207
+
208
+
209
+ def _save_ledger(path, data):
210
+ fd, tmp = tempfile.mkstemp(dir=os.path.dirname(path), suffix=".tmp")
211
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
212
+ json.dump(data, fh, indent=2, ensure_ascii=False)
213
+ os.replace(tmp, path)
214
+ # EVERY ledger mutation invalidates the standing gate: a gate computed
215
+ # against the previous ledger state must never survive it (`evaluate`
216
+ # rewrites gate.json right after; a crash in between leaves NO gate —
217
+ # which fails closed — rather than a stale pass).
218
+ with contextlib.suppress(FileNotFoundError):
219
+ os.remove(os.path.join(os.path.dirname(path), "gate.json"))
220
+
221
+
222
+ def _load_report_strict(path):
223
+ """A report that cannot be parsed into the expected shape must fail CLOSED —
224
+ never fall through to a permissive gate."""
225
+ try:
226
+ with open(path, encoding="utf-8") as fh:
227
+ report = json.load(fh)
228
+ except Exception as e:
229
+ raise SystemExit(f"FATAL: cannot parse report {path}: {type(e).__name__}")
230
+ if not isinstance(report, dict):
231
+ raise SystemExit(f"FATAL: report {path} is not a JSON object")
232
+ findings = report.get("findings")
233
+ if findings is None:
234
+ findings = []
235
+ if not isinstance(findings, list):
236
+ raise SystemExit(f"FATAL: report {path} has a non-list findings field")
237
+ if not isinstance(report.get("overall_verdict", ""), str):
238
+ raise SystemExit(f"FATAL: report {path} has a non-string overall_verdict")
239
+ # Structural provenance floor: every real Anti-AR report names its
240
+ # adjudicator and carries a coverage map. A bare {verdict, findings} stub
241
+ # is not a report this gate will speak for. (This raises the bar for a
242
+ # skipped-sweep shortcut; it is NOT cryptographic provenance — see the
243
+ # launcher's Trust boundary note.)
244
+ if not (isinstance(report.get("adjudicator"), str) and report["adjudicator"].strip()):
245
+ raise SystemExit(f"FATAL: report {path} names no adjudicator — not an "
246
+ "Anti-AR report; run the pinned sweep")
247
+ if not isinstance(report.get("coverage"), dict):
248
+ raise SystemExit(f"FATAL: report {path} carries no coverage map — not an "
249
+ "Anti-AR report; run the pinned sweep")
250
+ for i, f in enumerate(findings):
251
+ # Schema drift fails CLOSED: a finding this tool cannot classify must
252
+ # stop the gate, not silently lose its obligation (e.g. a critical
253
+ # finding whose _verdict_weight arrives as the STRING "1" would
254
+ # otherwise read as non-weight-1 and never gate).
255
+ if not isinstance(f, dict):
256
+ raise SystemExit(f"FATAL: report {path} findings[{i}] is not an object")
257
+ w = f.get("_verdict_weight", 1)
258
+ if isinstance(w, bool) or not isinstance(w, int) or w not in (0, 1):
259
+ raise SystemExit(f"FATAL: report {path} findings[{i}] has _verdict_weight "
260
+ f"{w!r} (must be 0 or 1) — schema drift; see Pin-bump checklist")
261
+ for key in ("severity", "_severity_final"):
262
+ v = f.get(key)
263
+ if v is not None and v not in ("critical", "major", "minor", "info"):
264
+ raise SystemExit(f"FATAL: report {path} findings[{i}] has unknown "
265
+ f"{key} {v!r} — schema drift; see Pin-bump checklist")
266
+ if f.get("severity") is None and f.get("_severity_final") is None:
267
+ raise SystemExit(f"FATAL: report {path} findings[{i}] carries no severity "
268
+ "at all — schema drift; see Pin-bump checklist")
269
+ report["findings"] = findings
270
+ return report
271
+
272
+
273
+ def cmd_update(args):
274
+ """Fold a fresh report into the ledger. APPEND-ONLY: new findings open new
275
+ obligations; existing ones are untouched except for two escalations that can
276
+ only move TOWARD caution — severity ratchets up to the historical max, and a
277
+ RESOLVED obligation whose finding RECURS re-opens (the fix evidently didn't
278
+ hold; the old resolution is archived, never erased). OPEN obligations whose
279
+ fingerprint is absent from this report gain an UNRESOLVED_DISAPPEARANCE note
280
+ (they stay OPEN — disappearance is not resolution). WAIVED stays closed
281
+ (a human already dispositioned it)."""
282
+ report = _load_report_strict(args.report)
283
+ report_sha = _sha256_file(args.report)
284
+ _assert_report_not_stale(args.report, args.paper_dir)
285
+ path = os.path.join(_forensics_dir(args.paper_dir), "obligations.json")
286
+ ledger = _load_ledger(path)
287
+ by_id = {o["obligation_id"]: o for o in ledger["obligations"]}
288
+
289
+ current = {}
290
+ for f in report["findings"]:
291
+ if _is_obligation_bearing(f):
292
+ current[fingerprint(f)] = f
293
+
294
+ opened = reopened = 0
295
+ for fid, f in current.items():
296
+ if fid in by_id:
297
+ o = by_id[fid]
298
+ o["last_seen_report"] = report_sha
299
+ # a closed disappearance episode is archived, never erased — the
300
+ # appeared→vanished→reappeared trail is itself audit evidence
301
+ gone = o.pop("unresolved_disappearance", None)
302
+ if gone is not None:
303
+ o.setdefault("disappearance_history", []).append(
304
+ {**gone, "reappeared_at": _now(), "reappeared_in_report": report_sha})
305
+ # severity ratchet: minor->critical escalates, never de-escalates
306
+ new_sev, old_sev = _severity(f), o.get("severity", "minor")
307
+ if SEV_RANK.get(new_sev, 0) > SEV_RANK.get(old_sev, 0):
308
+ o["severity"] = new_sev
309
+ o.setdefault("_escalations", []).append(
310
+ {"from": old_sev, "to": new_sev, "at": _now(), "report": report_sha})
311
+ # recurrence after resolution: the fix did not hold — re-open
312
+ if o["status"] == "RESOLVED":
313
+ o.setdefault("previous_resolutions", []).append(o.pop("resolution"))
314
+ o["status"] = "OPEN"
315
+ o["recurred_after_resolution"] = {"at": _now(), "report": report_sha}
316
+ reopened += 1
317
+ continue
318
+ ledger["obligations"].append({
319
+ "obligation_id": fid,
320
+ "status": "OPEN",
321
+ "severity": _severity(f),
322
+ "skill": f.get("skill"),
323
+ "pattern_id": f.get("pattern_id"),
324
+ "title": f.get("title", ""),
325
+ "finding_snapshot": f, # immutable record of the accusation
326
+ "first_seen_report": report_sha,
327
+ "last_seen_report": report_sha,
328
+ "opened_at": _now(),
329
+ })
330
+ opened += 1
331
+ ledger["last_report_sha256"] = report_sha # binds gate to the folded report
332
+
333
+ vanished = 0
334
+ for o in ledger["obligations"]:
335
+ if o["status"] == "OPEN" and o["obligation_id"] not in current \
336
+ and o.get("last_seen_report") != report_sha:
337
+ d = o.get("unresolved_disappearance")
338
+ if d is None:
339
+ o["unresolved_disappearance"] = {
340
+ "noted_at": _now(), "absent_from_report": report_sha,
341
+ "note": "finding no longer detected but obligation was never "
342
+ "resolved with evidence — disappearance is not resolution",
343
+ }
344
+ vanished += 1
345
+ else: # still absent: extend the episode, never overwrite its start
346
+ d["last_absent_report"] = report_sha
347
+ d["last_noted_at"] = _now()
348
+
349
+ _save_ledger(path, ledger)
350
+ # Archive the folded report verbatim: `fresh` re-reads the VERDICT from
351
+ # this sha-verified copy and recomputes the decision, instead of trusting
352
+ # a stored (editable) policy token.
353
+ with open(args.report, "rb") as src:
354
+ blob = src.read()
355
+ fd, tmp = tempfile.mkstemp(dir=_forensics_dir(args.paper_dir), suffix=".tmp")
356
+ with os.fdopen(fd, "wb") as fh:
357
+ fh.write(blob)
358
+ os.replace(tmp, os.path.join(_forensics_dir(args.paper_dir), "last_report.json"))
359
+ print(f"obligations: +{opened} opened, {reopened} re-opened (recurrence), "
360
+ f"{vanished} unresolved-disappearance, "
361
+ f"{sum(1 for o in ledger['obligations'] if o['status'] == 'OPEN')} open total -> {path}")
362
+ return 0
363
+
364
+
365
+ VERIFIED_BY_RE = re.compile(r"(human|checker|cross-family-review):\S.*")
366
+
367
+
368
+ def cmd_resolve(args):
369
+ if args.fix_type not in FIX_TYPES:
370
+ raise SystemExit(f"FATAL: fix_type must be one of {FIX_TYPES}")
371
+ if not os.path.isfile(args.evidence):
372
+ raise SystemExit(f"FATAL: evidence file does not exist: {args.evidence} "
373
+ "(a resolution without evidence is a reworded flag)")
374
+ if not VERIFIED_BY_RE.fullmatch((args.verified_by or "").strip()):
375
+ raise SystemExit("FATAL: --verified-by must be typed provenance — "
376
+ "'human:<name>', 'checker:<tool>', or "
377
+ "'cross-family-review:<thread-id>' (a freehand token is "
378
+ "not a receipt)")
379
+ path = os.path.join(_forensics_dir(args.paper_dir), "obligations.json")
380
+ ledger = _load_ledger(path)
381
+ for o in ledger["obligations"]:
382
+ if o["obligation_id"] == args.obligation_id:
383
+ if o["status"] != "OPEN":
384
+ raise SystemExit(f"FATAL: obligation is {o['status']}, not OPEN")
385
+ o["status"] = "RESOLVED"
386
+ o["resolution"] = {
387
+ "fix_type": args.fix_type,
388
+ # abspath: the receipt is re-verified against this file on
389
+ # every later gate — evidence must be durable and unchanged
390
+ "evidence_path": os.path.abspath(args.evidence),
391
+ "evidence_sha256": _sha256_file(args.evidence),
392
+ "verified_by": args.verified_by.strip(),
393
+ "resolved_at": _now(),
394
+ }
395
+ _save_ledger(path, ledger)
396
+ print(f"resolved {args.obligation_id} ({args.fix_type})")
397
+ return 0
398
+ raise SystemExit(f"FATAL: no obligation {args.obligation_id}")
399
+
400
+
401
+ def cmd_waive(args):
402
+ if not re.fullmatch(r"human:\S.*", (args.approver or "").strip()) \
403
+ or not (args.reason or "").strip():
404
+ raise SystemExit("FATAL: waive requires --approver 'human:<name>' and "
405
+ "--reason. The tool cannot authenticate humanity — the "
406
+ "typed prefix makes a non-human waiver an explicit false "
407
+ "record with a permanent paper trail, not a lazy default.")
408
+ path = os.path.join(_forensics_dir(args.paper_dir), "obligations.json")
409
+ ledger = _load_ledger(path)
410
+ for o in ledger["obligations"]:
411
+ if o["obligation_id"] == args.obligation_id:
412
+ if o["status"] != "OPEN":
413
+ raise SystemExit(f"FATAL: obligation is {o['status']}, not OPEN")
414
+ o["status"] = "WAIVED" # a waiver is NOT a resolution
415
+ o["waiver"] = {"approver": args.approver, "reason": args.reason,
416
+ "waived_at": _now()}
417
+ _save_ledger(path, ledger)
418
+ print(f"waived {args.obligation_id} (approver: {args.approver})")
419
+ return 0
420
+ raise SystemExit(f"FATAL: no obligation {args.obligation_id}")
421
+
422
+
423
+ def _closure_invalid(o):
424
+ """A ledger entry counts as validly closed ONLY with its receipt: RESOLVED
425
+ needs a typed resolution (legal fix_type, hashed evidence, verifier);
426
+ WAIVED needs a human waiver record. A closed status WITHOUT the receipt —
427
+ or any unknown status — is not 'closed', it is malformed: fail toward
428
+ caution (a hand-edited `"status": "RESOLVED"` must not open the gate)."""
429
+ st = o.get("status")
430
+ if st == "OPEN":
431
+ return False
432
+ if st == "RESOLVED":
433
+ r = o.get("resolution")
434
+ if not (isinstance(r, dict) and r.get("fix_type") in FIX_TYPES
435
+ and isinstance(r.get("evidence_sha256"), str)
436
+ and re.fullmatch(r"[0-9a-f]{64}", r["evidence_sha256"])
437
+ and VERIFIED_BY_RE.fullmatch((r.get("verified_by") or "").strip())):
438
+ return True
439
+ # the receipt is re-verified, not remembered: the evidence file must
440
+ # still exist and still hash to what was recorded at closure time
441
+ p = r.get("evidence_path")
442
+ return not (isinstance(p, str) and os.path.isfile(p)
443
+ and _sha256_file(p) == r["evidence_sha256"])
444
+ if st == "WAIVED":
445
+ w = o.get("waiver")
446
+ return not (isinstance(w, dict)
447
+ and re.fullmatch(r"human:\S.*", (w.get("approver") or "").strip())
448
+ and (w.get("reason") or "").strip())
449
+ return True
450
+
451
+
452
+ def _decide(verdict, ledger, report_sha):
453
+ """The ONE deterministic decision function — cmd_gate computes with it and
454
+ cmd_fresh RE-computes with it (a stored policy token is display, never
455
+ authority: editing gate.json's decision field must change nothing)."""
456
+ open_obl = [o for o in ledger["obligations"] if o.get("status") == "OPEN"]
457
+ open_critical = [o for o in open_obl if o.get("severity") == "critical"]
458
+ weird = [o for o in ledger["obligations"] if _closure_invalid(o)]
459
+ # The gate only speaks for a report the ledger has actually folded in.
460
+ # A MISSING ledger (or one that never folded anything) is unbound too —
461
+ # deleting obligations.json must not turn a CLEAN report into exit 0.
462
+ unbound = ledger.get("last_report_sha256") != report_sha
463
+
464
+ if verdict in ("HARD_FLAGS", "REVIEW_UNAVAILABLE") or open_critical or weird or unbound:
465
+ decision = BLOCK
466
+ elif verdict == "SOFT_FLAGS" or open_obl:
467
+ decision = WARN
468
+ elif verdict == "CLEAN_GIVEN_EVIDENCE":
469
+ decision = NO_NEW_BLOCKER
470
+ else:
471
+ decision = BLOCK # unknown verdict token: fail closed
472
+ return decision, open_obl, open_critical, weird, unbound
473
+
474
+
475
+ def cmd_gate(args):
476
+ report = _load_report_strict(args.report)
477
+ verdict = report.get("overall_verdict", "")
478
+ report_sha = _sha256_file(args.report)
479
+ _assert_report_not_stale(args.report, args.paper_dir)
480
+ path = os.path.join(_forensics_dir(args.paper_dir), "obligations.json")
481
+ ledger = _load_ledger(path)
482
+ decision, open_obl, open_critical, weird, unbound = _decide(verdict, ledger, report_sha)
483
+
484
+ exec_family = _executor_family(args.executor_model)
485
+ claims = os.path.join(args.paper_dir, "claims.json")
486
+ # Which dimensions never ran. The upstream verdict folds incompleteness in only
487
+ # when it would otherwise be CLEAN, so a SOFT_FLAGS sweep can silently be a
488
+ # partial one. This gate does not re-decide on it — it says so out loud.
489
+ unavailable_dims = sorted(k for k, v in (report.get("coverage") or {}).items()
490
+ if v == "review_unavailable")
491
+ gate = {
492
+ "gate_version": GATE_VERSION,
493
+ "generated_at": _now(),
494
+ # the upstream verdict, VERBATIM — this gate never re-labels it
495
+ "upstream_verdict": verdict,
496
+ "upstream_adjudicator": report.get("adjudicator", ""),
497
+ "policy_decision": decision,
498
+ "anti_ar_commit": args.anti_ar_commit,
499
+ "report_sha256": report_sha,
500
+ "ledger_bound": not unbound,
501
+ "malformed_ledger_entries": len(weird),
502
+ # binds this gate to the paper text it audited (checked by `fresh`)
503
+ "paper_fingerprint": _paper_fingerprint(args.paper_dir),
504
+ # binds this gate to the exact obligations ledger it judged — an
505
+ # out-of-band edit (e.g. hand-deleting one obligation) reads as
506
+ # LEDGER_DRIFT in `fresh` even when the recomputed decision agrees
507
+ "obligations_sha256": _sha256_file(path) if os.path.isfile(path) else None,
508
+ # upstream's span-anchored claims ledger (informational provenance —
509
+ # honestly labeled: this is NOT the obligations ledger)
510
+ "claims_ledger_sha256": _sha256_file(claims) if os.path.isfile(claims) else None,
511
+ "observability_level": report.get("observability_level"),
512
+ "coverage": report.get("coverage", {}),
513
+ "unavailable_dimensions": unavailable_dims,
514
+ "open_obligations": len(open_obl),
515
+ "open_critical_obligations": len(open_critical),
516
+ # honest provenance: the sweep's auditors are GPT-family. For a Claude
517
+ # executor that is cross-family PROPOSAL provenance; for a Codex executor
518
+ # it is same-family. Either way this gate only raises flags — it records
519
+ # provenance, it does not (cannot) grant acceptance.
520
+ "executor_model": args.executor_model,
521
+ "proposal_provenance": ("cross-family" if exec_family not in (AUDITOR_FAMILY, "unknown")
522
+ else ("same-family" if exec_family == AUDITOR_FAMILY
523
+ else "unknown")),
524
+ }
525
+ out = os.path.join(_forensics_dir(args.paper_dir), "gate.json")
526
+ fd, tmp = tempfile.mkstemp(dir=os.path.dirname(out), suffix=".tmp")
527
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
528
+ json.dump(gate, fh, indent=2, ensure_ascii=False)
529
+ os.replace(tmp, out)
530
+ never_ran = (f"; never ran: {', '.join(unavailable_dims)}" if unavailable_dims else "")
531
+ print(f"forensics gate: {decision} (upstream: {verdict or '(missing)'}; "
532
+ f"open obligations: {len(open_obl)}, critical: {len(open_critical)}"
533
+ f"{never_ran}) -> {out}")
534
+ return 0 if decision != BLOCK else 1
535
+
536
+
537
+ def cmd_fresh(args):
538
+ """The downstream preflight, in one exit code. Exit 0 ⟺ a gate exists AND
539
+ it speaks for the CURRENT paper content AND for the CURRENT obligations
540
+ ledger AND its decision is pass-capable (WARN / NO_NEW_BLOCKER). Missing
541
+ gate, post-gate edits, an unbound ledger, BLOCK, or any unknown policy
542
+ token → exit 1: a resumed or post-edit run must re-sweep + `evaluate`,
543
+ never inherit an old (or hand-crafted) pass."""
544
+ out = os.path.join(_forensics_dir(args.paper_dir), "gate.json")
545
+ if not os.path.isfile(out):
546
+ print("forensics fresh: NO_GATE — gate.json missing; run `evaluate` first")
547
+ return 1
548
+ with open(out, encoding="utf-8") as fh:
549
+ gate = json.load(fh)
550
+ if gate.get("gate_version") != GATE_VERSION:
551
+ print(f"forensics fresh: VERSION_MISMATCH — gate is v{gate.get('gate_version')!r}, "
552
+ f"tool is v{GATE_VERSION}; re-run `evaluate`")
553
+ return 1
554
+ if args.anti_ar_commit and gate.get("anti_ar_commit") != args.anti_ar_commit:
555
+ print(f"forensics fresh: PIN_MISMATCH — gate was produced at pin "
556
+ f"{gate.get('anti_ar_commit')!r}, launcher pins {args.anti_ar_commit!r}; "
557
+ "findings from an older adjudicator must be re-audited (pin-bump rule)")
558
+ return 1
559
+ if gate.get("paper_fingerprint") != _paper_fingerprint(args.paper_dir):
560
+ print("forensics fresh: STALE — paper sources/deliverables changed after "
561
+ "the gate; re-run the sweep + `evaluate`")
562
+ return 1
563
+ ledger_path = os.path.join(_forensics_dir(args.paper_dir), "obligations.json")
564
+ current_ledger_sha = _sha256_file(ledger_path) if os.path.isfile(ledger_path) else None
565
+ if gate.get("obligations_sha256") is None \
566
+ or gate.get("obligations_sha256") != current_ledger_sha:
567
+ print("forensics fresh: LEDGER_DRIFT — obligations.json changed after the "
568
+ "gate (even an edit that keeps the same decision); re-run `evaluate`")
569
+ return 1
570
+ ledger = _load_ledger(ledger_path)
571
+ if gate.get("report_sha256") is None \
572
+ or gate.get("report_sha256") != ledger.get("last_report_sha256"):
573
+ print("forensics fresh: UNBOUND — gate does not match the current "
574
+ "obligations ledger; re-run `evaluate`")
575
+ return 1
576
+ # Re-derive the decision from the sha-verified ARCHIVED report + the live
577
+ # ledger — never from the gate's stored token (a one-field edit of
578
+ # gate.json from BLOCK to WARN must change nothing).
579
+ archived = os.path.join(_forensics_dir(args.paper_dir), "last_report.json")
580
+ if not os.path.isfile(archived) \
581
+ or _sha256_file(archived) != ledger.get("last_report_sha256"):
582
+ print("forensics fresh: NO_ARCHIVE — the folded report is missing or does "
583
+ "not hash to the ledger's binding; re-run `evaluate`")
584
+ return 1
585
+ verdict = _load_report_strict(archived).get("overall_verdict", "")
586
+ decision, _open_obl, _crit, _weird, _unbound = _decide(
587
+ verdict, ledger, gate["report_sha256"])
588
+ if decision != gate.get("policy_decision"):
589
+ print(f"forensics fresh: MISMATCH — gate.json says "
590
+ f"{gate.get('policy_decision')!r} but the recomputed decision is "
591
+ f"{decision!r}; the gate artifact does not reproduce — re-run `evaluate`")
592
+ return 1
593
+ if decision not in (WARN, NO_NEW_BLOCKER):
594
+ print(f"forensics fresh: {decision} — not pass-capable; the gate is closed")
595
+ return 1
596
+ stale_dims = gate.get("unavailable_dimensions", [])
597
+ never_ran = (f"; never ran: {', '.join(stale_dims)}" if stale_dims else "")
598
+ print(f"forensics fresh: OK ({decision}; recomputed from the archived report "
599
+ f"and current ledger{never_ran})")
600
+ return 0
601
+
602
+
603
+ def main(argv=None):
604
+ ap = argparse.ArgumentParser(description="Typed gate + obligations for /integrity-forensics.")
605
+ sub = ap.add_subparsers(dest="cmd", required=True)
606
+
607
+ g = sub.add_parser("gate", help="compute the policy decision from an Anti-AR report")
608
+ g.add_argument("--report", required=True)
609
+ g.add_argument("--paper-dir", required=True)
610
+ g.add_argument("--anti-ar-commit", required=True,
611
+ help="the SHA-pin the launcher ran (provenance)")
612
+ g.add_argument("--executor-model", default="claude",
613
+ help="the pipeline's executor, for honest provenance labeling")
614
+
615
+ u = sub.add_parser("update", help="fold a report's findings into the append-only ledger")
616
+ u.add_argument("--report", required=True)
617
+ u.add_argument("--paper-dir", required=True)
618
+
619
+ e = sub.add_parser("evaluate", help="atomic update + gate in one transaction (preferred)")
620
+ e.add_argument("--report", required=True)
621
+ e.add_argument("--paper-dir", required=True)
622
+ e.add_argument("--anti-ar-commit", required=True)
623
+ e.add_argument("--executor-model", default="claude")
624
+
625
+ r = sub.add_parser("resolve", help="close ONE obligation with typed, hashed evidence")
626
+ r.add_argument("--paper-dir", required=True)
627
+ r.add_argument("--obligation-id", required=True)
628
+ r.add_argument("--fix-type", required=True)
629
+ r.add_argument("--evidence", required=True)
630
+ r.add_argument("--verified-by", required=True)
631
+
632
+ w = sub.add_parser("waive", help="human-approved waiver (never a resolution)")
633
+ w.add_argument("--paper-dir", required=True)
634
+ w.add_argument("--obligation-id", required=True)
635
+ w.add_argument("--approver", required=True)
636
+ w.add_argument("--reason", required=True)
637
+
638
+ f = sub.add_parser("fresh", help="verify gate.json still matches the current paper text")
639
+ f.add_argument("--paper-dir", required=True)
640
+ f.add_argument("--anti-ar-commit", default=None,
641
+ help="when given, the gate must have been produced at this pin")
642
+
643
+ a = ap.parse_args(argv)
644
+ if a.cmd == "gate":
645
+ # gate does not mutate the ledger, but it must read it and write
646
+ # gate.json under the same lock — otherwise a concurrent `evaluate`
647
+ # could land a BLOCK gate that this older read then overwrites
648
+ with _ledger_lock(a.paper_dir):
649
+ return cmd_gate(a)
650
+ if a.cmd == "fresh":
651
+ # same lock as the mutators: a concurrent resolve/waive/update must
652
+ # not interleave between fresh's reads (gate, ledger, archive)
653
+ with _ledger_lock(a.paper_dir):
654
+ return cmd_fresh(a)
655
+ if a.cmd == "evaluate":
656
+ # one lock across update AND gate — the emitted gate.json provably
657
+ # describes the ledger state this same transaction produced
658
+ with _ledger_lock(a.paper_dir):
659
+ rc = cmd_update(a)
660
+ if rc != 0:
661
+ return rc
662
+ return cmd_gate(a)
663
+ with _ledger_lock(a.paper_dir):
664
+ return {"update": cmd_update, "resolve": cmd_resolve,
665
+ "waive": cmd_waive}[a.cmd](a)
666
+
667
+
668
+ if __name__ == "__main__":
669
+ sys.exit(main())