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,818 @@
1
+ #!/usr/bin/env python3
2
+ """Bind an ARIS review to Copilot CLI's native rubber-duck subagent.
3
+
4
+ Copilot persists machine-readable session events for the root model, tool call,
5
+ subagent lifecycle, resolved reviewer model, and tool result. This helper turns
6
+ that host-owned event chain into a small evidence artifact. It deliberately
7
+ does not accept executor/reviewer model names from the caller.
8
+
9
+ The protocol prevents a recent, unrelated Copilot session from being
10
+ mistaken for the current one:
11
+
12
+ 1. ``marker`` runs as one short root tool call and emits a unique caller token.
13
+ 2. After that call returns (and Copilot has persisted it), ``challenge`` binds
14
+ the marker event and records the host-reported executor model.
15
+ 3. The caller includes the returned nonce in a native ``rubber-duck`` task.
16
+ ``verify`` then requires a successful, completed child subagent with the
17
+ same tool-call id, extracts its host-reported model and raw result, and
18
+ rejects same/unknown-family pairs.
19
+
20
+ The resulting evidence is revalidated by ``review_gate.py`` before a native
21
+ positive verdict is allowed to stop the loop.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ from dataclasses import dataclass
28
+ from datetime import datetime, timezone
29
+ import hashlib
30
+ import json
31
+ import os
32
+ from pathlib import Path
33
+ import re
34
+ import secrets
35
+ import stat
36
+ import sys
37
+ from typing import Any, Iterable
38
+
39
+
40
+ CHALLENGE_SCHEMA = "aris.copilot-native.challenge/v1"
41
+ EVIDENCE_SCHEMA = "aris.copilot-native.evidence/v1"
42
+ NONCE_PREFIX = "ARIS_REVIEW_NONCE="
43
+ MAX_EVENT_LOG_BYTES = 256 * 1024 * 1024
44
+ KNOWN_FAMILIES = {"openai", "anthropic", "google"}
45
+
46
+
47
+ class EvidenceError(RuntimeError):
48
+ """A deterministic evidence failure with a stable process exit code."""
49
+
50
+ def __init__(self, message: str, *, code: int = 2) -> None:
51
+ super().__init__(message)
52
+ self.code = code
53
+
54
+
55
+ class PolicyRejected(EvidenceError):
56
+ """The host event chain is real, but it violates reviewer policy."""
57
+
58
+ def __init__(self, message: str) -> None:
59
+ super().__init__(message, code=10)
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class EventRecord:
64
+ event: dict[str, Any]
65
+ end_offset: int
66
+
67
+
68
+ def derive_model_family(model: str) -> str:
69
+ """Derive a provider family from an exact model id, failing closed."""
70
+
71
+ name = (model or "").strip().lower()
72
+ families: set[str] = set()
73
+ if re.search(r"(^|[^a-z0-9])(gpt|chatgpt|codex|oracle|o1|o3|o4)([^a-z0-9]|$)", name):
74
+ families.add("openai")
75
+ if re.search(r"(^|[^a-z0-9])(claude|sonnet|opus|haiku|anthropic)([^a-z0-9]|$)", name):
76
+ families.add("anthropic")
77
+ if re.search(r"(^|[^a-z0-9])(gemini|google)([^a-z0-9]|$)", name):
78
+ families.add("google")
79
+ return next(iter(families)) if len(families) == 1 else "unknown"
80
+
81
+
82
+ def _sha256(data: bytes) -> str:
83
+ return hashlib.sha256(data).hexdigest()
84
+
85
+
86
+ def _canonical_json(data: dict[str, Any]) -> bytes:
87
+ return json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
88
+
89
+
90
+ def _parse_timestamp(value: Any) -> datetime:
91
+ if not isinstance(value, str) or not value:
92
+ raise EvidenceError("event is missing an ISO-8601 timestamp")
93
+ try:
94
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
95
+ except ValueError as exc:
96
+ raise EvidenceError(f"invalid event timestamp: {value}") from exc
97
+ if parsed.tzinfo is None:
98
+ parsed = parsed.replace(tzinfo=timezone.utc)
99
+ return parsed.astimezone(timezone.utc)
100
+
101
+
102
+ def _safe_regular_file(path: Path) -> None:
103
+ try:
104
+ info = path.lstat()
105
+ except FileNotFoundError as exc:
106
+ raise EvidenceError(f"file not found: {path}") from exc
107
+ if stat.S_ISLNK(info.st_mode) or not stat.S_ISREG(info.st_mode):
108
+ raise EvidenceError(f"expected a non-symlink regular file: {path}")
109
+ if hasattr(os, "getuid") and info.st_uid != os.getuid():
110
+ raise EvidenceError(f"refusing event file owned by another user: {path}")
111
+
112
+
113
+ def _read_event_log(path: Path) -> tuple[bytes, list[EventRecord]]:
114
+ _safe_regular_file(path)
115
+ size = path.stat().st_size
116
+ if size > MAX_EVENT_LOG_BYTES:
117
+ raise EvidenceError(f"Copilot event log exceeds {MAX_EVENT_LOG_BYTES} bytes: {path}")
118
+ raw = path.read_bytes()
119
+ records: list[EventRecord] = []
120
+ offset = 0
121
+ for line_number, line in enumerate(raw.splitlines(keepends=True), start=1):
122
+ offset += len(line)
123
+ payload = line.rstrip(b"\r\n")
124
+ if not payload:
125
+ continue
126
+ try:
127
+ event = json.loads(payload)
128
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
129
+ raise EvidenceError(f"malformed Copilot event JSON at {path}:{line_number}") from exc
130
+ if not isinstance(event, dict):
131
+ raise EvidenceError(f"non-object Copilot event at {path}:{line_number}")
132
+ records.append(EventRecord(event=event, end_offset=offset))
133
+ return raw, records
134
+
135
+
136
+ def _session_metadata(records: Iterable[EventRecord]) -> tuple[str, str]:
137
+ starts = [record.event for record in records if record.event.get("type") == "session.start"]
138
+ if len(starts) != 1:
139
+ raise EvidenceError(f"expected exactly one session.start event, found {len(starts)}")
140
+ data = starts[0].get("data") or {}
141
+ context = data.get("context") or {}
142
+ session_id = data.get("sessionId")
143
+ cwd = context.get("cwd")
144
+ if not isinstance(session_id, str) or not session_id:
145
+ raise EvidenceError("session.start is missing sessionId")
146
+ if not isinstance(cwd, str) or not cwd:
147
+ raise EvidenceError("session.start is missing context.cwd")
148
+ return session_id, str(Path(cwd).resolve())
149
+
150
+
151
+ def _event_arguments_text(event: dict[str, Any]) -> str:
152
+ data = event.get("data") or {}
153
+ try:
154
+ return json.dumps(data.get("arguments") or {}, sort_keys=True, ensure_ascii=False)
155
+ except (TypeError, ValueError):
156
+ return ""
157
+
158
+
159
+ def _tool_result_text(event: dict[str, Any]) -> str:
160
+ data = event.get("data") or {}
161
+ result = data.get("result") or {}
162
+ if not isinstance(result, dict):
163
+ return ""
164
+ parts = [
165
+ value
166
+ for value in (result.get("content"), result.get("detailedContent"))
167
+ if isinstance(value, str)
168
+ ]
169
+ return "\n".join(parts)
170
+
171
+
172
+ def _is_root_event(event: dict[str, Any]) -> bool:
173
+ return not event.get("agentId")
174
+
175
+
176
+ def _atomic_write(path: Path, payload: bytes, *, replace: bool = False) -> None:
177
+ path = path.resolve()
178
+ path.parent.mkdir(parents=True, exist_ok=True)
179
+ if path.exists() and not replace:
180
+ raise EvidenceError(f"refusing to overwrite existing artifact: {path}")
181
+ temp = path.with_name(f".{path.name}.{os.getpid()}.{secrets.token_hex(6)}.tmp")
182
+ flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
183
+ descriptor = os.open(temp, flags, 0o600)
184
+ try:
185
+ with os.fdopen(descriptor, "wb") as handle:
186
+ handle.write(payload)
187
+ handle.flush()
188
+ os.fsync(handle.fileno())
189
+ if path.exists() and not replace:
190
+ raise EvidenceError(f"refusing to overwrite existing artifact: {path}")
191
+ os.replace(temp, path)
192
+ finally:
193
+ try:
194
+ temp.unlink()
195
+ except FileNotFoundError:
196
+ pass
197
+
198
+
199
+ def _write_json(path: Path, data: dict[str, Any], *, replace: bool = False) -> None:
200
+ _atomic_write(path, json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False).encode() + b"\n", replace=replace)
201
+
202
+
203
+ def _load_json(path: Path) -> dict[str, Any]:
204
+ _safe_regular_file(path)
205
+ try:
206
+ value = json.loads(path.read_text(encoding="utf-8"))
207
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
208
+ raise EvidenceError(f"invalid JSON artifact: {path}") from exc
209
+ if not isinstance(value, dict):
210
+ raise EvidenceError(f"JSON artifact must be an object: {path}")
211
+ return value
212
+
213
+
214
+ def _iter_session_logs(root: Path) -> Iterable[Path]:
215
+ if not root.is_dir():
216
+ return []
217
+ return sorted(root.glob("*/events.jsonl"))
218
+
219
+
220
+ def create_challenge(
221
+ *,
222
+ output: Path,
223
+ binding: str,
224
+ cwd: Path,
225
+ session_root: Path,
226
+ max_age_seconds: int,
227
+ replace: bool = False,
228
+ ) -> dict[str, Any]:
229
+ """Bind this helper invocation to one current root Copilot session."""
230
+
231
+ if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.:-]{15,127}", binding):
232
+ raise EvidenceError("--binding must be a 16..128 character stable token")
233
+ canonical_cwd = str(cwd.resolve())
234
+ now = datetime.now(timezone.utc)
235
+ candidates: list[tuple[Path, bytes, EventRecord, EventRecord, str, str]] = []
236
+
237
+ for event_path in _iter_session_logs(session_root.resolve()):
238
+ try:
239
+ raw, records = _read_event_log(event_path)
240
+ session_id, session_cwd = _session_metadata(records)
241
+ except EvidenceError:
242
+ continue
243
+ if session_cwd != canonical_cwd:
244
+ continue
245
+ for record in records:
246
+ event = record.event
247
+ if event.get("type") != "tool.execution_start" or not _is_root_event(event):
248
+ continue
249
+ data = event.get("data") or {}
250
+ if str(data.get("toolName") or "").lower() not in {"bash", "shell", "powershell"}:
251
+ continue
252
+ model = data.get("model")
253
+ if not isinstance(model, str) or not model.strip():
254
+ continue
255
+ age = (now - _parse_timestamp(event.get("timestamp"))).total_seconds()
256
+ if age < -5 or age > max_age_seconds:
257
+ continue
258
+ argument_text = _event_arguments_text(event)
259
+ if binding not in argument_text or "marker" not in argument_text:
260
+ continue
261
+ tool_call_id = data.get("toolCallId")
262
+ marker_json = json.dumps(
263
+ {"schema": CHALLENGE_SCHEMA, "status": "marker", "binding": binding},
264
+ sort_keys=True,
265
+ ensure_ascii=False,
266
+ )
267
+ completions = [
268
+ candidate
269
+ for candidate in records
270
+ if candidate.event.get("type") == "tool.execution_complete"
271
+ and _is_root_event(candidate.event)
272
+ and (candidate.event.get("data") or {}).get("toolCallId") == tool_call_id
273
+ and (candidate.event.get("data") or {}).get("success") is True
274
+ and marker_json in _tool_result_text(candidate.event)
275
+ ]
276
+ if len(completions) != 1 or completions[0].end_offset <= record.end_offset:
277
+ continue
278
+ candidates.append(
279
+ (event_path.resolve(), raw, record, completions[0], session_id, model.strip())
280
+ )
281
+
282
+ if not candidates:
283
+ raise EvidenceError(
284
+ "binding marker is not present in a current Copilot root session event",
285
+ code=3,
286
+ )
287
+ if len(candidates) != 1:
288
+ raise EvidenceError(
289
+ f"ambiguous Copilot session binding: found {len(candidates)} matching events",
290
+ code=4,
291
+ )
292
+
293
+ event_path, raw, record, completion, session_id, executor_model = candidates[0]
294
+ executor_family = derive_model_family(executor_model)
295
+ if executor_family not in KNOWN_FAMILIES:
296
+ raise EvidenceError(f"host-reported executor model has unknown family: {executor_model}", code=5)
297
+ event = record.event
298
+ tool_call_id = (event.get("data") or {}).get("toolCallId")
299
+ event_id = event.get("id")
300
+ if not isinstance(tool_call_id, str) or not tool_call_id:
301
+ raise EvidenceError("challenge tool event is missing toolCallId")
302
+ if not isinstance(event_id, str) or not event_id:
303
+ raise EvidenceError("challenge tool event is missing id")
304
+
305
+ completion_id = completion.event.get("id")
306
+ if not isinstance(completion_id, str) or not completion_id:
307
+ raise EvidenceError("challenge marker completion is missing id")
308
+ prefix = raw[: completion.end_offset]
309
+ challenge = {
310
+ "schema": CHALLENGE_SCHEMA,
311
+ "status": "bound",
312
+ "binding": binding,
313
+ "nonce": secrets.token_urlsafe(24),
314
+ "issued_at": now.isoformat().replace("+00:00", "Z"),
315
+ "cwd": canonical_cwd,
316
+ "session_id": session_id,
317
+ "session_events": str(event_path),
318
+ "challenge_event_id": event_id,
319
+ "challenge_completion_event_id": completion_id,
320
+ "challenge_tool_call_id": tool_call_id,
321
+ "challenge_event_timestamp": event.get("timestamp"),
322
+ "executor_model": executor_model,
323
+ "executor_family": executor_family,
324
+ "event_prefix_bytes": len(prefix),
325
+ "event_prefix_sha256": _sha256(prefix),
326
+ }
327
+ _write_json(output, challenge, replace=replace)
328
+ return challenge
329
+
330
+
331
+ def _require_string(data: dict[str, Any], key: str) -> str:
332
+ value = data.get(key)
333
+ if not isinstance(value, str) or not value:
334
+ raise EvidenceError(f"artifact is missing non-empty string field: {key}")
335
+ return value
336
+
337
+
338
+ def _validate_challenge_prefix(challenge: dict[str, Any]) -> tuple[Path, bytes, list[EventRecord]]:
339
+ if challenge.get("schema") != CHALLENGE_SCHEMA or challenge.get("status") != "bound":
340
+ raise EvidenceError("unsupported or unbound Copilot challenge")
341
+ event_path = Path(_require_string(challenge, "session_events")).resolve()
342
+ raw, records = _read_event_log(event_path)
343
+ prefix_length = challenge.get("event_prefix_bytes")
344
+ if not isinstance(prefix_length, int) or prefix_length <= 0 or prefix_length > len(raw):
345
+ raise EvidenceError("challenge event prefix length is invalid")
346
+ if _sha256(raw[:prefix_length]) != _require_string(challenge, "event_prefix_sha256"):
347
+ raise EvidenceError("Copilot challenge event prefix changed after binding")
348
+ session_id, session_cwd = _session_metadata(records)
349
+ if session_id != _require_string(challenge, "session_id"):
350
+ raise EvidenceError("challenge session id does not match its event log")
351
+ if session_cwd != str(Path(_require_string(challenge, "cwd")).resolve()):
352
+ raise EvidenceError("challenge cwd does not match its event log")
353
+
354
+ matches = [
355
+ record
356
+ for record in records
357
+ if record.end_offset <= prefix_length
358
+ and record.event.get("id") == challenge.get("challenge_event_id")
359
+ and record.event.get("type") == "tool.execution_start"
360
+ ]
361
+ if len(matches) != 1:
362
+ raise EvidenceError("challenge root tool event is missing or duplicated")
363
+ event = matches[0].event
364
+ data = event.get("data") or {}
365
+ if not _is_root_event(event):
366
+ raise EvidenceError("challenge was not issued by the root Copilot agent")
367
+ if data.get("toolCallId") != challenge.get("challenge_tool_call_id"):
368
+ raise EvidenceError("challenge tool-call id mismatch")
369
+ if data.get("model") != challenge.get("executor_model"):
370
+ raise EvidenceError("challenge executor model mismatch")
371
+ if _require_string(challenge, "binding") not in _event_arguments_text(event):
372
+ raise EvidenceError("challenge binding token is absent from the host tool event")
373
+ completion_matches = [
374
+ record
375
+ for record in records
376
+ if record.end_offset <= prefix_length
377
+ and record.event.get("id") == challenge.get("challenge_completion_event_id")
378
+ and record.event.get("type") == "tool.execution_complete"
379
+ ]
380
+ if len(completion_matches) != 1:
381
+ raise EvidenceError("challenge marker completion is missing or duplicated")
382
+ completion = completion_matches[0]
383
+ completion_data = completion.event.get("data") or {}
384
+ expected_marker = json.dumps(
385
+ {
386
+ "schema": CHALLENGE_SCHEMA,
387
+ "status": "marker",
388
+ "binding": _require_string(challenge, "binding"),
389
+ },
390
+ sort_keys=True,
391
+ ensure_ascii=False,
392
+ )
393
+ if (
394
+ not _is_root_event(completion.event)
395
+ or completion_data.get("toolCallId") != challenge.get("challenge_tool_call_id")
396
+ or completion_data.get("success") is not True
397
+ or expected_marker not in _tool_result_text(completion.event)
398
+ or completion.end_offset <= matches[0].end_offset
399
+ ):
400
+ raise EvidenceError("challenge marker completion does not prove a successful marker call")
401
+ return event_path, raw, records
402
+
403
+
404
+ def validate_challenge_artifact(path: Path) -> dict[str, Any]:
405
+ """Revalidate a bound challenge before selecting an external fallback."""
406
+
407
+ challenge = _load_json(path.resolve())
408
+ _validate_challenge_prefix(challenge)
409
+ executor_model = _require_string(challenge, "executor_model")
410
+ executor_family = derive_model_family(executor_model)
411
+ if executor_family not in KNOWN_FAMILIES:
412
+ raise EvidenceError(
413
+ f"host-reported executor model has unknown family: {executor_model}"
414
+ )
415
+ if challenge.get("executor_family") != executor_family:
416
+ raise EvidenceError("challenge executor family does not match its host model")
417
+ return challenge
418
+
419
+
420
+ def _native_prompt(arguments: dict[str, Any]) -> str:
421
+ for key in ("prompt", "query", "input"):
422
+ value = arguments.get(key)
423
+ if isinstance(value, str):
424
+ return value
425
+ return ""
426
+
427
+
428
+ def _is_rubber_duck_invocation(event: dict[str, Any], nonce: str) -> bool:
429
+ if event.get("type") != "tool.execution_start" or not _is_root_event(event):
430
+ return False
431
+ data = event.get("data") or {}
432
+ arguments = data.get("arguments") or {}
433
+ if not isinstance(arguments, dict):
434
+ return False
435
+ tool_name = str(data.get("toolName") or "").lower().replace("_", "-")
436
+ agent_name = arguments.get("agent_type", arguments.get("agentType"))
437
+ native_agent = tool_name == "task" and agent_name == "rubber-duck"
438
+ native_tool = tool_name in {"rubber-duck", "rubberduck"}
439
+ if not (native_agent or native_tool):
440
+ return False
441
+ marker = re.compile(rf"(?m)^{re.escape(NONCE_PREFIX + nonce)}\s*$")
442
+ return marker.search(_native_prompt(arguments)) is not None
443
+
444
+
445
+ def _extract_response(result: Any) -> str:
446
+ if not isinstance(result, dict):
447
+ raise EvidenceError("native reviewer tool result is not an object")
448
+ content = result.get("content")
449
+ detailed = result.get("detailedContent")
450
+ if not isinstance(content, str) or not content.strip():
451
+ raise EvidenceError("native reviewer tool result has no text content")
452
+ if (
453
+ isinstance(detailed, str)
454
+ and len(detailed) >= len(content)
455
+ and "(Full response provided to agent)" not in detailed
456
+ ):
457
+ return detailed
458
+ return content
459
+
460
+
461
+ def _evidence_identity(data: dict[str, Any]) -> str:
462
+ fields = {
463
+ "session_id": data.get("session_id"),
464
+ "challenge_event_id": data.get("challenge_event_id"),
465
+ "tool_call_id": data.get("tool_call_id"),
466
+ "executor_model": data.get("executor_model"),
467
+ "reviewer_model": data.get("reviewer_model"),
468
+ "response_sha256": data.get("response_sha256"),
469
+ "event_prefix_sha256": data.get("event_prefix_sha256"),
470
+ }
471
+ return f"cne_{_sha256(_canonical_json(fields))[:32]}"
472
+
473
+
474
+ def verify_native_review(
475
+ *,
476
+ challenge_path: Path,
477
+ output: Path,
478
+ response_output: Path,
479
+ replace: bool = False,
480
+ ) -> dict[str, Any]:
481
+ """Verify and extract one native rubber-duck review from host events."""
482
+
483
+ challenge = _load_json(challenge_path.resolve())
484
+ event_path, raw, records = _validate_challenge_prefix(challenge)
485
+ nonce = _require_string(challenge, "nonce")
486
+ challenge_time = _parse_timestamp(challenge.get("challenge_event_timestamp"))
487
+
488
+ invocations = [
489
+ record
490
+ for record in records
491
+ if _parse_timestamp(record.event.get("timestamp")) >= challenge_time
492
+ and _is_rubber_duck_invocation(record.event, nonce)
493
+ ]
494
+ if len(invocations) != 1:
495
+ raise EvidenceError(
496
+ f"expected exactly one nonce-bound native rubber-duck invocation, found {len(invocations)}",
497
+ code=6,
498
+ )
499
+ invocation = invocations[0]
500
+ challenge_prefix_length = challenge.get("event_prefix_bytes")
501
+ if not isinstance(challenge_prefix_length, int) or invocation.end_offset <= challenge_prefix_length:
502
+ raise EvidenceError("native rubber-duck invocation does not follow the bound challenge")
503
+ invocation_data = invocation.event.get("data") or {}
504
+ tool_call_id = invocation_data.get("toolCallId")
505
+ if not isinstance(tool_call_id, str) or not tool_call_id:
506
+ raise EvidenceError("native rubber-duck invocation is missing toolCallId")
507
+
508
+ executor_model = invocation_data.get("model")
509
+ if not isinstance(executor_model, str) or not executor_model:
510
+ raise EvidenceError("native invocation is missing host-reported executor model")
511
+ if executor_model != challenge.get("executor_model"):
512
+ raise EvidenceError("executor model changed between challenge and native review")
513
+
514
+ starts = [
515
+ record
516
+ for record in records
517
+ if record.event.get("type") == "subagent.started"
518
+ and (record.event.get("data") or {}).get("toolCallId") == tool_call_id
519
+ ]
520
+ completions = [
521
+ record
522
+ for record in records
523
+ if record.event.get("type") == "subagent.completed"
524
+ and (record.event.get("data") or {}).get("toolCallId") == tool_call_id
525
+ ]
526
+ failures = [
527
+ record
528
+ for record in records
529
+ if record.event.get("type") == "subagent.failed"
530
+ and (record.event.get("data") or {}).get("toolCallId") == tool_call_id
531
+ ]
532
+ tool_completions = [
533
+ record
534
+ for record in records
535
+ if record.event.get("type") == "tool.execution_complete"
536
+ and (record.event.get("data") or {}).get("toolCallId") == tool_call_id
537
+ ]
538
+ if failures:
539
+ raise EvidenceError("native rubber-duck subagent failed", code=7)
540
+ if (
541
+ len(tool_completions) == 1
542
+ and (tool_completions[0].event.get("data") or {}).get("success") is not True
543
+ ):
544
+ raise EvidenceError("native rubber-duck tool execution did not succeed", code=7)
545
+ if len(starts) != 1 or len(completions) != 1 or len(tool_completions) != 1:
546
+ raise EvidenceError(
547
+ "native rubber-duck lifecycle is incomplete or ambiguous "
548
+ f"(started={len(starts)}, completed={len(completions)}, tool_complete={len(tool_completions)})",
549
+ code=7,
550
+ )
551
+ start_data = starts[0].event.get("data") or {}
552
+ completion_data = completions[0].event.get("data") or {}
553
+ tool_completion_data = tool_completions[0].event.get("data") or {}
554
+ if start_data.get("agentName") != "rubber-duck" or completion_data.get("agentName") != "rubber-duck":
555
+ raise EvidenceError("nonce-bound task was not completed by the rubber-duck subagent")
556
+ if starts[0].event.get("agentId") != tool_call_id or completions[0].event.get("agentId") != tool_call_id:
557
+ raise EvidenceError("rubber-duck lifecycle is not bound to its child agent id")
558
+ if not (
559
+ invocation.end_offset < starts[0].end_offset
560
+ <= completions[0].end_offset
561
+ < tool_completions[0].end_offset
562
+ ):
563
+ raise EvidenceError("native rubber-duck lifecycle events are out of order")
564
+ completion_executor_model = tool_completion_data.get("model")
565
+ if isinstance(completion_executor_model, str) and completion_executor_model != executor_model:
566
+ raise EvidenceError("root executor model changed before native tool completion")
567
+
568
+ start_model = start_data.get("model")
569
+ reviewer_model = completion_data.get("model") or start_model
570
+ if not isinstance(reviewer_model, str) or not reviewer_model:
571
+ raise EvidenceError("native reviewer completion is missing its resolved model")
572
+ if isinstance(start_model, str) and start_model and start_model != reviewer_model:
573
+ raise EvidenceError("native reviewer model changed within one subagent lifecycle")
574
+
575
+ response = _extract_response(tool_completion_data.get("result"))
576
+ response_bytes = response.encode("utf-8")
577
+ _atomic_write(response_output, response_bytes, replace=replace)
578
+
579
+ executor_family = derive_model_family(executor_model)
580
+ reviewer_family = derive_model_family(reviewer_model)
581
+ if executor_family not in KNOWN_FAMILIES or reviewer_family not in KNOWN_FAMILIES:
582
+ status = "rejected"
583
+ reason = "unknown-model-family"
584
+ elif executor_family == reviewer_family:
585
+ status = "rejected"
586
+ reason = "same-model-family"
587
+ else:
588
+ status = "verified"
589
+ reason = "host-event-verified-cross-family"
590
+
591
+ final_offset = max(invocation.end_offset, starts[0].end_offset, completions[0].end_offset, tool_completions[0].end_offset)
592
+ prefix = raw[:final_offset]
593
+ evidence: dict[str, Any] = {
594
+ "schema": EVIDENCE_SCHEMA,
595
+ "status": status,
596
+ "reason": reason,
597
+ "verified_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
598
+ "binding": challenge.get("binding"),
599
+ "nonce": nonce,
600
+ "cwd": challenge.get("cwd"),
601
+ "session_id": challenge.get("session_id"),
602
+ "session_events": str(event_path),
603
+ "challenge_event_id": challenge.get("challenge_event_id"),
604
+ "challenge_completion_event_id": challenge.get("challenge_completion_event_id"),
605
+ "challenge_tool_call_id": challenge.get("challenge_tool_call_id"),
606
+ "tool_call_id": tool_call_id,
607
+ "agent_name": "rubber-duck",
608
+ "executor_model": executor_model,
609
+ "executor_model_source": "host-session-event",
610
+ "executor_family": executor_family,
611
+ "reviewer_model": reviewer_model,
612
+ "reviewer_model_source": "host-session-event",
613
+ "reviewer_family": reviewer_family,
614
+ "family_relation": "different" if executor_family != reviewer_family and "unknown" not in {executor_family, reviewer_family} else "same" if executor_family == reviewer_family and executor_family != "unknown" else "unknown",
615
+ "independence_verified": status == "verified",
616
+ "response_path": str(response_output.resolve()),
617
+ "response_sha256": _sha256(response_bytes),
618
+ "event_prefix_bytes": len(prefix),
619
+ "event_prefix_sha256": _sha256(prefix),
620
+ }
621
+ evidence["evidence_id"] = _evidence_identity(evidence)
622
+ _write_json(output, evidence, replace=replace)
623
+ if status != "verified":
624
+ raise PolicyRejected(f"native reviewer evidence rejected: {reason}")
625
+ return evidence
626
+
627
+
628
+ def validate_evidence_artifact(path: Path) -> dict[str, Any]:
629
+ """Revalidate a verified evidence artifact against its event-log prefix."""
630
+
631
+ evidence = _load_json(path.resolve())
632
+ if evidence.get("schema") != EVIDENCE_SCHEMA:
633
+ raise EvidenceError("unsupported Copilot native evidence schema")
634
+ if evidence.get("status") != "verified" or evidence.get("independence_verified") is not True:
635
+ raise EvidenceError("Copilot native evidence is not verified")
636
+ if evidence.get("agent_name") != "rubber-duck":
637
+ raise EvidenceError("evidence agent is not rubber-duck")
638
+
639
+ event_path = Path(_require_string(evidence, "session_events")).resolve()
640
+ raw, records = _read_event_log(event_path)
641
+ prefix_length = evidence.get("event_prefix_bytes")
642
+ if not isinstance(prefix_length, int) or prefix_length <= 0 or prefix_length > len(raw):
643
+ raise EvidenceError("evidence event prefix length is invalid")
644
+ prefix = raw[:prefix_length]
645
+ if _sha256(prefix) != _require_string(evidence, "event_prefix_sha256"):
646
+ raise EvidenceError("Copilot evidence event prefix was modified")
647
+
648
+ session_id, session_cwd = _session_metadata(records)
649
+ if session_id != evidence.get("session_id") or session_cwd != str(Path(_require_string(evidence, "cwd")).resolve()):
650
+ raise EvidenceError("evidence session metadata mismatch")
651
+ tool_call_id = _require_string(evidence, "tool_call_id")
652
+ nonce = _require_string(evidence, "nonce")
653
+ challenge_matches = [
654
+ record
655
+ for record in records
656
+ if record.end_offset <= prefix_length
657
+ and record.event.get("id") == evidence.get("challenge_event_id")
658
+ and record.event.get("type") == "tool.execution_start"
659
+ ]
660
+ challenge_completions = [
661
+ record
662
+ for record in records
663
+ if record.end_offset <= prefix_length
664
+ and record.event.get("id") == evidence.get("challenge_completion_event_id")
665
+ and record.event.get("type") == "tool.execution_complete"
666
+ ]
667
+ if len(challenge_matches) != 1 or len(challenge_completions) != 1:
668
+ raise EvidenceError("evidence challenge marker lifecycle is missing or duplicated")
669
+ challenge_data = challenge_matches[0].event.get("data") or {}
670
+ challenge_completion_data = challenge_completions[0].event.get("data") or {}
671
+ expected_marker = json.dumps(
672
+ {
673
+ "schema": CHALLENGE_SCHEMA,
674
+ "status": "marker",
675
+ "binding": _require_string(evidence, "binding"),
676
+ },
677
+ sort_keys=True,
678
+ ensure_ascii=False,
679
+ )
680
+ if (
681
+ not _is_root_event(challenge_matches[0].event)
682
+ or not _is_root_event(challenge_completions[0].event)
683
+ or challenge_data.get("toolCallId") != evidence.get("challenge_tool_call_id")
684
+ or challenge_completion_data.get("toolCallId") != evidence.get("challenge_tool_call_id")
685
+ or challenge_completion_data.get("success") is not True
686
+ or expected_marker not in _tool_result_text(challenge_completions[0].event)
687
+ or challenge_data.get("model") != evidence.get("executor_model")
688
+ or challenge_completions[0].end_offset <= challenge_matches[0].end_offset
689
+ ):
690
+ raise EvidenceError("evidence challenge marker lifecycle is invalid")
691
+
692
+ prefix_records = [record for record in records if record.end_offset <= prefix_length]
693
+ invocations = [record for record in prefix_records if _is_rubber_duck_invocation(record.event, nonce)]
694
+ invocations = [record for record in invocations if (record.event.get("data") or {}).get("toolCallId") == tool_call_id]
695
+ starts = [record for record in prefix_records if record.event.get("type") == "subagent.started" and (record.event.get("data") or {}).get("toolCallId") == tool_call_id]
696
+ completions = [record for record in prefix_records if record.event.get("type") == "subagent.completed" and (record.event.get("data") or {}).get("toolCallId") == tool_call_id]
697
+ failures = [record for record in prefix_records if record.event.get("type") == "subagent.failed" and (record.event.get("data") or {}).get("toolCallId") == tool_call_id]
698
+ tool_completions = [record for record in prefix_records if record.event.get("type") == "tool.execution_complete" and (record.event.get("data") or {}).get("toolCallId") == tool_call_id]
699
+ if not (len(invocations) == len(starts) == len(completions) == len(tool_completions) == 1):
700
+ raise EvidenceError("evidence lifecycle events are missing or duplicated")
701
+ if failures:
702
+ raise EvidenceError("evidence contains a failed native reviewer lifecycle")
703
+ start_data = starts[0].event.get("data") or {}
704
+ completion_data = completions[0].event.get("data") or {}
705
+ tool_data = tool_completions[0].event.get("data") or {}
706
+ if start_data.get("agentName") != "rubber-duck" or completion_data.get("agentName") != "rubber-duck":
707
+ raise EvidenceError("evidence lifecycle agent mismatch")
708
+ if starts[0].event.get("agentId") != tool_call_id or completions[0].event.get("agentId") != tool_call_id:
709
+ raise EvidenceError("evidence child agent id mismatch")
710
+ if not (
711
+ challenge_completions[0].end_offset < invocations[0].end_offset
712
+ < starts[0].end_offset
713
+ <= completions[0].end_offset
714
+ < tool_completions[0].end_offset
715
+ ):
716
+ raise EvidenceError("evidence lifecycle events are out of order")
717
+ if not tool_data.get("success"):
718
+ raise EvidenceError("evidence tool completion is not successful")
719
+ executor_model = (invocations[0].event.get("data") or {}).get("model")
720
+ reviewer_model = completion_data.get("model") or start_data.get("model")
721
+ if executor_model != evidence.get("executor_model") or reviewer_model != evidence.get("reviewer_model"):
722
+ raise EvidenceError("evidence model ids differ from host lifecycle events")
723
+ if start_data.get("model") not in {None, "", reviewer_model}:
724
+ raise EvidenceError("evidence reviewer model changed within its lifecycle")
725
+ if tool_data.get("model") not in {None, "", executor_model}:
726
+ raise EvidenceError("evidence executor model changed before tool completion")
727
+ executor_family = derive_model_family(str(executor_model or ""))
728
+ reviewer_family = derive_model_family(str(reviewer_model or ""))
729
+ if executor_family not in KNOWN_FAMILIES or reviewer_family not in KNOWN_FAMILIES or executor_family == reviewer_family:
730
+ raise EvidenceError("evidence no longer establishes a known cross-family pair")
731
+ if evidence.get("executor_family") != executor_family or evidence.get("reviewer_family") != reviewer_family:
732
+ raise EvidenceError("evidence family fields do not match its model ids")
733
+ if evidence.get("family_relation") != "different":
734
+ raise EvidenceError("evidence family relation is not different")
735
+ if evidence.get("executor_model_source") != "host-session-event" or evidence.get("reviewer_model_source") != "host-session-event":
736
+ raise EvidenceError("evidence model sources are not host session events")
737
+
738
+ response = _extract_response(tool_data.get("result"))
739
+ response_path = Path(_require_string(evidence, "response_path")).resolve()
740
+ _safe_regular_file(response_path)
741
+ response_bytes = response_path.read_bytes()
742
+ if response_bytes != response.encode("utf-8") or _sha256(response_bytes) != evidence.get("response_sha256"):
743
+ raise EvidenceError("native reviewer response artifact does not match the host tool result")
744
+ if _evidence_identity(evidence) != evidence.get("evidence_id"):
745
+ raise EvidenceError("native evidence id does not match its bound fields")
746
+ return evidence
747
+
748
+
749
+ def build_parser() -> argparse.ArgumentParser:
750
+ parser = argparse.ArgumentParser(description=__doc__)
751
+ subparsers = parser.add_subparsers(dest="command", required=True)
752
+
753
+ marker = subparsers.add_parser("marker", help="emit a binding marker in its own root tool call")
754
+ marker.add_argument("--binding", required=True)
755
+
756
+ challenge = subparsers.add_parser("challenge", help="bind the current Copilot root session")
757
+ challenge.add_argument("--output", type=Path, required=True)
758
+ challenge.add_argument("--binding", required=True)
759
+ challenge.add_argument("--cwd", type=Path, default=Path.cwd())
760
+ challenge.add_argument("--session-root", type=Path, default=Path.home() / ".copilot" / "session-state")
761
+ challenge.add_argument("--max-age-seconds", type=int, default=60)
762
+ challenge.add_argument("--replace", action="store_true")
763
+
764
+ verify = subparsers.add_parser("verify", help="verify one completed rubber-duck subagent call")
765
+ verify.add_argument("--challenge", type=Path, required=True)
766
+ verify.add_argument("--output", type=Path, required=True)
767
+ verify.add_argument("--response-output", type=Path, required=True)
768
+ verify.add_argument("--replace", action="store_true")
769
+
770
+ validate_challenge = subparsers.add_parser(
771
+ "validate-challenge",
772
+ help="revalidate a challenge before an opposite-family fallback",
773
+ )
774
+ validate_challenge.add_argument("--challenge", type=Path, required=True)
775
+
776
+ validate = subparsers.add_parser("validate", help="revalidate an existing evidence artifact")
777
+ validate.add_argument("--evidence", type=Path, required=True)
778
+ return parser
779
+
780
+
781
+ def main() -> int:
782
+ args = build_parser().parse_args()
783
+ try:
784
+ if args.command == "marker":
785
+ if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.:-]{15,127}", args.binding):
786
+ raise EvidenceError("--binding must be a 16..128 character stable token")
787
+ result = {"schema": CHALLENGE_SCHEMA, "status": "marker", "binding": args.binding}
788
+ elif args.command == "challenge":
789
+ if args.max_age_seconds < 1 or args.max_age_seconds > 600:
790
+ raise EvidenceError("--max-age-seconds must be within 1..600")
791
+ result = create_challenge(
792
+ output=args.output,
793
+ binding=args.binding,
794
+ cwd=args.cwd,
795
+ session_root=args.session_root,
796
+ max_age_seconds=args.max_age_seconds,
797
+ replace=args.replace,
798
+ )
799
+ elif args.command == "verify":
800
+ result = verify_native_review(
801
+ challenge_path=args.challenge,
802
+ output=args.output,
803
+ response_output=args.response_output,
804
+ replace=args.replace,
805
+ )
806
+ elif args.command == "validate-challenge":
807
+ result = validate_challenge_artifact(args.challenge)
808
+ else:
809
+ result = validate_evidence_artifact(args.evidence)
810
+ print(json.dumps(result, sort_keys=True, ensure_ascii=False))
811
+ return 0
812
+ except EvidenceError as exc:
813
+ print(json.dumps({"status": "error", "error": str(exc), "exit_code": exc.code}), file=sys.stderr)
814
+ return exc.code
815
+
816
+
817
+ if __name__ == "__main__":
818
+ raise SystemExit(main())